开始使用Laravel Livewire

525 阅读6分钟

Getting Started with Laravel Livewire

作为开发者, 我们总是在寻找能让我们的生活更轻松的工具, 库, 和框架.这对Laravel开发者来说也不例外。这也是我们为什么喜欢这个框架的原因, 因为一切都变得简单了 - 让我们专注于建立强大的应用程序,而不是被如何实现的东西所困扰。

在本教程中, 我们将看到另一个工具,它可以使你作为一个Laravel开发者的生活更容易。具体来说, 我们将研究Livewire, 一个用于Laravel的全栈框架, 允许我们建立动态界面而不需要编写大量的JavaScript。所有的事情基本上都是用你作为一个Laravel开发者已经熟悉的工具完成的。

什么是Livewire?

Livewire是一个库,它允许我们使用Blade和一点点的JavaScript来构建反应式和动态界面。我说 "一点点 "是因为我们只打算写JavaScript来通过浏览器事件传递数据,并对其做出响应。

你可以使用Livewire来实现以下功能,而不需要重新加载页面。

  • 分页
  • 表单验证
  • 通知
  • 文件上传预览

请注意,Livewire并不限于上述功能。你可以用它来做更多的事情。上面的功能只是你可能想在你的应用程序中实现的最常见的功能。

Livewire vs Vue

Vue一直是Laravel开发者的首选前端框架,可以为他们的应用程序添加互动性。如果你已经在使用Vue来驱动你的应用程序,那么Livewire对你来说是可以选择学习的。但如果你是在Laravel中做前端的新手,而你想用Livewire来替代Vue,那么是的,你可以用Livewire来替代Vue。学习曲线
,不会像学习Vue那样陡峭,因为你主要是用Blade来编写你的模板文件。

更多关于Livewire和Vue的比较信息,请查看 "Laravel Livewire vs Vue"。

应用程序概述

我们将创建一个实时CRUD应用程序。所以它基本上是一个没有页面重载的CRUD应用。Livewire将处理所有更新UI所需的AJAX请求。这包括通过搜索字段过滤结果,通过列标题排序,以及简单的分页(上一页和下一页)。创建和编辑用户将利用Bootstrap Modals。

Live CRUD

你可以在GitHub repo上查看这个项目的源代码。

前提条件

本教程假定你有PHP应用开发的经验。对Laravel的经验是有帮助的,但不是必须的。如果你只知道普通的PHP或其他PHP框架,你仍然可以跟着学习。

本教程假定你的机器上已经安装了以下软件。

如果你使用的是Mac,一个更方便的选择是安装DBnginLaravel Valet,而不是安装MySQL和NGINX。

设置项目

你现在可以创建一个新的Laravel项目:

composer create-project laravel/laravel livecrud

导航到livecrud 文件夹内,它将生成。这将是项目的根目录, 你在这里执行本教程中的所有命令.

下一步是使用你选择的数据库管理工具创建一个MySQL数据库.将数据库命名为livecrud

安装后端依赖项

我们只有一个后端依赖项,那就是Livewire。用以下命令安装它。

composer require livewire/livewire:2.3

注意:我们安装的是我在创建演示时使用的特定版本。如果你以后再看这个,建议你安装最新的版本。不要忘记在GitHub repo上查看项目的更新日志,以确保你没有遗漏任何东西。

设置数据库

更新用于创建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.jsresources/sass/app.scss 文件。

php artisan ui bootstrap

接下来,将Font Awsome添加到resources/sass/app.scss 文件中。默认情况下,那里应该已经有了字体、变量和Bootstrap导入。

// 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 指令来有条件地显示一些东西。

我们现在转到组件类。打开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),
        ]);
    }
}

如前所述,我们已经通过wire:modelsearch 变量的值绑定到了客户端的一个特定的文本字段上。因此,每次用户键入什么,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 . '%');
            });
}

继续阅读 在SitePoint开始使用Laravel Livewire.