---
title: "PetController.processCreationForm() — 詳細設計書"
class: "org.springframework.samples.petclinic.owner.PetController"
method: "processCreationForm"
fqn: "org.springframework.samples.petclinic.owner.PetController.processCreationForm"
file: "src/main/java/org/springframework/samples/petclinic/owner/PetController.java"
lines: "106–127"
loc: 22
tags:
  - DD
  - petclinic
  - Owner
  - Pet
  - CRUD
  - Spring MVC
---

# 業務ロジック — PetController.processCreationForm() [22 LOC]

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

## 1. 役割

### PetController.processCreationForm()

`processCreationForm` メソッドは、ペットクリニック管理システムにおける「新しいペット情報登録」の受付を担う HTTP エントリポイントである。このメソッドは `/owners/{ownerId}/pets/new` に対する POST リクエストに応答し、クライアントから送信されたペット登録フォームのデータを受領して、バリデーション・重複チェック・永続化を一連の処理として実行する。

業務的に説明すると、特定の飼い主（`Owner`）に対して、新しいペット（`Pet`）の情報を登録するプロセスを遂行する。具体的には、（1）ペット名が既存のペットと重複していないかをチェックし、（2）生年月日が未来の日付になっていないかを検証し、（3）バリデーションに成功した場合のみ、ペットオブジェクトを飼い主のペット一覧に追加してデータベースに永続化する、という三段構えの処理フローを踏む。

このメソッドは Spring MVC の `@PostMapping` アノテーションによって HTTP エンドポイントとして公開されており、クライアントからのフォーム送信を直接受ける画面終端のコントローラーである。また、バリデーション結果に応じて、同じフォーム画面への戻り値（`VIEWS_PETS_CREATE_OR_UPDATE_FORM`）または飼い主詳細画面へのリダイレクト（`redirect:/owners/{ownerId}`）を返すことで、エラー表示と成功画面の遷移を一元制御するデザインパターンを採用している。

エラー時は `BindingResult` を通じてフィールドレベルのエラーメッセージを付与し、成功時は `RedirectAttributes` によってフラッシュ属性として成功メッセージ（`"New Pet has been Added"`）を付与することで、リダイレクト先画面でユーザーにフィードバックを提供する。

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

```mermaid
flowchart TD
    START([\"POST /pets/new\"])

    START --> COND1{\"petに名前があり<br/>pet isNew() かつ<br/>owner.getPet 重複あり\"}

    COND1 -- true --> DUP[\"result.rejectValue: 重複エラー\"]
    COND1 -- false --> CHECK_BD

    DUP --> CHECK_BD{\"pet.birthDate が今日より未来?\"}
    CHECK_BD -- true --> BD_ERR[\"result.rejectValue: 生年月日エラー\"]
    CHECK_BD -- false --> CHECK_RESULT

    BD_ERR --> CHECK_RESULT{\"result.hasErrors?\"}
    CHECK_RESULT -- true --> RETURN_FORM[\"return VIEWS_PETS_CREATE_OR_UPDATE_FORM\"]
    CHECK_RESULT -- false --> ADD_PET[\"owner.addPet(pet)\"]

    ADD_PET --> SAVE_OWNER[\"owners.save(owner)\"]
    SAVE_OWNER --> FLASH_MSG[\"redirectAttributes.addFlashAttribute: メッセージ\"]
    FLASH_MSG --> REDIRECT[\"return redirect:/owners/{ownerId}\"]
    REDIRECT --> END_NODE([\"End\"])

    RETURN_FORM --> END_NODE
```

**処理フロー概要:**

