# Org / Springframework / Samples / Petclinic / Owner/controllers

## Overview

This module groups the Spring MVC controllers that handle owner, pet, and visit workflows in Petclinic. It appears to exist as the web-facing orchestration layer for the `owner` package: each controller turns HTTP requests into repository lookups, validation, model population, and redirects back to the appropriate view.

Together, these controllers manage the full lifecycle of the owner domain from the UI perspective: searching for owners, creating and editing owner records, adding and updating pets, and booking visits for pets.

## Key Classes and Interfaces

### `OwnerController`

**Link:** [OwnerController](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:48)

`OwnerController` handles owner-centric pages and forms. It owns the flow for creating owners, searching by last name, editing owner details, and rendering the owner detail page. It depends only on `OwnerRepository`, which makes it the entry point for most owner management actions.

Important responsibilities:

- Prevents mass-assignment of owner identifiers through `WebDataBinder` configuration.
- Provides a shared `@ModelAttribute("owner")` lookup method so request handlers can work with either a new owner or an existing one.
- Implements the search flow that supports empty searches, single-match redirects, and paginated result lists.
- Handles create and update form submission with validation and flash messages.

Key methods:

- [OwnerController.OwnerController(OwnerRepository owners)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:55) — injects the repository used for persistence and lookup.
- [OwnerController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:59) — blocks binding to `id` and nested `*.id` fields so clients cannot overwrite identifiers from the form.
- [OwnerController.findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:64) — resolves the `owner` model attribute, returning a new `Owner` for create flows or loading one from the repository for update/show flows.
- [OwnerController.initCreationForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) — returns the owner create/edit form view.
- [OwnerController.processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:77) — validates and saves a new owner, then redirects to the newly created owner page.
- [OwnerController.initFindForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:89) — returns the owner search form view.
- [OwnerController.processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result, Model model)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:94) — searches owners by last name, handling no results, single result, and multiple results separately.
- [OwnerController.addPaginationModel(int page, Model model, Page<Owner> paginated)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:121) — populates pagination metadata and the current page of owners for the list view.
- [OwnerController.findPaginatedForOwnersLastName(int page, String lastname)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:130) — creates a `Pageable` and delegates to `owners.findByLastNameStartingWith(...)`.
- [OwnerController.initUpdateOwnerForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:136) — returns the same owner form for editing.
- [OwnerController.processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:141) — validates updates, checks the form id against the URL id, saves the owner, and redirects back to the detail page.
- [OwnerController.showOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:166) — loads an owner and renders the detail view.

Design notes:

- The controller uses a single create/update form view (`owners/createOrUpdateOwnerForm`) for both new and edited owners.
- Search is intentionally flexible: submitting no last name treats the query as the broadest possible search.
- For multiple search hits, it returns a paginated list rather than forcing a redirect.

### `PetController`

**Link:** [PetController](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46)

`PetController` manages pets within the context of a specific owner. It is mapped at `/owners/{ownerId}`, which means every request is scoped to an owner, and it coordinates both `OwnerRepository` and `PetTypeRepository` to populate forms and persist changes.

Important responsibilities:

- Loads the owning `Owner` and the current `Pet` into the model for each request.
- Supplies the list of pet types used by the form.
- Validates pet data, including duplicate-name checks and birth-date checks.
- Handles both creation and editing through shared form views and a shared update helper.

Key methods:

