Enable add Vet

This commit is contained in:
Amir Firouz 2024-11-17 01:36:18 -08:00
parent 317562a170
commit ebd570af25
10 changed files with 262 additions and 34 deletions

View file

@ -74,6 +74,7 @@ class OwnerController {
@PostMapping("/owners/new")
public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
System.out.println("owner/new:"+owner);
if (result.hasErrors()) {
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;

View file

@ -0,0 +1,69 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.vet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.Formatter;
import org.springframework.samples.petclinic.owner.OwnerRepository;
import org.springframework.samples.petclinic.owner.PetType;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.util.Collection;
import java.util.Locale;
/**
* Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting
* from Spring 3.0, Formatters have come as an improvement in comparison to legacy
* PropertyEditors. See the following links for more details: - The Spring ref doc:
* https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#format
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Michael Isvy
*/
@Component
public class SpecialtyFormatter implements Formatter<Specialty> {
VetRepository vetRepository;
@Autowired
public SpecialtyFormatter(VetRepository vetRepository) {
this.vetRepository = vetRepository;
}
public String print(Specialty specialty) {
return specialty.getName();
}
@Override
public Specialty parse(String text, Locale locale) throws ParseException {
Collection<Specialty> specialties = this.vetRepository.findSpecialties();
for (Specialty specialty : specialties) {
if (specialty.getName().equals(text)) {
return specialty;
}
}
throw new ParseException("specialty not found: " + text, 0);
}
@Override
public String print(Specialty object, Locale locale) {
return object.toString();
}
}

View file

@ -0,0 +1,17 @@
package org.springframework.samples.petclinic.vet;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.samples.petclinic.vet.Specialty;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Repository
public interface SpecialtyRepository extends JpaRepository<Specialty, Long> {
@Query("SELECT s FROM Specialty s ORDER BY s.name")
@Transactional(readOnly = true)
List<Specialty> findSpecialties();
}

View file

@ -15,11 +15,7 @@
*/
package org.springframework.samples.petclinic.vet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
@ -45,12 +41,14 @@ import jakarta.xml.bind.annotation.XmlElement;
@Table(name = "vets")
public class Vet extends Person {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
inverseJoinColumns = @JoinColumn(name = "specialty_id"))
private Set<Specialty> specialties;
private Collection<Specialty> specialties;
protected Set<Specialty> getSpecialtiesInternal() {
protected Collection<Specialty> getSpecialtiesInternal() {
if (this.specialties == null) {
this.specialties = new HashSet<>();
}
@ -63,6 +61,9 @@ public class Vet extends Person {
PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedSpecs);
}
public void setSpecialties(Collection<Specialty> specialties) {
this.specialties = specialties;
}
public int getNrOfSpecialties() {
return getSpecialtiesInternal().size();

View file

@ -15,16 +15,22 @@
*/
package org.springframework.samples.petclinic.vet;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.samples.petclinic.model.NamedEntity;
import org.springframework.samples.petclinic.owner.Owner;
import org.springframework.samples.petclinic.owner.PetType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
/**
* @author Juergen Hoeller
@ -36,9 +42,16 @@ import org.springframework.web.bind.annotation.ResponseBody;
class VetController {
private final VetRepository vetRepository;
private final SpecialtyRepository specialtyRepository;
public VetController(VetRepository clinicService) {
@Autowired
public VetController(VetRepository clinicService, SpecialtyRepository specialtyRepository) {
this.vetRepository = clinicService;
this.specialtyRepository = specialtyRepository;
}
@ModelAttribute("specialties")
public Collection<Specialty> populateSpecialties() {
return this.specialtyRepository.findSpecialties();
}
@GetMapping("/vets.html")
@ -50,7 +63,32 @@ class VetController {
vets.getVetList().addAll(paginated.toList());
return addPaginationModel(page, paginated, model);
}
@GetMapping("/vets/find")
public String initFindForm() {
return "vets/findVets";
}
@ModelAttribute("vet")
public Vet findVet(@PathVariable(name = "vetId", required = false) Integer vetId) {
return vetId == null ? new Vet()
: this.vetRepository.findById(vetId)
.orElseThrow(() -> new IllegalArgumentException("Vet not found with id: " + vetId
+ ". Please ensure the ID is correct " + "and the owner exists in the database."));
}
static final String VIEWS_VETS_CREATE_OR_UPDATE_FORM = "vets/createOrUpdateVetForm";
@GetMapping("/vets/new")
public String initCreationForm() {
System.out.println("Creating a new vet");
return VIEWS_VETS_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/vets/new")
public String processCreationForm(@Valid Vet vet, BindingResult result, Model model) {
if (result.hasErrors()) {
return VIEWS_VETS_CREATE_OR_UPDATE_FORM;
}
this.vetRepository.save(vet);
return "redirect:/vets.html";
}
private String addPaginationModel(int page, Page<Vet> paginated, Model model) {
List<Vet> listVets = paginated.getContent();
model.addAttribute("currentPage", page);
@ -59,6 +97,37 @@ class VetController {
model.addAttribute("listVets", listVets);
return "vets/vetList";
}
@GetMapping("/vets")
public String processFindForm(@RequestParam(defaultValue = "1") int page, Vet vet, BindingResult result,
Model model) {
// allow parameterless GET request for /owners to return all records
System.out.println("Find vets:"+vet);
if (vet.getLastName() == null) {
vet.setLastName(""); // empty string signifies broadest possible search
}
// find owners by last name
Page<Vet> vetsResults = findPaginatedForVetsLastName(page, vet.getLastName());
if (vetsResults.isEmpty()) {
// no owners found
result.rejectValue("lastName", "notFound", "not found");
return "vets/findVets";
}
/*if (vetsResults.getTotalElements() == 1) {
// 1 owner found
vet = vetsResults.iterator().next();
return "redirect:/vets/" + vet.getId();
}*/
// multiple owners found
return addPaginationModel(page, vetsResults, model);
}
private Page<Vet> findPaginatedForVetsLastName(int page, String lastname) {
int pageSize = 5;
Pageable pageable = PageRequest.of(page - 1, pageSize);
return vetRepository.findByLastNameStartingWith(lastname, pageable);
}
private Page<Vet> findPaginated(int page) {
int pageSize = 5;
@ -66,13 +135,13 @@ class VetController {
return vetRepository.findAll(pageable);
}
@GetMapping({ "/vets" })
public @ResponseBody Vets showResourcesVetList() {
// Here we are returning an object of type 'Vets' rather than a collection of Vet
// objects so it is simpler for JSon/Object mapping
Vets vets = new Vets();
vets.getVetList().addAll(this.vetRepository.findAll());
return vets;
}
// @GetMapping({ "/vets" })
// public @ResponseBody Vets showResourcesVetList() {
// // Here we are returning an object of type 'Vets' rather than a collection of Vet
// // objects so it is simpler for JSon/Object mapping
// Vets vets = new Vets();
// vets.getVetList().addAll(this.vetRepository.findAll());
// return vets;
// }
}

View file

@ -15,14 +15,23 @@
*/
package org.springframework.samples.petclinic.vet;
import jakarta.annotation.Nonnull;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.validation.Valid;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.owner.Owner;
import org.springframework.samples.petclinic.owner.PetType;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.*;
/**
* Repository class for <code>Vet</code> domain objects All method names are compliant
@ -35,24 +44,21 @@ import java.util.Collection;
* @author Sam Brannen
* @author Michael Isvy
*/
public interface VetRepository extends Repository<Vet, Integer> {
public interface VetRepository extends JpaRepository<Vet, Integer> {
/**
* Retrieve all <code>Vet</code>s from the data store.
* @return a <code>Collection</code> of <code>Vet</code>s
* Retrieve {@link Vet}s from the data store by last name, returning all owners
* whose last name <i>starts</i> with the given name.
* @param lastName Value to search for
* @return a Collection of matching {@link Vet}s (or an empty Collection if none
* found)
*/
@Transactional(readOnly = true)
@Cacheable("vets")
Collection<Vet> findAll() throws DataAccessException;
Page<Vet> findByLastNameStartingWith(String lastName, Pageable pageable);
/**
* Retrieve all <code>Vet</code>s from data store in Pages
* @param pageable
* @return
* @throws DataAccessException
*/
@Query("SELECT s FROM Specialty s ORDER BY s.name")
@Transactional(readOnly = true)
@Cacheable("vets")
Page<Vet> findAll(Pageable pageable) throws DataAccessException;
List<Specialty> findSpecialties();
}

View file

@ -21,6 +21,8 @@ INSERT INTO types VALUES (default, 'lizard');
INSERT INTO types VALUES (default, 'snake');
INSERT INTO types VALUES (default, 'bird');
INSERT INTO types VALUES (default, 'hamster');
INSERT INTO types VALUES (default, 'panda');
INSERT INTO owners VALUES (default, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
INSERT INTO owners VALUES (default, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');

View file

@ -59,6 +59,10 @@
<span class="fa fa-th-list" aria-hidden="true"></span>
<span>Veterinarians</span>
</li>
<li th:replace="~{::menuItem ('/vets/find','vets','find veterinarians','search','Find Veterinarians')}">
<span class="fa fa-search" aria-hidden="true"></span>
<span>Find Veterinarians</span>
</li>
<li
th:replace="~{::menuItem ('/oups','error','trigger a RuntimeException to see how it is handled','exclamation-triangle','Error')}">

View file

@ -0,0 +1,25 @@
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'vets')}">
<body>
<h2>Vet</h2>
<form th:object="${vet}" class="form-horizontal" id="add-vet-form" method="post">
<div class="form-group has-feedback">
<input
th:replace="~{fragments/inputField :: input ('First Name', 'firstName', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Last Name', 'lastName', 'text')}" />
<div th:replace="~{fragments/selectField :: select ('Specialties', 'specialties', ${specialties})}"></div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button
th:with="text=${vet['new']} ? 'Add Vet' : 'Update Vet'"
class="btn btn-primary" type="submit" th:text="${text}">Add
Vet</button>
</div>
</div>
</form>
</body>
</html>

View file

@ -0,0 +1,34 @@
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'vets')}">
<body>
<h2>Find Vets</h2>
<form th:object="${vet}" th:action="@{/vets}" method="get"
class="form-horizontal" id="search-vet-form">
<div class="form-group">
<div class="control-group" id="lastNameGroup">
<label class="col-sm-2 control-label">Last name </label>
<div class="col-sm-10">
<input class="form-control" th:field="*{lastName}" size="30"
maxlength="80" /> <span class="help-inline"><div
th:if="${#fields.hasAnyErrors()}">
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
</div></span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Find
Vet</button>
</div>
</div>
<a class="btn btn-primary" th:href="@{/vets/new}">Add Vet</a>
</form>
</body>
</html>