Hướng dẫn tạo trang OTA giống booking.com với Meta Box – P2- Tạo bộ lọc tìm kiếm trong trang archive

Trong phần 1 của series “Hướng dẫn tạo trang OTA giống booking.com với Meta Box”, chúng ta đã hoàn thành việc tạo post type cho khách sạn và tạo các custom field để nhập dữ liệu cho việc book phòng. Như đã hứa, ở phần 2, mình sẽ hướng dẫn cách tạo bộ lọc ở ngoài trang archive và chèn bộ lọc này vào widget.

Sau đây là kết quả của phần 2 này. Như bạn thấy, mình tạo được bộ lọc ở sidebar trên trang archive:

Tạo bộ lọc tìm kiếm ở sidebar trên trang archive của trang OTA bằng plugin Meta Box

Lưu ý: bạn không cần phải thêm plugin nào ngoài những plugin đã cài ở phần 1 nhé.

Sau đây là các bước thực hiện:

Tạo bộ lọc tìm kiếm

Bước 1: Tạo bộ lọc và shortcode tương ứng trên trang archive

Chúng ta cần tạo bộ lọc và shortcode cho các bộ lọc đó ở trong file functions.php. Dưới đây là code mình sử dụng cho từng bộ lọc:

Tạo bộ lọc và shortcode cho “Search Hotel” – bộ lọc chính:

function justread_shortcode_main_filter() {
	ob_start();
	?>

	<div class="filter-hotel">
		<p>Search Hotel</p>
		<input class="filter-input" id="location" type="" name="" placeholder="Destination">
		<input class="filter-input" id="check-in-date" type="" name="" placeholder="Check-in date">
		<input class="filter-input" id="check-out-date" type="" name="" placeholder="Check-out date">
		<input style="width: 48%; float: left;" class="filter-input" id="adults" type="" name="" placeholder="Adults">
		<input style="width: 48%; float: right;" class="filter-input" id="children" type="" name="" placeholder="Children">
		<input class="filter-action" type="submit" name="" value="Search">
	</div>

	<?php
	return ob_get_clean();
}
add_shortcode( 'justread_shortcode_main_filter', 'justread_shortcode_main_filter' );

Tạo bộ lọc và shortcode cho “Star rating” phân loại theo số sao của khách sạn:

function justread_shortcode_rate_hotel() {
	ob_start();
	$terms = get_terms( array(
		'taxonomy'   => 'rate',
		'hide_empty' => false,
	) );
	foreach ( $terms as $term ) {
		echo '<a class="filter-sidebar filter-checkbox" data-rate-value="' . $term->name . '">
				<input class="filter-checkbox__input" type="checkbox" name="filter-rate" value="' . $term->name . '">
				<div class="filter-checkbox__label">
					<span class="filter_label">' . $term->name . '</span>
					<span class="filter_count">(' . $term->count . ')</span>
				</div>
			  </a>';
	}
	return ob_get_clean();
}
add_shortcode( 'justread_shortcode_rate_hotel', 'justread_shortcode_rate_hotel' );

Tạo bộ lọc và shortcode cho “Property type” phân loại khách sạn:

function justread_shortcode_hotel_type() {
    ob_start();
    ?>

    <a class="filter-sidebar filter-hotel-type" data-hotel-type-value="hotel">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Hotel</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-hotel-type" data-hotel-type-value="apartment">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Apartment</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-hotel-type" data-hotel-type-value="homestay">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Homestay</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-hotel-type" data-hotel-type-value="villa">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Villa</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-hotel-type" data-hotel-type-value="resort">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Resort</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-hotel-type" data-hotel-type-value="motel">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Motel</span>
   	 </div>
    </a>
    <?php

    return ob_get_clean();
}
add_shortcode( 'justread_shortcode_hotel_type', 'justread_shortcode_hotel_type' );

Tạo bộ lọc và shortcode cho “Facilities” để lọc theo cơ sở vật chất của khách sạn:

function justread_shortcode_facilities() {
    ob_start();
    ?>

    <a class="filter-sidebar filter-facilities" data-facilities-value="parking">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Parking</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-facilities" data-facilities-value="retaurant">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Restaurant</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-facilities" data-facilities-value="wi-fi">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Free wifi</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-facilities" data-facilities-value="swimming-pool">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Swimming pool</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-facilities" data-facilities-value="family-room">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Family room</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-facilities" data-facilities-value="bus-from-airport">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Bus from airport</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-facilities" data-facilities-value="elevator">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Elevator</span>
   	 </div>
    </a>

    <?php
    return ob_get_clean();
}
add_shortcode( 'justread_shortcode_facilities', 'justread_shortcode_facilities' );