- [PetController.PetController(OwnerRepository owners, PetTypeRepository types)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:56) — injects repositories for owner and pet type lookups.
- [PetController.populatePetTypes()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61) — exposes all pet types as a model attribute for dropdowns or selectors.
- [PetController.findOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:66) — resolves the owner for the current route.
- [PetController.findPet(@PathVariable("ownerId") int ownerId, @PathVariable(name = "petId", required = false) Integer petId)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:74) — resolves the pet for edit flows or creates a new `Pet` for create flows.
- [PetController.initOwnerBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:88) — blocks binding of owner identifiers.
- [PetController.initPetBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:93) — installs a `PetValidator` and blocks binding of pet identifiers.
- [PetController.initCreationForm(Owner owner, ModelMap model)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:99) — prepares a new pet and attaches it to the owner before showing the form.
- [PetController.processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:106) — validates and saves a newly created pet, then redirects to the owner page.
- [PetController.initUpdateForm()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:129) — returns the shared pet form view for updates.
- [PetController.processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:134) — validates and applies edits to an existing pet.
- [PetController.updatePetDetails(Owner owner, Pet pet)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167) — updates an existing pet in place or adds a new one if the owner does not already have the pet in memory.

Design notes:

- The controller relies on the `owner` path variable as the boundary for all pet operations.
- Both create and edit paths use the same form view (`pets/createOrUpdatePetForm`).
- Duplicate pet names are checked against the owner's current pets, and future birth dates are rejected.
- `updatePetDetails` centralizes persistence logic so the update handler stays focused on validation and flow control.

### `VisitController`

**Link:** [VisitController](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:41)

`VisitController` handles booking visits for a specific pet under a specific owner. It is the smallest of the three controllers, but it plays an important coordination role: it reloads owner and pet state, prepares a `Visit` for the form, and persists the visit through the owner aggregate.

Important responsibilities:

- Prevents binding to identifier fields.
- Loads the current owner and pet before request handling so visit forms always have fresh domain objects.
- Places the pet and owner into the model for the visit view.
- Creates a `Visit` object and attaches it to the pet before the form is shown.
- Saves the visit by adding it to the owner and persisting the owner aggregate.

Key methods:

- [VisitController.VisitController(OwnerRepository owners)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:46) — injects the repository used to reload and save owners.
- [VisitController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:50) — prevents identifier binding for visit-related forms.
- [VisitController.loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId, Map<String, Object> model)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:62) — loads the owner and pet, adds them to the model, creates a new visit, and associates that visit with the pet.
- [VisitController.initNewVisitForm()](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:84) — returns the visit creation form view.
- [VisitController.processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:91) — validates and saves a new visit, then redirects to the owner page.

Design notes:

- The `@ModelAttribute("visit")` method is doing a lot of work: it ensures the form always receives a fresh `Visit`, and it also injects `owner` and `pet` into the model for view rendering.
- The comments in the code indicate that Spring MVC calls `loadPetWithVisit(...)` before both the GET and POST handlers, which keeps the form and submission flow aligned.
- Visit persistence happens by saving the owner after adding the visit to the pet, which suggests the aggregate root is the owner.

## How It Works

### 1) Owner lifecycle

A typical owner create flow looks like this:

1. A browser requests `/owners/new`.
2. [OwnerController.initCreationForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) returns `owners/createOrUpdateOwnerForm`.
3. The form posts back to `/owners/new`.
4. [OwnerController.processCreationForm(...)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:77) validates the bound `Owner`.
5. If validation fails, the same form view is returned and an error flash message is added.
6. If validation succeeds, the controller saves the owner through `OwnerRepository` and redirects to `/owners/{ownerId}`.

Search follows a three-way branch:

1. `/owners` or `/owners?lastName=...` arrives at [OwnerController.processFindForm(...)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:94).
2. The controller normalizes a missing last name to the empty string.
3. [OwnerController.findPaginatedForOwnersLastName(int page, String lastname)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:130) requests a 5-item page from the repository.
4. If nothing is found, the form is shown again with a field error.
5. If exactly one owner is found, the request redirects directly to that owner detail page.
6. If multiple owners are found, [OwnerController.addPaginationModel(int page, Model model, Page<Owner> paginated)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:121) prepares the list view model.

### 2) Pet lifecycle under an owner

Pet actions are nested under `/owners/{ownerId}` so the owner is always part of the route context.