1. **ペット名重複チェック** — ペットの名称が空ではなく、かつ新規登録（`isNew()` が `true`）であり、かつ当該飼い主名下に同名の既存ペットが存在する場合、フィールドエラーとして重複エラーを付与する。
2. **生年月日バリデーション** — ペットの生年月日が設定されており、かつ今日の日付より未来である場合、フィールドエラーとして型 mismatch エラーを付与する。
3. **結果判定と分岐** — バリデーションエラーが存在する場合は登録フォーム画面に戻し、エラーがなければペット登録処理へ進む。
4. **ペット登録・保存** — ペットを飼い主のペット一覧に追加し、`OwnerRepository` を通じて `owners` テーブルへの保存を実行する。
5. **成功メッセージとリダイレクト** — 成功メッセージをフラッシュ属性として設定後、飼い主詳細画面へリダイレクトする。

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|---------------------|
| 1 | `owner` | `Owner` | 登録対象のペットを所有する飼い主の情報。`@ModelAttribute` アノテーションによって、`/owners/{ownerId}` パスから `findOwner()` メソッド経由で取得された `Owner` エンティティがバインドされる。飼い主は `owners` テーブルにマッピングされた `id`、`firstName`、`lastName`、`address`、`city`、`telephone` の各属性を持つ。 |
| 2 | `pet` | `@Valid Pet` | クライアントから送信されたペット登録フォームのデータ。`@Valid` アノテーションにより、`PetValidator` によるバリデーションが適用される。`Pet` エンティティは `pets` テーブルにマッピングされ、`name`（ペット名）、`birthDate`（生年月日）、`type`（ペットの種類）の属性を含む。`isNew()` が `true` の場合は未登録（ID なし）の新規ペットとして扱う。 |
| 3 | `result` | `BindingResult` | バリデーション結果を保持するオブジェクト。`pet` 引数の直後に配置される Spring MVC の規約に従い、`@Valid` によるバリデーションおよび明示的なチェック（重複チェック、生年月日チェック）で発生したエラーが `rejectValue` 経由で蓄積される。`hasErrors()` でエラーの有無を判定する。 |
| 4 | `redirectAttributes` | `RedirectAttributes` | リダイレクト先へ一時的にデータを渡すためのオブジェクト。`addFlashAttribute` により、リダイレクト後のリクエストでのみ参照可能なフラッシュ属性として成功メッセージ（`"New Pet has been Added"`）を設定する。 |

**外部状態（インスタンスメソッド経由で読み込まれる状態）:**

| 状態 | 種別 | 業務的説明 |
|------|------|------------------|
| `owners` | フィールド（`OwnerRepository`） | 飼い主エンティティの永続化を担う Spring Data JPA リポジトリ。`save()` によって `owners` テーブルへの INSERT/UPDATE を実行する。 |
| `VIEWS_PETS_CREATE_OR_UPDATE_FORM` | 定数（`String`） | フォーム画面のビュー名。値は `"pets/createOrUpdatePetForm"`。バリデーションエラー時にこのビューを返す。 |

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

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

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `StringUtils.hasText` | Spring Util | - | `pet.getName()` が空でない文字列かをチェック |
| R | `pet.isNew` | `BaseEntity` | - | ペットの `id` が `null` かどうか（未登録判定） |
| R | `owner.getPet` | `Owner` | - | 同名の既存ペットが存在するか検索（`pets` テーブル参照） |
| E | `result.rejectValue` | `BindingResult` | - | バリデーションエラーをフィールドレベルで付与 |
| R | `LocalDate.now` | Java API | - | 現在日付を取得して生年月日の有効性チェックに使用 |
| R | `pet.getBirthDate` | `Pet` | `pets` テーブル | ペットの生年月日を取得 |
| E | `result.rejectValue` | `BindingResult` | - | 生年月日が未来の場合にエラーを付与 |
| R | `result.hasErrors` | `BindingResult` | - | バリデーションエラーが存在するか判定 |
| C | `owner.addPet` | `Owner` | `pets` テーブル | ペットを飼い主のペット一覧に追加 |
| U | `owners.save` | `OwnerRepository` | `owners`, `pets` テーブル | 飼い主情報および関連するペット情報をデータベースに永続化（`JpaRepository.save` 経由） |
| E | `redirectAttributes.addFlashAttribute` | `RedirectAttributes` | - | 成功メッセージを一時的に保存 |

**CRUD分類の補足:**

