# (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 is the request-handling entry point for owner search on the `/owners` screen. Its business purpose is to search the owner master data by last name and then route the user to the appropriate outcome based on the number of matches found. If the search condition is missing, it broadens the search to the widest possible scope by treating the last name as an empty string, which effectively supports a parameterless search request. The method implements a routing/dispatch pattern: it delegates data retrieval to a repository-backed pagination helper, then dispatches the user flow into one of three outcomes: no match, exactly one match, or multiple matches.

From a system perspective, it acts as the decision hub for the owner lookup use case. When no owner is found, it adds a validation-style error to the binding result and returns the search screen so the user can refine the criteria. When exactly one owner is found, it short-circuits the flow and redirects directly to the owner detail page. When multiple owners are found, it prepares pagination data and returns the owner list screen. The method therefore bridges user input, search execution, and view/navigation selection in a single controller-level operation.

## 2. Processing Pattern (Detailed Business Logic)

```mermaid
flowchart TD
    START(["processFindForm(params)"])
    A["Read owner.getLastName()"]
    B{"lastName is null?"}
    C["Set lastName to empty string"]
    D["Call findPaginatedForOwnersLastName(page, lastName)"]
    E{"ownersResults is empty?"}
    F["Reject lastName with notFound and return owners/findOwners"]
    G{"ownersResults has exactly one element?"}
    H["Take the single Owner from ownersResults"]
    I["Return redirect:/owners/{ownerId}"]
    J["Call addPaginationModel(page, model, ownersResults)"]
    K["Return owners/ownersList"]
    END_NODE(["Return / Next"])

    START --> A
    A --> B
    B -->|Yes| C
    B -->|No| D
    C --> D
    D --> E
    E -->|Yes| F
    E -->|No| G
    G -->|Yes| H
    H --> I
    G -->|No| J
    J --> K
    F --> END_NODE
    I --> END_NODE
    K --> END_NODE
```

## 3. Parameter Analysis

| No | Parameter Name | Type | Business Description |
|----|---------------|------|---------------------|
| 1 | `page` | `@RequestParam(defaultValue = "1") int` | Pagination position requested by the user for the owner search result set. It controls which page of owners is retrieved and displayed when multiple matches exist. If omitted, the method starts from the first page. |
| 2 | `owner` | `Owner` | Search criteria carrier populated from request parameters, especially the owner's last name. It represents the user-entered search condition rather than a persisted owner record in this flow. |
| 3 | `result` | `BindingResult` | Validation and field-error container for the search request. It is used to attach the `lastName` not-found error when the search returns no owners. |
| 4 | `model` | `Model` | View model container for the paginated owner list screen. It is populated only when multiple owners are found and the UI must render page state and result rows. |

Instance fields / external state read by the method:
- `owners` repository via the delegated helper `findPaginatedForOwnersLastName(page, lastName)`.
- The request-bound `owner` object, specifically `owner.getLastName()`.
- The Spring MVC `BindingResult` and `Model` objects supplied by the framework.

## 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 | `owner.getLastName()` | Owner | `Owner` | Reads the search key from the request-bound owner criteria object. |
| R | `findPaginatedForOwnersLastName(page, lastName)` | OwnerController | `Owner` via `OwnerRepository.findByLastNameStartingWith` | Retrieves matching owners using the last-name prefix search and pagination. |
| R | `ownersResults.isEmpty()` | `Page<Owner>` | `Owner` | Checks whether the search returned any owners. |
| R | `ownersResults.getTotalElements()` | `Page<Owner>` | `Owner` | Checks whether exactly one owner matched the search criteria. |
| R | `ownersResults.iterator().next()` | `Page<Owner>` | `Owner` | Extracts the single matched owner for direct navigation. |
| C | `result.rejectValue("lastName", "notFound", "not found")` | BindingResult | - | Adds a search validation error to the last name field when no matches are found. |
| C | `addPaginationModel(page, model, ownersResults)` | OwnerController | `Owner` | Populates the model for the paginated owner list screen. |

The method performs no create, update, or delete persistence operation. Its business scope is read-only search, error registration, and navigation control.

## 5. Dependency Trace

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

This method ultimately calls the private helper `findPaginatedForOwnersLastName(page, lastName)` and, for the multi-result branch, `addPaginationModel(page, model, ownersResults)`. The caller search returned one direct usage within `OwnerController` itself, indicating that this method is the controller entry point for the owner search screen rather than a reusable service called from other classes.

## 6. Per-Branch Detail Blocks

**Block 1** — [INITIALIZE SEARCH KEY] (L97-L99)

> Reads the last-name search criteria from the request-bound `owner` object and normalizes a missing value to an empty string so that the lookup can widen to all owners.

| # | Type | Code |
|---|------|------|
| 1 | SET | `String lastName = owner.getLastName();` |
| 2 | IF | `if (lastName == null)` |
| 3 | SET | `lastName = ""; // empty string signifies broadest possible search` |

**Block 2** — [READ OPERATION] (L102)

> Executes the paginated owner lookup using the normalized last-name search key.

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

**Block 3** — [IF ownersResults is empty] (L103-L107)

> Handles the no-match case by marking the last-name field as invalid and returning the search form so the user can correct the query.

| # | Type | Code |
|---|------|------|
| 1 | IF | `if (ownersResults.isEmpty())` |
| 2 | C | `result.rejectValue("lastName", "notFound", "not found");` |
| 3 | RETURN | `return "owners/findOwners";` |

**Block 4** — [ELSE-IF exactly one owner found] (L109-L112)

> Handles the single-match optimization by selecting the owner and redirecting immediately to the owner detail page.

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

**Block 5** — [ELSE multiple owners found] (L114-L115)

> Handles the general list view by populating pagination metadata and returning the paged owner list screen.

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

### Nested block detail

**Block 5.1** — [CALL addPaginationModel(page, model, ownersResults)] (L115)

> Delegates page metadata and search results into the MVC model for rendering the paginated list screen.

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

## 7. Glossary

| Term | Type | Business Meaning |
|------|------|------------------|
| `Owner` | Entity | Pet clinic customer/owner master record. Represents the person who owns one or more pets. |
| `owner` | Field / Request model | Request-bound owner criteria object used for searching, not necessarily a persisted owner record. |
| `lastName` | Field | Owner surname used as the search key in the owner lookup flow. |
| `page` | Parameter | Requested result page number for paginated owner search. |
| `BindingResult` | Framework term | Spring MVC container for validation and field-level errors associated with the bound request model. |
| `Model` | Framework term | MVC data container used to pass attributes to the view template. |
| `Page<Owner>` | Technical term | Paginated collection of owner search results with total count and page metadata. |
| `owners` | Repository | Persistence gateway used by the controller to query owner data. |
| `findPaginatedForOwnersLastName` | Method | Internal helper that performs paginated prefix search on owner last names. |
| `addPaginationModel` | Method | Internal helper that prepares the MVC model for the paginated owner list view. |
| `notFound` | Validation code | Error code assigned when the owner search returns no matches. |
| `owners/findOwners` | View name | Search form screen for finding owners. |
| `owners/ownersList` | View name | Result list screen that displays matched owners with pagination. |
| `redirect:/owners/{ownerId}` | Navigation pattern | Redirect to the owner detail page when exactly one owner matches the search. |
| `PageRequest` | Technical term | Spring Data pageable request object used to request a specific page of results. |
| `Pageable` | Technical term | Pagination contract passed to the repository query. |
| `FTTH` | Acronym | Fiber To The Home — not used in this method, but included only if relevant in broader domain contexts. |