1. The framework resolves the owner with [PetController.findOwner(...)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:66).
2. For new-pet requests, [PetController.initCreationForm(Owner owner, ModelMap model)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:99) creates a blank `Pet` and adds it to the owner.
3. The shared form view displays available pet types from [PetController.populatePetTypes()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61).
4. On submit, [PetController.processCreationForm(...)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:106) checks for duplicate names and future birth dates before saving.
5. Edit flows reuse the same form view. [PetController.processUpdateForm(...)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:134) validates the same business rules and then delegates to [PetController.updatePetDetails(...)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167).
6. The update helper either mutates the existing pet instance in place or adds the pet if it is not already present, then saves the owner aggregate.

### 3) Visit booking

Visit booking is tied to both owner and pet identifiers.

1. A request to `/owners/{ownerId}/pets/{petId}/visits/new` first triggers [VisitController.loadPetWithVisit(...)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:62).
2. That method reloads the owner, finds the pet, attaches both objects to the model, creates a new `Visit`, and adds it to the pet.
3. The GET handler [VisitController.initNewVisitForm()](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:84) returns the visit form view.
4. On submission, [VisitController.processNewVisitForm(...)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:91) validates the `Visit`.
5. If valid, the visit is added to the pet through the owner aggregate and the owner is saved.
6. The user is redirected back to the owner page with a success flash message.

## Data Model

This module works with the following domain objects, as implied by the controller signatures and method calls:

- `Owner` — the aggregate root for owner, pet, and visit workflows.
- `Pet` — owned by an `Owner` and edited through nested owner routes.
- `Visit` — associated with a `Pet` and created from the visit booking form.
- `PetType` — lookup data used to populate the pet form.

The controllers suggest these relationships:

- An `Owner` can have multiple `Pet` instances.
- A `Pet` can have multiple `Visit` instances.
- `PetType` is reference data chosen when creating or editing a pet.

## Dependencies and Integration

This module integrates with the rest of Petclinic primarily through repositories and Spring MVC infrastructure:

- [OwnerRepository](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:55) is the main persistence dependency for all three controllers.
- [PetTypeRepository](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:56) provides pet type reference data.
- Spring MVC annotations such as `@GetMapping`, `@PostMapping`, `@ModelAttribute`, and `@InitBinder` coordinate request routing and form binding.
- Validation is performed with `@Valid` and controller-specific validators such as `PetValidator`.
- Pagination in owner search uses Spring Data `Page`, `Pageable`, and `PageRequest`.

The module also relies heavily on flash attributes to communicate success or error states across redirects.

## Notes for Developers

- The controllers intentionally reject binding to `id` fields. If you add new nested form fields that represent identifiers, consider whether they should also be protected from client-side binding.
- Owner, pet, and visit forms are tightly coupled to their view names. If you rename views, update the constants and hard-coded strings together.
- `OwnerController.processUpdateOwnerForm(...)` performs an explicit form-id-versus-URL-id check. That is an important safeguard if the form or routing changes.
- `PetController.processCreationForm(...)` and `processUpdateForm(...)` both apply a future birth-date check. Keep that business rule aligned if validation is moved elsewhere.
- `VisitController.loadPetWithVisit(...)` has side effects beyond returning a model object: it also mutates the model map and attaches a new visit to the pet. That behavior is easy to overlook when adding new handlers.
- The `owner` aggregate is saved after pet and visit changes, which means persistence expectations are centered on the owner root rather than saving child objects directly.

## Relationships

```mermaid
flowchart TD
OwnerController["OwnerController"] --> OwnerRepository["OwnerRepository"]
PetController["PetController"] --> OwnerRepository["OwnerRepository"]
PetController --> PetTypeRepository["PetTypeRepository"]
VisitController["VisitController"] --> OwnerRepository
OwnerRepository --> Owner["Owner"]
Owner --> Pet["Pet"]
Pet --> Visit["Visit"]
```
