쇼핑몰 프로젝트

쇼핑몰 프로젝트 --리뷰 수정, 삭제, 아이템 검색, 정렬(화면구현)

개발공부중인지니 2024. 6. 26. 02:42

 

 

먼저 리뷰를 수정, 삭제 할거다

 

 

레파지토리

public ItemReview findOneReview(Long itemReviewId) {
    return em.find(ItemReview.class, itemReviewId);
}

 

 

서비스

public ItemReview findOneItemReview(Long itemReviewId) {
    return itemReviewRepository.findOneReview(itemReviewId);
}

 

@Transactional
public void updateItemReview(Long itemReviewId, ItemReviewDto itemReviewDto) {

    ItemReview itemReview = itemReviewRepository.findOneReview(itemReviewId);

    if (itemReview == null) {
        throw new IllegalArgumentException("ItemReview not found with id: " + itemReviewId);
    }

    Long iRId = itemReview.changeItemReview(
            itemReviewDto.getItemScore(),
            itemReviewDto.getItemReviewName(),
            itemReviewDto.getItemReviewContent()
    );


    //평점 업데이트
    itemReview.getItem().updateItemRatings();

}

 

디티오

package ypjs.project.dto.itemdto;

import lombok.Data;
import lombok.Getter;

import java.time.LocalDateTime;

@Data
public class ItemReviewDto {

    private Long itemId;
    private Long itemReviewId;
    private int itemScore;
    private String itemReviewName;
    private String itemReviewContent;


    public ItemReviewDto() {}


    public ItemReviewDto(Long itemId, Long itemReviewId, int itemScore, String itemReviewName, String itemReviewContent) {
        this.itemId = itemId;
        this.itemReviewId = itemReviewId;
        this.itemScore = itemScore;
        this.itemReviewName = itemReviewName;
        this.itemReviewContent = itemReviewContent;
    }




}

 

 

 

컨트롤러

// 수정보기
@GetMapping("/ypjs/itemReview/update/{itemReviewId}")
public String updateItemReview(@PathVariable("itemReviewId") Long itemReviewId, Model model) {
    ItemReview findItemReview = itemReviewService.findOneItemReview(itemReviewId);

    ItemReviewDto itemReview = new ItemReviewDto(
            findItemReview.getItem().getItemId(),
            findItemReview.getItemReviewId(),
            findItemReview.getItemScore(),
            findItemReview.getItemReviewName(),
            findItemReview.getItemReviewContent()

    );



    model.addAttribute("itemReview", itemReview);

    // 반환할 뷰 이름 (템플릿 파일 경로, 예: templates/itemReview/update.html)
    return "itemReview/itemReviewUpdate";
}



//수정등록
@ResponseBody
@PutMapping("/ypjs/itemReview/update/{itemReviewId}")
public ResponseEntity updateItemReview(@PathVariable(name ="itemReviewId") Long itemReviewId,
                                       @RequestBody @Valid ItemReviewDto itemReviewDto,
                                       HttpSession session) {

    //멤버정보 찾기
    LoginDto.ResponseLogin responseLogin = (LoginDto.ResponseLogin) session.getAttribute("member");


    itemReviewService.updateItemReview(itemReviewId, itemReviewDto);
    ItemReview findItemReview = itemReviewService.findOneItemReview(itemReviewId);

    return ResponseEntity.ok().body(findItemReview.getItem().getItemId());

}

 

 

응답 방식은 나머지 컨트롤러도 다 저렇게 바꿨다.

 

 

타임리프에 버튼 추가

itemReview/itemReviewGet

 <input type="hidden" id="itemId" th:value="${itemReview.itemId}">
            <!-- 수정 및 삭제 버튼 -->
            <div class="container">
                <div class="row justify-content-end">
                    <div class="col-auto">
                        <a th:href="@{/ypjs/itemReview/update/{itemReviewId}(itemReviewId=${itemReview.itemReviewId})}" class="btn btn-warning">수정하기</a>
                    </div>
                    <div class="col-auto">

                        <button type="button"
                                class="btn btn-danger btn-sm btn-delete-itemReview"
                                th:data-itemReviewId="${itemReview.itemReviewId}"

                                style="padding: 2px 10px; font-size: 16px; width: 90px; height: 40px;">
                            삭제하기
                        </button>
                    </div>

                </div>
            </div>
        </div>
    </div>
</div>

 

히든으로 아이템아이디 값 받아와야 삭제 후 원하는 화면(아이템 리뷰 리스트)으로 이동 가능

 

수정 화면

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:replace="frame/header :: head">
    <title>Zay Shop - Product Listing Page</title>
