# 業務ロジック — Owner.getPet() [11 LOC]

| 項目 | 値 |
|-------|-------|
| 完全修飾名 | `org.springframework.samples.petclinic.owner.Owner` |
| レイヤー | Service（パッケージ: `org.springframework.samples.petclinic.owner`） |
| モジュール | `owner` (パッケージ: `org.springframework.samples.petclinic.owner`) |

## 1. 役割

### Owner.getPet()

本メソッドは、飼い主（Owner）が保持しているペット一覧の中から、指定された名前に一致するペットを一件検索して返す業務ロジックである。名前の比較は **大文字小文字を区別しない（case-insensitive）** 方式で行われるため、ユーザーがどのケースで入力しても確実に検索できる。さらに `ignoreNew` パラメータによって、まだデータベースに保存されていない仮のペット（新規作成中）を結果から除外するかどうかを制御できる。この振る舞いは、ペット登録フォームや編集画面において、実存的なペットを正しく特定するために必要不可欠である。

本メソッドは検索ルーティング／ディスパッチのパターンを採用しており、単一のパラメータペア（名前と新規無視フラグ）で複数の条件分岐を駆使して最適なペットを決定する。システムの larger context において、コントローラ層（`PetController`、`VisitController`）から頻繁に呼び出される共有ユーティリティメソッドとして機能し、画面からのリクエストが特定のペットに対応するビジットや更新処理へとルーティングされるための基盤となっている。

条件分岐としては、名前が null または不一致の場合は探索を継続し、一致した場合は `ignoreNew` が false またはペットが新規でない場合にそのペットを返す。すべてのペットを探索しても該当しなかった場合は null を返す。

## 2. 処理パターン（詳細業務ロジック）

```mermaid
flowchart TD
    START(["getPet(name, ignoreNew)"])
    STEP1["for each pet in getPets()"]
    STEP2["compName = pet.getName()"]
    COND1{"compName != null && compName.equalsIgnoreCase(name)"}
    COND2{"!ignoreNew || !pet.isNew()"}
    RETURN_PET(["return pet"])
    RETURN_NULL(["return null"])

    START --> STEP1
    STEP1 --> STEP2
    STEP2 --> COND1
    COND1 -->|true| COND2
    COND1 -->|false| STEP1
    COND2 -->|true| RETURN_PET
    COND2 -->|false| STEP1
    RETURN_PET --> END(["End"])
    STEP1 --> END
    END --> RETURN_NULL
```

**条件ノードの説明:**

- `COND1`: ペット名が null でなく、かつ指定された名前と大文字小文字を区別せずに一致する。一致しない場合は `STEP1`（次のペットへ）に戻り、探索を継続する。
- `COND2`: `ignoreNew` が false（新規ペットも含める）、またはペットが新規でない（`isNew() == false`、つまりデータベースに保存済み）。新規ペットを除外するべきかつ対象ペットが新規の場合、探索を継続する。

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|----------------------|
| 1 | `name` | `String` | 検索対象のペットの名前。ユーザーが入力したペット名に対応し、大文字小文字を区別せずに比較される。null の場合、一致するペットは存在しないものとして扱う。 |
| 2 | `ignoreNew` | `boolean` | 未保存（新規作成中）のペットを結果から除外するかどうかのフラグ。`true` の場合、まだデータベースに保存されていないペット（`isNew() == true`）は検索対象から外される。`false` の場合、新規ペットも含めて検索される。主にペット登録・編集フォームで利用される。 |
| 3 | `this.pets` | `Collection<Pet>` | インスタンスフィールド。この Owner が保持しているすべてのペットのコレクション。`getPets()` メソッドを通じて参照される。探索対象となるデータソース。 |

**呼び出されるメソッドの解説:**

- `Owner.getPets()`: この Owner に紐づくすべてのペットのコレクションを返す。探索のループ対象となる。
- `Pet.getName()` (`NamedEntity.getName()` から継承): 個々のペットの名前文字列を返す。比較対象となる。
- `Pet.isNew()` (`BaseEntity.isNew()` から継承): ペットがデータベースに保存済みかどうかを判定する。`id == null` の場合、つまり主キーが未割り当ての場合は `true` を返す（新規未保存）。

## 4. CRUD操作／呼び出しサービス

### コード解析グラフからの事前抽出エビデンス:

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `Owner.getPets` | Owner | - | この Owner が保持するすべてのペットのコレクションを取得 |
| R | `pet.getName` | NamedEntity | - | ペットの名前を取得して比較用に確保 |
| R | `pet.isNew` | BaseEntity | - | ペットが新規未保存かどうかを判定 |
| R | `String.equalsIgnoreCase` | Java String | - | 指定された名前と大文字小文字を区別せずに比較 |

**分類基準:**

- **R (Read)**: 本メソッドは読み取り専用であり、ペット一覧の走査と名前によるフィルタリングのみを行う。データベースへの書き込み（Create/Update/Delete）は行わない。
- 永続化層（JPA）では `pets` テーブルが `Pet` エンティティマッピングの対象となるが、本メソッド自体は既存のペットコレクションに対してインメモリで走査を行うのみである。

## 5. 依存関係トレース

