Phân trang trong EzyPlatform
Giới thiệu về PaginationModel và chức năng
1. PaginationModel:
Đầu tiên để hiểu về cách phân trang trong EzyPlatform
chúng ta có Class PaginationModel
với các thuộc tính sau:
Trong đó:
PageToken
chứa các dữ liệu về nútnext
vàprev
với kiểu dữ liệu làString
Continuation
có chứa 2 thuộc tính kiểm trahasNext
vàhasPrev
với kiểu dữ liệu làBoolean
nhằm kiểm tra xem điểm đầu và điểm cuối của dữ liệu
2. PaginationPostService
Trong bộ thư viện Ezyarticle
chúng ta có các package service được cấu hình sẵn các phương thức hỗ trợ cho việc sử dụng dễ dàng hơn.
Ở đây khi làm việc với các bài Post ta có Class PaginationPostService
sử dụng cho việc phân trang các bài Post:
ta có thể thấy Class này được extends tới Abstract Class CommonPaginationService
đây là 1 class được sử dụng chung cho việc phân trang cho các Entity trong dự án của bạn, Class này nằm trong thư mục ezyplatform.service các bạn có thể tìm hiểu thêm tại đường dẫn:
package org.youngmonkeys.ezyplatform.service;
3. PaginationModelFetchers
Class này được sử dụng cho việc lấy dữ liệu phân trang, nó cung cấp các phương thức để lấy trang đầu tiên, trang cuối, trang tiếp theo và trang trước dựa trên các tham số như PaginationService, filter, nextPageToken, prevPageToken, thông tin là trang cuối (lastPage) và giới hạn số phần tử trên trang (limit).
- Class này nằm trong package:
package org.youngmonkeys.ezyplatform.pagination;
4.CommonValidator
Class này sử dụng các phương thức để thực hiện các kiểm tra và xử lý lỗi cho các tham số đầu vào trong Web
- Class này nằm trong package:
package org.youngmonkeys.ezyplatform.validator;
Cách hoạt động:
- Class
CommonValidator
có các phương thức chủ yếu để kiểm tra tính hợp lệ của các đầu vào từ người dùng. - Nếu một điều kiện không được đáp ứng (ví dụ: pageSize không hợp lệ, kích thước Collection quá lớn, v.v.), nó sẽ ném ra một HttpBadRequestException với một thông báo lỗi phù hợp.
- Sử dụng Collections.singletonMap để chỉ định lỗi cụ thể (ví dụ: "pageSize": "invalid").
Cách cài đặt
B1: đầu tiên chúng ta tạo ra Class Response
chứa các thông tin Model cần gửi tới Font-End theo ví dụ sau:
Trong Class này mình chỉ lấy ra các nội dung chính của bài viết như: tiêu đề, nội dung, hình ảnh..
B2: tiếp theo tại Class Service
của bạn hãy thêm các thông tin sau:
@Service
@AllArgsConstructor
public class WebBlogPostControllerService { private final WebPostFilterFactory postFilterFactory; private final WebPaginationPostService paginationPostService; private final WebBlogPostModelDecorator blogPostModelDecorator; public PaginationModel<WebBlogPostResponse> getBlogPostPagination( String keyword, String nextPageToken, String prevPageToken, boolean lastPage, int limit ) { PaginationModel<PostModel> pagination = PaginationModelFetchers.getPaginationModel( this.paginationPostService, postFilterFactory.newDefaultPostFilterBuilder(keyword) .postStatus(PostStatus.PUBLISHED.toString()) .build(), nextPageToken, prevPageToken, lastPage, limit ); return blogPostModelDecorator.decorateBlogPostPagination( pagination ); }
Chúng ta có thể thấy tại phương thức getBlogPostPagination
được tạo ra với nhiệm vụ lấy danh sách Blog để phân trang dựa trên các tham số được truyền vào và sau khi có được pagination, phương thức này sẽ gửi dữ liệu tới Class Decorator để tiến hành bước tiếp theo
B3: sau khi có dữ liệu, chúng ta tiếp tục tạo ra Class Decorator
với các thông tin như sau:
@EzySingleton
@AllArgsConstructor
public class WebBlogPostModelDecorator { private final WebMediaService mediaService; private final WebPostSlugService postSlugService; private final WebBlogModelToResponseConverter blogModelToResponseConverter; public PaginationModel<WebBlogPostResponse> decorateBlogPostPagination( PaginationModel<PostModel> pagination ) { List<PostModel> models = pagination.getItems(); List<Long> postIds = newArrayList( models, PostModel::getId ); Map<Long, String> slugByPostId = postSlugService.getLatestSlugMapByPostIds( postIds ); Set<Long> imageIds = newHashSet( models, PostModel::getFeaturedImageId ); Map<Long, MediaNameModel> imageById = mediaService .getMediaNameMapByIds(imageIds); return pagination.map(it -> blogModelToResponseConverter.toBlogPostResponse( it, slugByPostId.get(it.getId()), imageById.get(it.getFeaturedImageId()) ) ); }
}
Class này có nhiệm vụ lấy ra các thông tin được gửi tới từ phương thức getBlogPostPagination
trước đó và tiến hành tìm kiếm các thông tin mà phương thức blogModelToResponseConverter.toBlogPostResponse
(được khởi tạo ở bước 1) yêu cầu
B4: Cuối cùng tại Controller chúng ta truyền tham số và gọi tới Service như sau:
@Controller
@AllArgsConstructor
public class HomeController { private final WebBlogPostControllerService blogPostControllerService; private final WebCommonValidator commonValidator; @DoGet("/") public View home( @RequestParam(value = "keyword") String keyword, @RequestParam(value = "nextPageToken") String nextPageToken, @RequestParam(value = "prevPageToken") String prevPageToken, @RequestParam(value = "lastPage") boolean lastPage, @RequestParam(value = "limit", defaultValue = "2") int limit ) { commonValidator.validatePageSize(limit); return View.builder() .template("home") .addVariable( "postPagination", blogPostControllerService.getBlogPostPagination( keyword, nextPageToken, prevPageToken, lastPage, limit ) ) .addVariable(VIEW_VARIABLE_PAGE_TITLE, "home") .build(); }
Tại controller, ta sử dụng CommonValidator
sẽ kiểm tra xem các giá trị pageSize có hợp lệ hay không, nếu các giá trị hợp lệ chúng ta sẽ gửi Variable tới giao diện như sau:
.addVariable( "postPagination", blogPostControllerService.getBlogPostPagination( keyword, nextPageToken, prevPageToken, lastPage, limit )