</head>
<body>
<!--header-->
<header th:replace="frame/header :: header"></header>

<!-- Start Content -->
<div class="container py-5">
    리뷰수정
</div>
<!-- End Content -->

<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Summernote 스타일 및 스크립트 -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote-bs4.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote-bs4.min.js"></script>

<!-- JS 파일 포함 -->
<script src="/js/jquery-1.11.0.min.js"></script>
<script src="/js/summernote-lite.js"></script>
<script src="/js/summernote-ko-KR.js"></script>
<script src="/js/item/itemReview.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote.min.js"></script>





<!-- Start Contact -->
<div class="container py-5">
    <div class="row py-5">
        <form class="col-md-9 m-auto" method="put">
            <!-- 폼 요소들 -->
            <div class="row">

                <input type="hidden" id="itemReviewId" th:value="${itemReview.itemReviewId}">


                <div class="form-group mb-3">
                    <label for="itemReviewName">제목</label>
                    <input type="text" class="form-control mt-1" id="itemReviewName" name="itemReviewName" th:value="${itemReview.itemReviewName}" placeholder="제목">
                </div>
                <div class="form-group mb-3">
                    <label for="itemScore">점수</label>
                    <input type="text" class="form-control mt-1" id="itemScore" name="itemScore" th:value="${itemReview.itemScore}" placeholder="점수">
                </div>
                <div class="mb-3">
                    <label for="itemReviewContent">내용</label>
                    <textarea class="form-control" id="itemReviewContent" name="itemReviewContent" th:text="${itemReview.itemReviewContent}" rows="10"></textarea>
                </div>

                <!-- Summernote 초기화 스크립트 -->
                <script th:inline="javascript">
                    $(document).ready(function() {
                        $("#itemReviewContent").summernote({
                            height: 600, // 높이 설정
                            placeholder: '내용을 입력하세요...', // 플레이스홀더 설정
                            callbacks: {
                                onChange: function(contents, $editable) {
                                    // 내용이 변경될 때 처리할 로직 추가 가능
                                }
                            }
                        });
                    });
                </script>

                <!-- 글 등록 버튼 -->
                <div class="row">
                    <div class="col text-end mt-2">
                        <button type="button" id="btn-itemReviewUpdate" class="btn btn-success btn-lg px-3">리뷰수정</button>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>
<!-- End Contact -->

<footer th:replace="frame/footer :: footer"></footer>
</body>
</html>


 

 

js

let itemBoardObject = {
    init: function() {
        let _this = this;

        $("#btn-itemReviewPost").on("click", function() {
            alert("리뷰등록 버튼이 클릭 됨");
            _this.insert();
        }),
         $("#btn-itemReviewUpdate").on("click", function() {
                    alert("수정이 요청되었습니다.");
                    _this.update();
                }),

         $(document).on("click", ".btn-delete-itemReview", function() {
                   // 클릭된 버튼의 데이터 속성에서 itemReviewId를 가져옴
                   let itemReviewId = $(this).data("itemreviewid");
                   alert("삭제가 요청되었습니다.");
                    // 아이템 리뷰 삭제 함수 호출
                   _this.deleteItemReview(itemReviewId);
                    });


    },

    insert: function() {
        alert("리뷰 등록이 요청되었습니다.");

        let itemId = $("#itemId").val();  // URL에 사용할 itemId를 가져옴

        let data = {
            itemId: itemId,  // itemId를 데이터에 포함
            memberId: $("#memberId").val(),  // 멤버 ID 추가
            itemReviewName: $("#itemReviewName").val(),
            itemScore: $("#itemScore").val(),
            itemReviewContent: $("#itemReviewContent").val(),
        };

        $.ajax({
            type: "POST",
            url: "/ypjs/itemReview/post/" + itemId,  // URL에 itemId를 추가
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            success: function(response) {
                alert("리뷰가 성공적으로 등록되었습니다.");
                window.location.href = "/ypjs/item/get/" + itemId;  // 성공 후 리디렉션
            },
            error: function(error) {
                alert("에러 발생: " + JSON.stringify(error));
            }
        });
    },


     update: function() {

        let itemReviewId = $("#itemReviewId").val();


            let updateData = {

                itemReviewName: $("#itemReviewName").val(),
                itemScore: $("#itemScore").val(),
                itemReviewContent: $("#itemReviewContent").val(),
            };

            $.ajax({
                type: "PUT",

                url: "/ypjs/itemReview/update/" + itemReviewId,
                data: JSON.stringify(updateData),
                contentType: "application/json; charset=utf-8",
                success: function(response) {
                window.location.href = "/ypjs/itemReview/get/" + response ;

                },
                error: function(error) {
                    alert("에러 발생: " + JSON.stringify(error));
                    // 에러 발생 시 적절히 처리하도록 수정이 필요할 수 있습니다.
                }
            });
        },




         deleteItemReview: function(itemReviewId) {

                 let itemId = $("#itemId").val();

                 $.ajax({
                     type: "DELETE",
                     url: "/ypjs/itemReview/delete/" + itemReviewId,
                     success: function(response) {
                         alert("리뷰가 성공적으로 삭제되었습니다.");

                         window.location.href = "/ypjs/itemReview/get/" + itemId;
                     },
                     error: function(xhr, status, error) {
                         alert("에러 발생: " + error);
                     }
                 });
             }


        };



