Livewire для PHP: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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')"                    

Рекомендаций