### コード解析グラフからの事前抽出エビデンス:

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|----------------------------------|-------------------------------|
| 1 | Controller:PetController | `PetController.findPet` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 2 | Controller:PetController | `PetController.processCreationForm` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 3 | Controller:PetController | `PetController.processUpdateForm` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 4 | Controller:PetController | `PetController.updatePetDetails` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 5 | Controller:VisitController | `VisitController.loadPetWithVisit` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 6 | Controller:VisitController | `VisitController.processNewVisitForm` -> `Owner.addVisit` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 7 | Test:OwnerControllerTests | `OwnerControllerTests.setup` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 8 | Test:ClinicServiceTests | `ClinicServiceTests.shouldAddNewVisitForPet` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 9 | Test:ClinicServiceTests | `ClinicServiceTests.shouldFindVisitsByPetId` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 10 | Test:ClinicServiceTests | `ClinicServiceTests.shouldInsertPetIntoDatabaseAndGenerateId` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |
| 11 | Test:ClinicServiceTests | `ClinicServiceTests.shouldUpdatePetName` -> `Owner.getPet` | `isNew` [-], `getName` [R], `getPets` [R] |

本メソッドは主に Web アプリケーションのコントローラ層から呼び出され、ペット管理機能の中核を担っている。`PetController` ではペット検索・登録・編集・詳細更新のすべてのフローで本メソッドが使用される。`VisitController` ではペットにビジット（診察履歴）を紐づける処理でも使用されている。

## 6. 分岐ごとの詳細ブロック

**ブロック 1** — [FOR] `for (Pet pet : getPets())` (L136)

> Owner が保持するすべてのペットを順に走査し、条件に合致するペットを探すループ処理。

| # | 種別 | コード |
|---|------|------|
| 1 | CALL | `getPets()` — Owner が保持するペットコレクションを取得 |
| 2 | SET | `pet` — ループ変数として現在のペットを代入 |

**ブロック 1.1** — [IF] `compName != null && compName.equalsIgnoreCase(name)` (L137-L142)

> ペット名が null でなく、かつ指定された名前と大文字小文字を区別せずに一致する場合に処理を行う分岐。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `compName = pet.getName()` — ペットの名前を NamedEntity から取得 [L137] |
| 2 | SET | `name` — 引数：検索対象のペット名（大文字小文字区別なし） [L138] |
| 3 | CALL | `pet.getName()` — 現在のペットの名前を取得 |
| 4 | EXEC | `compName.equalsIgnoreCase(name)` — 大文字小文字を区別せずに文字列比較 |

**ブロック 1.1.1** — [IF] `!ignoreNew || !pet.isNew()` (L139-L141)

> ignoreNew が false（新規ペットも含める）、またはペットが新規でない（データベース保存済み）場合に、該当ペットを返す。新規ペットを除外する対象である場合は、ループを継続して次のペットへ進む。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `ignoreNew` — 引数：新規ペットを無視するフラグ [L139] |
| 2 | SET | `pet.isNew()` — BaseEntity.isNew() から継承：id が null ならば true（新規未保存） |
| 3 | RETURN | `return pet` — 条件に合致したペットを返す [L140] |

**ブロック 1.2** — [ELSE] 名前が不一致または null の場合 (L135-L143)

> 名前が一致しない、または null の場合はループを継続し、次のペットを探索する。すべてのペットを探索し尽くした後も一致しなかった場合は null を返す。

| # | 種別 | コード |
|---|------|------|
| 1 | EXEC | ループ継続（次の Pet へ進む） |

**ブロック 2** — [RETURN] 該当なしの場合 (L143)

> すべてのペットを探索したが条件に合致するペットが見つからなかった場合、null を返す。

| # | 種別 | コード |
|---|------|------|
| 1 | RETURN | `return null` — 該当する名前のペットが存在しないことを示す |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|------------------|
| `Pet` | Entity | ペット（動物）を表すドメインエンティティ。`NamedEntity` を継承し、名前・生年月日・種類・診察履歴（Visits）を保持する。JPA テーブル `pets` にマッピングされる。 |
| `Owner` | Entity | 飼い主を表すドメインエンティティ。ペットの一覧を管理し、各ペットの名前検索を行う。`NamedEntity` を継承する。 |
| `NamedEntity` | Entity | 名前プロパティを持つエンティティの基本クラス。`BaseEntity` を継承し、`name` フィールドと `getName()` / `setName()` メソッドを提供する。 |
| `BaseEntity` | Entity | 主キー（`id`）と新規判定（`isNew()`）を持つエンティティの基本クラス。`id == null` の場合に新規未保存と判定する。 |
| `isNew()` | Method | ペットがデータベースに保存済みかどうかを判定する。`id == null` の場合 `true` を返す（新規未保存）。 |
| `ignoreNew` | Parameter | 未保存のペットを除外するフラグ。フォーム処理で実在するペットのみを対象にする場合に `true` が設定される。 |
| `getPets()` | Method | この Owner が保持するすべてのペットのコレクションを返す。 |
| `equalsIgnoreCase` | Method | 大文字小文字を区別せずに文字列が等しいかを判定する Java String メソッド。 |
| `pets` (DB) | Table | JPA 永続化ターゲットのデータベーステーブル名。`Pet` エンティティがマッピングされる。 |
| `type_id` (DB) | Column | `pets` テーブルの外部キー。`pet_types` テーブルを参照し、ペットの種類（犬・猫・鳥など）を特定する。 |
| `visits` (DB) | Association | `pets` テーブルと `visits` テーブルの 1 対多関係。各ペットの診察履歴を保持する。 |
