---

# (DD21) Business Logic — OwnerController.processFindForm() [26 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `org.springframework.samples.petclinic.owner.OwnerController` |
| Layer | Controller |
| Module | `owner` (Package: `org.springframework.samples.petclinic.owner`) |

## 1. Role

### OwnerController.processFindForm()

This method implements the owner search-and-routing entry point for the PetClinic owner management flow. It accepts the search form submission for owners, derives the search key from the bound `Owner` object, and then decides which business path should be taken based on the search outcome.

The method supports three distinct service outcomes: no matches, exactly one match, and multiple matches. When no owners are found, it adds a field-level validation error to the form and returns the search view so the user can refine the criteria. When exactly one owner is found, it performs a direct navigation to that owner’s detail page. When multiple owners are found, it prepares a paginated owner list and returns the list view. This is a classic routing/dispatch pattern in a web controller, because the method does not persist data itself; instead, it classifies the search result and delegates the next screen or model assembly step.

At a business level, the method is the decision hub between search, validation feedback, and list/detail navigation. It is an entry point used by the owner search screen and serves as the controller-side orchestrator for owner lookup behavior.

## 2. Processing Pattern (Detailed Business Logic)

```mermaid
flowchart TD
START(["processFindForm(params)"])
A["Read owner lastName from bound Owner"]
B{"lastName is null"}
C["Set lastName to empty string"]
D["Call findPaginatedForOwnersLastName(page, lastName)"]
E{"ownersResults is empty"}
F["Reject binding error on lastName with notFound"]
G["Return owners/findOwners"]
H{"ownersResults totalElements == 1"}
I["Take single Owner from Page iterator"]
J["Return redirect:/owners/{id}"]
K["Call addPaginationModel(page, model, ownersResults)"]
L["Return owners/ownersList"]
END(["End"])
START --> A
A --> B
B -->|yes| C
B -->|no| D
C --> D
D --> E
E -->|yes| F
F --> G
G --> END
E -->|no| H
H -->|yes| I
I --> J
J --> END
H -->|no| K
K --> L
L --> END
```

## 3. Parameter Analysis

| No | Parameter Name | Type | Business Description |
|----|---------------|------|---------------------|
| 1 | `page` | `@RequestParam(defaultValue = "1") int` | Pagination request number for the owner search result set. A value of `1` means the first page of results, and higher values request subsequent result pages when the search returns multiple owners. |
| 2 | `owner` | `Owner` | Bound search criteria object carrying the owner last-name filter from the search form. The method reads `owner.getLastName()` and uses it as the search key. |
| 3 | `result` | `BindingResult` | Form validation and error container. It receives a field error when no owners are found so the UI can display a business message on the last-name field. |
| 4 | `model` | `Model` | View model used to publish pagination metadata and the owner list when multiple matches exist. |

Instance fields / external state read by the method:
- `owners` repository dependency indirectly through the called search method
- `pageSize = 5` indirectly through pagination logic in the called method
- Web request binding state from `Owner owner`

## 4. CRUD Operations / Called Services

### Pre-computed evidence from code analysis graph:

| CRUD | SC / CBS | SC Code | Entity / DB | Operation Description |
|------|----------|---------|-------------|----------------------|
| C | `OwnerController.addPaginationModel` | OwnerController | - | Calls `addPaginationModel` in `OwnerController` |
| R | `OwnerController.findPaginatedForOwnersLastName` | OwnerController | - | Calls `findPaginatedForOwnersLastName` in `OwnerController` |
| C | `VetController.addPaginationModel` | VetController | - | Calls `addPaginationModel` in `VetController` |

| CRUD | SC / CBS | SC Code | Entity / DB | Operation Description |
|------|----------|---------|-------------|----------------------|
| R | `findPaginatedForOwnersLastName` | OwnerController | `Owner` repository via `owners.findByLastNameStartingWith(...)` | Searches owners whose last name starts with the provided value and returns a paginated result set. |
| C | `addPaginationModel` | OwnerController | `Model` attributes | Populates the UI model with current page, total pages, total items, and owner list for the results screen. |

Notes:
- `result.rejectValue(...)` is a validation/error registration action rather than a database CRUD operation.
- `owner = ownersResults.iterator().next()` is an in-memory selection from the query result, not a persistence write.

