# (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 entry point for the PetClinic owner module. It receives the search form input, normalizes the last-name criteria, and performs a paged lookup of owners whose last name starts with the supplied text. Business-wise, it supports three distinct outcomes: no matching owners, exactly one matching owner, or multiple matching owners.

When no match is found, the method pushes a field-level validation error back into the search form and returns the search view so the user can revise the criteria. When exactly one owner is found, it performs a routing-style redirect directly to that owner's detail page, optimizing the user journey by skipping an unnecessary list page. When multiple owners are found, it delegates to the pagination model builder so the results can be displayed in a list view with paging metadata.

The method follows a dispatcher pattern for query-result handling: it interprets the search result cardinality and routes the request to the appropriate presentation path. It also acts as a controller-level adapter between the web form model (`Owner`) and the repository-based search operation, preserving the search behavior used by the broader owner maintenance screens.

## 2. Processing Pattern (Detailed Business Logic)

```mermaid
flowchart TD
START(["processFindForm(params)"])
A["Read owner.lastName"]
B{ "lastName is null?" }
C["Set lastName to empty string"]
D["Call findPaginatedForOwnersLastName(page, lastName)"]
E{ "ownersResults is empty?" }
F["Reject lastName field with notFound"]
G["Return owners/findOwners"]
H{ "ownersResults total elements equals 1?" }
I["Take first owner from page"]
J["Return redirect:/owners/{id}"]
K["Call addPaginationModel(page, model, ownersResults)"]
L["Return owners/ownersList"]
END(["Return / Next"])
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
```

**CRITICAL — Constant Resolution:**
No repository or controller constants are used in this method body. The only literal search fallback is the empty string (`""`), which means “search across the broadest possible last-name prefix.”

## 3. Parameter Analysis

| No | Parameter Name | Type | Business Description |
|----|---------------|------|---------------------|
| 1 | `page` | `@RequestParam(defaultValue = "1") int` | Page number requested by the user for owner search results. It can be any positive integer and controls which slice of matching owners is loaded from the repository. If omitted, the controller uses page 1 by default. |
| 2 | `owner` | `Owner` | Form-backing owner object carrying the search criteria, primarily the last-name prefix entered by the user. The method reads `owner.getLastName()` and treats a missing value as a broad match request. |
| 3 | `result` | `BindingResult` | Validation and binding outcome container for the search form. It is used to register the business error when no owners are found so the UI can display the “not found” message against the last-name field. |
| 4 | `model` | `Model` | View model used to populate pagination metadata and the list of owners when the search returns multiple records. It is only used in the list-result branch. |

Instance fields or external state read by the method:
- `owners` repository field indirectly through `findPaginatedForOwnersLastName(page, lastName)`
- Spring MVC binding state from the incoming `Owner` and `BindingResult`
- Request-scoped `Model` for the multi-result presentation branch

## 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` |

Analyze all method calls within this method and classify each as a CRUD operation.

| CRUD | SC / CBS | SC Code | Entity / DB | Operation Description |
|------|----------|---------|-------------|----------------------|
| R | `OwnerController.findPaginatedForOwnersLastName` | OwnerController | `Owner` repository | Reads a paginated subset of owners whose last name starts with the supplied search text. |
| C | `OwnerController.addPaginationModel` | OwnerController | - | Builds the model attributes required to render the multi-result owner list view. |
| R | `BindingResult.rejectValue` | Spring MVC | - | Records a field-level validation error for the last-name search field when no matching owners are found. |
| R | `Page.isEmpty` | Spring Data `Page` | `Owner` | Checks whether the paged search returned zero matches. |
| R | `Page.getTotalElements` | Spring Data `Page` | `Owner` | Checks whether exactly one owner matched the search criteria. |
| R | `Page.iterator().next` | Spring Data `Page` | `Owner` | Extracts the single matching owner for direct redirect to the owner detail page. |

## 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:OwnerController | `OwnerController.processFindForm` | `findPaginatedForOwnersLastName [R] Owner` |