Tạo bộ lọc và shortcode cho “Room facility” để chọn theo cơ sở vật chất của các phòng trong khách sạn:

function justread_shortcode_room_facilities() {
    ob_start();
    ?>

    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="kitchen">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Kitchen</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="bathroom">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Bathroom</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="air-conditioning">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Air conditioning</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="toilet">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Toilet</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="tv">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">TV</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="balcony">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Balcony</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="washing-machine">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Washing machine</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-room-facilities" data-room-facilities-value="sea-view">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Sea view</span>
   	 </div>
    </a>

    <?php
    return ob_get_clean();
}
add_shortcode( 'justread_shortcode_room_facilities', 'justread_shortcode_room_facilities' );

Tạo bộ lọc và shortcode cho “Price per night” để lọc theo giá phòng/đêm:

function justread_shortcode_hotel_price() {
    ob_start();
    ?>

    <a class="filter-sidebar filter-price" data-min-price="0" data-max-price="50">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">Under $50</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-price" data-min-price="50" data-max-price="100">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">From $50 to $100</span>
   	 </div>
    </a>
    <a class="filter-sidebar filter-price" data-min-price="100" data-max-price="200">
   	 <input class="filter-checkbox__input" type="checkbox">
   	 <div class="filter-checkbox__label">
   		 <span class="filter_label">From $100 to $200</span>
   	 </div>
    </a>
    <?php

    return ob_get_clean();
}
add_shortcode( 'justread_shortcode_hotel_price', 'justread_shortcode_hotel_price' );
  • Trong tất cả các đoạn code trên, hàm có dạng function justread_shortcode_hotel_price dùng để gắn vào shortcode justread_shortcode_hotel_price.
  • Còn hàm có dạng add_shortcode( 'justread_shortcode_hotel_price', 'justread_shortcode_hotel_price' ) dùng để tạo shortcode dạng justread_shortcode_hotel_price – đây cũng là shortcode bạn sẽ cần gắn vào widget như hướng dẫn ở bước 2. Bạn có thể đặt tên shortcode tùy ý sao cho dễ nhớ nhất nhé.

Bước 2: Chèn shortcode vào widget

Để chèn shortcode vào widget, rất đơn giản, bạn vào Appearance > Widgets, kéo thả widget Text vào sidebar.

Để chèn shortcode vào widget của website WordPress OTA giống booking.com

Sau đó thêm shortcode vào như sau:

Thêm shortcode cho bộ lọc tìm kiếm  của trang archive trên website OTA giống booking.com

Sau khi thêm shortcode vào 6 widget riêng dạng Text và đặt widget trên sidebar, bạn nhớ ấn Save.

Bây giờ ra ngoài front end bạn sẽ thấy các bộ lọc hiển thị ra nhưng chưa được đẹp mắt cho lắm. Nếu bạn muốn hiển thị chúng giống như ảnh của mình ở đầu bài, hãy thêm css vào file style.css của child theme nhé. Bạn tham khảo css của mình tại đây.

Tạo tính năng tìm kiếm

Bước 1: Tạo và khai báo file js để làm tính năng cho bộ lọc

Để bộ lọc hoạt động được (nhận thông tin cần lọc từ người dùng và hiển thị ra bài viết tương ứng), chúng ta cần tạo thêm một file js. Mình đặt tên file này là filter-hotel.js và khai báo nó trong file functions.php bằng đoạn code sau:

function justread_custom_scripts() {
	$terms = get_terms( array(
		'taxonomy'    => 'location',
		'hide_empty' => false,
	) );
	foreach ( $terms as $term ) {
		$location[] = $term->name;
	}
	$object = [
		'ajax_url' => admin_url( 'admin-ajax.php' ),
		'location_autocomplete' => $location,
	];

	wp_enqueue_script( 'jquery-ui-autocomplete' );
	wp_enqueue_script( 'justread-ajax-filter-hotel', get_stylesheet_directory_uri() . '/js/filter-hotel.js', array( 'jquery' ), '', true );
	wp_localize_script( 'justread-ajax-filter-hotel', 'ajax_object', $object );
}
add_action( 'wp_enqueue_scripts', 'justread_custom_scripts' );

File js này có nhiệm vụ gửi các thông tin cần lọc đến file functions.php, nhận dữ liệu trả về từ file functions.php rồi hiển thị nó ra frontend. Vậy nên file js này có nội dung như sau:

jQuery( function ( $ ) {
	function filterHotel() {
		$( '.check-in-date, .check-out-date' ).datepicker({
			minDate: 0,
			numberOfMonths: 2,
			showButtonPanel: true,

		});
		var location = ajax_object.location_autocomplete;
		$( '#location' ).autocomplete({
			source: location
		});


		rate_list  = [];
		hotel_type = [];
		facilities  = [];
		room_facilities = [];

		$( 'input.filter-checkbox__input' ).removeAttr( 'checked' );
		$( '.filter-action, .filter-checkbox, .filter-hotel-type, .filter-facilities, .filter-room-facilities, .filter-price' ).on( 'click', function() {
			var location   = $( '#location' ).val(),
			adults  = $( '#adults' ).val(),
			children     = $( '#children' ).val(),
			min_price  = $(this).attr( 'data-min-price' ),
			max_price  = $(this).attr( 'data-max-price' );

			rate_list.push( $(this).attr( 'data-rate-value' ) );
			hotel_type.push( $(this).attr( 'data-hotel-type-value' ) );
			facilities.push( $(this).attr( 'data-facilities-value' ) );
			room_facilities.push( $(this).attr( 'data-room-facilities-value' ) );

			var input_check = $(this).find( 'input' ).attr( 'checked' );
			
			$(this).find( 'input' ).attr( 'checked', 'checked' );
			jQuery.ajax({
				url: ajax_object.ajax_url,
				type: "POST",
				data: {
					action: 'justread_filter_hotel',
					rate: rate_list,
					location: location,
					hotel_type: hotel_type,
					facilities: facilities,
					room_facilities: room_facilities,
					min_price: min_price,
					max_price: max_price,
					adults: adults,
					children: children,
				},
				success: function(response) {
					$( '.site-main' ).html(response.post);
				}
			});
		});
	}

	filterHotel();
} );

Các bạn có thể tham khảo nội dung toàn bộ file filter-hotel.js ở đây.

Bước 2: Lấy và trả về các dữ liệu tương ứng khi tìm kiếm

Khi người dùng click vào các bộ lọc để chọn loại khách sạn họ muốn, file filter-hotel.js sẽ nhận các dữ liệu này và truyền đến file functions.php. File functions.php có nhiệm vụ lấy và trả về các bài viết (khách sạn) đáp ứng được yêu cầu mà file filter-hotel.js đang cần.

Để file functions.php thực hiện được công việc trên, chúng ta cần thêm đoạn code sau đây vào file functions.php:

function justread_filter_hotel() {
	$location    	= isset( $_POST['location'] ) ? $_POST['location'] : '';
    	$rate        	= isset( $_POST['rate'] ) ? $_POST['rate'] : '';
   	 $hotel_type  	= isset( $_POST['hotel_type'] ) ? $_POST['hotel_type'] : '';
    	$facilities  	= isset( $_POST['facilities'] ) ? $_POST['facilities'] : '';
    	$room_facilities = isset( $_POST['room_facilities'] ) ? $_POST['room_facilities'] : '';
    	$min_price   	= isset( $_POST['min_price'] ) ? $_POST['min_price'] : '';
    	$max_price   	= isset( $_POST['max_price'] ) ? $_POST['max_price'] : '';
    	$adults      	= isset( $_POST['adults'] ) ? $_POST['adults'] : '';
    	$children    	= isset( $_POST['children'] ) ? $_POST['children'] : '';

	$rate_array = array(
		'taxonomy' => 'rate',
		'field'    => 'name',
		'terms'    => $rate,
		'operator' => 'IN',
	);
	$rate_array = $rate ? $rate_array : '';

	$location_array = array(
		'taxonomy' => 'location',
		'field'    => 'name',
		'terms'    => array( $location ),
		'operator' => 'IN',
	);
	$location_array = $location ? $location_array : '';

	$hotel_type_array = array(
		'key'     => 'hotel_type',
		'value'   => $hotel_type,
		'compare' => 'IN',
	);
	$hotel_type_array = $hotel_type ? $hotel_type_array : '';

	$facilities_array = array(
		'key'     => 'facilities',
		'value'   => $facilities,
		'compare' => 'IN',
	);
	$facilities_array = $facilities ? $facilities_array : '';

	$room_facilities_array = array(
		'key'     => 'room_facilities',
		'value'   => $room_facilities,
		'compare' => 'IN',
	);
	$room_facilities_array = $room_facilities ? $room_facilities_array : '';


	$price_array = array(
		'key'     => 'price_p',
		'value'   => array( $min_price, $max_price ),
		'compare' => 'BETWEEN',
		'type'    => 'NUMERIC',
	);
	$price_array = $max_price ? $price_array : '';

	$adults_array  = array(
		'key'     => 'adults',
		'value'   => $adults,
		'type'    => 'NUMERIC',
		'compare' => '>=',
	);
	$adults_array = $adults ? $adults_array : '';

	$children_array  = array(
		'key'     => 'children',
		'value'   => $children,
		'type'    => 'NUMERIC',
		'compare' => '>=',
	);
	$children_array = $children ? $children_array : '';

	$query_arr = array(
		'post_type' => 'hotel',
		'post_status' => 'publish',
		'tax_query' => array(
			'relation' => 'AND',
			$rate_array,
			$location_array,
		),
		'meta_query' => array(
			'relation' => 'AND',
			$adults_array,
			$hotel_type_array,
			$facilities_array,
			$room_facilities_array,
			$price_array,
		),
	);
	$query = new WP_Query( $query_arr );

	if ( $query->have_posts() ) :
		ob_start();
		while ( $query->have_posts() ) : $query->the_post();
			get_template_part( 'template-parts/content', 'hotel' );
		endwhile;
		$posts = ob_get_clean();
	else :
		$posts = '<h1>' . __( 'No post', 'justread' ) .'</h1>';
	endif;


	$return = array(
		'post' => $posts,
	);
	wp_send_json( $return );
}
add_action( 'wp_ajax_justread_filter_hotel', 'justread_filter_hotel' );
add_action( 'wp_ajax_nopriv_justread_filter_hotel', 'justread_filter_hotel' );

