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

## Overview

This module contains the web controllers for the Petclinic owner area. It appears to own the request handling for managing owners, their pets, and their visits, translating HTTP form submissions into repository operations on the domain model. The controllers also enforce a few web-layer rules such as field binding restrictions, validation, duplicate checks, and redirect/message behavior after successful saves.

In practice, this module is the Spring MVC entry point for the owner workflow: finding owners, creating and editing owner records, adding and updating pets, and booking visits.

## Key Classes and Interfaces

### `OwnerController`

[OwnerController](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:48) handles the owner-centric screens and actions. It is responsible for creating owners, searching owners by last name, updating owner details, and rendering the owner detail page. The controller depends only on `OwnerRepository`, which means the owner aggregate is the boundary for persistence operations in this part of the application.

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 protects against over-posting and prevents clients from changing identifiers through form submissions.
- [OwnerController.findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:64) is a `@ModelAttribute` helper that either creates a new `Owner` for create flows or loads an existing owner by ID for update and detail flows.
- [OwnerController.initCreationForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) returns the owner 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 new owner detail 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) performs last-name search, handles no-result and single-result special cases, and routes multi-result queries to a paginated list.
- [OwnerController.addPaginationModel(int page, Model model, Page<Owner> paginated)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:121) populates the model with pagination 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 the repository with a fixed page size of 5.
- [OwnerController.initUpdateOwnerForm()](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:136) returns the same owner 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 and saves owner updates, including an explicit check that the form ID matches the URL ID.
- [OwnerController.showOwner(@PathVariable("ownerId") int ownerId)](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:166) loads an owner and returns the owner detail page via `ModelAndView`.

Design notes:

- The controller treats search by last name as the primary owner lookup path.
- Create and update share the same form view, which keeps the UI consistent and reduces template duplication.
- The update path includes an extra ID mismatch check, which is a defensive measure beyond binder restrictions.

### `PetController`

[PetController](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46) owns the lifecycle of pets nested under an owner. It coordinates owner loading, pet loading, pet-type lookup, validation, and persistence of changes back through the owning `Owner` aggregate. This class depends on both `OwnerRepository` and `PetTypeRepository`, which matches its job of connecting pet forms to both the owner data and the list of allowed pet types.

Important methods:

- [PetController.populatePetTypes()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61) exposes all pet types as a model attribute so forms can render type selection controls.
- [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 fast if the owner ID is invalid.
- [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 creation flows or loads an existing pet from the owner for edit flows.
- [PetController.initOwnerBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:88) prevents binding to owner identifiers.
- [PetController.initPetBinder(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:93) installs `PetValidator` and blocks ID binding for pet forms.
- [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 returning 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) checks for duplicate pet names, rejects future birth dates, saves the new pet through the owner aggregate, and redirects back to the owner page.
- [PetController.initUpdateForm()](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:129) returns the shared pet form view.
- [PetController.processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:134) validates updates, checks for duplicate names against the current owner, and delegates persistence to `updatePetDetails(...)`.
- [PetController.updatePetDetails(Owner owner, Pet pet)](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167) either updates an existing pet in place or adds a new pet, then saves the owner aggregate.

Design notes:

- Pet handling is aggregate-centric: the controller does not persist pets independently, but saves through `OwnerRepository`.
- Both create and update flows share the same template, which implies the form is built to handle both cases.
- Duplicate pet name checks are done in the controller rather than relying only on lower layers.

### `VisitController`

[VisitController](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:41) manages booking visits for a specific pet. It loads the owner and pet on every request, prepares a `Visit` model object, and then attaches the visit back to the pet through the owner aggregate when the form is submitted. Like the pet controller, it depends on `OwnerRepository` because visits are persisted by saving the owner aggregate.

Important methods:

- [VisitController.setAllowedFields(WebDataBinder dataBinder)](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:50) blocks ID binding to prevent clients from manipulating identifiers.
- [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 a `@ModelAttribute` method that loads the owner and pet, places them into 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 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 submitted visit, adds it to the pet, saves the owner, and redirects back to the owner page.

Design notes:

- The `loadPetWithVisit(...)` method is doing two jobs: loading current domain state and ensuring the visit object is associated with the pet before view rendering or form processing.
- The controller stores `pet` and `owner` into the model manually, which makes them available to the form and downstream handlers.
- Visit creation is intentionally nested under a pet route, reinforcing that visits belong to pets, not directly to owners.

## How It Works

### Owner workflow

1. A user opens `/owners/new` and receives the shared owner form view.
2. On submission, Spring binds fields to `Owner`, while `setAllowedFields(...)` prevents identifier tampering.
3. `processCreationForm(...)` validates the object, saves it through `OwnerRepository`, and redirects to the new owner detail page.
4. Search requests to `/owners` use the last name field. The controller treats an empty last name as the broadest search.
5. Search results follow three branches:
   - none found - reject the last name field and return to the search form,
   - exactly one found - redirect to that owner’s detail page,
   - many found - build a paginated list view.

### Pet workflow

1. Requests under `/owners/{ownerId}` first resolve the owner via `@ModelAttribute`.
2. For creation, `initCreationForm(...)` prepares a new `Pet` and associates it with the owner so the form can bind correctly.
3. On submission, `processCreationForm(...)` adds controller-level checks that are not fully handled by bean validation alone:
   - duplicate pet names under the same owner,
   - birth dates in the future.
4. If validation passes, the controller adds the pet to the owner and saves the owner aggregate.
5. Update follows the same model, but `updatePetDetails(...)` either mutates the existing pet in place or adds a new pet if it cannot be found by ID.

### Visit workflow

1. For `/owners/{ownerId}/pets/{petId}/visits/new`, `loadPetWithVisit(...)` runs before the handler method.
2. It reloads owner and pet, stores them in the model, and creates a new `Visit` attached to the pet.
3. The GET handler only selects the form view because the model has already been prepared.
4. On POST, `processNewVisitForm(...)` validates the visit and, if valid, adds it to the pet and saves the owner.

### Request flow diagram

```mermaid
flowchart TD
OwnerController["OwnerController"] --> OwnerRepository["OwnerRepository"]
PetController["PetController"] --> OwnerRepository["OwnerRepository"]
PetController --> PetTypeRepository["PetTypeRepository"]
VisitController["VisitController"] --> OwnerRepository
OwnerController --> OwnerForm["Owner create or update form"]
PetController --> PetForm["Pet create or update form"]
VisitController --> VisitForm["Visit create form"]
```

## Data Model

This module works with the domain objects `Owner`, `Pet`, `Visit`, and `PetType`.

- `Owner` is the aggregate root for most operations. Owner creation, update, lookup, pet management, and visit booking all save through the owner repository.
- `Pet` belongs to an owner. The controller code implies pets are accessed through owner helper methods such as `getPet(...)`, `addPet(...)`, and `getPet(name, ...)`.
- `Visit` belongs to a pet. Visit creation is nested under a pet route and the controller adds the visit to the pet before saving the owner.
- `PetType` is a reference data set used to populate selection options on pet forms.

The code suggests a domain model where relationships are managed from the owner aggregate rather than by separate repositories for pets and visits.

## Dependencies and Integration

- **Spring MVC** - the module uses `@Controller`, `@RequestMapping`, `@GetMapping`, `@PostMapping`, `@ModelAttribute`, and `@InitBinder` to define request handling and model preparation.
- **Validation** - `@Valid` and `BindingResult` are used to enforce form constraints and report errors back to the view.
- **Pagination** - `OwnerController` uses `Page`, `PageRequest`, and `Pageable` to present search results in pages of five owners.
- **Redirect feedback** - `RedirectAttributes` carries flash messages after successful create and update operations.
- **Repositories** - the controllers integrate with `OwnerRepository` and `PetTypeRepository`; no other persistence mechanism is visible here.

The relationships are intentionally narrow: the controllers do not contain business logic unrelated to web orchestration, and they delegate persistence to repositories while keeping form-specific rules at the boundary.

```mermaid
sequenceDiagram
participant U as User
participant OC as OwnerController
participant OR as OwnerRepository
participant PC as PetController
participant PR as PetTypeRepository
participant VC as VisitController
U ->> OC - submit owner search or form
OC ->> OR - read or save owner data
U ->> PC - submit pet form
PC ->> PR - load pet types
PC ->> OR - read or save owner and pet data
U ->> VC - submit visit form
VC ->> OR - load owner and pet
VC ->> OR - save visit through owner aggregate
```

## Notes for Developers

- **IDs are protected at the binder level, but not only there.** The controllers consistently disallow `id` binding and also perform explicit consistency checks in update flows.
- **Aggregate saves are the norm.** If you add new owner-owned data in this area, expect to persist it by loading the owner, mutating the aggregate, and saving the owner back.
- **Form views are reused.** Several create and edit handlers return the same template. When changing templates, make sure they still support both modes.
- **Duplicate-name validation is controller-level.** If duplicate pet names should behave differently, this is the place to adjust the rule.
- **Error handling is fail-fast.** Missing owner or pet IDs throw `IllegalArgumentException`, so callers and tests should expect exceptions rather than nulls.
- **Page size is hard-coded.** Owner search uses a fixed page size of 5 in `findPaginatedForOwnersLastName(...)`.
