В этой статье я расскажу вам как сделать полностью интерактивный поиск и пагинацию результатов без строчки Javascript.
Если вы смотрели Laracon 2019, то, возможно, видели удивительный новый пакет от Caleb Porzio под названием Livewire. Вот как оно описано на сайте:
Livewire — это полнофункциональный фреймворк для Laravel, который делает создание динамических интерфейсов таким же простым, как написание ванильного PHP (буквально).
Я начал писать на Laravel два года назад, и одной из моих самых больших проблем было создание интерактивных страниц. Laravel позволил мне легко перейти к веб-разработке, но я обнаружил, что использование Vue.js или React чересчур запутано и усложнено. Когда Калеб показал простой счетчик, я сразу подумал — Livewire будет идеален для поиска. И сейчас я расскажу вам как использовать Livewire для интерактивного поиска записей Eloquent с пагинацией.
Настройка Livewire ( документация )
composer require livewire/livewire
Добавьте его на все страницы, где вы хотите выполнять поиск, прямо перед закрывающим тегом body:
@livewireAssets </body> </html>
Livewire работает так — он подключая данные от компонентов Livewire на фронтенде напрямую к бэкендовому контроллеру Livewire. Эти контроллеры затем могут перерендерить компонент без необходимости обновления всей страницы. Наша цель — передать поисковый запрос из формы ввода на бэкенд и использовать его для поиска в нашей модели Eloquent. Результаты обновляются в реальном времени!
Наполняем базу данных (необязательно)
Для этого примера я использую свежеустановленное приложение Laravel. Если вы используете существующее приложение, то можете пропустить этот шаг. В противном случае, чтобы получить данные для поиска, вы можете заполнить таблицу пользователей дефолтной фабрикой пользователей Laravel (UserFactory). Добавьте это в свой файл DatabaseSeeder.php:
public function run() { factory(App\User::class, 500)->create(); }
Затем сделайте в консоли php artisan db:seed
и у вас появится 500 пользователей для поиска!
Создаем Поисковый Компонент
Прежде всего, давайте создадим контроллер поиска и шаблон Livewire:
php artisan make:livewire search
Это создаст новый контроллер в App\Http\Livewire под названием Search.php. И шаблон с именем search.blade.php в папке шаблонов Livewire.
Создаем Поле поиска + переменная
Давайте добавим в search.blade.php форму ввода и забиндим ее к Livewire:
<input type="text" wire:model="searchTerm" />
Установив wire:model="searchTerm"
Livewire автоматически обновит переменную $searchTerm в контроллере Search.php. Давайте её добавим:
class Search extends Component { public $searchTerm; public function render() { return view('livewire.search') } }
Я разместил всё на дефолтной главной странице Laravel, но вы можете добавить на любую страницу, какую захотите. Просто добавьте @livewire('search')
в blade-шаблон там, где он вам нужен.У меня сейчас так:
Вывод записей Eloquent
Давайте начнем с передачи всех пользователей компоненту и отображения их просто списком (без поиска). Поскольку мы собираемся сделать это интерактивным, нам нужно сделать всё это в файле search.blade.php. Просто сделайте еще одну переменную под именем $users и присвойте ей User::all().
class Search extends Component { public $searchTerm; public $users; public function render() { $this->users = User::all(); return view('livewire.search') } }
Теперь, когда страница будет рендерится, она передаст переменную $users в шаблон поиска.
Мы можем использовать её в Blade как обычно в цикле вывести всех пользователей:
<div> <input type="text" wire:model="searchTerm"> <ul> @foreach($users as $user) <li> <p> {{ $user->name }} </p> </li> @endforeach </ul> </div>
Теперь мы видим всех наших пользователей:
В поисках Eloquent
Чтобы получить желаемые результаты, нужно отфильтровать списки по тому, что ищет пользователь. Вместо того, чтобы возвращать User::all(), мы используем SQL команду ilike
для поиска соответствующих записей в базе данных. Поскольку некоторые пользователи могут искать только фамилию или отчество, мы можем окружить наш поисковый запрос знаками %
обозначающими любое количество символов с любой стороны нашего запроса. Все вместе это выглядит так:
class Search extends Component { public $searchTerm; public $users; public function render() { $searchTerm = '%' . $this->searchTerm . '%'; $this->users = User::where('name', 'ilike', $searchTerm)->get(); return view('livewire.search') } }
Вот и всё! Если мы перейдем на нашу страницу, мы сможем искать и мгновенно получать результат по всем пользователм. И мы не написали ни одной строки на javascript!
Что насчет пагинации?
У некоторых из наших пользователей есть сотни страниц контента, нам нужен был простой способ интерактивного поиска и пагинации всех результатов. Ниже я расскажу как сделать это, используя стандартную нумерацию страниц Laravel.
Пагинация + Вывод
Делаем в search.blade.php пагинацию результатов и её вывод через {{ $users->onEachSide(1)->links() }}
. Чтобы разбить на страницы с помощью Eloquent, замените запрос на такой:
$this->users = User::where('name', 'ilike', $searchTerm)->paginate(10);
Теперь видна навигация по результатам. Однако вы можете заметить, что каждый клик перезагружает всю страницу и выдает ошибку при попытке поиска и переключения страниц. Нам нужно будет обновить дефолтный шаблон пагинации для работы с Livewire.
Делаем кастомную пагинацию
Вместо того, чтобы писать совершенно новую систему пагинации, давайте используем дефолтную из Laravel и модифицируем ее новыми функциями Livewire. Вы можете найти Bootstrap пагинацию в папке resources/views/vendor/pagination/ в файле bootstrap-4.blade.php. Мы видим, что каждая кнопка и номер страницы являются ссылкой, указывающие на предыдущий или следующий URL:
Давайте скопипастим этот файл в livewire-pagination.blade.php. Заменим все ссылки href="url"
(их должно быть три) на href="#"
, чтобы страница не перезагружалась. И добавим Livewire метод клик каждой ссылке:
wire:click="setPage('{{$paginator->previousPageURL()}}')"
Обновленная пагинация теперь выглядит так:
Нам также нужно обновить наш контроллер добавив метод setPage(url):
public function setPage($url) { //пока пусто }
Чтобы отобразить эту кастомную пагинацию, нужно указать её в параметрах вызова из шаблона
{{ $users->onEachSide(1)->links('livewire-pagination') }}
Обновление пагинации в контроллере
Теперь нам нужен способ обновить результаты пагинации и компонент на основе текущей выбранной страницы. К счастью, в Laravel Paginator есть метод currentPageResolver(), который позволяет нам предварительно настроить отображаемую страницу. Мы можем передать текущий номер страницы этому методу, и он автоматически обновит результаты.
Чтобы получить номер выбранной страницы, нам нужно разобрать URL-адрес, переданный в метод setPage($url). Мы также должны объявить переменную $currentPage. Все вместе это выглядит так:
namespace App\Http\LiveWire; use App\User; use Livewire\Component; use Illuminate\Pagination\Paginator; class Search extends Component { public $searchTerm; public $users; public $currentPage = 1; public function render() { $searchTerm = '%' . $this->searchTerm . '%'; $this->users = User::where('name', 'ilike', $searchTerm)->get(); return view('livewire.search') } public function setPage($url) { $this->currentPage = explode('page=', $url)[1]; Paginator::currentPageResolver(function ()) { return $this->currentPage; }); } }
Теперь, при каждом использовании пагинации, результаты будут обновляться для отображения нужной страницы. Когда пользователи выполняют поиск, их результаты также будут разбиты на страницы, и нумерация страниц будет сбрасываться при каждом поиске, чтобы избежать путаницы:
Надеюсь, что вам это было полезно!
Автор: Branick Weix
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.