## 5. Dependency Trace

Trace who calls this method and what this method ultimately calls.

| # | Caller (Screen/Batch) | Call Chain (Full Path to this Method) | Terminal (SC / CRUD / Entity) |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Screen:Owner search flow | `OwnerController.processFindForm` | `findPaginatedForOwnersLastName [R] Owner` |

Observed downstream terminals from this method:
- `findPaginatedForOwnersLastName` [R] `Owner`
- `addPaginationModel` [C] `Model` attributes
- `result.rejectValue(...)` validation feedback, no DB entity

## 6. Per-Branch Detail Blocks

**Block 1** — IF `(owner.getLastName() == null)` (L96)

> Normalize missing search criteria so the controller can perform a broad owner search instead of failing on a null input.

| # | Type | Code |
|---|------|------|
| 1 | SET | `String lastName = owner.getLastName();` |
| 2 | IF | `if (lastName == null)` |

**Block 1.1** — THEN `(lastName == null)` (L97)

| # | Type | Code |
|---|------|------|
| 1 | SET | `lastName = "";` // empty string signifies broadest possible search |

**Block 2** — CALL `(search owners by last name)` (L101)

> Execute the paginated lookup for owners using the normalized last-name search key.

| # | Type | Code |
|---|------|------|
| 1 | CALL | `findPaginatedForOwnersLastName(page, lastName);` |
| 2 | SET | `Page<Owner> ownersResults = ...;` |

**Block 3** — IF `(ownersResults.isEmpty())` (L102)

> Handle the no-match scenario by returning the search screen with a field-level validation message.

| # | Type | Code |
|---|------|------|
| 1 | IF | `if (ownersResults.isEmpty())` |

**Block 3.1** — THEN `(ownersResults.isEmpty())` (L103-L105)

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `result.rejectValue("lastName", "notFound", "not found");` |
| 2 | RETURN | `return "owners/findOwners";` |

**Block 4** — IF `(ownersResults.getTotalElements() == 1)` (L108)

> Handle the single-match scenario by routing directly to the owner detail page.

| # | Type | Code |
|---|------|------|
| 1 | IF | `if (ownersResults.getTotalElements() == 1)` |

**Block 4.1** — THEN `(ownersResults.getTotalElements() == 1)` (L109-L111)

| # | Type | Code |
|---|------|------|
| 1 | SET | `owner = ownersResults.iterator().next();` |
| 2 | RETURN | `return "redirect:/owners/" + owner.getId();` |

**Block 5** — CALL `(multiple owners found)` (L114)

> Handle the multi-match scenario by preparing pagination metadata and the result list view.

| # | Type | Code |
|---|------|------|
| 1 | CALL | `addPaginationModel(page, model, ownersResults);` |
| 2 | RETURN | `return ...;` |

## 7. Glossary

| Term | Type | Business Meaning |
|------|------|------------------|
| `Owner` | Entity | PetClinic owner master record representing a customer who owns pets. |
| `lastName` | Field | Owner surname used as the primary search key in the owner lookup screen. |
| `page` | Field | Result page number used when a search returns multiple owners. |
| `BindingResult` | Technical term | Spring MVC validation result holder used to attach form field errors. |
| `Model` | Technical term | Spring MVC view model used to pass display data to the UI template. |
| `notFound` | Validation code | Message code used when no owners match the entered search criteria. |
| `owners/findOwners` | View | Search form page shown when the lookup returns no matches. |
| `owners/ownersList` | View | Results list page shown when multiple owners match the search. |
| `redirect:/owners/{id}` | Navigation pattern | Server-side redirect to the selected owner’s detail page. |
| pagination | Business term | Splitting a long result set into smaller pages for easier viewing. |
| broadest possible search | Business term | Search mode that treats an empty last-name filter as a request to list all owners starting from the first page. |
| repository | Technical term | Data access component used to query owner records from persistent storage. |
| `findByLastNameStartingWith` | Repository method | Query that returns owners whose last name begins with the supplied text. |
| `currentPage` | Model attribute | Current page number shown in the results UI. |
| `totalPages` | Model attribute | Total number of pages available for the current search. |
| `totalItems` | Model attribute | Total number of matched owner records. |
| `listOwners` | Model attribute | Collection of owners displayed on the results page. |
