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

## Overview

This subpackage contains the Spring MVC controllers that implement the owner-facing workflow in Petclinic: searching for owners, creating and editing owners, managing pets, and booking visits. It appears to be the web layer for the `owner` domain, translating HTTP requests into repository operations and view model updates.

The controllers share a common pattern: they load domain objects with `@ModelAttribute`, protect identity fields with binder restrictions, validate incoming form data, and then either render a form view again or persist changes and redirect back to the owner details page.

## Key Classes and Interfaces

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

`OwnerController` handles owner creation, search, update, and detail display. It is the main entry point for owner lookup and maintenance flows, and it depends only on `OwnerRepository` for persistence.

Key responsibilities:

- Prevents binding of server-controlled identifiers with `@InitBinder`.
- Exposes an `owner` model attribute for create, edit, and show flows.
- Implements owner search with pagination.
- Handles create and update submission, including error reporting and redirect messages.

Important methods:

- [OwnerController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:59) disallows binding to `id` and nested `*.id` fields. This is a security and integrity measure so request data cannot overwrite database identifiers.
- [OwnerController.findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:64) returns a new `Owner` when no ID is present, or loads the existing owner from the repository when the route includes an owner ID. If the owner cannot be found, it throws an `IllegalArgumentException`.
- [OwnerController.initCreationForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) returns the owner create/update form view.
- [OwnerController.processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:77) validates the submitted owner, returns the form with an error flash message on failure, or saves the owner and redirects to the new owner detail page on success.
- [OwnerController.initFindForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:89) returns the owner search form.
- [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 by last name, supports a parameterless `/owners` request by treating the last name as empty, and branches based on search results: no match, one match, or multiple matches.
- [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 with paging metadata and the current page of owners.
- [OwnerController.findPaginatedForOwnersLastName(int page, String lastname)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:130) delegates to `owners.findByLastNameStartingWith(...)` using a fixed page size of 5.
- [OwnerController.initUpdateOwnerForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:136) returns the same form view used for creation.
- [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 the submitted owner, checks that the form ID matches the path variable, saves the owner, and redirects back to the owner detail page. The explicit ID comparison prevents accidental or malicious updates to the wrong record.
- [OwnerController.showOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:166) loads one owner and returns `owners/ownerDetails` in a `ModelAndView`.

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

`PetController` manages pets belonging to an owner. It is scoped to `/owners/{ownerId}` and coordinates both the owner aggregate and the separate `PetTypeRepository`, so it can display form choices and keep pet data consistent with the owning `Owner`.

Key responsibilities:

- Loads owner and pet model attributes before request handling.
- Supplies the list of available pet types to views.
- Validates pet forms, including duplicate names and future birth dates.
- Creates new pets and updates existing pets through the owner aggregate.

Important methods:

- [PetController.populatePetTypes()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61) returns all pet types for use in dropdowns and form rendering.
- [PetController.findOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:66) loads the owner for the current route and throws if the owner does not exist.
- [PetController.findPet(@PathVariable("ownerId") int ownerId, @PathVariable(name = "petId", required = false) Integer petId)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:74) returns a new `Pet` for create flows, or resolves an existing pet from the owner when editing. This keeps the form backed by the correct domain object before binding happens.
- [PetController.initOwnerBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:88) prevents owner ID tampering.
- [PetController.initPetBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:93) attaches `PetValidator` and also protects identifier fields.
- [PetController.initCreationForm(Owner owner, ModelMap model)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:99) adds a new pet to the owner and returns the pet form view.
- [PetController.processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:106) enforces unique pet names per owner, rejects future birth dates, saves the updated owner, and redirects to the owner detail page with a flash message.
- [PetController.initUpdateForm()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:129) returns the same pet form view for editing.
- [PetController.processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:134) checks for duplicate names excluding the pet being edited, validates the birth date, and then updates the aggregate.
- [PetController.updatePetDetails(Owner owner, Pet pet)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167) is the persistence helper that either mutates an existing pet or adds a new one before saving the owner. It asserts that the pet ID is present, which means update flows depend on a bound identifier.

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

`VisitController` manages visits for a specific pet. It sits on top of the owner aggregate and loads both the owner and pet into the model before rendering the visit form, so the visit submission can be associated with the correct pet.

Key responsibilities:

- Protects identifier binding.
- Loads owner, pet, and visit model attributes together.
- Renders the visit creation form.
- Saves a visit through the owner aggregate.

Important methods:

- [VisitController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:50) disallows binding to identifier fields.
- [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, puts both into the model, creates a new `Visit`, and attaches it to the pet before returning it as the `visit` model attribute. The method comment says it runs before every request handler in the controller, which ensures the form always has fresh domain state.
- [VisitController.initNewVisitForm()](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:84) returns the visit 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 the visit, adds it to the pet through the owner, saves the owner, and redirects back to the owner detail page with a success message.

## How It Works

### Owner lifecycle

1. A user opens `/owners/new` or `/owners/{ownerId}/edit`.
2. Spring MVC prepares the owner model attribute using `findOwner(...)` when needed.
3. The corresponding form view is shown.
4. On submit, the controller validates the `Owner` object.
5. If validation fails, the form is shown again.
6. If validation succeeds, the owner is saved through `OwnerRepository` and the user is redirected to the owner details page.

### Owner search and pagination

1. `/owners/find` displays the search form.
2. `/owners` accepts a search request, optionally with `page`.
3. If the form does not supply a last name, the controller searches with an empty string, which appears to mean "match broadly".
4. The repository query uses a page size of 5.
5. If there are no matches, the last-name field is rejected with a `notFound` error.
6. If there is exactly one match, the controller redirects directly to that owner.
7. If there are multiple matches, the controller populates paging attributes and returns the list view.

### Pet lifecycle

1. A request enters `/owners/{ownerId}/pets/new` or `/owners/{ownerId}/pets/{petId}/edit`.
2. `PetController` loads the owner, and for edit flows also loads the pet.
3. The pet form uses the `types` model attribute to present pet type options.
4. On create, the controller checks for duplicate names under the same owner and rejects future birth dates.
5. On update, it checks that any new name does not collide with another pet on the same owner.
6. If validation passes, the controller updates the owner aggregate and saves it.
7. The browser is redirected back to the owner detail page.

### Visit lifecycle

1. A request enters the visit route for a specific owner and pet.
2. `loadPetWithVisit(...)` loads the owner and pet, adds both to the model, and prepares a new `Visit` instance.
3. The visit form is rendered.
4. On submit, the visit is validated.
5. If there are no errors, the visit is added to the pet through the owner and the owner is saved.
6. The user is redirected back to the owner details page with a flash message.

## Mermaid relationship diagram

```mermaid
flowchart TD
OwnerController["OwnerController"] --> OwnerRepository["OwnerRepository"]
PetController["PetController"] --> OwnerRepository["OwnerRepository"]
PetController --> PetTypeRepository["PetTypeRepository"]
VisitController["VisitController"] --> OwnerRepository["OwnerRepository"]
OwnerController --> OwnerDetailsView["owners/ownerDetails"]
OwnerController --> OwnerListView["owners/ownersList"]
PetController --> PetFormView["pets/createOrUpdatePetForm"]
VisitController --> VisitFormView["pets/createOrUpdateVisitForm"]
```

## Data Model

This module works with the owner domain model rather than introducing its own DTOs.

- `Owner` is the aggregate root for owner, pet, and visit operations.
- `Pet` is created and edited under an owner, and the controller relies on owner methods such as `addPet(...)` and `getPet(...)`.
- `Visit` is created under a pet, but it is saved by updating the owning `Owner`.
- `PetType` is a reference data type used to populate the pet form.

The controllers intentionally keep these operations inside the owner aggregate, which suggests the domain model is responsible for maintaining consistency between owners, pets, and visits.

## Dependencies and Integration

The controllers integrate with the rest of the application through a small set of Spring MVC and domain abstractions:

- `@Controller`, `@GetMapping`, `@PostMapping`, `@RequestMapping`, `@ModelAttribute`, `@InitBinder`, and `@PathVariable` define the HTTP entry points.
- `BindingResult` and `@Valid` drive validation feedback in form workflows.
- `RedirectAttributes` carries flash messages after successful saves or validation failures.
- `Model`, `ModelMap`, and `ModelAndView` feed template rendering.
- `Page`, `PageRequest`, and `Pageable` support owner search pagination.
- `OwnerRepository` is the persistence gateway for all three controllers.
- `PetTypeRepository` supplies lookup data for pet forms.
- `PetValidator` is attached directly in `PetController`.

## Notes for Developers

- All three controllers disallow binding to `id` fields. If you add new nested forms or new mutable identifiers, review binder protection carefully.
- `OwnerController` and `PetController` both rely on path-variable-backed model attributes. If a route changes, make sure the associated `@ModelAttribute` methods still receive the variables they expect.
- `OwnerController.processFindForm(...)` uses a fixed page size of 5. If the UX changes, update both the query helper and the list view assumptions together.
- `PetController.processCreationForm(...)` and `processUpdateForm(...)` enforce name uniqueness at the controller layer. If the domain model gains stricter rules, consider moving shared validation there.
- `VisitController.loadPetWithVisit(...)` eagerly adds a new `Visit` to the pet before form submission. That means form rendering depends on a live domain object graph, not a detached DTO.
- Several methods throw `IllegalArgumentException` when an owner or pet cannot be resolved. This appears to be the module's current strategy for signaling invalid URLs.