$(document).ready(function() {
    itemBoardObject.init();
});




 

등록 ,수정, 삭제 같이 

 

 

 

 

삭제 

레파지토리

//리뷰 삭제
public void deleteItemReview(Long itemReviewId) {
    ItemReview findItemReivew = em.find(ItemReview.class, itemReviewId);
    em.remove(findItemReivew);
}

 

서비스

// 리뷰 삭제
@Transactional
 public void deleteItemReview(Long itemReviewId) {
     ItemReview itemReview = findOneItemReview(itemReviewId);
     Item item = itemReview.getItem();

     itemReviewRepository.deleteItemReview(itemReviewId);

     item.removeItemReview(itemReview);

     item.updateItemRatings();



 }

 

 

컨트롤러

//삭제
@DeleteMapping("/ypjs/itemReview/delete/{itemReviewId}")
public ResponseEntity deleteItemReview(@PathVariable("itemReviewId") Long itemReviewId){
    itemReviewService.deleteItemReview(itemReviewId);

    return ResponseEntity.ok().build();
}

 

 

 

검색, 정렬

 

레파지토리 

//카테고리당 아이템리스트
public List<Item> findAllItemPagingSortByAndKeyword(Long categoryId, String keyword, Pageable pageable, String sortBy) {
    String queryString = "select distinct i from Item i" +
            " join fetch i.category c" +
            " where i.category.categoryId = :categoryId";

    // 검색 조건 추가
    boolean hasKeyword = (keyword != null && !keyword.isEmpty());
    if (hasKeyword) {
        queryString += " and i.itemName like :keyword";
    }


    queryString += " order by "; // 기본 order by 절을 먼저 추가
    switch (sortBy) {
        case "itemRatings":
            queryString += "i.itemRatings desc, i.itemId desc"; // itemRatings로 정렬, 동일한 rating이면 itemId로 정렬
            break;
        case "likeCount":
            queryString += "i.likeCount desc, i.itemId desc"; // likeCount로 정렬, 동일한 likeCount이면 itemId로 정렬
            break;
        case "itemId":
        default:
            queryString += "i.itemId desc"; // 기본적으로 itemId로 정렬
            break;
    }

    TypedQuery<Item> query = em.createQuery(queryString, Item.class)
            .setParameter("categoryId", categoryId)
            .setFirstResult((int) pageable.getOffset())
            .setMaxResults(pageable.getPageSize());

    // 검색 키워드 파라미터 설정
    if (hasKeyword) {
        query.setParameter("keyword", "%" + keyword + "%");
    }

    return query.getResultList();
}


//아이템 전체리스트
public List<Item> findAllItem(String keyword, Pageable pageable, String sortBy) {
    // 기본 쿼리
    String queryString = "select i from Item i";

    // 검색 조건 추가
    boolean hasKeyword = (keyword != null && !keyword.isEmpty());
    if (hasKeyword) {
        queryString += " where i.itemName like :keyword";
    }

    // 정렬 조건
    queryString += " order by ";
    switch (sortBy) {
        case "itemRatings":
            queryString += "i.itemRatings desc, i.itemId desc";
            break;
        case "likeCount":
            queryString += "i.likeCount desc, i.itemId desc";
            break;
        case "itemId":
        default:
            queryString += "i.itemId desc";
            break;
    }

    TypedQuery<Item> query = em.createQuery(queryString, Item.class)
            .setFirstResult((int) pageable.getOffset())
            .setMaxResults(pageable.getPageSize());

    // 검색 키워드 파라미터 설정
    if (hasKeyword) {
        query.setParameter("keyword", "%" + keyword + "%");
    }

    return query.getResultList();
}

 

if문으로 했을 때는 정렬이 안됐는데 switch로 바꾸니까 정렬 됨

 

 

서비스

