Создание “умного поиска” с помощью Laravel и Typeahead.js

Урок по созданию поиска с подсказками, используя Laravel и Typeahead.js.

Вступление

Каждый крупный сайт или веб приложение должен быть оснащён системой продвинутого поиска. Это позволит пользователям быстро и легко находить нужный контент: юзеров, товары, статьи.

В этом уроке мы расширим возможности простого элемента поиска, снабдив рядом всевозможных вариантов, взяв их из базы данных. При этом в качестве инструментария будем использовать Laravel, jQuery и Typeahead (Bloodhound в качестве системы подсказок), а так же Bootstrap для стилизации. Давайте начнём!

Typeahead.js

Typeahead.js — это гибкая JavaScript библиотека, которая позволяет создать поиск с использованием авто-заполнения. Библиотеку можно разбить на два компонента:

  1. Typeahead - Отображение (интерфейс)
  2. Bloodhound - Система подсказок

Каждый из этих компонент можно использовать в отдельности. В нашем уроке мы задействуем оба инструмента. Пример работы Typeahead.

Стартуем

Для начала нам необходимо подключить Typeahead.js.

  1. Самый быстрый вариант:
    • Установка через Bower: $ bower install typeahead.js
  2. Классический вариант:
    • Скачать zip файл с последней версией библиотеки.
    • Скачать отдельные компоненты:
      • bloodhound.js (система подсказок)
      • typeahead.jquery.js (интерфейс)
      • typeahead.bundle.js (bloodhound.js + typeahead.jquery.js)
      • typeahead.bundle.min.js
  3. Подключение через CDN
    • jQuery: https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js
    • Typeahead: https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js

Заметка: для работы bloodhound.js и typeahead.jquery.js необходимо подключить jQuery 1.9+.

В нашем примере подключим библиотеку через CDN. Для демонстрации “фокуса“ Typeahead нам потребуется простой текстовый элемент. В результате у нас получится следующий базовый HTML код:

<!DOCTYPE html>
<html lang="en">
  <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>Laravel and Typeahead Tutorial</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>Laravel and Typeahead Tutorial</h1>
    <hr>
    <form class="typeahead" role="search">
      <div class="form-group">
        <input type="search" name="q" class="form-control" placeholder="Search" autocomplete="off">
      </div>
    </form>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins and Typeahead) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <!-- Bootstrap JS -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    <!-- Typeahead.js Bundle -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script>
  </body>
</html>

Инициализация

Для инициализации Typeahead потребуется следующий код:

$(".search-input").typeahead(options, [*datasets])

Подсказка: выставите атрибут autocomplete="off" для того чтобы отключить авто-заполнение браузера, которое используется по умолчанию.

Настройка

При инициализации typeahead мы можем передать ряд настроек:

  • highlight – если true, то поисковая фраза будет “обвёрнута” в элемент strong с классом {{classNames.highlight}}. По умолчанию false.
  • hint – если false, то typeahead не будет показывать подсказку. По умолчанию: true.
  • minLength – минимальное количество символов, после введения которого будет активирован поиск вариантов подсказок. По умолчанию: 1.

Все настройки можно найти тут.

В нашем случае конфигурация будет такой:

$("#navbar-search-input").typeahead({
    hint: true,
    highlight: true,
    minLength: 1
});

Наборы данных

Typeahead может считывать данные в различных форматах. Возьмём пример поиска на Facebook. При вводе нескольких символов результат поиска делится на группы, такие как пользователи, сообщества, страницы, игры и так далее. В нашем случае будем осуществлять поиск только по пользователям.

Наборы данных могут быть представлены в нескольких форматах. Самые часто используемые:

  • name – название набора данных. Будет использован шаблон {{classNames.dataset}} - для формирования класса элемента DOM. Может состоять исключительно из знака подчёркивания, дефиса, букв (a-z) и цифр.
  • source – данные с подсказками. Ожидается функция с параметрами (query, syncResults, asyncResults). Поисковая строка, а так же данные полученные в результате AJAX запроса. Вместо всего перечисленного, можно передать объект Bloodhound.
  • limit – максимальное количество подсказок. По умолчанию: 5.

Все возможные варианты можно найти тут.

Bloodhound

Данные будут запрашиваться с сервера. Всю основную работу сделает Bloodhound:

var engine = new Bloodhound({
    remote: {
        url: '/query?q=%QUERY%',
        wildcard: '%QUERY%'
    },
    datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
    queryTokenizer: Bloodhound.tokenizers.whitespace
});

Url где будет сформирован ответ настроим чуть позже. В datumTokenizer передаётся массив в формате JSON; в нашем случае 'q'.

Теперь все данные будут доступны через свойство source:

source: engine.ttAdapter()

Шаблоны

Typeahead позволяет полностью настроить стиль отображения подсказок. В нашем случае воспользуемся компонентами Bootstrap-а.

templates: {
    empty: [
        '<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'
    ],
    header: [
        '<div class="list-group search-results-dropdown">'
    ],
    suggestion: function (data) {
         return '<a href="' + data.profile.username + '" class="list-group-item">' + data.name + ' - @' + data.profile.username + '</a>'
    }
}

Конечный код на данном этапе:

jQuery(document).ready(function($) {
    // Set the Options for "Bloodhound" suggestion engine
    var engine = new Bloodhound({
        remote: {
            url: '/find?q=%QUERY%',
            wildcard: '%QUERY%'
        },
        datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
        queryTokenizer: Bloodhound.tokenizers.whitespace
    });

    $(".search-input").typeahead({
        hint: true,
        highlight: true,
        minLength: 1
    }, {
        source: engine.ttAdapter(),

        name: 'usersList',

        templates: {
            empty: [
                '<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'
            ],
            header: [
                '<div class="list-group search-results-dropdown">'
            ],
            suggestion: function (data) {
                return '<a href="' + data.profile.username + '" class="list-group-item">' + data.name + '- @' + data.profile.username + '</a>'
      }
        }
    });
});

Laravel

Для реализации части бэк-энд-а создадим новый Laravel проект, используя Composer.

composer create-project --prefer-dist laravel/laravel app-name

Вы можете использовать и другие варианты конфигурации.

Функция поиска

Для реализации поиска воспользуемся компонентом Searchable. Данный пакет включает трэйт с специальной функцией с помощью которой можно осуществить поиск по любому полю таблицы.

Установка компонента:

composer require "nicolaslopezj/searchable:1.*"

Подключаем трэйт к модели которая представляет собой таблицу. Далее определяем правила поиска (указываем колонки, которые будут задействованы) через свойство $searchable.

<?php

namespace App;

use App\Profile;
use Nicolaslopezj\Searchable\SearchableTrait;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use SearchableTrait;

    protected $searchable = [
        'columns' => [
            'users.name' => 10,
            'profiles.username' => 5,
            'profiles.bio' => 3,
            'profiles.country' => 2,
            'profiles.city' => 1,
        ],
        'joins' => [
            'profiles' => ['users.id','profiles.user_id'],
        ],
    ];

    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

Теперь поиск пользователей может быть осуществлён следующим образом:

// Простой поиск
$users = User::search($query)->get();

// Поиск по связным таблицам
$users = User::search($query)
            ->with('profile')
            ->get();

Маршруты

Мы должны настроить маршрутизатор для того чтобы Bloodhound обратился по указанному ранее адресу и получил JSON данные. Добавляем маршрут в файл routes.php.

Route::get('query', 'SearchController@query');

Контроллер

Из настройки маршрута видно, что нам потребуется создать контроллер calledSearchController с методом 'query'. Для этого достаточно выполнить команду:

php artisan make:controller SearchController

После этого добавляем следующий метод в контроллер:

public function find(Request $request)
{
    return User::search($request->get('q'))->with('profile')->get();
}

По умолчанию данные будут возвращены в формате JSON.

Общая картина

Нам осталось добавить маршрут, который приведёт пользователя на главную страницу.

Route::get('/', function () {
    return view('welcome');
});

Полная версия нашей HTML страницы:

<!DOCTYPE html>
<html lang="en">
  <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>Laravel and Typeahead Tutorial</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>Laravel and Typeahead Tutorial</h1>
    <hr>
    <form class="typeahead" role="search">
      <div class="form-group">
        <input type="search" name="q" class="form-control" placeholder="Search" autocomplete="off">
      </div>
    </form>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins  and Typeahead) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <!-- Bootstrap JS -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    <!-- Typeahead.js Bundle -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script>
    <!-- Typeahead Initialization -->
    <script>
        jQuery(document).ready(function($) {
            // Set the Options for "Bloodhound" suggestion engine
            var engine = new Bloodhound({
                remote: {
                    url: '/find?q=%QUERY%',
                    wildcard: '%QUERY%'
                },
                datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
                queryTokenizer: Bloodhound.tokenizers.whitespace
            });

            $(".search-input").typeahead({
                hint: true,
                highlight: true,
                minLength: 1
            }, {
                source: engine.ttAdapter(),

                // This will be appended to "tt-dataset-" to form the class name of the suggestion menu.
                name: 'usersList',

                // the key from the array we want to display (name,id,email,etc...)
                templates: {
                    empty: [
                        '<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'
                    ],
                    header: [
                        '<div class="list-group search-results-dropdown">'
                    ],
                    suggestion: function (data) {
                        return '<a href="' + data.profile.username + '" class="list-group-item">' + data.name + ' - @' + data.profile.username + '</a>'
              }
                }
            });
        });
    </script>
  </body>
</html>

Итог

В этом уроке мы реализовали продвинутый поиск, используя Laravel и Typeahead. Данные инструменты позволили без особых усилий справиться с этой далеко не простой задачей.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: https://scotch.io/tutorials/implementing-smart-search-with-laravel-and-typeahead-js
Перевел: Станислав Протасевич
Урок создан: 1 Июля 2016
Просмотров: 18045
Правила перепечатки


5 последних уроков рубрики "PHP"

  • Фильтрация данных с помощью zend-filter

    Когда речь идёт о безопасности веб-сайта, то фраза "фильтруйте всё, экранируйте всё" всегда будет актуальна. Сегодня поговорим о фильтрации данных.

  • Контекстное экранирование с помощью zend-escaper

    Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

  • Подключение Zend модулей к Expressive

    Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.

  • Совет: отправка информации в Google Analytics через API

    Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.

  • Подборка PHP песочниц

    Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

или авторизуйтесь, чтобы добавлять комментарии, оценивать уроки и сохранять их в личном кабинете
  • 13 Июля 2016 12:26
    Денис Евсеев
    А где можно результат посмотреть работы ?
  • 15 Июля 2016 13:17
    alienk1
    спасибо! но хочу заметить, что не все знакомы с фреймворком laravel, не мешало б просто показать работу js библиотеки. А дальше уже кто куда хочет - прикрутит! Но еще раз спасибо! я о ней не знал)
^ Наверх ^