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

## Overview

This module contains the Spring MVC controllers that drive the owner-facing workflows in Petclinic. It appears to be responsible for creating, searching, viewing, and editing owners, managing a pet’s details, and booking visits for pets. Together, these controllers form the web layer for the `owner` package and translate HTTP requests into repository operations and model updates.

The module centers on three controllers: [`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). Each controller uses Spring MVC annotations, model attributes, and form binding to keep request handling small and explicit.

## Key Classes and Interfaces

### `OwnerController`

[`OwnerController`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:48) handles owner-level pages and form submissions. It provides the flows for creating an owner, searching owners by last name, updating owner details, and displaying a single owner record.

**Design role:** this controller acts as the main entry point for owner management. It coordinates request binding, validation, pagination, and persistence through an [`OwnerRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:51).

**Key methods:**

- [`OwnerController.OwnerController(OwnerRepository owners)`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:55) injects the repository used to read and save owners.
- [`OwnerController.setAllowedFields(WebDataBinder dataBinder)`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:59) blocks binding to `id` fields, including nested `*.id`, which helps prevent clients from tampering with persisted identifiers.
- [`OwnerController.findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId)`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:64) is a `@ModelAttribute` method that supplies the `owner` model object. If no `ownerId` is present it creates a new `Owner`; otherwise it loads the owner from the repository or throws an error if the ID is invalid.
- [`OwnerController.initCreationForm()`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72) returns the owner creation/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 failure it returns the form view and adds an error flash message; on success it persists the owner and redirects to the 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 with paging. It supports an empty search string, handles the no-results and single-result cases specially, and otherwise delegates to pagination rendering.
- [`OwnerController.addPaginationModel(int page, Model model, Page<Owner> paginated)`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:121) copies paging metadata and the current page of owners into the model.
- [`OwnerController.findPaginatedForOwnersLastName(int page, String lastname)`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:130) performs the repository query using a fixed page size of 5.
- [`OwnerController.initUpdateOwnerForm()`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:136) returns the owner edit form.
- [`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 edits. It also checks that the bound form ID matches the URL path ID before saving, which avoids accidental or malicious ID mismatches.
- [`OwnerController.showOwner(@PathVariable("ownerId") int ownerId)`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:166) loads an owner and returns a `ModelAndView` for the owner details page.

**Why it exists:** owner search and editing are common high-frequency tasks in the application, so this controller packages the user-facing behavior around a single repository and a consistent set of form and detail views.

### `PetController`

[`PetController`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:46) manages pets that belong to a specific owner. It handles pet creation and updates, provides the list of available pet types, and ensures that pet edits are safe and validated.

**Design role:** this controller lives under the owner path (`/owners/{ownerId}`) because a pet is always edited in the context of its owner. It combines owner loading, pet loading, binder configuration, and duplicate-name checks in a single place.

**Key methods:**

- [`PetController.PetController(OwnerRepository owners, PetTypeRepository types)`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:56) injects the repositories used to load owners and available pet types.
- [`PetController.populatePetTypes()`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:61) exposes the list of pet types as a shared model attribute called `types`, making it available to both create and edit forms.
- [`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) supplies the `pet` model object. If there is no `petId`, it creates a new `Pet`; otherwise it resolves the pet through the owner. This keeps the form backing object aligned with the current owner context.
- [`PetController.initOwnerBinder(WebDataBinder dataBinder)`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:88) protects owner identifiers from binding.
- [`PetController.initPetBinder(WebDataBinder dataBinder)`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:93) registers a [`PetValidator`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:95) and also blocks `id` binding. This is the primary validation hook for pet form submissions.
- [`PetController.initCreationForm(Owner owner, ModelMap model)`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:99) prepares a new `Pet`, associates it with the owner in memory, 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) validates the new pet, rejects duplicate names for the same owner, rejects future birth dates, saves 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 same create/update form for editing.
- [`PetController.processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes)`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:134) performs the update path. It checks for duplicate names within the owner’s pets, validates the birth date, and then delegates to [`updatePetDetails`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167).
- [`PetController.updatePetDetails(Owner owner, Pet pet)`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167) applies the submitted fields to an existing pet or adds the pet if it does not exist. It then saves the owner. This method is the core write path for pet changes.

**Why it exists:** pet handling is tightly coupled to the owner aggregate, so this controller keeps the logic close to the owner context rather than spreading pet updates across the application.

### `VisitController`

[`VisitController`](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:41) handles visit booking for a specific pet under a specific owner. It sets up the visit form, loads the current owner and pet, and persists the new visit.

**Design role:** this controller is the narrowest of the three. It exists to make the nested route `/owners/{ownerId}/pets/{petId}/visits/new` work cleanly while ensuring the pet and owner are always fresh and valid for the current request.

**Key methods:**

