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
호출