Начало работы с Laravel Livewire

Laravel Livewire Программирование и разработка

Laravel Livewire

Как разработчики, мы всегда ищем инструменты, библиотеки и фреймворки, которые сделают нашу жизнь проще. Для разработчиков Laravel это не исключение. Вот почему мы с самого начала любим этот фреймворк, так как для нас всё делается легко — это позволяет нам сосредоточиться на создании отличных приложений, а не увязнуть в том, как что-то реализовать.

В этом руководстве мы рассмотрим ещё один инструмент, который может облегчить вашу жизнь как разработчика Laravel. В частности, мы рассмотрим Livewire, фреймворк с полным стеком для Laravel, который позволяет нам создавать динамические интерфейсы без написания большого количества JavaScript. По сути, всё делается с помощью инструментов, с которыми вы уже знакомы как разработчик Laravel.

Что такое Livewire?

Livewire — это библиотека, которая позволяет нам создавать реактивные и динамические интерфейсы, используя Blade и немного JavaScript. Я говорю «немного», потому что мы собираемся написать JavaScript только для передачи данных через события браузера и ответа на них.

Вы можете использовать Livewire для реализации следующих функций без перезагрузки страницы:

  • разбивка на страницы
  • проверка формы
  • уведомления
  • предварительный просмотр загружаемых файлов

Обратите внимание, что Livewire не ограничивается перечисленными выше функциями. Вы можете использовать его для гораздо большего. Вышеупомянутые функции являются наиболее распространёнными из тех, которые вы можете реализовать в своих приложениях.

Livewire против Vue

Vue всегда был предпочтительной интерфейсной средой для разработчиков Laravel, чтобы добавить интерактивности в свои приложения. Если вы уже используете Vue для поддержки своих приложений, использование Livewire необязательно. Но если вы новичок в создании внешнего интерфейса в Laravel и рассматриваете Livewire как замену Vue, то да, вы можете использовать Livewire в качестве замены Vue. Кривая обучения
не будет такой сложной, как изучение Vue, поскольку вы в основном будете использовать Blade для написания файлов шаблонов.

Для получения дополнительной информации о сравнении Livewire и Vue ознакомьтесь с «Laravel Livewire vs Vue».

Обзор приложения

Мы собираемся создать живое приложение CRUD. Так что это в основном приложение CRUD без перезагрузки страницы. Livewire будет обрабатывать все запросы AJAX, необходимые для обновления пользовательского интерфейса. Это включает фильтрацию результатов через поле поиска, сортировку по заголовку столбца и простую разбивку на страницы (предыдущая и следующая). Создание и редактирование пользователей будет использовать Bootstrap Modals.

Обзор приложения

Предпосылки

В этом руководстве предполагается, что у вас есть опыт разработки приложений PHP. Опыт работы с Laravel полезен, но не обязателен. Вы всё равно можете следовать указаниям, если знаете только простой PHP или другую платформу PHP.

В этом руководстве предполагается, что на вашем компьютере установлено следующее программное обеспечение:

  • PHP
  • MySQL
  • NGINX
  • Композитор
  • Узел и npm

Если вы используете Mac, более удобным вариантом вместо установки MySQL и NGINX будет установка DBngin и Laravel Valet.

Настройка проекта

Теперь вы можете создать новый проект Laravel:

composer create-project laravel/laravel livecrud

Перейдите в livecrudпапку, которую он создаст. Это будет корневая папка проекта, в которой вы будете выполнять все команды в этом руководстве.

Следующим шагом является создание базы данных MySQL с помощью выбранного вами инструмента управления базой данных. Назовите базу данных как livecrud.

Установите внутренние зависимости

У нас есть только одна зависимость для серверной части — Livewire. Установите его с помощью следующей команды:

composer require livewire/livewire:2.3

Примечание: мы устанавливаем конкретную версию, которую я использовал при создании демонстрации. Если вы читаете это в будущем, рекомендуется установить самую последнюю версию. Не забудьте проверить журнал изменений проекта в их репозитории GitHub, чтобы убедиться, что вы ничего не упустили.

Настройка базы данных

Обновите миграцию по умолчанию для создания usersтаблицы и добавьте настраиваемые поля, которые мы собираемся использовать:

// database/migrations/<timestamp>_create_users_table.php
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->enum('user_type', ['admin', 'user'])->default('user'); // add this
        $table->tinyInteger('age'); // add this
        $table->string('address')->nullable(); // add this
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

Затем обновите database/factories/UserFactory.phpфайл и введите значения в добавленные нами настраиваемые поля:

// database/factories/UserFactory.php
public function definition()
{
    return [
        'name' => $this->faker->name,
        'email' => $this->faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),

        // add these
        'user_type' => 'user',
        'age' => $this->faker->numberBetween(18, 60),
        'address' => $this->faker->address,
    ];
}

Наконец, откройте database/seeders/DatabaseSeeder.phpфайл и раскомментируйте вызов, чтобы создать фиктивных пользователей:

// database/seeders/DatabaseSeeder.php
public function run()
{
    \App\Models\User::factory(100)->create();
}

Не забудьте обновить свой.envфайл тестовой базой данных, которую вы собираетесь использовать. В этом случае я назвал базу данных как livecrud. Как только это будет сделано, запустите миграции и сеялку для заполнения базы данных:

php artisan migrate
php artisan db:seed

Настройка клиентских зависимостей

Чтобы упростить задачу, мы будем использовать каркас Laravel для Bootstrap. Чтобы использовать это, вам сначала нужно установить laravel/uiпакет:

composer require laravel/ui

Затем установите Bootstrap 4. Это добавляет конфигурации на ваш webpack.mix.jsфайл и создаёт resources/js/app.jsи resources/sass/app.scssфайлы:

php artisan ui bootstrap

Затем добавьте в resources/sass/app.scssфайл Font Awsome. По умолчанию там уже должны быть импортированные шрифты, переменные и начальная загрузка:

// Fonts
@import url("https://fonts.googleapis.com/css?family=Nunito");

// Variables
@import "variables";

// Bootstrap
@import "~bootstrap/scss/bootstrap";

// add these:
@import "~@fortawesome/fontawesome-free/scss/fontawesome";
@import "~@fortawesome/fontawesome-free/scss/brands";
@import "~@fortawesome/fontawesome-free/scss/regular";
@import "~@fortawesome/fontawesome-free/scss/solid";

Как только это будет сделано, установите все зависимости:

npm install @fortawesome/fontawesome-free
npm install

Создайте компонент Livewire

Вы можете использовать make:livewireкоманду для создания нового компонента Livewire:

php artisan make:livewire LiveTable

Это создаст следующие файлы:

  • app/Http/Livewire/LiveTable.php — контроллер для компонента
  • resources/views/livewire/live-table.blade.php — файл представления для компонента

Откройте resources/views/livewire/live-table.blade.phpфайл и добавьте следующее:

<div>
    <div class="row mb-4">
        <div class="col-md-12">
          <div class="float-right mt-5">
              <input wire:model="search" class="form-control" type="text" placeholder="Search Users...">
          </div>
        </div>
    </div>

    <div class="row">
        @if ($users->count())
        <table class="table">
            <thead>
                <tr>
                    <th>
                        <a wire:click.prevent="sortBy('name')" role="button" href="#">
                            Name
                            @include('includes.sort-icon', ['field' => 'name'])
                        </a>
                    </th>
                    <th>
                        <a wire:click.prevent="sortBy('email')" role="button" href="#">
                            Email
                            @include('includes.sort-icon', ['field' => 'email'])
                        </a>
                    </th>
                    <th>
                        <a wire:click.prevent="sortBy('address')" role="button" href="#">
                            Address
                            @include('includes.sort-icon', ['field' => 'address'])
                        </a>
                    </th>
                    <th>
                        <a wire:click.prevent="sortBy('age')" role="button" href="#">
                            Age
                            @include('includes.sort-icon', ['field' => 'age'])
                        </a>
                    </th>
                    <th>
                        <a wire:click.prevent="sortBy('created_at')" role="button" href="#">
                        Created at
                        @include('includes.sort-icon', ['field' => 'created_at'])
                        </a>
                    </th>
                    <th>
                        Delete
                    </th>
                    <th>
                        Edit
                    </th>
                </tr>
            </thead>
            <tbody>
                @foreach ($users as $user)
                    <tr>
                        <td>{{ $user->name }}</td>
                        <td>{{ $user->email }}</td>
                        <td>{{ $user->address }}</td>
                        <td>{{ $user->age }}</td>
                        <td>{{ $user->created_at->format('m-d-Y') }}</td>
                        <td>
                            <button class="btn btn-sm btn-danger">
                            Delete
                            </button>
                        </td>
                        <td>
                            <button class="btn btn-sm btn-dark">
                            Edit
                            </button>
                        </td>
                    </tr>
                @endforeach
            </tbody>
        </table>
        @else
            <div class="alert alert-warning">
                Your query returned zero results.
            </div>
        @endif
    </div>

    <div class="row">
        <div class="col">
            {{ $users->links() }}
        </div>
    </div>
</div>

Это очень много кода, поэтому давайте разберём его сверху вниз. Во-первых, у нас есть поле поиска для поиска пользователей. Мы хотим, чтобы пользователи могли видеть результаты своего запроса по мере ввода. Мы реализуем это с помощью wire:model. Это позволяет нам передать имя переменной из класса компонента ( LiveTable). Независимо от того, что пользователь вводит в это поле, он будет синхронизирован со значением этой переменной. В этом случае мы связываем searchпеременную:

<input wire:model="search" class="form-control" type="text" placeholder="Search Users...">

Позже в коде класса компонента LiveTable вы увидите связанную переменную, как в приведённом ниже коде. В Livewire они называются свойствами. Если вы переходите из Vue, то это эквивалент состояния. Только publicсвойства могут быть доступны непосредственно из внешнего интерфейса:

// app/Http/Livewire/LiveTable.php
<?php
class LiveTable extends Component
{
  public $search = ''; // don't add this yet
}

Далее у нас есть заголовки таблиц. Здесь мы используем wire:click.preventдля прослушивания событий щелчка в элементе ссылки. В Livewire они называются действиями. По сути, они позволяют вам отслеживать события браузера, но реагировать на них с помощью методов в серверной части. Использование.preventпредотвращает действие браузера по умолчанию. Значение, которое вы ему задаете, — это имя метода, который вы хотите выполнить в классе компонента. В данном случае это sortBy. Затем мы передаем имя столбца, который хотим отсортировать:

<th>
  <a wire:click.prevent="sortBy('name')" role="button" href="#">
      Name
      @include('includes.sort-icon', ['field' => 'name'])
  </a>
</th>

Вот как выглядит соответствующий метод в классе компонента. Мы добавим код для этого позже:

// app/Http/Livewire/LiveTable.php
public function sortBy($field)
{
  //
}

В приведенный выше код мы включили еще один файл представления с именем sort-icon. Создайте resources/views/includes/sort-icon.blade.phpфайл и добавьте следующее. Это отобразит текущий значок сортировки на основе текущей сортировки, выбранной пользователем:

@if ($sortField !== $field)
    <i class="text-muted fas fa-sort"></i>
@elseif ($sortAsc)
    <i class="fas fa-sort-up"></i>
@else
    <i class="fas fa-sort-down"></i>
@endif

Это почти все, что касается разметки. Остальной код в основном такой же, как и ваш стандартный шаблон Blade. Таким образом, мы по-прежнему используем links()метод для отображения разбивки на страницы и @ifдирективу для условного отображения чего-либо.

Читайте также:  Возврат массива из функции C++

Теперь перейдем к классу компонентов. Откройте app/Http/Livewire/LiveTable.phpфайл и обновите его, чтобы он содержал следующий код:

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use Livewire\WithPagination;
use App\Models\User;

class LiveTable extends Component
{
    use WithPagination;

    public $sortField = 'name'; // default sorting field
    public $sortAsc = true; // default sort direction
    public $search = '';

    public function sortBy($field)
    {
        if ($this->sortField === $field) {
            $this->sortAsc = !$this->sortAsc;
        } else {
            $this->sortAsc = true;
        }

        $this->sortField = $field;
    }

    public function render()
    {
        return view('livewire.live-table', [
            'users' => User::search($this->search)
                ->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')
                ->simplePaginate(10),
        ]);
    }
}

Как упоминалось ранее, мы связали значение searchпеременной с определенным текстовым полем на стороне клиента через расширение wire:model. Таким образом, каждый раз, когда пользователь что-то вводит, searchпеременная также обновляется. И когда он обновляется, компонент также перерисовывается. Это потому, что в render()функции мы зависим от значения searchпеременной для получения пользовательских данных. Итак, для каждого нажатия клавиши мы фактически извлекаем данные из базы данных, предоставляя запрос пользователя и выбранную сортировку (мы рассмотрим, как это улучшить, позже в разделе Оптимизация):

User::search($this->search)
                ->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')
                ->simplePaginate(10)

sortBy()Метод, что мы используем, чтобы обновить поле для сортировки таблицы пользователей. Каждое поле можно отсортировать по возрастанию или убыванию. По умолчанию, щелчок по полю сортировки сортирует его по возрастанию. Повторный щелчок по нему просто сделает наоборот:

public function sortBy($field)
{
    if ($this->sortField === $field) {
        $this->sortAsc = !$this->sortAsc; // if field is already sorted, use the opposite instead
    } else {
        $this->sortAsc = true; // sort selected field by ascending by default
    }

    $this->sortField = $field;
}

При фильтрации usersтаблицы используем search()метод. Но мы этого еще не добавили. Обновите app/Models/User.phpфайл, чтобы включить search()метод. Это фильтрует таблицу пользователей, чтобы возвращать только пользователей типа user. Тогда остальные условия будут полями, которые мы хотим использовать для фильтрации поля поиска:

protected $casts = [
    //
];

public static function search($query)
{
    return empty($query) ? static::query()->where('user_type', 'user')
        : static::where('user_type', 'user')
            ->where(function($q) use ($query) {
                $q
                    ->where('name', 'LIKE', '%'. $query . '%')
                    ->orWhere('email', 'LIKE', '%' . $query . '%')
                    ->orWhere('address', 'LIKE ', '%' . $query . '%');
            });
}

Использование компонента Livewire

Как только это будет сделано, функции поиска и сортировки должны быть готовы к работе. Откройте routes/web.phpфайл и замените существующий маршрут следующим:

Route::get('/', function () {
    return view('index');
});

Затем создайте resources/views/index.blade.phpфайл и добавьте следующее. Здесь мы используем созданный нами компонент Live Table. Мы можем отобразить его на странице так же, как и стандартный компонент. Единственное отличие состоит в том, что нам нужно добавить к имени компонента префикс, livewire:а также использовать его @livewireScriptsдля рендеринга файла JavaScript Livewire:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ config('app.name') }}</title>
    <link rel="stylesheet" type="text/css" href="{{ asset('css/app.css') }}">
</head>
<body>

    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-9">
                <livewire:live-table />
            </div>
        </div>
    </div>

    @livewireScripts
    <script src="{{ asset('js/app.js') }}"></script>

</body>
</html>

На этом этапе вы можете запустить приложение. Самый простой способ — обслужить проект с помощью Artisan:

php artisan serve

Затем откройте приложение в своём браузере по адресу http://127.0.0.1:8000/.

Если вы настроили локальный домен через Laravel Valet или какой-либо другой инструмент, вы также можете использовать его.

Удаление пользователей

Далее реализуем удаление пользователей. Как и раньше, мы используем wire:clickдля прослушивания событий щелчка по кнопке удаления. Только на этот раз мы не будем напрямую вызывать метод в классе компонента. Это потому, что это операция удаления. Мы не хотим, чтобы пользователи по ошибке удалили кого-то, поэтому нам нужно показать какое-то подтверждение, прежде чем мы продолжим удаление. Это идеальный вариант использования для Livewire Events. Это позволяет нам отправлять и получать определённые события на сервер и с него. Вы можете использовать его, вызвав $emit()метод. Его первым аргументом будет имя события, а последующие аргументы — это аргументы, которые вы хотите передать слушателю для этого события. В этом случае мы имеемdeleteTriggered событие, и мы передаём идентификатор и имя пользователя в качестве аргументов слушателю.

Откройте resources/views/livewire/live-table.blade.phpфайл и обновите код кнопки удаления:

<button class="btn btn-sm btn-danger" wire:click="$emit('deleteTriggered', {{ $user->id }}, '{{ $user->name }}')">
  Delete
</button>

Затем мы можем прослушивать это событие либо на сервере, либо на стороне клиента. Поскольку всё, что нам нужно, это показать подтверждение при срабатывании этого события, мы слушаем его на стороне клиента. Создайте resources/js/users.jsфайл и добавьте следующее. Как видите, мы получаем доступ к idи nameпользователя через аргументы, переданные слушателю:

Livewire.on("deleteTriggered", (id, name) => {
    const proceed = confirm(`Are you sure you want to delete ${name}`);

    if (proceed) {
        Livewire.emit("delete", id);
    }
});

Как только пользователь соглашается, мы генерируем событие, которое фактически удаляет пользователя. Чтобы отслеживать события в серверной части, создайте $listenersмассив, содержащий имена слушателей и методы класса, которым они сопоставляются. В этом случае имя события и метод совпадают, поэтому мы просто добавляем delete.. delete()Метод затем удалит пользователя с соответствующим id:

// app/Http/Livewire/LiveTable.php
protected $listeners = ['delete'];

public function sortBy($field)
{
    //
}

public function delete($id)
{
    User::find($id)
        ->delete();
}

Если вам нужно какое-то уведомление при удалении пользователя, вы можете отправить событие браузера :

User::find($id)
        ->delete();
$this->dispatchBrowserEvent('user-deleted', ['user_name' => $user->name]); // add this

Затем на стороне клиента прослушайте это событие через стандартный API прослушивателя событий браузера. Здесь мы просто показываем предупреждение, которое содержит имя удалённого пользователя:

// resources/js/users.js
window.addEventListener("user-deleted", (event) => {
    alert(`${event.detail.user_name} was deleted!`);
});

Наконец, не забудьте добавить users.jsфайл в webpack.mix.js:

// webpack.mix.js
mix.js("resources/js/app.js", "public/js")
    .sass("resources/sass/app.scss", "public/css")
    .js("resources/js/users.js", "public/js") // add this
    .sourceMaps();

На этом этапе удаление пользователей должно работать, если вы попробуете его в своём браузере.

Создание новых пользователей

Приступим к созданию новых пользователей. Откройте resources/views/livewire/live-table.blade.php файл и добавьте кнопку для создания нового пользователя. Опять же, мы используем wire:click для запуска события под названием triggerCreate:

<div>
    <div class="row mb-4">
        <div class="col-md-12">
            <div class="float-left mt-5">
                <button class="btn btn-success" wire:click="$emit('triggerCreate')">Create New User</button>
            </div>

            <div class="float-right mt-5">
                <input wire:model="search" class="form-control" type="text" placeholder="Search Users...">
            </div>
        </div>
    </div>

    <!-- code for the users table from earlier -->
    <div class="row">
        @if ($users->count())
        @endif
    </div>
</div>

Затем в своём resources/js/users.jsфайле прослушайте это событие и откройте модальное окно:

Livewire.on("triggerCreate", () => {
    $("#user-modal").modal("show");
});

Примечание: приведённый выше код на самом деле не является подходом «Livewire Purist». Обычно, если вы используете Livewire, вы хотите, чтобы вся интерактивность выполнялась Livewire, включая модальные окна. В этом случае мы используем jQuery для открытия модального окна. Это простительно, поскольку это всего лишь одна строчка кода. Но если вы хотите использовать Livewire, правильный способ сделать это — использовать Livewire для всего. Вы не можете смешивать и сопоставлять его с jQuery. Это поможет упростить задачу, когда позже вам понадобится добавить внешние тесты.

На самом деле мы ещё не создали компонент Livewire, так что давайте сделаем это:

php artisan make:livewire UserForm

Как и в Live Table, здесь создаётся класс компонента, а также файл представления:

  • app/Http/Livewire/UserForm.php
  • resources/views/livewire/user-form.blade.php

Сначала откройте файл представления и добавьте следующее. Этот код будет выглядеть знакомым, поскольку по большей части это просто стандартный шаблон Blade для визуализации формы. Единственное отличие состоит в том, что мы добавили в него некоторые атрибуты Livewire, с которыми вы уже знакомы:

<!-- resources/views/livewire/user-form.blade.php -->
<div>
    <form wire:submit.prevent="save">
        <div class="form-group">
            <label for="name">Name</label>
            <input type="text" class="form-control" id="name" name="name" wire:model="name">
            @error('name') <span class="text-danger">{{ $message }}</span> @enderror
        </div>

        <div class="form-group">
            <label for="exampleInputPassword1">Email</label>
            <input type="email" class="form-control" id="email" name="email" wire:model="email">
            @error('email') <span class="text-danger">{{ $message }}</span> @enderror
        </div>

        <div class="form-group">
            <label for="age">Age</label>
            <input type="number" class="form-control" id="age" name="age" wire:model="age">
            @error('age') <span class="text-danger">{{ $message }}</span> @enderror
        </div>

        <div class="form-group">
            <label for="address">Address</label>
            <input type="text" class="form-control" id="address" name="address" wire:model="address">
            @error('address') <span class="text-danger">{{ $message }}</span> @enderror
        </div>

        <button class="btn btn-primary" type="submit">Save</button>
    </form>
</div>

На этом этапе вы уже знаете, что оно wire:submit.prevent=»save»будет запущено при нажатии кнопки сохранения..preventпредотвращает действие по умолчанию, которым является фактическая отправка формы. Затем мы используем wire:modelдля привязки каждого поля к определённому свойству в классе компонента.

Читайте также:  Учебное пособие по Python: начало работы с Jupyter Notebook

Ещё одна особенность этого кода: это стандартный код шаблона Blade для отображения ошибок формы. Вам может быть интересно, что он здесь делает, когда мы находимся в компоненте Livewire. Что ж, ответ в том, что Livewire также может использовать этот код, чтобы показывать нам ошибки формы. Поэтому, если пользователь не указал значение в определённое поле при отправке формы, сервер издаст крик и вызовет следующие ошибки:

@error('name') <span class="text-danger">{{ $message }}</span> @enderror

Затем откройте resources/views/index.blade.phpфайл и добавьте разметку для модального окна:

<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-9">
            <livewire:live-table />
        </div>
    </div>
</div>

<!-- add this -->
<div class="modal" tabindex="-1" role="dialog" id="user-modal">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">User</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>

            <div class="modal-body">
                <livewire:user-form>
            </div>
        </div>
    </div>
</div>
Теперь, когда у нас есть код для внешнего интерфейса, давайте посмотрим на него. Откройте app/Http/Livewire/UserForm.phpи добавьте следующее:
<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\User; // add this

class UserForm extends Component
{

    // add these
    public $name;
    public $email;
    public $age;
    public $address;

    public function render()
    {
        return view('livewire.user-form');
    }

    // add this
    public function save()
    {
        $validated = $this->validate([
            'name' => 'required|min:10',
            'email' => 'required|email|min:10',
            'age' => 'required|integer',
            'address' => 'required|min:10',
        ]);

        User::create(array_merge($validated, [
            'user_type' => 'user',
            'password' => bcrypt($this->email)
        ]));

        $this->resetForm();
        $this->dispatchBrowserEvent('user-saved', ['action' => 'created', 'user_name' => $this->name]);
        $this->emitTo('live-table', 'triggerRefresh');
    }

    public function resetForm()
    {
        $this->user_id = null;
        $this->name = null;
        $this->email = null;
        $this->age = null;
        $this->address = null;
    }

}

Это много, но большая часть из них уже должна иметь для вас смысл, поскольку мы уже использовали их ранее. Поэтому я не буду их подробно останавливаться. Вместо этого давайте просто рассмотрим причины, по которым я закодировал это определённым образом.

Во-первых, я проверил форму. Здесь ничего нового. Это просто стандартный код проверки формы Laravel. Но почему я использовал это вместо класса Request? Это потому, что способ работы Livewire несовместим с классом Request. По сути, класс Laravel Request работает только со стандартным HTTP-запросом. Это означает, что он перенаправляет пользователя на предыдущую страницу в случае ошибки проверки. В Livewire такого не может быть, так как всё делается через AJAX:

$validated = $this->validate([
    'name' => 'required|min:10',
    'email' => 'required|email|min:10',
    'age' => 'required|integer',
    'address' => 'required|min:10',
]);

Далее идет этот фрагмент кода. Здесь мы используем emitTo()вместо emit(). Это даёт возможность различным компонентам общаться друг с другом через события. Он принимает имя компонента в качестве первого аргумента и имя события в качестве второго:

$this->dispatchBrowserEvent('user-saved', ['action' => 'created', 'user_name' => $this->name]);
$this->emitTo('live-table', 'triggerRefresh');

Почему мы используем два отдельных события (одно событие браузера и одно событие Livewire), когда мы можем использовать только одно? Что ж, это правда, что мы можем использовать только одно событие. Проблема в том, что нам также необходимо обновить таблицу данных после создания пользователя. В настоящее время я не знаю, как запустить перезагрузку определённого компонента со стороны клиента, поэтому я использовал два отдельных события — одно для скрытия модального окна и отображения предупреждения, а другое для обновления таблицы данных.

Теперь, когда вы знаете причину кода, давайте перейдём к обработчикам этих событий. Добавьте в resources/js/users.jsфайл следующее:

window.addEventListener("user-saved", (event) => {
    $("#user-modal").modal("hide");
    alert(`User ${event.detail.user_name} was ${event.detail.action}!`);
});

Затем в классе компонентов LiveTable добавьте слушателя для triggerRefresh. Это немного отличается от deleteслушателя, поскольку мы указываем на $refreshфункцию, которую нам фактически не нужно объявлять как метод класса. Это потому, что это встроенный метод для всех классов компонентов Livewire, который позволяет нам перезагружать весь компонент:

// app/Http/Livewire/LiveTable.php
    protected $listeners = ['delete', 'triggerRefresh' => '$refresh'];

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

Обновление существующих пользователей

Последняя часть функциональности, которую мы реализуем, — это обновление пользователей. Обновите кнопку редактирования в resources/views/livewire/live-table.blade.phpфайле следующим образом. Поскольку мы находимся в компоненте LiveTable, а функция редактирования должна быть в компоненте UserForm, мы должны использовать его $emitTo()для отправки triggerEditсобытия в компонент UserForm. В отличие от ранее, когда мы предоставляли только отдельные значения, здесь мы предоставляем весь userобъект:

<td>
    <button class="btn btn-sm btn-dark" wire:click="$emitTo('user-form', 'triggerEdit', {{ $user }})">Edit</button>
</td>

Чтобы прослушать triggerEditсобытие, откройте app/Http/Livewire/UserForm.phpфайл и добавьте следующее. Отдельный userобъект передаётся в эту функцию, и мы используем его для заполнения полей формы значениями. Обратите внимание, что вместо объекта вы получаете доступ к отдельным полям, как в массиве. Как только это будет сделано, отправьте dataFetchedсобытие:

protected $listeners = ['triggerEdit'];

public function resetForm()
{
    //
}

