Kết thúc phần 3 của series bài viết về việc xây dựng website đặt phòng khách sạn với Meta Box, chúng ta đã có một hệ thống cơ bản để tạo và đặt phòng cho một khách sạn vừa và nhỏ rồi. Thế nhưng, bạn vẫn cần một công cụ nào đó để tổng hợp toàn bộ các booking đó để dễ dạng quản lý và sắp xếp lịch làm việc cho phù hợp đúng không.
Vì vậy, trong phần 4 này, mình sẽ tiếp tục hướng dẫn các bạn để đưa thông tin các booking lên một trang để các bạn có thể xem được lịch các booking một cách trực quan, đồng thời cũng dễ dàng kiểm tra thông tin từng booking.
Mình sẽ sử dụng một công cụ có giao diện gần giống như Google Calendar vậy. Mỗi booking sẽ được đưa vào lịch này và hiển thị như một event trong lịch. Hy vọng cách làm này sẽ phù hợp với bạn. Bạn hãy thử xem nhé!
Chuẩn bị
Để phục vụ cho các thao tác trong phần này, các bạn cần chuẩn bị một số thứ như sau:
- Đã thực hiện theo hướng dẫn ở các phần 1-2-3 trong series này. Đặc biệt lưu ý là:
Ở trong các phần trước, mình đặt tên cácpost_type
của mình là room và booking, cùng với một group field được tạo bởi plugin Meta Box làgroup_booking
trong mỗi bài viết booking (đặt phòng). Nếu bạn đã thay đổi các tên này khi thao tác ở phần 1-2-3, thì ở trong các đoạn code trong bài viết này, bạn cũng cần sửa lại tương ứng cho khớp nhé. - Sử dụng một thư viện jQuery có tên là FullCalendar. Hướng dẫn sử dụng thư viện tại đây.
Bây giờ, cùng bắt tay vào thực hiện nhé.
Bước 1: Tạo một plugin mới cho việc quản lý booking
1.1. Tạo thư mục cho plugin mới
Bạn truy cập vào wp-content/plugin
và tạo một thư mục mới nhé. Thư mục này chính là thư mục mà mình sử dụng để làm plugin quản lý các booking, nên mình đặt tên là mb-hotel-management
. Mình đặt tiền tố là mb-
để biết là sử dụng cho Meta Box và cũng là để phân biệt với các plugin khác, tránh bị trùng tên.
1.2. Tạo các tập tin cần thiết cho plugin
Mỗi plugin thì đều có ít nhất 2 tập tin cơ bản đó là 1 file PHP chính và một file readme.txt
.
- File PHP chính sẽ được dùng để viết code, mình đặt tên file là
mb-hotel-management.php
. - File
readme.txt
sẽ được dùng để chứa các thông tin về tên, thông tin các phiên bản cũng như các thông tin tổng quan khác về plugin. Nếu plugin bạn tạo sẽ chỉ dùng cho cá nhân bạn, thì bạn cũng có thể bỏ qua file này.
Bên trong thư mục plugin mà mình tạo, các folder và file sẽ có như sau:

Đối với file PHP chính của plugin (mb-hotel-management.php
), bạn thêm đoạn code như sau và điền các thông tin vào để khai báo các thông tin cơ bản của plugin:
/** * Plugin Name: // Tên của plugin * Plugin URL: // Địa chỉ trang chủ của plugin * Description: // Phần mô tả cho plugin * Version: // Phiên bản của plugin * Author: // Tên tác giả, người thực hiện plugin này * Author URL: // Địa chỉ trang chủ của tác giả * License: GPLv2 or later // Thông tin license của plugin, nếu không quan tâm thì bạn cứ để GPLv2 vào đây */
1.3. Kích hoạt plugin
Sau khi hoàn thành hai bước trên, nếu bạn thao tác đúng, thì bạn sẽ thấy plugin mới của bạn hiển thị trong danh sách các plugin có trên website.

