
Создаем форму поиска в нужном шаблоне:
<form action="{{route('searchSimple')}}" method="GET" class="search-simple"> <div class="row"> <div class="col-xs-10"> <div class="form-group"> <input type="text" class="form-control" name="q" value="{{ old('q') }}" required> </div> </div> <div class="col-xs-2"> <div class="form-group"> <input class="btn btn-info" type="submit" value="Искать"> </div> </div> </div> </form>Поисковую фразу введенную в поле формы передаем методом GET.
Маршрут.
Route::get('searchSimple', 'SearchController@index')->name('searchSimple');
Миграция.
Для использования полнотекстового поиска нужно определить полнотекстовые индексы для полей таблицы, по содержимому которых будем искать. Я покажу на примере таблицы students и добавлю полнотекстовые индексы для полей name и email:
public function up() { \Illuminate\Support\Facades\DB::statement('ALTER TABLE students ADD FULLTEXT search(name, email)'); }
Контроллер (SearchController):
public function index(Request $request){ $q = $request->input('q'); $max_page = 30; //Полнотекстовый поиск с пагинацией $results = $this->search($q, $max_page); return view('search.index', [ 'include' => 'search.table', 'students' => $results, ])->render(); }
В данном примере, для простоты, я не использую валидацию и разрешаю искать даже по одному символу. В реальном проекте, ее стоит использовать.
Поиск по БД я вынес в отдельный метод контроллера «search», при крупном проекте можно вынести еще дальше, например в класс репозитория.
/** * Полнотекстовый поиск. * * @param string $q Строка содержащая поисковый запрос. Может быть несколько фраз разделенных пробелом. * @param integer $count Количество найденных результатов выводимых на одной странице (для пагинации) * @return \Illuminate\Pagination\LengthAwarePaginator */ public function search($q, $count){ $query = mb_strtolower($q, 'UTF-8'); $arr = explode(" ", $query); //разбивает строку на массив по разделителю /* * Для каждого элемента массива (или только для одного) добавляет в конце звездочку, * что позволяет включить в поиск слова с любым окончанием. * Длинные фразы, функция mb_substr() обрезает на 1-3 символа. */ $query = []; foreach ($arr as $word) { $len = mb_strlen($word, 'UTF-8'); switch (true) { case ($len <= 3): { $query[] = $word . "*"; break; } case ($len > 3 && $len <= 6): { $query[] = mb_substr($word, 0, -1, 'UTF-8') . "*"; break; } case ($len > 6 && $len <= 9): { $query[] = mb_substr($word, 0, -2, 'UTF-8') . "*"; break; } case ($len > 9): { $query[] = mb_substr($word, 0, -3, 'UTF-8') . "*"; break; } default: { break; } } } $query = array_unique($query, SORT_STRING); $qQeury = implode(" ", $query); //объединяет массив в строку // Таблица для поиска $results = Students::whereRaw( "MATCH(name,email) AGAINST(? IN BOOLEAN MODE)", // name,email - поля, по которым нужно искать $qQeury)->paginate($count) ; return $results; }
Алгоритм преобразования поисковой фразы/фраз может быть любой. В данном примере я добавил звездочку в конец фразы - она заменяет любое количество символов, чтобы у пользователя была возможность не набирать слово (в данном случае фамилию или имя студента) целиком.
Для поиска части строки, а не строгого соответствия, кроме звездочек нужно использовать модификатор IN BOOLEAN MODE. Такой запрос аналогичен результату при использовании LIKE ="text%", только использует меньше ресурсов благодаря системе индексов MySQL.
Данный метод позволяет проводить поиск сразу по нескольким фразам разделенным пробелом, в результате в запрос при вводе «Ната Ан» попадет:
"нат* ан*"
Так же можно сформировать массив поисковых фраз и передать его в метод whereRaw:
array($qQeury))
Переменная с результатами поиска передается в шаблон где доступна как $students.
В шаблоне как обычно - выводим, в цикле, данные:
@foreach($students as $student) <tr> <td class="align-left">{{$student->id}}</td> <td class="align-left">{{$student->name }}</td> <td class="align-left">{{$student->email }}</td> ...
Для работы пагинации нужно дополнительно передать с GET параметрами поисковую фразу (q), для этого вместо стандартного вывода кнопок со ссылками
{{$students->links()}}пишем
{{$students->appends(['q' => \Illuminate\Support\Facades\Input::get('q')])->links()}}или можно передать вообще все существующие параметры, кроме указанных:
{{ $students->appends(\Illuminate\Support\Facades\Input::except('_token'))->links() }}
иначе возникнет ошибка наподобии:
SQLSTATE[42000]: Syntax error or access violation: 1064 syntax error, unexpected $end, expecting FTS_TERM or FTS_NUMB or '*' (SQL: select count(*) as aggregate from `students` where MATCH(name,email) AGAINST(* IN BOOLEAN MODE))
Так же я писал как сделать AJAX пагинацию на Laravel, при которой результаты поиска будут добавляться динамически при скролле содержимого.