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

## Overview

This module appears to implement the web controller layer for the Petclinic owner area. It handles the main owner lifecycle in the UI: searching for owners, creating and updating owners, managing pets belonging to an owner, and booking visits for pets. The controllers work together around a shared domain model and repository layer, which makes this package the main request-handling entry point for the owner-related screens.

At a high level, the module keeps controller responsibilities separated by concern: `OwnerController` handles owner search and profile pages, `PetController` handles pet forms and pet updates, and `VisitController` handles visit booking. They all follow the same Spring MVC style of request mapping, model preparation, validation, and redirect-after-post behavior.

## Key Classes and Interfaces

### `OwnerController`

[OwnerController](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:48) handles owner-centric pages and actions. It is the controller you hit when working with owner creation, lookup, update, and details pages.

It depends on [OwnerRepository](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:52) and uses it to persist and query owners. The controller also declares a shared form-binding rule through `setAllowedFields(...)`, which prevents clients from binding `id` fields directly.

Important methods:

- [OwnerController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:59) disallows `id` and nested `*.id` fields from request binding. This is a defensive measure to prevent over-posting and accidental identity changes.
- [OwnerController.findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:64) prepares an `owner` model attribute. If no path variable is present, it returns a new `Owner`; otherwise it loads the owner from the repository and fails fast if the owner does not exist.
- [OwnerController.initCreationForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) returns the owner create/update 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. On validation errors, it returns the form and adds a flash error message. On success, it saves the owner and redirects to the owner details page.
- [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) implements owner lookup by last name, including pagination and the special-case flows for no matches, one match, or many matches.
- [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 result list for the owner list view.
- [OwnerController.findPaginatedForOwnersLastName(int page, String lastname)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:130) builds a `PageRequest` and delegates to the repository search method.
- [OwnerController.initUpdateOwnerForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:136) returns the same create/update view for editing an owner.
- [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 and saves an owner update. It also checks that the form `id` matches the path `ownerId` before saving.
- [OwnerController.showOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:166) loads the owner details page and throws if the owner cannot be found.

Design notes:

- The controller uses one shared form template for both create and edit flows.
- It prefers redirects after successful POSTs, which helps avoid duplicate submissions on refresh.
- It uses flash attributes for user-visible status messages.
- The search endpoint is also the list endpoint: an empty last name means the broadest possible search.

### `PetController`

[PetController](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46) manages pets that belong to a specific owner. The controller is scoped under `/owners/{ownerId}`, which makes the owner context explicit for every pet action.

It depends on both [OwnerRepository](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:50) and [PetTypeRepository](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:52). The owner repository is used to load and persist the owning aggregate, while the pet type repository supplies the dropdown choices for pet type selection.

Important methods:

- [PetController.populatePetTypes()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61) exposes all available `PetType` values as a model attribute named `types`.
- [PetController.findOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:66) loads the owner for the current request and fails if it 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) prepares the `pet` model attribute. If `petId` is absent, it returns a new pet for creation; otherwise it loads the existing pet from the owner.
- [PetController.initOwnerBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:88) prevents binding of owner IDs.
- [PetController.initPetBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:93) installs a `PetValidator` and prevents binding of pet IDs.
- [PetController.initCreationForm(Owner owner, ModelMap model)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:99) creates a new `Pet`, attaches it to the owner, and returns the pet form.
- [PetController.processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:106) validates new-pet input, checks for duplicate names, rejects future birth dates, saves the owner aggregate, and redirects back to the owner page on success.
- [PetController.initUpdateForm()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:129) returns the same form for edits.
- [PetController.processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:134) validates edits, enforces name uniqueness within the owner, rejects future birth dates, and then updates the existing pet or adds it if needed.
- [PetController.updatePetDetails(Owner owner, Pet pet)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167) performs the actual update-or-add logic and persists the owner.

Design notes:

- Pet creation and pet editing share one form template.
- Validation happens in two layers: bean validation via `@Valid` and controller-level checks for business rules such as duplicate names and future birth dates.
- The controller treats the owner as the aggregate root: it updates pets through the owner and saves the owner, rather than saving pets independently.

### `VisitController`

[VisitController](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:41) handles booking visits for a pet under a specific owner. Like `PetController`, it is nested under the owner path, which lets it always resolve the owning context before processing the request.

It depends on [OwnerRepository](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:43) and uses the owner aggregate to load the relevant pet and persist the visit.

Important methods:

- [VisitController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:50) disallows direct binding of IDs.
- [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) is the key model-preparation method. It loads the owner, resolves the pet, places both into the model, creates a new `Visit`, attaches it to the pet, and returns the visit object for form binding.
- [VisitController.initNewVisitForm()](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:84) returns the visit creation 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, saves it through the owner aggregate, and redirects back to the owner details page after success.

Design notes:

- The pre-handler model setup is doing important work here: it ensures the correct pet and owner are available before the form renders or submits.
- The controller keeps the visit flow intentionally small; most of the heavy lifting is done by the owner and pet domain objects.

## How It Works

### Typical owner search flow

1. A user opens the owner search page through [OwnerController.initFindForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:89).
2. The search form submits to [OwnerController.processFindForm(...)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:94).
3. The controller extracts the last name, normalizes a missing value to an empty string, and queries the repository with pagination.
4. If no owners match, it rejects the form field and returns the search view.
5. If exactly one owner matches, it redirects straight to that owner’s details page.
6. If multiple owners match, it builds a paginated list model with [OwnerController.addPaginationModel(...)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:121) and renders the list page.

### Typical owner create/update flow

1. The user opens the form through [OwnerController.initCreationForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) or [OwnerController.initUpdateOwnerForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:136).
2. Spring binds the request into an `Owner` object, but the controller prevents ID binding with [OwnerController.setAllowedFields(...)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:59).
3. On submit, the controller validates the owner and either returns the form on errors or persists the owner through the repository.
4. After success, the controller uses a redirect plus flash message to take the user back to the owner details page.

### Typical pet create/update flow

1. A request under `/owners/{ownerId}` hits [PetController](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46).
2. Spring prepares the owner and pet model attributes via the `@ModelAttribute` methods.
3. For pet forms, the controller exposes the list of valid `PetType` values through [PetController.populatePetTypes()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61).
4. On creation, a new `Pet` is attached to the owner before the form is shown, and on submit the controller checks for duplicate names and future birth dates.
5. On update, the controller reuses the same business checks and either updates the existing pet or adds a new one if needed.
6. The owner aggregate is saved after the pet change, keeping the aggregate consistent.

### Typical visit booking flow

1. The visit form URL includes both owner and pet IDs.
2. [VisitController.loadPetWithVisit(...)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:62) runs before the request handler, loads the owner and pet, and seeds the model with both.
3. It also creates a new `Visit` and attaches it to the pet so the form can bind to a prepared object.
4. [VisitController.initNewVisitForm()](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:84) returns the visit form view.
5. On submit, [VisitController.processNewVisitForm(...)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:91) validates the visit, saves it through the owner, and redirects back to the owner page.

## Data Model

This module works with a small domain model centered on the owner aggregate:

- `Owner` is the main aggregate root used throughout the package.
- `Pet` belongs to an owner and is created, updated, and loaded through owner-owned methods.
- `Visit` belongs to a pet and is created as part of the visit booking flow.
- `PetType` supplies the list of valid pet categories shown in the UI.

The controllers do not introduce separate DTOs. They bind directly to the domain objects, then use repository and aggregate methods to keep the object graph in sync.

## Dependencies and Integration

The module integrates with Spring MVC and validation infrastructure in a standard way:

- `@Controller`, `@GetMapping`, `@PostMapping`, `@ModelAttribute`, and `@PathVariable` define the request handling surface.
- `BindingResult` and `@Valid` are used to combine validation feedback with controller-level business checks.
- `RedirectAttributes` carries user-visible flash messages across redirects.
- `WebDataBinder` is used to block ID fields from binding.
- `Page`, `PageRequest`, and `Pageable` support paginated owner search.

Repository integration is straightforward:

- [OwnerController](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:48), [PetController](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46), and [VisitController](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:41) all depend on [OwnerRepository](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:52).
- [PetController](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46) also depends on [PetTypeRepository](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:52).
- All persistence flows save through the owner aggregate rather than directly through nested entities.

## Notes for Developers

- **ID binding is intentionally blocked.** If you add new nested forms in this package, mirror the existing `setDisallowedFields("id", "*.id")` pattern unless you have a strong reason not to.
- **Owner is the persistence boundary.** New pet or visit flows should usually save the owner aggregate, not the child object in isolation.
- **Search behavior is opinionated.** An empty last-name search is treated as a broad search, and a single match auto-redirects to details. If you change the search UX, update both the controller logic and the associated view expectations.
- **Validation is layered.** Keep bean validation for structural checks and controller code for cross-field or domain-specific checks such as duplicate names or future dates.
- **Watch path-variable assumptions.** Several methods rely on `ownerId` and `petId` being present in the route. If you introduce new endpoints, make sure the `@ModelAttribute` methods still resolve cleanly.
- **Flash messages are part of the user flow.** Successful creates and updates set flash messages, so downstream views may expect them.

## Relationships

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

This diagram highlights the main controller split and the shared repository relationships that connect the module to the rest of the application.