Bạn hãy kích hoạt plugin. Sau khi kích hoạt thành công, thì chúng ta có thể bắt đầu các bước tiếp theo để xây dựng tính năng cho plugin này nhé.
Bước 2: Tạo trang giao diện trong backend cho plugin
Đầu tiên, mình sẽ tạo một trang giao diện trống trong backend cho plugin. Trang này sẽ được chúng ta sử dụng để hiển thị các thông tin về các booking. Việc thêm và hiển thị thông tin các booking lên trang này sẽ được thực hiện ở các bước sau nhé. Bước này chỉ tạm thời tạo trang thôi đã.
Trong thư mục inc của plugin, chúng ta tạo một file mới tên là front.php
. Sau đó, bạn mở file mb-hotel-management.php
lên và thêm đoạn code sau vào:
<?php class BookingManagement { public function __construct() { add_action( 'admin_menu', array( $this, 'create_menu_admin_panel' ) ); } public function create_menu_admin_panel() { add_menu_page( ‘Booking Management’, ‘Booking Management’, 'edit_posts', 'manager-booking', array($this, 'manager_booking' ) ); } public function booking_management() { if (!current_user_can( 'edit_posts' )) { wp_die(__('You do not have sufficient permission to access this page.') ); } include 'inc/front.php'; } } $Init = new BookingManagement(); ?>
Quay trở lại giao diện admin, bạn sẽ thấy menu Booking Management (Quản lý Booking) xuất hiện. Bạn click vào menu đó sẽ thấy xuất hiện một trang trắng, do chúng ta chưa viết nội dung gì cho file front.php
.

Bước 3: Import thư viện và các file vào plugin
Bây giờ, chúng ta sẽ import các thư viện và các file để tùy chỉnh giao diện vào plugin.
Bạn đến thư mục lib
của plugin và copy thư viện FullCalendar vào đó. Bạn có thể tham khảo thêm một số thông tin về các file cần thiết và cách cài đặt chi tiết ở đây.
Ngoài ra, mình sẽ tạo thêm 2 file nữa là plugin.css
và plugin.js
lần lượt trong 2 thư mục là css
và js
. Chúng ta sẽ sử dụng 2 file này để thực hiện các chỉnh sửa hay thêm các hàm chức năng chính cho plugin.
Tiếp tục, bạn thêm đoạn code sau vào hàm booking_management()
mà lúc trước chúng ta đặt vào file mb-hotel-management.php
để import các thư viện và khai báo các file tùy chỉnh trên.
wp_enqueue_style( 'fullcalendar-core', plugins_url( 'lib/fullcalendar/packages/core/main.css', __FILE__ ) ); wp_enqueue_style( 'fullcalendar-daygrid', plugins_url( 'lib/fullcalendar/packages/daygrid/main.css', __FILE__ ) ); wp_enqueue_style( 'css-custom', plugins_url( 'css/plugin.css', __FILE__ ) ); wp_enqueue_script( 'fullcalendar-core', plugins_url( 'lib/fullcalendar/packages/core/main.js', __FILE__ ) ); wp_enqueue_script( 'fullcalendar-daygrid', plugins_url( 'lib/fullcalendar/packages/daygrid/main.js', __FILE__ ) ); wp_enqueue_script( 'js-custom', plugins_url( 'js/plugin.js', __FILE__ ) );

