Livewire для PHP: полное руководство
1. Что это и зачем нужна
Livewire — это полнофункциональная библиотека для Laravel, которая позволяет создавать динамические, реактивные интерфейсы без необходимости писать JavaScript. Она работает на серверной стороне, используя Alpine.js для обновления DOM, но вся логика остаётся в PHP. Это идеальный инструмент для разработчиков, которые хотят быстро создавать интерактивные веб-приложения, не углубляясь в сложные фронтенд-фреймворки.
Основная цель Livewire — упростить разработку современных веб-интерфейсов, сохраняя при этом привычный Laravel-подход. Вы пишете компоненты на PHP, а Livewire автоматически обрабатывает AJAX-запросы, обновляет DOM и управляет состоянием. Это особенно полезно для форм, панелей управления, чатов и любых других интерактивных элементов.
Библиотека решает проблему дублирования кода между фронтендом и бэкендом, позволяя разработчикам сосредоточиться на логике приложения, а не на настройке WebSocket или сложных JavaScript-библиотек. Livewire идеально подходит для проектов, где важна скорость разработки и простота поддержки.
2. Установка
Для установки Livewire используйте Composer — стандартный менеджер пакетов для PHP. Выполните следующую команду в корне вашего Laravel-проекта:
composer require livewire/livewire
После установки Livewire автоматически зарегистрирует свои сервис-провайдеры. Если вы используете Laravel 8 или новее, дополнительная настройка не требуется. Для старых версий может потребоваться добавить в config/app.php:
'providers' => [
// ...
Livewire\LivewireServiceProvider::class,
];
Также убедитесь, что в вашем шаблоне (обычно layouts/app.blade.php) подключены скрипты и стили Livewire:
@livewireStyles
</head>
<body>
@livewireScripts
</body>
3. Быстрый старт — минимальный рабочий пример
Создадим простой компонент счётчика. Сначала выполните artisan-команду:
php artisan make:livewire counter
Это создаст два файла: app/Http/Livewire/Counter.php и resources/views/livewire/counter.blade.php. Отредактируем PHP-класс:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class Counter extends Component
{
public $count = 0;
public function increment()
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}
И шаблон:
<div>
<h1>{{ $count }}</h1>
<button wire:click="increment">+1</button>
</div>
Теперь вставьте компонент в любую Blade-вьюху:
<livewire:counter />
Готово! При нажатии на кнопку счётчик увеличивается без перезагрузки страницы.
4. Основные методы/функции/классы
4.1. render()
Сигнатура: public function render(): View
Описание: Обязательный метод, который возвращает Blade-шаблон компонента. Вызывается при каждой отрисовке.
public function render()
{
return view('livewire.my-component', ['data' => $this->getData()]);
}
4.2. mount()
Сигнатура: public function mount(...$params): void
Описание: Аналог конструктора, вызывается один раз при первом рендеринге компонента. Используется для инициализации свойств.
public $userId;
public $user;
public function mount($userId)
{
$this->userId = $userId;
$this->user = User::find($userId);
}
4.3. emit()
Сигнатура: public function emit(string $event, ...$params): void
Описание: Отправляет событие другим компонентам или JavaScript. Полезно для межкомпонентного взаимодействия.
public function save()
{
// ... сохранение данных
$this->emit('userSaved', $this->userId);
}
4.4. emitTo()
Сигнатура: public function emitTo(string $component, string $event, ...$params): void
Описание: Отправляет событие конкретному компоненту по имени.
public function notifyAdmin()
{
$this->emitTo('admin-panel', 'newUserRegistered', $this->user);
}
4.5. dispatchBrowserEvent()
Сигнатура: public function dispatchBrowserEvent(string $event, array $data = []): void
Описание: Отправляет событие в браузер, которое можно перехватить через JavaScript.
public function showSuccess()
{
$this->dispatchBrowserEvent('show-toast', ['message' => 'Успешно!']);
}
4.6. validate()
Сигнатура: public function validate(array $rules = null, array $messages = [], array $attributes = []): array
Описание: Валидирует публичные свойства компонента по заданным правилам.
public $email;
public $password;
public function login()
{
$this->validate([
'email' => 'required|email',
'password' => 'required|min:8',
]);
// Логика входа
}
4.7. reset()
Сигнатура: public function reset(...$properties): void
Описание: Сбрасывает указанные публичные свойства к значениям по умолчанию.
public $name = '';
public $email = '';
public function resetForm()
{
$this->reset('name', 'email');
}
4.8. resetValidation()
Сигнатура: public function resetValidation(string $property = null): void
Описание: Сбрасывает ошибки валидации для указанного свойства или всех сразу.
public function clearErrors()
{
$this->resetValidation();
}
4.9. updated()
Сигнатура: public function updated(string $propertyName): void
Описание: Хук, вызываемый после обновления любого публичного свойства. Можно использовать для реактивного поведения.
public $search = '';
public function updatedSearch()
{
$this->results = User::where('name', 'like', "%{$this->search}%")->get();
}
4.10. hydrate() и dehydrate()
Сигнатура: public function hydrate(): void и public function dehydrate(): void
Описание: Хуки жизненного цикла. hydrate() вызывается при гидратации компонента (перед каждым запросом), dehydrate() — перед отправкой ответа.
public function hydrate()
{
// Загружаем дополнительные данные
$this->loadPermissions();
}
public function dehydrate()
{
// Очищаем временные данные
unset($this->tempData);
}
5. Реальный практический пример
Создадим компонент для управления списком задач (Task Manager) с возможностью добавления, удаления и отметки выполнения.
Команда для создания:
php artisan make:livewire task-manager
PHP-класс (app/Http/Livewire/TaskManager.php):
<?php
namespace App\Http\Livewire;
use App\Models\Task;
use Livewire\Component;
use Livewire\WithPagination;
class TaskManager extends Component
{
use WithPagination;
public $newTaskTitle = '';
public $filter = 'all'; // all, active, completed
public $editingTaskId = null;
public $editingTaskTitle = '';
protected $rules = [
'newTaskTitle' => 'required|min:3|max:255',
'editingTaskTitle' => 'required|min:3|max:255',
];
protected $messages = [
'newTaskTitle.required' => 'Введите название задачи',
'newTaskTitle.min' => 'Название должно содержать минимум 3 символа',
'editingTaskTitle.required' => 'Название не может быть пустым',
];
public function addTask()
{
$this->validate(['newTaskTitle' => $this->rules['newTaskTitle']]);
Task::create([
'title' => $this->newTaskTitle,
'completed' => false,
]);
$this->newTaskTitle = '';
$this->emit('taskAdded');
$this->dispatchBrowserEvent('show-toast', ['message' => 'Задача добавлена!']);
}
public function toggleTask($taskId)
{
$task = Task::findOrFail($taskId);
$task->update(['completed' => !$task->completed]);
$this->emit('taskToggled');
}
public function editTask($taskId)
{
$task = Task::findOrFail($taskId);
$this->editingTaskId = $taskId;
$this->editingTaskTitle = $task->title;
}
public function updateTask()
{
$this->validate(['editingTaskTitle' => $this->rules['editingTaskTitle']]);
$task = Task::findOrFail($this->editingTaskId);
$task->update(['title' => $this->editingTaskTitle]);
$this->editingTaskId = null;
$this->editingTaskTitle = '';
$this->emit('taskUpdated');
}
public function cancelEdit()
{
$this->editingTaskId = null;
$this->editingTaskTitle = '';
}
public function deleteTask($taskId)
{
Task::findOrFail($taskId)->delete();
$this->emit('taskDeleted');
$this->dispatchBrowserEvent('show-toast', ['message' => 'Задача удалена!']);
}
public function clearCompleted()
{
Task::where('completed', true)->delete();
$this->emit('completedCleared');
}
public function render()
{
$query = Task::query();
if ($this->filter === 'active') {
$query->where('completed', false);
} elseif ($this->filter === 'completed') {
$query->where('completed', true);
}
$tasks = $query->orderBy('created_at', 'desc')->paginate(10);
$stats = [
'total' => Task::count(),
'completed' => Task::where('completed', true)->count(),
'active' => Task::where('completed', false)->count(),
];
return view('livewire.task-manager', [
'tasks' => $tasks,
'stats' => $stats,
]);
}
}
Blade-шаблон (resources/views/livewire/task-manager.blade.php):
<div>
<!-- Статистика -->
<div class="mb-4 p-4 bg-gray-100 rounded">
<span>Всего: {{ $stats['total'] }} | </span>
<span>Активных: {{ $stats['active'] }} | </span>
<span>Завершённых: {{ $stats['completed'] }}</span>
</div>
<!-- Форма добавления -->
<form wire:submit.prevent="addTask" class="mb-4">
<div class="flex gap-2">
<input
type="text"
wire:model="newTaskTitle"
placeholder="Новая задача..."
class="flex-1 p-2 border rounded"
>
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Добавить
</button>
</div>
@error('newTaskTitle') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</form>
<!-- Фильтры -->
<div class="mb-4 flex gap-2">
<button wire:click="$set('filter', 'all')"
class="px-3 py-1 rounded {{ $filter === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-200' }}">
Все
</button>
<button wire:click="$set('filter', 'active')"