post-image

Xây dựng chức năng tìm kiếm thông minh trong Laravel với Typeahead.js

Tổng quan

Giới thiệu

Chào mọi người, đến hẹn lại lên.

Thì như mọi người cũng đã biết, tìm kiếm là một chức năng quan trọng và không thể thiếu trong các ứng dụng Website. Đặc biệt trong các ứng dụng lớn, các chức năng như tìm kiếm thông minh từ những từ khóa đã nhập vào, gợi ý từ khóa tìm kiếm, … là những chức năng không thể thiếu.

Chính vì vậy, trong bài viết này mình sẽ giới thiệu cho các bạn về Typeahead.js. Đây là một thư viện JavaScript linh hoạt, nó hỗ trợ mạnh mẽ cho việc xây dựng một hệ thống tìm kiếm thông minh tương tự như tìm kiếm trên Google, Facebook hay Twitter.

Sơ lược về Typeahead.js

Typeahead gồm 2 thành phần chính là:

  • Typeahead: Là phần hiển thị giao diện người dùng
    • Hiển thị gợi ý cho người dùng sau khi nhập từ khóa.
    • Hiển thị các gợi ý từ khóa trên ô nhập dữ liệu.
    • In đậm các từ, các chữ trùng với từ khóa đã nhập.
    • Hỗ trợ các tùy chỉnh về giao diện, sự kiện linh hoạt.
    • ……
  • Bloodhound Engine: Là một Bộ máy gợi ý
    • Lấy các dữ liệu từ trước, giảm độ delay khi hiển thị gợi ý.
    • Sử dụng Local Storage giảm số lượng các request đến máy chủ.
    • Cho phép các dữ liệu được hardcode.
    • Sử dụng rate limit và bộ đệm cho các request đến máy chủ, giảm nhẹ việc tải dữ liệu.
    • …..

Cài đặt

Npm

Chạy lệnh sau để cài đặt:

npm install typeahead.js
Code language: CSS (css)

CDN

Đây là link cdn cho các bạn muốn nhúng thẳng Typeahead vào ứng dụng của mình https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js

Chú ýTypeahead yêu cầu phiên bản jquery từ 1.9 trở lên.

Tạo Typeahead

Để khởi tạo Typeahead với bất kể trường text input nào, ta sử dụng đoạn code sau:

$(".search-input").typeahead(options, [*datasets])
Code language: JavaScript (javascript)

Trong đó:

  • options: Các tùy chọn cấu hình khi khởi tạo Typeahead như:
    • highlight : in đậm các chữ, các từ trong gợi ý trùng với các từ khóa đã nhập, mặc định tùy chọn này là false.
    • hint: hiển thị từ gợi ý trong ô nhập dữ liệu, mặc định tùy chọn này là true.
    • minLength: số kí tự tối thiểu cần phải nhập để chức năng gợi ý bắt đầu hoạt động, mặc định của tùy chọn này là 1.
    • ….. (Bạn có thể xem thêm các tùy chọn khác ở đây)
$(".search-input").typeahead({ hint: true, highlight: true, minLength: 1 });
Code language: JavaScript (javascript)
  • datasets: Một Typeahead có thể có nhiều datasets, mỗi datasets sẽ trả về một tập gợi ý theo từ khóa đã nhập. Một số tùy chọn cấu hình của datasets:
    • name: tên của datasets
    • source: Nguồn dữ liệu dùng cho gợi ý, có thể là một thể hiện của Bloodhound (Bộ máy thực hiện các gợi ý).
    • limit: số kí tự tối đa hiển thị của gợi ý, mặc định là 5.
    • ….. (Bạn có thể xem thêm các tùy chọn khác ở đây)

Tạo Bloodhound Engine

Đối với source trong datasets, chúng ta cần lấy dữ liệu từ server. Để làm việc này chúng ta cần BloodHound. Nó là bộ máy gợi ý của Typeahead.js, nó đưa ra nhiều tính năng nâng cao để lấy dữ liệu từ xa, đồng thời sử dụng bộ đệm để tăng tốc.

var engine = new Bloodhound({ remote: { url: '/search/name?value=%QUERY%', wildcard: '%QUERY%' }, datumTokenizer: Bloodhound.tokenizers.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace });
Code language: JavaScript (javascript)

Bạn cần chú ý rằng đường dẫn /search/name sẽ được thiết lập trong route, và value sẽ là tham số truyền đến controller của ứng dụng Laravel (ở phần dưới). Như vậy chúng ta đã có dữ liệu và có thể dùng để thiết lập cho source trong datasets của Typeahead.

source: engine.ttAdapter()
Code language: CSS (css)

Tạo Templates cho các gợi ý

Typeahead cho phép bạn sử dụng các templates để thay đổi style cho các gợi ý, bạn cũng có thể sử dụng Bootstrap ở đây.

templates: { empty: [ '<div class="header-title">Name</div><div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>' ], header: [ '<div class="header-title">Name</div><div class="list-group search-results-dropdown"></div>' ], suggestion: function (data) { return '<a href="/students/' + data.id + '" class="list-group-item">' + data.name + '</a>'; } }
Code language: HTML, XML (xml)

Tạo ứng dụng Laravel

Migration & Seeder

Ở đây chúng ta sẽ tạo một table students với các thuộc tính nameemail và studentcode. Tạo một Migration như sau:

<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateStudentsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('students', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email'); $table->string('student_code'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('students'); } }
Code language: HTML, XML (xml)

Chúng ta sẽ seeding 20,000 dữ liệu mẫu để testing cho ứng dụng. Sử dụng Faker để seeding, bạn hãy copy đoạn code sau và dán vào file databases/factories/UserFactory.php:

$factory->define(App\Student::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'student_code' => $faker->randomNumber() ]; });
Code language: PHP (php)

Tạo một seeder với tên là StudentsTableSeeder bằng lệnh:

php artisan make:seeder StudentsTableSeeder
Code language: CSS (css)

Và thêm đoạn code sau vào file databases/seeds/StudentsTableSeeder.php vừa tạo.

public function run() { factory(App\Student::class, 20000)->create(); }
Code language: PHP (php)

Đăng ký seeder vừa tạo vào trong file databases/seeds/DatabaseSeeder.php

public function run() { $this->call(StudentsTableSeeder::class); }
Code language: PHP (php)

Cuối cùng chạy migration và seeder đã tạo bằng lệnh:

php artisan migrate --seed

Route

Chúng ta sẽ tạo 1 route để hiển thị trang tìm kiếm, 1 route để hiển thị thông tin student và 2 route để thực hiện việc tìm kiếm student theo name , theo email và trả dữ liệu dạng JSON về cho Bloodhound . Bạn hãy copy đoạn code sau và dán vào file routes/web.php:

Route::get('/students', 'StudentController@index'); Route::get('/students/{id}', 'StudentController@show'); Route::get('/search/name', 'StudentController@searchByName'); Route::get('/search/email', 'StudentController@searchByEmail');
Code language: PHP (php)

Controller

Tạo một controller với tên là StudentController bằng lệnh:

php artisan make:controller StudentController
Code language: CSS (css)

Thêm các phương thức index()show()searchByName() và searchByEmail() vào StudentController vừa tạo.

public function index() { return view('index'); } public function show($id) { $student = Student::findOrFail($id); $data = 'Name: ' . $student->name . '<br/>Email: ' . $student->email . '<br/>Student Code: ' . $student->student_code ; return $data; } public function searchByName(Request $request) { $students = Student::where('name', 'like', '%' . $request->value . '%')->get(); return response()->json($students); } public function searchByEmail(Request $request) { $students = Student::where('email', 'like', '%' . $request->value . '%')->get(); return response()->json($students); }
Code language: PHP (php)

View

Chúng ta sẽ tạo một View index đơn giản với 1 ô input tìm kiếm. Ta cần include thư viện Typeahead vào phần header của View. Ở đây mình sẽ viết code Css và Javascript trong view cho thuận tiện, tuy nhiên các bạn nên tách ra file riêng để dễ dàng cho việc quản lý.

<!doctype html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Smart search in Laravel with Typeahead.js</title> {{ Html::style(asset('css/app.css')) }} {{ Html::script(asset('js/app.js')) }} {{ Html::script(asset('js/typeahead.bundle.min.js')) }} <!-- Styles --> <style> h3 { text-align: center; margin: 20px auto; } .content { text-align: center; } a { color: #333; } .header-title { padding: 5px 10px; background: #dadada; font-weight: bold; } </style> </head> <body> <h3>Smart search in Laravel with Typeahead.js</h3> <div class="flex-center position-ref full-height"> <div class="content"> <form class="typeahead" role="search"> <input type="search" name="q" class="form-control search-input" placeholder="Type something..." autocomplete="off"> </form> </div> </div> <script type="text/javascript"> /* TYPEAHEAD CODE */ </script> </body> </html>
Code language: HTML, XML (xml)

Thêm đoạn code khởi tạo Bloodhound và Typeahead như chúng ta đã tìm hiểu ở phần trên vào chỗ mình đã comment ở view index trên. Ta sẽ khởi tạo 2 datasets với 2 Bloodhound gợi ý tìm kiếm student theo name và email.

$(document).ready(function($) { var engine1 = new Bloodhound({ remote: { url: '/search/name?value=%QUERY%', wildcard: '%QUERY%' }, datumTokenizer: Bloodhound.tokenizers.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace }); var engine2 = new Bloodhound({ remote: { url: '/search/email?value=%QUERY%', wildcard: '%QUERY%' }, datumTokenizer: Bloodhound.tokenizers.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace }); $(".search-input").typeahead({ hint: true, highlight: true, minLength: 1 }, [ { source: engine1.ttAdapter(), name: 'students-name', display: function(data) { return data.name; }, templates: { empty: [ '<div class="header-title">Name</div><div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>' ], header: [ '<div class="header-title">Name</div><div class="list-group search-results-dropdown"></div>' ], suggestion: function (data) { return '<a href="/students/' + data.id + '" class="list-group-item">' + data.name + '</a>'; } } }, { source: engine2.ttAdapter(), name: 'students-email', display: function(data) { return data.email; }, templates: { empty: [ '<div class="header-title">Email</div><div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>' ], header: [ '<div class="header-title">Email</div><div class="list-group search-results-dropdown"></div>' ], suggestion: function (data) { return '<a href="/students/' + data.id + '" class="list-group-item">' + data.email + '</a>'; } } } ]); });
Code language: JavaScript (javascript)

Kết luận

Qua bài viết này mình đã giới thiệu cho các bạn về Typeahead.js. Một thư viện hỗ trợ linh hoạt cho việc xây dựng một hệ thống tìm kiếm thông minh.

Hy vọng với bài viết này, các bạn đã biết thêm được một thư viện Javascript bổ ích.

Các bạn có thể tham khảo các bài viết hay về Laravel tại đây.


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Tham khảo: Viblo

Leave a Reply

Your email address will not be published. Required fields are marked *