Xây dựng website đặt phòng khách sạn với Meta Box – P4 – Trang quản lý booking

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:

  1. Đã 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ác post_type của mình là room 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é.
  2. 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:

Các thư mục bên trong một plugin quản lý booking
Các thư mục chính bên trong plugin

Đố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.

Plugin quản lý booking đã được kích hoạt

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.

Trang quản lý booking khách sạn trong backend
Trang Quản lý Booking trong backend

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 plugin.js lần lượt trong 2 thư mục là cssjs. 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__ ) );
Import thư viện và các file vào plugin quản lý booking khác sạn

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:

Các file và thư viện mà bạn import vào plugin quản lý booking khách sạn đã hoạt động

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.

Hiển thị các dữ liệu đặt phòng khách sạn trên lịch

4.2. Tạo thanh Navbar chức năng

Để 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 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:

Tạo thanh Navbar để xem lịch booking của một loại phòng nhất định

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!

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *