Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Livewire: интерактивность без JavaScript

Счётчик, живой поиск, форма с проверкой на лету обычно требуют JavaScript и отдельного API. Livewire позволяет написать всё это на PHP: компонент хранит состояние на сервере, а страница обновляется сама, без перезагрузки.

Установка

composer require livewire/livewire
php artisan make:livewire counter

Команда создаёт класс app/Livewire/Counter.php и шаблон resources/views/livewire/counter.blade.php.

Первый компонент

namespace App\Livewire;
 
use Livewire\Component;
 
class Counter extends Component
{
    public int $count = 0;
 
    public function increment(): void
    {
        $this->count++;
    }
 
    public function render()
    {
        return view('livewire.counter');
    }
}
<div>
    <button wire:click="increment">+1</button>
    <span>{{ $count }}</span>
</div>

Открытые свойства класса доступны в шаблоне. Щелчок по кнопке отправляет запрос на сервер, вызывает метод и обновляет разметку.

Вставка на страницу

<livewire:counter />

Привязка поля

wire:model связывает поле формы со свойством:

class PostSearch extends Component
{
    public string $query = '';
 
    public function render()
    {
        return view('livewire.post-search', [
            'posts' => Post::where('title', 'like', "%{$this->query}%")->limit(10)->get(),
        ]);
    }
}
<div>
    <input type="search" wire:model.live.debounce.300ms="query" placeholder="Поиск">
 
    <ul>
        @foreach ($posts as $post)
            <li wire:key="{{ $post->id }}">{{ $post->title }}</li>
        @endforeach
    </ul>
</div>

Слово live обновляет компонент при вводе, debounce.300ms ждёт паузы в наборе, чтобы не отправлять запрос на каждую букву.

Форма с проверкой

use Livewire\Attributes\Validate;
 
class CreatePost extends Component
{
    #[Validate('required|min:5')]
    public string $title = '';
 
    #[Validate('required')]
    public string $body = '';
 
    public function save()
    {
        $this->validate();
 
        Post::create(['title' => $this->title, 'body' => $this->body, 'user_id' => auth()->id()]);
 
        return $this->redirect('/posts');
    }
 
    public function render()
    {
        return view('livewire.create-post');
    }
}
<form wire:submit="save">
    <input type="text" wire:model="title">
    @error('title') <p class="error">{{ $message }}</p> @enderror
 
    <textarea wire:model="body"></textarea>
    @error('body') <p class="error">{{ $message }}</p> @enderror
 
    <button type="submit">
        <span wire:loading.remove>Сохранить</span>
        <span wire:loading>Сохраняем…</span>
    </button>
</form>

wire:loading показывает элемент, пока идёт запрос.

Что выбрать

ИнструментКогда подходит
Blade и немного Alpine.jsОбычные страницы с парой интерактивных мелочей
LivewireИнтерактивные формы, фильтры, таблицы — без отдельного фронтенда
Inertia с React или VueСложный интерфейс, команда с фронтенд-разработчиками

Частая ошибка

Открытые свойства компонента передаются в браузер, и посетитель может изменить их перед отправкой. Нельзя доверять свойству вроде $userId или $price: проверяйте права и данные в каждом методе, как в обычном контроллере. Свойства, которые клиенту менять нельзя, помечают атрибутом #[Locked].

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.