//카테고리당 아이템 조회(정렬,검색,페이징)
public List<ItemListDto> finaAllItemPagingSortBy(Long categoryId, String keyword, Pageable pageable, String sortBy) {
    List<Item> items = itemRepository.findAllItemPagingSortByAndKeyword(categoryId, keyword, pageable, sortBy);

    List<ItemListDto> result = items.stream()
            .map(ItemListDto::new)
            .collect(Collectors.toList());

    return result;

}



//아이템 아이디 전체 조회
public List<ItemListDto> findAllItem(String keyword, Pageable pageable, String sortBy) {
    List<Item> items = itemRepository.findAllItem(keyword, pageable, sortBy);


    List<ItemListDto> result = items.stream()
            .map(ItemListDto::new)
            .collect(Collectors.toList());

    return result;

}

 

 

컨트롤러

//카테고리당 아이템 조회(정렬,검색,페이징(페이징받아서 페이징))
@GetMapping("/ypjs/categoryItem/get/{categoryId}")
public String getAllCategoryItem(@PathVariable("categoryId") Long categoryId,
                                 @RequestParam(value = "page",defaultValue = "0") int page,
                                 @RequestParam(value = "size",defaultValue = "6") int size,
                                 @RequestParam(value = "sortBy", defaultValue = "itemId") String sortBy,
                                 @RequestParam(value = "keyword", required = false) String keyword,
                         Model model) {

    Pageable pageable = PageRequest.of(page, size);

    Category category = categoryService.findOneCategory(categoryId);

    List<ItemListDto> items = itemService.finaAllItemPagingSortBy(categoryId, keyword, pageable, sortBy);

    model.addAttribute("items",items);
    model.addAttribute("category", category);
    model.addAttribute("sortBy", sortBy); // 정렬 옵션을 다시 모델에 추가
    model.addAttribute("keyword", keyword); //검색조건 유지



    return "item/itemCategoryList";


}



//아이템 전체 조회
@GetMapping("/ypjs/item/get")
public String getAllItem(
                                 @RequestParam(value = "page",defaultValue = "0") int page,
                                 @RequestParam(value = "size",defaultValue = "6") int size,
                                 @RequestParam(value = "sortBy", defaultValue = "itemId") String sortBy,
                                 @RequestParam(value = "keyword", required = false) String keyword,
                                 Model model) {

    Pageable pageable = PageRequest.of(page, size);

    List<ItemListDto> items = itemService.findAllItem(keyword, pageable, sortBy);

    model.addAttribute("items", items);
    model.addAttribute("sortBy", sortBy); // 정렬 옵션을 다시 모델에 추가
    model.addAttribute("keyword", keyword); //검색조건 유지


    return "item/itemList";


}

 

 

 

타임리프

item/itemList

<div class="col-lg-6">
    <!-- 검색 폼 및 정렬 폼 통합 -->
    <form th:action="@{/ypjs/item/get}" method="get" class="input-group">
        <input type="text" id="keyword" name="keyword" class="form-control"
               placeholder="제목으로 검색하기" th:value="${keyword}">
        <div class="input-group-append">
            <button type="submit" class="btn btn-link text-dark">
                <i class="fa fa-search"></i>
            </button>
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <div class="col-auto">
            <select class="form-control" name="sortBy" id="sortBy" onchange="this.form.submit()">
                <option value="itemId" th:selected="${sortBy == 'itemId'}">최신순</option>
                <option value="itemRatings" th:selected="${sortBy == 'itemRatings'}">별점 높은 순</option>
                <option value="likeCount" th:selected="${sortBy == 'likeCount'}">찜많은순</option>
            </select>
        </div>
    </form>
</div>

 

 

item/itemCategoryList

<div class="col-lg-6">
    <!-- 검색 폼 및 정렬 폼 통합 -->
    <form th:action="@{/ypjs/categoryItem/get/{categoryId}(categoryId=${category.categoryId})}" method="get" class="input-group">
        <input type="text" id="keyword" name="keyword" class="form-control"
               placeholder="제목으로 검색하기" th:value="${keyword}">
        <div class="input-group-append">
            <button type="submit" class="btn btn-link text-dark">
                <i class="fa fa-search"></i>
            </button>
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <div class="col-auto">
            <select class="form-control" name="sortBy" id="sortBy" onchange="this.form.submit()">
                <option value="itemId" th:selected="${sortBy == 'itemId'}">최신순</option>
                <option value="itemRatings" th:selected="${sortBy == 'itemRatings'}">별점 높은 순</option>
                <option value="likeCount" th:selected="${sortBy == 'likeCount'}">찜많은순</option>
            </select>
        </div>
    </form>
</div>


 

추가

 

 

 

 

모든 아이템 별점 높은 순 

 

 

 

 

카테고리 4번에 제목 2 별점 높은 순