Laravel и Livewire
Laravel и Livewire
После первой программы на Laravel у вас классический сайт — форма отправляется, страница полностью перезагружается, сервер отдаёт новый HTML.
Для кнопок "+1 к лайкам", живого поиска по таблице, модальных окон без перезагрузки обычно пишут JavaScript (Vue, React) или берут Livewire.
Livewire — библиотека, где "компонент" — это PHP-класс + Blade-шаблон. При клике или вводе Livewire отправляет лёгкий AJAX-запрос на Laravel, обновляет свойства класса и перерисовывает только нужный фрагмент HTML. Отдельный SPA и сборка webpack для простых задач часто не нужны.
| Подход | Когда уместен |
|---|---|
| Blade + формы | Простые CRUD, SEO-страницы, минимум JS |
| Livewire | Интерактив внутри Laravel одной командой PHP |
| Sanctum + SPA | Отдельный фронт на React/Vue, мобильное приложение |
| Inertia.js | Vue/React, но маршруты остаются в Laravel |
Как это работает (без магии)
В layout обязательны @livewireStyles и @livewireScripts — они подключают небольшой JS, который перехватывает действия. CSRF-токен в layout (@csrf или meta) нужен для POST-запросов Livewire.
Установка
composer require livewire/livewire
Разбор:
- Команда добавляет пакет Livewire в зависимости проекта Laravel.
- После установки появляется инфраструктура для серверных интерактивных компонентов без SPA.
- Это базовый шаг, после которого можно генерировать
make:livewireкомпоненты.
В resources/views/layouts/app.blade.php перед </body>:
@livewireStyles
@livewireScripts
Разбор:
@livewireStylesподключает стили и служебные ресурсы Livewire.@livewireScriptsдобавляет клиентский JS, который отправляет обновления на сервер и патчит DOM.- Без этих директив
wire:*-атрибуты в шаблонах не будут работать.
Первый компонент — счётчик
php artisan make:livewire Counter
Разбор:
- Команда создает пару файлов: PHP-класс компонента и Blade-шаблон.
- Это ускоряет старт, потому что структура компонента формируется по стандарту Laravel.
- Дальше логика и UI компонента развиваются независимо от контроллеров.
Создаются:
app/Livewire/Counter.php— логика;resources/views/livewire/counter.blade.php— разметка.
app/Livewire/Counter.php:
Код ITЗагрузка примера кода…
Разбор:
public int $count— состояние компонента, которое Livewire синхронизирует между сервером и UI.increment()иdecrement()меняют состояние на сервере, а Livewire автоматически обновляет HTML.render()возвращает шаблон компонента и вызывается при каждом цикле обновления.- Такой компонент не требует ручного JS для простых интерактивных действий.
| Элемент | Смысл |
|---|---|
public int $count | Свойство, видимое шаблону и сериализуемое при запросах Livewire |
increment() | Публичный метод — можно вызвать из шаблона через wire:click |
render() | Какой Blade вернуть |
Шаблон resources/views/livewire/counter.blade.php:
<div>
<h2>Счётчик: {{ $count }}</h2>
<button type="button" wire:click="increment">+</button>
<button type="button" wire:click="decrement">−</button>
</div>
Разбор:
{{ $count }}рендерит текущее состояние компонента в интерфейс.wire:click="increment"иwire:click="decrement"привязывают кнопки к серверным методам класса.type="button"предотвращает случайную отправку формы, если компонент вложен в<form>.- Интерфейс обновляется без полной перезагрузки страницы.
wire:click="increment" — по клику вызвать метод increment на сервере. Важно type="button", иначе кнопка внутри <form> отправит форму целиком.
Подключение на странице:
@extends('layouts.app')
@section('content')
<h1>Livewire</h1>
<livewire:counter />
@endsection
Разбор:
- Шаблон страницы подключает layout и вставляет компонент через тег
<livewire:counter />. - Это стандартный способ монтирования Livewire-компонента в Blade-страницу.
- Компонент изолирован, поэтому его можно переиспользовать на других страницах.
Маршрут:
Route::view('/livewire-demo', 'counter-page');
Разбор:
Route::view(...)быстро связывает URL со статическим Blade-шаблоном без отдельного контроллера.- Для демо-страниц и простых экранов это минимальный и читаемый вариант.
- Livewire-компонент внутри этой view берёт на себя всю интерактивную логику.
php artisan serve → /livewire-demo. Страница не перезагружается при +/- .
wire:model — связь поля и свойства
Аналог v-model во Vue: значение input синхронизируется с PHP-свойством.
php artisan make:livewire TaskSearch
Разбор:
- Генерируется отдельный компонент поиска, чтобы не смешивать его с логикой счетчика.
- Такой декомпозицией проще управлять производительностью и сопровождением.
- Каждый компонент имеет собственное состояние и шаблон.
Код ITЗагрузка примера кода…
Разбор:
- Свойство
$queryхранит строку поиска, синхронизированную с input. when($this->query, ...)условно добавляет фильтр только при непустом поисковом запросе.orderByDesc('id')->limit(20)ограничивает нагрузку и размер ответа.compact('tasks')передает результат в Blade для отображения.
Шаблон:
<div>
<input type="search"
wire:model.live.debounce.300ms="query"
placeholder="Поиск…" />
<ul class="mt-4">
@forelse ($tasks as $task)
<li>{{ $task->title }}</li>
@empty
<li>Ничего не найдено</li>
@endforelse
</ul>
</div>
Разбор:
wire:model.live.debounce.300ms="query"связывает поле ввода и свойство с задержкой, уменьшая число запросов.- Блок
@forelseкорректно покрывает оба состояния: данные есть и данных нет. - Вывод результата остается полностью серверным, но UX ощущается как "живой".
- Такой шаблон хорошо подходит для внутренних панелей и поиска по справочникам.
Разбор директив
| Директива | Смысл |
|---|---|
wire:model="query" | Двусторонняя связь с $query |
.live | Обновлять при каждом вводе (без ожидания blur) |
.debounce.300ms | Ждать 300 мс паузы в наборе — меньше запросов к серверу |
Модель Task: php artisan make:model Task -m, поле title в миграции.
Валидация
Те же правила, что в HTTP-контроллере:
public string $title = '';
public function save(): void
{
$this->validate([
'title' => 'required|string|max:200',
]);
Task::create(['title' => $this->title]);
$this->reset('title');
$this->dispatch('task-saved');
}
Разбор:
$this->validate(...)применяет те же правила, что и в контроллере Laravel.- При успешной валидации
Task::create(...)сохраняет запись в БД. reset('title')очищает поле формы после сохранения.dispatch('task-saved')позволяет уведомить другие части интерфейса о событии.
<input wire:model="title" />
@error('title') <span class="text-red-600">{{ $message }}</span> @enderror
<button type="button" wire:click="save">Добавить</button>
Разбор:
wire:model="title"синхронизирует текстовое поле и свойство компонента.@error('title')выводит сообщение валидации только для конкретного поля.wire:click="save"запускает серверный метод сохранения без полной перезагрузки страницы.- Такой паттерн формирует полноценный интерактивный CRUD с минимальным JavaScript.
@error — директива Laravel для сообщений валидации. dispatch('task-saved') — событие для других JS/Livewire-компонентов (опционально).
Livewire и API — разные клиенты
| Сценарий | Инструмент |
|---|---|
| Сайт в Blade, кнопки и формы на сервере | Livewire |
| Мобильное приложение, отдельный React | Sanctum API |
Один проект может совмещать: публичная часть на Livewire, /api для мобильного клиента.
Тестирование Livewire
Livewire::test(Counter::class)
->call('increment')
->assertSet('count', 1);
Разбор:
Livewire::test(...)поднимает компонент в тестовом окружении без браузера.call('increment')имитирует пользовательское действие.assertSet('count', 1)проверяет, что состояние изменилось ожидаемым образом.- Такой тест быстро валидирует бизнес-логику компонента и защищает от регрессий.
Без браузера проверяется логика компонента.
Частые ошибки
| Симптом | Причина |
|---|---|
| Полная перезагрузка страницы | Нет @livewireScripts или type="submit" без wire:submit |
| 419 | Нет CSRF в layout |
| Свойство не меняется | Свойство private или опечатка в wire:model |
| Медленный поиск | Убрали debounce; нет индекса на title в БД |
Что попробовать
- Filament — админка на Livewire без ручной вёрстки таблиц.
- Trait
WithPaginationдля длинных списков. - Очереди — тяжёлую работу после
save()уводить в job.
Связанные материалы
Производительность Livewire-компонентов
Интерактивные экраны быстро растут, поэтому полезно сразу держать дисциплину запросов и рендеринга.
| Прием | Польза |
|---|---|
debounce на поиске | Меньше HTTP-запросов при наборе |
пагинация WithPagination | Короче SQL и меньше HTML в ответе |
| индексы в БД для полей фильтрации | Быстрее выборка |
| кеширование справочников | Быстрее первичный рендер |
Мини-проверка
- Включите debugbar или profiler.
- Посмотрите число запросов на один ввод символа.
- Оптимизируйте компонент до стабильной задержки интерфейса.
Когда выбрать Livewire, а когда API + SPA
Выбор удобно делать по клиентам системы.
- Один веб-клиент и команда PHP: Livewire дает быструю реализацию.
- Несколько клиентов (web, mobile, partner API): логично строить API-слой как в Laravel API с Sanctum.
- Внутренняя админка: отличный сценарий для Filament.
Такой подход экономит время команды и снижает архитектурные развилки в середине проекта.
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.