Tiếp theo, bạn mở file plugin.js
và thêm đoạn code sau vào:
document.addEventListener('DOMContentLoaded', function() { var calendarEl = document.getElementById('calendar'); var calendar = new FullCalendar.Calendar(calendarEl, { plugins: ['dayGrid'], defaultView: 'dayGridMonth', defaultDate: '2020-02-07', header: { left: 'prev,next today', center: 'title', }, events: [ { title : 'event1', start : '2020-02-27' }, { title : 'event2', start : '2020-02-15', end : '2020-02-17' }, { title : 'event3', start : '2020-02-09T12:30:00', allDay : false // will make the time show } ] }); calendar.render(); });
Cuối cùng, mở file front.php
và thêm đoạn code sau:
<div class="wrap"> <h1 class="wp-heading-inline">Booking Managerment</h1> <div id='calendar'></div> </div>
Cả hai đoạn code trên là để kiểm tra xem các file và thư viện mà ta import vào plugin đã hoạt động chưa. Nếu bạn thao tác đúng thì kết quả sẽ như sau:

Bước 4: Xây dựng tính năng hoàn chỉnh cho plugin
Ý tưởng về trang quản lý booking của mình là sẽ tạo mỗi lịch cho mỗi một loại phòng. Mỗi sự kiện trên lịch sẽ đại diện cho một booking và hiển thị như event1, event2, và event3, … như mình đã tạo ở mẫu trên.
Sau đó, mình sẽ tạo thêm một lịch chung, gộp chung lịch của các loại phòng vào một lịch chung chung này để người quản lý xem được lịch đặt phòng của tất cả các loại phòng trên cùng một trang.
Để làm được như vậy, chúng ta sẽ cần code khá nhiều ở bước 4 này và cũng có khá nhiều cách tùy biến. Bạn tham khảo cách làm của mình dưới đây. Nếu có phần nào cần giải đáp hoặc trao đổi thêm, hãy để lại comment để chúng ta cùng thảo luận nhé.
4.1. Hiển thị dữ liệu booking lên lịch
Mình sẽ dùng AJAX để tạo ra một biến chứa tất cả các lịch booking của khách sạn. Sau đó, mình sẽ gọi lại hàm của FullCalendar để đưa các booking này hiển thị lên lịch.
Ở hàm _contruct
của plugin, mình tạo thêm 2 action:
add_action( 'wp_ajax_feed_events', array( $this, 'feed_events_func') ); add_action( 'wp_ajax_nopriv_feed_events', array( $this, 'feed_events_func' ) );
Tiếp theo, mình tạo hàm feed_events_func()
để trả về lịch booking ở dạng JSON như sau:
public function feed_events_func(){ $args = array( 'post_type' => 'booking', 'posts_per_page' => -1, ); $query = new WP_Query( $args ); if ( $query->have_posts() ) { $events = array(); while( $query->have_posts() ) { $query->the_post(); $bookings = get_post_meta( get_the_ID(), 'group_booking', true ); if ($bookings) { foreach ($bookings as $key => $booking) { $room = $booking['room']; $begin = $booking['check_in']; $end = $booking['check_out']; $e['title'] = "#".get_the_ID().' '.get_the_title($room); $e['start'] = $begin; $e['end'] = $end; $e['url'] = get_edit_post_link(get_the_ID(),''); $e['classNames'] = 'room-'.$room; $e['allDay'] = true; array_push($events, $e); } } } echo json_encode($events); } wp_die(); }
Trong đó:
$events
: mảng chứa tất cả các booking.'title'
,'start'
,'end'
,'url'
, … : là các tham số trong event. Bạn có thể tham khảo thêm các tham số tại đây. Mình sẽ giải thích một vài tham số đặc biệt như:'title'
: là tên của sự kiện và sẽ được hiển thị lên lịch. Ở đây mình đặt tên dưới dạng là#{booking_id}
{Tên loại phòng}.'url'
: là trang đích khi người dùng click vào tên booking trên lịch.'classNames'
: mình dùng để đặt class cho mỗi booking. Cái này sẽ được dùng và giải thích rõ hơn ở bước sau nhé.
Bây giờ bạn mở tiếp file plugin.js
rồi thêm đoạn code dưới đây để tải dữ liệu JSON trả về ở trên:
$.ajax({ type: 'post', url: ajaxurl, data: {action: 'feed_events'}, error: function(err){ console.log(err); }, success: function (data) { init_calendar(data); } }) function init_calendar(events){ var calendarEl = document.getElementById('calendar'); var calendar = new FullCalendar.Calendar(calendarEl, { plugins: [ 'dayGrid' ], defaultView: 'dayGridMonth', header: { left: 'prev,next today', center: 'title', right: '' }, events: events, eventPositioned (view, element) { displayBookings(); }, }); calendar.render(); }
Trong đó:
- Đoạn AJAX là để lấy biến chứa tất cả booking từ hàm
feed_events_func
. - Hàm
init_fullcalendar(events)
là hàm nguyên bản nhưng mình truyền vào biến events là dữ liệu lúc AJAX trả về.
Sau bước này, bạn sẽ có được lịch đặt phòng của tất cả các booking, và các booking này đều được hiển thị trên lịch. Sau khi click vào mỗi booking, thì người dùng sẽ được chuyển đến trang thông tin đặt phòng, chính là tham số 'url'
mà mình truyền vào ở trên.

Để tiện cho việc xem lịch booking của từng loại phòng, mình tạo thêm một thanh Navbar. Mỗi một nút trên thanh Navbar là một loại phòng. Khi click vào mỗi nút này, thì bạn sẽ xem được lịch booking của riêng một loại phòng đó, và lịch book của các loại phòng khác sẽ được ẩn đi.
Chúng ta sẽ chỉ cần sử dụng một chút CSS và JavaScript là được.
Ở trên mình đã đặt tên cho class của mỗi booking ở dạng room-{$room_id}
. Do đó, mình chỉ cần xử lý để khi click vào loại phòng nào thì các class có tên tương ứng với loại phòng đó mới được hiển thị. Nói đơn giản hơn thì đây chính là mình tạo bộ lọc bằng room_id
.
Bạn mở file front.php
trong thư mục inc
ra và thêm đoạn code dưới đây vào bên trên đoạn khai báo lịch <div id='calendar'></div>
. Cái này là để tạo thanh navbar chứa danh sách các loại phòng, mỗi loại phòng là 1 nút.
<div class="navbar mb-4"> <?php $args = arry( 'post_type' => 'room', 'posts_per_page' => -1, 'post__not_in' => array(498) ); $query = new WP_Query( $args ); if ( $query->have_posts() ) { echo '<button class="button filter-bookings" data-room="all>ALL</button'; while( $query->have_posts() ) { $query->the_post(); ?> <button class="button filter-bookings" data-room="<?php the ID(); ?>"><?php the_title(); ?></button> <?php } } ?> </div>
Trong đó: 'post__not_in'
: mình truyền vào ID (498) là ID của extra-bed
. Ở các phần trước mình đặt extra-bed
là 1 loại phòng, nên mình không muốn thống kê nó lên lịch.
Bây giờ mình sẽ xử lý để khi click vào mỗi nút (1 loại phòng) thì sẽ chỉ hiển thị các booking của loại phòng đó. Bạn thêm đoạn code dưới đây vào file plugin.js
:
$('.filter-bookings').on('click', function(event){ event.stopPropagation(); var room_id = $(this).attr('data-room'); $('.wrap').attr('class', 'wrap ' + room_id); displayBookings(); }) function displayBookings(){ var classes = $('.wrap').attr('class'); if (classes != 'wrap') { room = classes.replace("wrap ", ""); if (room == 'all') { $('[class*="room-"]').show(); } else { $('[class*="room-"]').hide(); $('.room-'+room).show(); } } }
Trong đó:
displayBookings()
là hàm dùng để ẩn các booking.$('.filter-bookings')
là để bắt sự kiện lúc các button trên thanh navbar được click.
Đến đây, bạn đã có thể xem được từng loại phòng bằng cách click vào nút tương ứng, các loại phòng không liên quan sẽ được ẩn đi. Tuy nhiên, khi bạn chuyển sang xem lịch booking ở 1 tháng khác trong năm, thì tất cả các booking vẫn được hiển thị chứ không ẩn đi. Đây là do đoạn JavaScript của mình không được thực hiện khi mà các element html được render lại. Vì vậy, mình phải bắt thêm cả sự kiện lúc render booking và gọi lại hàm một lần nữa.
Ở thư viện FullCalendar v4.0, mình sử dụng callback có tên là eventPositioned. Callback này sẽ được thực hiện khi mà sự kiện cuối cùng được load.
Bạn thêm đoạn code sau vào lúc gọi thư viện FullCalendar:
eventPositioned (view, element) { displayBookings(); }
Và đây là kết quả cuối cùng sau khi bạn hoàn thiện tất cả các thao tác trên:

Còn đây là toàn bộ source code của plugin mình đã tạo, bạn tham khảo nhé:
Lời cuối
Ở phần 4 này, các bạn cũng đã được tìm hiểu cơ bản về cách lập trình một plugin trong WordPress, cách sử dụng và tiếp cận thư viện FullCalendar và các API của nó. Tuy là chưa thể đi sâu vào từng vấn đề, nhưng hy vọng bài viết này cũng như cả 4 bài viết trong series này có thể mở ra vài ý tưởng và thông tin cho các bạn để phát triển thêm các chức năng mới cho plugin cũng như phục vụ cho việc quản lý đặt phòng trên website.
Nếu bạn cần trao đổi thêm, hãy để lại comment nhé. Chúc các bạn thành công!