The search results indicate the only direct Java caller discovered in the current codebase is the controller itself through its own internal flow; the method is exposed as a web entry point via `@GetMapping("/owners")` rather than being invoked by another Java class. From this method, the terminal paths are:
- `findPaginatedForOwnersLastName(page, lastName)` → owner repository query path [R]
- `addPaginationModel(page, model, ownersResults)` → model construction path [C]
- `result.rejectValue(...)` → validation feedback path [R]

## 6. Per-Branch Detail Blocks

**Block 1** — **SET** and **IF** `(read owner last name and normalize blank search key)` (L97-L100)

> Establishes the search criterion from the form-backing owner object and broadens the search to an empty prefix when the user did not enter a last name.

| # | Type | Code |
|---|------|------|
| 1 | SET | `String lastName = owner.getLastName();` // read the submitted last-name search value |
| 2 | IF | `if (lastName == null)` // detect parameterless or blank search input |
| 3 | SET | `lastName = "";` // use empty string as the broadest search prefix |

**Block 2** — **CALL** `(find paginated owners by last-name prefix)` (L103)

> Delegates the actual lookup to the repository-backed pagination helper.

| # | Type | Code |
|---|------|------|
| 1 | CALL | `findPaginatedForOwnersLastName(page, lastName);` // load the matching owners page |

**Block 3** — **IF** `(ownersResults is empty)` (L104-L108)

> Handles the “no match found” business case by returning the search form with a field-level error.

| # | Type | Code |
|---|------|------|
| 1 | IF | `if (ownersResults.isEmpty())` // no matching owners were found |
| 2 | EXEC | `result.rejectValue("lastName", "notFound", "not found");` // flag the search field with a business error message |
| 3 | RETURN | `return "owners/findOwners";` // redisplay the search form |

**Block 4** — **IF** `(ownersResults total elements equals 1)` (L110-L114)

> Handles the single-match optimization by redirecting directly to the one matching owner’s detail page.

| # | Type | Code |
|---|------|------|
| 1 | IF | `if (ownersResults.getTotalElements() == 1)` // exactly one owner matched the search criteria |
| 2 | SET | `owner = ownersResults.iterator().next();` // extract the single matching owner from the page |
| 3 | RETURN | `return "redirect:/owners/" + owner.getId();` // route the user directly to the owner profile |

**Block 5** — **CALL / RETURN** `(multiple owners found)` (L116-L118)

> Handles the list-result path by enriching the model with pagination data and returning the results list view.

| # | Type | Code |
|---|------|------|
| 1 | CALL | `addPaginationModel(page, model, ownersResults);` // populate the result list model and return the list view |
| 2 | RETURN | `return addPaginationModel(page, model, ownersResults);` // show the paginated owner list |

## 7. Glossary

| Term | Type | Business Meaning |
|------|------|------------------|
| `owner` | Field | Form-backing owner object used here as the search criteria carrier, mainly for the last-name filter. |
| `lastName` | Field | Owner family name entered by the user as the search prefix. |
| `page` | Field | Requested result page number for paginated owner search. |
| `result` | Technical term | Spring `BindingResult` object that carries validation or binding errors back to the UI. |
| `model` | Technical term | Spring MVC model used to pass view data such as the owner list and pagination metadata. |
| `Owner` | Business entity | Customer/animal owner record in the PetClinic domain. |
| `OwnerRepository` | Repository | Persistence abstraction used to query owner data from storage. |
| `Page` | Technical term | Spring Data paged result wrapper containing a slice of search results plus paging metadata. |
| `Pageable` | Technical term | Paging request descriptor used to select the requested result window. |
| `PageRequest` | Technical term | Factory for creating paging requests with a page index and page size. |
| `rejectValue` | Technical term | Spring MVC validation API used to attach a field-level error message to the form. |
| `redirect:/owners/{id}` | Technical term | MVC redirect instruction to the owner detail page after a unique match is found. |
| `findOwners` | Screen/view | Owner search form screen shown when no matching owners are found. |
| `ownersList` | Screen/view | Owner list screen shown when multiple matching owners are found. |
| `parameterless GET request` | Business term | A request made without search criteria, which the controller interprets as “show all owners” through the broadest last-name prefix. |
| `broadest possible search` | Business term | Search behavior that matches all owners because the last-name prefix is empty. |
| `notFound` | Message code | Validation code used to indicate that the search returned no owners. |
