Перейти к основному содержимому

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.jsVue/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
Мобильное приложение, отдельный ReactSanctum 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 в БД

Что попробовать

  1. Filament — админка на Livewire без ручной вёрстки таблиц.
  2. Trait WithPagination для длинных списков.
  3. Очереди — тяжёлую работу после save() уводить в job.

Связанные материалы


Производительность Livewire-компонентов

Интерактивные экраны быстро растут, поэтому полезно сразу держать дисциплину запросов и рендеринга.

ПриемПольза
debounce на поискеМеньше HTTP-запросов при наборе
пагинация WithPaginationКороче SQL и меньше HTML в ответе
индексы в БД для полей фильтрацииБыстрее выборка
кеширование справочниковБыстрее первичный рендер

Мини-проверка

  1. Включите debugbar или profiler.
  2. Посмотрите число запросов на один ввод символа.
  3. Оптимизируйте компонент до стабильной задержки интерфейса.

Когда выбрать Livewire, а когда API + SPA

Выбор удобно делать по клиентам системы.

  • Один веб-клиент и команда PHP: Livewire дает быструю реализацию.
  • Несколько клиентов (web, mobile, partner API): логично строить API-слой как в Laravel API с Sanctum.
  • Внутренняя админка: отличный сценарий для Filament.

Такой подход экономит время команды и снижает архитектурные развилки в середине проекта.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.