Giải thích:

  • 'post_type' => 'hotel': ‘hotel’ là slug của post type đã tạo ở phần 1
  • 'wp_ajax_justread_filter_hotel''wp_ajax_nopriv_justread_filter_hotel' là 2 hook để thực hiện ajax. Hai hook này được đặt tên theo quy tắc như sau: wp_ajax_my_actionwp_ajax_nopriv_my_action.

Chưa hết, do các field thuộc phòng khách sạn (Room) là các subfield của một field dạng group nên không thể query trực tiếp được. Vì thế, bạn phải thêm chúng thành một post meta riêng biệt ở trong database để query lấy dữ liệu. Để làm được điều này, bạn thêm đoạn code dưới đây vào file functions.php:

function justread_add_field_group( $post_id ) {
	$rooms = get_post_meta( $post_id, 'group_room', true );
	delete_post_meta( $post_id, 'price_p' );
	delete_post_meta( $post_id, 'adults' );
	delete_post_meta( $post_id, 'children' );
	delete_post_meta( $post_id, 'room_facilities' );
	foreach ($rooms as $key => $room) {
		add_post_meta( $post_id, 'price_p', (int)$room['price'] );
		add_post_meta( $post_id, 'adults', (int)$room['adults'] );
		add_post_meta( $post_id, 'children', (int)$room['children'] );
		foreach ( $room['room_facilities'] as $key => $facilities ) {
			add_post_meta( $post_id, 'room_facilities', $facilities );
		}
	}
}
add_action( 'rwmb_field-for-hotel_after_save_post', 'justread_add_field_group' );

Các bạn có thể tham khảo toàn bộ nội dung file functions.php ở đây.

Bước 3: Hiển thị kết quả tìm kiếm ra front end

Sau khi thực hiện xong bước 2, file functions.php sẽ trả dữ liệu về và file filter-hotel.js sẽ nhận và in ra dữ liệu này để hiển thị lên frontend bằng đoạn mã sau:

success: function(response) {
    $( '.site-main' ).html(response.post);
} 

Trên thực tế, mình đã thêm đoạn code này vào file js ngay từ bước 1 rồi nên bạn không cần thao tác gì thêm ở đây.

Bây giờ, bạn ra trang archive của khách sạn và thử vài bộ lọc, bạn sẽ thấy kết quả như sau:

Thử bộ lọc tìm kiếm bằng plugin Meta Box trên trang archive của website OTA giống booking.com

Vậy là bộ lọc đã hoạt động rồi đấy!

Lời cuối

Sau phần 2 này, bạn đã có được bộ lọc hoàn chỉnh để giúp người dùng tìm kiếm khách sạn một cách nhanh chóng và hiệu quả. Phần tiếp theo, cũng là phần cuối cùng, mình sẽ hướng dẫn cách tạo bộ lọc trong trang single của khách sạn bằng plugin Meta Box. Đừng quên theo dõi nhé! Nếu có bất kỳ thắc mắc nào, bạn hãy chia sẻ ở phần bình luận, chúng mình sẽ giúp bạn giải đáp.

Để lại bình luận

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 *