package jpabook.jpashop.repository;
import jpabook.jpashop.domain.OrderStatus;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class OrderSearch {
private String memberName; //회원이름
private OrderStatus orderStatus; //주문상태[ORDER, CANCEL]
}
package jpabook.jpashop.service;
...
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
...
/**
* 주문 목록 검색
* @param orderSearch
* @return List<Order>
*/
public List<Order> findOrders(OrderSearch orderSearch){
return orderRepository.findAllByCriteria(orderSearch);
}
}
findAllByCriteria : 이전에 OrderRepository에 동적 쿼리에 대한 컨디션 조합을 predicate를 활용해서 만들었던 코드를 사용해서 검색 기능 수행
Criteria는 JPQL의 작성을 도와주는 빌더 클래스로 자바 코드 기반임서비스 단에서 로직이 간단하면 service가 아닌 controller에서 repository에 접근해서 해당 기능을 직접 수행하도록 구현해도 괜찮음
package jpabook.jpashop.web;
...
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
...
/**
* 주문 목록 검색
*/
@GetMapping("/orders")
public String orderList(@ModelAttribute("orderSearch") OrderSearch orderSearch, Model model){
List<Order> orders = orderService.findOrders(orderSearch);
model.addAttribute("orders", orders);
return "order/orderList";
}
}
OrderSearch 객체에 담겨서 넘어옴findOrders 메서드의 입력값으로 검색 조건으로 받은 OrderSearch를 넘김order가 list 형태로 넘어오면 이를 orderList 화면에 뿌려줌model.addAttribute("orderSearch", orderSearch); 코드를 추가하지 않아도 ModelAttribute를 통해 OrderSearch가 order와 동일하게 화면에 객체로 전달이 됨package jpabook.jpashop.web;
...
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
````...
/**
* 주문 취소
*/
@PostMapping("/orders/{orderId}/cancel")
public String cancelOrder(@PathVariable("orderId") Long orderId){
orderService.cancelOrder(orderId);
return "redirect:/orders";
}
}
order/orderList
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header"/>
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<div>
<div>
<form th:object="${orderSearch}" class="form-inline">
<div class="form-group mb-2">
<input type="text" th:field="*{memberName}" class="formcontrol" placeholder="회원명"/>
</div>
<div class="form-group mx-sm-1 mb-2">
<select th:field="*{orderStatus}" class="form-control">
<option value="">주문상태</option>
<option th:each=
"status : ${T(jpabook.jpashop.domain.OrderStatus).values()}"
th:value="${status}"
th:text="${status}">option
</option>
</select>
</div>
<button type="submit" class="btn btn-primary mb-2">검색</button>
</form>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>회원명</th>
<th>대표상품 이름</th>
<th>대표상품 주문가격</th>
<th>대표상품 주문수량</th>
<th>상태</th>
<th>일시</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${orders}">
<td th:text="${item.id}"></td>
<td th:text="${item.member.name}"></td>
<td th:text="${item.orderItems[0].item.name}"></td>
<td th:text="${item.orderItems[0].orderPrice}"></td>
<td th:text="${item.orderItems[0].count}"></td>
<td th:text="${item.status}"></td>
<td th:text="${item.orderDate}"></td>
<td>
<a th:if="${item.status.name() == 'ORDER'}" href="#"
th:href="'javascript:cancel('+${item.id}+')'"
class="btn btn-danger">CANCEL</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer"/>
</div> <!-- /container -->
</body>
<script>
function cancel(id) {
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/orders/" + id + "/cancel");
document.body.appendChild(form);
form.submit();
}
</script>
</html>
<form th:object="${orderSearch}" class="form-inline">
<div class="form-group mb-2">
<input type="text" th:field="*{memberName}" class="formcontrol" placeholder="회원명"/>
</div>
<div class="form-group mx-sm-1 mb-2">
<select th:field="*{orderStatus}" class="form-control">
<option value="">주문상태</option>
<option th:each=
"status : ${T(jpabook.jpashop.domain.OrderStatus).values()}"
th:value="${status}"
th:text="${status}">option
</option>
</select>
</div>
<button type="submit" class="btn btn-primary mb-2">검색</button>
</form>
OrderSearch가 담겨서 화면에 object로 전달됨orderSearch에 입력값으로 전달 받은 회원명과 주문상태가 각각 binding됨localhost:8000/orders?memberName=test&orderStatus=ORDER<option th:each=
"status : ${T(jpabook.jpashop.domain.OrderStatus).values()}"
th:value="${status}"
th:text="${status}">option
</option>
OrderStatus Enum에 있는 값을 가져와서 화면에 셀렉트 박스 형태로 보여주는 것ORDER와 CANCEL 중에 하나를 선택할 수 있게 됨<td>
<a th:if="${item.status.name() == 'ORDER'}" href="#"
th:href="'javascript:cancel('+${item.id}+')'"
class="btn btn-danger">CANCEL</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer"/>
</div> <!-- /container -->
</body>
<script>
function cancel(id) {
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/orders/" + id + "/cancel");
document.body.appendChild(form);
form.submit();
}
</script>
ORDER인 경우에만 cancel 버튼이 노출되도록 개발cancel 버튼을 누르게 되면 하단에 작성된 javascript 코드를 호출하게 됨/orders/cacel 호출