public function triggerEdit($user)
{
    $this->user_id = $user['id'];
    $this->name = $user['name'];
    $this->email = $user['email'];
    $this->age = $user['age'];
    $this->address = $user['address'];

    $this->emit('dataFetched', $user);
}

Слушайте dataFetchedсобытие на стороне клиента. Поскольку в этот момент мы уже заполнили отдельные поля значениями, мы можем просто открыть модальное окно:

// resources/js/users.js
Livewire.on("dataFetched", (user) => {
  $("#user-modal").modal("show");
});

Наконец, обновите save()метод в классе компонента UserForm для обработки обновлений. Для этого проверьте значение user_idполя. Если у него есть значение, это означает, что пользователь в настоящее время обновляется. В противном случае создаем его:

// app/Http/Livewire/UserForm.php
public function save()
{
    $validated = $this->validate([
        'name' => 'required|min:10',
        'email' => 'required|email|min:10',
        'age' => 'required|integer',
        'address' => 'required|min:10',
    ]);

    if ($this->user_id) {
        User::find($this->user_id)
            ->update([
                'name' => $this->name,
                'email' => $this->email,
                'age' => $this->age,
                'address' => $this->address,
            ]);

        $this->dispatchBrowserEvent('user-saved', ['action' => 'updated', 'user_name' => $this->name]);
    } else {
        User::create(array_merge($validated, [
            'user_type' => 'user',
            'password' => bcrypt($this->email)
        ]));

        $this->dispatchBrowserEvent('user-saved', ['action' => 'created', 'user_name' => $this->name]);
    }

    $this->resetForm();
    $this->emitTo('live-table', 'triggerRefresh');
}

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

Оптимизация

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

Поле поиска

Как вы уже могли заметить, ввод в поле поиска почти сразу вызывает запрос AJAX, который извлекает обновлённые данные с сервера. Это каждый раз отправляет запрос к базе данных, так что это не совсем идеально. По умолчанию Livewire применяет к входам дребезг 150 мс. Мы хотим увеличить эту задержку, чтобы Livewire не отправлял запрос на сервер, пока пользователь всё ещё набирает текст. В приведённом ниже коде добавлена ​​задержка 800 мс, так что есть заметная задержка. Поиграйте с этим значением, чтобы убедиться, что есть идеальный баланс:

<!-- resources/views/livewire/live-table.blade.php -->
<input wire:model.debounce.800ms="search">

Поля формы

Ещё одна быстрая оптимизация, которую мы могли бы сделать, — это обновление полей формы в форме пользователя. Как и поле поиска, запрос отправляется на сервер почти мгновенно, когда вы что-то вводите. На этот раз вместо добавления дебаунда мы используем ленивое обновление. Это отправит запрос на сервер только тогда, когда пользователь выйдет за пределы текстового поля:

<!-- resources/views/livewire/user-form.blade.php -->
<input wire:model.lazy="name">

Заключение

В этом руководстве вы изучили основы использования Livewire, чтобы сделать ваши приложения Laravel более динамичными, без написания целого набора JavaScript для выборки и отправки данных на сервер.

В частности, мы создали простое приложение CRUD, которое использует Livewire, чтобы избавиться от необходимости в интерфейсных пакетах, таких как Datatables, для реализации поиска и сортировки таблиц. Мы также устранили необходимость полного обновления страницы для отправки формы. Наконец, мы использовали как события Livewire, так и события браузера, чтобы заставить интерфейсную и внутреннюю части взаимодействовать друг с другом без необходимости писать код AJAX.од AJAX.

 

Оцените статью
bestprogrammer.ru
Добавить комментарий

  1. Capone

    а исходники есть, например, на гитхабе?

    Ответить
  2. Георгий

    Очень хорошая подача материала. На этой статье было первое знакомство с LiveWire. Быстро уловил азы. Круто, что автор оставил некоторые моменты нераскрытыми, что подстегнуло «скумекать» самому.
    Спасибо. Огромный респект

    Ответить
  3. Capone

    вот исходники на гитхаб https://github.com/dietercoopman/livewire-crud-demo

    Ответить