- **R（Read）**: `StringUtils.hasText()` による文字列の有効性チェック、`pet.isNew()` による未登録フラグの判定、`owner.getPet(name, true)` による同名ペットの存在確認、`pet.getBirthDate()` による生年月日の読取、`LocalDate.now()` による現在日付の取得、`result.hasErrors()` によるエラー状態の判定。
- **C（Create）**: `owner.addPet(pet)` によるペットの飼い主への紐づけ追加。`Pet` が `isNew()` の場合、`Owner.addPet()` 内部で `pets` リストに追加される。
- **U（Update）**: `owners.save(owner)` による飼い主エンティティの保存。`JpaRepository.save()` は新規エンティティの場合は INSERT、既存エンティティの場合は UPDATE を実行する。関連する `pets` テーブルのレコードもカスケード接続により同時に永続化される。
- **E（Execute）**: エラー付与およびフラッシュ属性設定は DB 操作を伴わない実行操作。

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

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（本メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Client:POST | `POST /owners/{ownerId}/pets/new` → `PetController.processCreationForm` | `owner.addPet [C] pets` → `owners.save [U] owners` |
| 2 | Test: `PetControllerTests.processCreationFormSuccess()` | `MockMvc.perform(post(...))` → `processCreationForm` | `owner.addPet [C] pets` → `owners.save [U] owners` |
| 3 | Test: `PetControllerTests.processCreationFormWithBlankName()` | `MockMvc.perform(post(...))` → `processCreationForm` | `result.rejectValue [E] name field` |
| 4 | Test: `PetControllerTests.processCreationFormWithDuplicateName()` | `MockMvc.perform(post(...))` → `processCreationForm` | `result.rejectValue [E] name duplicate` |
| 5 | Test: `PetControllerTests.processCreationFormWithMissingPetType()` | `MockMvc.perform(post(...))` → `processCreationForm` | `result.rejectValue [E] type field (via PetValidator)` |
| 6 | Test: `PetControllerTests.processCreationFormWithInvalidBirthDate()` | `MockMvc.perform(post(...))` → `processCreationForm` | `result.rejectValue [E] birthDate typeMismatch` |

**呼び出し元についての注記:**
- 本メソッドは `@PostMapping("/pets/new")` でマッピングされており、`/owners/{ownerId}/pets/new` に対するブラウザからの POST リクエストが主要な呼び出し元である。
- `@ModelAttribute("owner")` の `findOwner()` メソッドが HTTP リクエストごとに `ownerId` から `Owner` を取得し、`@ModelAttribute("pet")` の `findPet()` メソッドが新規登録時には空の `Pet` オブジェクトを生成してバインドする。
- テストクラス `PetControllerTests` において、`MockMvc` を使用して POST リクエストをシミュレートするテストが複数書かれている。

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

### Block 1 — IF 条件: ペット名重複チェック (L109)

> クライアントから送信されたペット名が、当該飼い主のもとに既に存在するペットと重複していないかを検証する。ペット名が空文字でないこと、かつ新規登録（`isNew()` == `true`）であること、かつ同名の既存ペットが存在すること、の3条件がすべて真の場合にエラーを付与する。

| # | 種別 | コード |
|---|------|------|
| 1 | R | `StringUtils.hasText(pet.getName())` — ペット名が空でなければ true |
| 2 | R | `pet.isNew()` — `BaseEntity.isNew()` を経由。`id == null` の場合 true（未登録） |
| 3 | R | `owner.getPet(pet.getName(), true)` — 同一所有者名下で同名のペットを検索。`ignoreNew=true` なので、未登録のペットは対象外 |
| 4 | E | `result.rejectValue("name", "duplicate", "already exists")` — フィールド "name" に重複エラーを付与 |

### Block 2 — IF 条件: 生年月日の有効性チェック (L114)

> ペットの生年月日が今日の日付より未来になっていないかを検証する。生年月日が設定されており（`null` でなく）、かつ現在日付より未来である場合、バリデーションエラーを付与する。

| # | 種別 | コード |
|---|------|------|
| 1 | R | `pet.getBirthDate()` — `Pet.birthDate` の値を取得 |
| 2 | R | `LocalDate.now()` — Java API により現在日付を取得 |
| 3 | R | `pet.getBirthDate().isAfter(currentDate)` — 生年月日が今日より未来かどうか判定 |
| 4 | E | `result.rejectValue("birthDate", "typeMismatch.birthDate")` — 生年月日フィールドにエラーを付与 |

