В этой статье я расскажу вам как сделать полностью интерактивный поиск и пагинацию результатов без строчки 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.