- [`VisitController.VisitController(OwnerRepository owners)`](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:46) injects the owner repository used to resolve the current aggregate.
- [`VisitController.setAllowedFields(WebDataBinder dataBinder)`](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:50) prevents binding to `id` 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) is a `@ModelAttribute` hook that loads the owner and pet, places them in the model, creates a fresh `Visit`, and associates that visit with the pet. The method comments explain that this ensures fresh data and guarantees that the pet object has an ID even without session scope.
- [`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 and saves a visit. If validation passes, it adds the visit to the owner’s pet collection, persists the owner, and redirects back to the owner page with a flash message.

**Why it exists:** visit creation is a separate user action from pet editing, but it still depends on the same owner and pet aggregate. Keeping it in its own controller keeps the responsibility focused and the routing explicit.

## How It Works

### Typical owner flow

1. A user opens the owner creation form through [`initCreationForm`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:72).
2. Spring binds submitted fields into an `Owner` object, while the `@InitBinder` rule prevents identifier tampering.
3. [`processCreationForm`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:77) validates the form.
4. If validation fails, the controller returns the form view and places an error message in flash scope.
5. If validation succeeds, the owner is saved and the browser is redirected to the new owner’s detail page.

### Typical owner search flow

1. The search form is displayed via [`initFindForm`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:89).
2. [`processFindForm`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:94) reads the last name filter. An empty value is treated as a broad search.
3. The controller queries the repository with paging through [`findPaginatedForOwnersLastName`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:130).
4. If nothing matches, the result is attached to the binding errors and the search page is shown again.
5. If exactly one owner matches, the controller redirects directly to that owner’s detail page.
6. If multiple owners match, [`addPaginationModel`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:121) prepares the list view with paging metadata.

### Typical pet flow

1. A request arrives under `/owners/{ownerId}` and Spring resolves the owner through [`findOwner`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:66).
2. If a pet is being edited, [`findPet`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:74) resolves the pet from the owner; otherwise it creates a fresh one.
3. [`initPetBinder`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:93) installs a validator and blocks `id` fields.
4. On submit, the controller rejects duplicate pet names for the same owner and disallows future birth dates.
5. On success, [`updatePetDetails`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:167) updates the existing pet or adds a new one, then saves the owner aggregate.

### Typical visit flow

1. Spring calls [`loadPetWithVisit`](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:62) before the request handler.
2. That method reloads the owner and pet, adds them to the model, and creates a new `Visit` linked to the pet.
3. The form view is rendered with fresh model state.
4. When the form is submitted, [`processNewVisitForm`](src/main/java/org/springframework/samples/petclinic/owner/VisitController.java:91) validates the visit and saves it through the owner aggregate.

## Data Model

This module works with the Petclinic domain objects tied to owner management:

- `Owner` is the root aggregate for the workflows in this package.
- `Pet` belongs to an owner and is created, updated, and looked up through the owner.
- `Visit` belongs to a pet and is booked through the nested visit route.
- `PetType` provides the set of valid pet classifications used by the forms.

The controllers consistently mutate and persist data through the owner aggregate rather than saving pets or visits independently. That suggests the domain is structured so owner is the natural write boundary for related pet and visit changes.

## Dependencies and Integration

The controllers depend on the following Spring MVC and Petclinic components:

- [`OwnerRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java:51) for loading and saving owners.
- [`PetTypeRepository`](src/main/java/org/springframework/samples/petclinic/owner/PetController.java:54) for populating the pet type list.
- Spring MVC annotations such as `@Controller`, `@GetMapping`, `@PostMapping`, `@ModelAttribute`, `@InitBinder`, and `@PathVariable` for request mapping and model preparation.
- `BindingResult` and `@Valid` for validation-driven form handling.
- `RedirectAttributes` for flash messages after successful operations.
- `Model`, `ModelMap`, and `ModelAndView` for passing data into views.

### Controller relationship map

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

## Notes for Developers

- All three controllers explicitly disallow binding to `id` fields. Preserve that pattern when adding new request handlers or form objects.
- `PetController` and `VisitController` rely on the owner aggregate being loaded before the request handler runs. If you add new nested routes, make sure the `@ModelAttribute` methods continue to establish the right model state.
- Duplicate pet name checks happen in controller code, not in the model layer shown here. If you extend pet creation or editing, keep those checks aligned with any domain-level constraints.
- `OwnerController.processFindForm` treats an empty last name as the broadest search. That behavior is intentional and allows `/owners` to return all owners.
- The update path in `OwnerController` performs an explicit path-vs-form ID comparison. If you change the form fields or URL structure, keep that safeguard in place.
- Visit creation currently saves through `owners.save(owner)` after attaching the visit to the pet. That indicates the repository is acting as the persistence boundary for the aggregate, so new visit-related logic should follow the same pattern unless the domain model changes.