### Block 3 — IF 条件: バリデーションエラー判定と分岐 (L118)

> 前述の重複チェック、生年月日チェック、および `PetValidator` によるバリデーションの結果を統合し、エラーの有無に応じて処理を分岐する。

**Block 3.A — true 分岐: エラーあり → フォーム画面に戻す (L119)**

| # | 種別 | コード |
|---|------|------|
| 1 | RETURN | `return VIEWS_PETS_CREATE_OR_UPDATE_FORM` — ビュー `"pets/createOrUpdatePetForm"` を返す（エラー付きフォーム再表示） |

**Block 3.B — false 分岐: エラーなし → ペット登録処理 (L122–L125)**

> バリデーションエラーがないため、ペット情報を永続化し、成功メッセージを付与した上でリダイレクトする。

| # | 種別 | コード |
|---|------|------|
| 1 | C | `owner.addPet(pet)` — `Owner.addPet()` を呼出。`Pet.isNew()` の場合、`pets` リストに追加 |
| 2 | U | `this.owners.save(owner)` — `OwnerRepository.save()` により `owners` テーブル（およびカスケードで `pets` テーブル）に永続化 |
| 3 | E | `redirectAttributes.addFlashAttribute("message", "New Pet has been Added")` — 成功メッセージをフラッシュ属性として設定 |
| 4 | RETURN | `return "redirect:/owners/{ownerId}"` — 飼い主詳細画面へリダイレクト |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|------------------|
| `PetController` | Class | ペット情報の登録・編集を処理する Spring MVC コントローラー。`/owners/{ownerId}` ベースの URL マッピングを定義する。 |
| `Owner` | Entity | 飼い主を表すエンティティ。`owners` テーブルにマッピングされ、`id`、`firstName`、`lastName`、`address`、`city`、`telephone` の属性を持つ。 |
| `Pet` | Entity | ペットを表すエンティティ。`pets` テーブルにマッピングされ、`name`、`birthDate`、`type` の属性を持つ。`NamedEntity` を介して `BaseEntity` から `id` を継承する。 |
| `PetType` | Entity | ペットの種別（犬、猫、ハムスター等）を表すエンティティ。`types` テーブルにマッピングされる。 |
| `OwnerRepository` | Interface | 飼い主エンティティの永続化を担う Spring Data JPA リポジトリ。`JpaRepository<Owner, Integer>` を拡張する。 |
| `BaseEntity` | Class | 全エンティティの基底クラス。`id` プロパティと `isNew()` メソッドを提供する。`isNew()` は `id == null` の場合に `true` を返し、未登録判定に使用される。 |
| `NamedEntity` | Class | `BaseEntity` を拡張する抽象エンティティ。`name` プロパティを追加し、名前付きエンティティの共通基底として使用される。 |
| `BindingResult` | Class | Spring MVC のバリデーション結果を保持するオブジェクト。`rejectValue` でフィールドレベルのエラーを追加し、`hasErrors` でエラー状態を判定する。 |
| `RedirectAttributes` | Class | リダイレクト先へ一時的なデータ（フラッシュ属性）を渡すための Spring MVC クラス。`addFlashAttribute` により、リダイレクト後のリクエストで参照可能な属性を設定する。 |
| `VIEWS_PETS_CREATE_OR_UPDATE_FORM` | Constant | フォーム画面のビュー名。値は `"pets/createOrUpdatePetForm"`。バリデーションエラー時にこのビューを返す。 |
| `isNew()` | Method | `BaseEntity` に定義される未登録判定メソッド。`id` が `null` の場合に `true` を返す。 |
| `pets` テーブル | DB | ペットの情報を格納するデータベーステーブル。`pets` エンティティにマッピングされる。 |
| `owners` テーブル | DB | 飼い主の情報を格納するデータベーステーブル。`Owner` エンティティにマッピングされる。 |
| `PetValidator` | Class | `Pet` オブジェクトのバリデーションを適用する Spring MVC バリデータ。`@InitBinder("pet")` で登録される。 |
