学习 Laravel Sushi - Eloquent 的数组驱动
发布时间:作者: Jason Beggs
上周,我发布了一篇文章 使用 Volt 和 Folio 构建示例应用程序。在这个例子中,我使用了 Caleb Porzio 的包,Sushi,来模拟一些示例数据。这让我对其他人使用 Sushi 做了什么感到好奇,所以我 发推 询问人们他们使用它的目的。在这篇文章中,我们将介绍 Sushi 的基本概念以及一些关于如何使用它的示例。
什么是 Laravel Sushi,它是如何工作的?
根据该包的 README 文档,Sushi 是“Eloquent 缺少的“数组”驱动程序。”换句话说,它允许您从除数据库以外的数据源创建 Eloquent 模型。最简单的使用方法是在 Model 文件中直接提供数据作为硬编码数组,并设置 $rows
属性。其他时候,您可以使用 getRows
来提供动态数据 - 来自 API 源、CSV 文件或您选择的任何其他地方。
那么它到底是如何工作的呢?Sushi 会获取您提供的数据,创建 Eloquent 模型,然后将它们缓存到 SQLite 数据库中。然后,您可以像查询任何标准 Eloquent 模型一样查询数据。
以下是一个非常基本的 Sushi 模型示例
<?php namespace App\Models; use Sushi\Sushi; class Role extends Model{ // Add the trait use Sushi; // Provide the data as a hardcoded array protected $rows = [ ['id' => 1, 'label' => 'admin'], ['id' => 2, 'label' => 'manager'], ['id' => 3, 'label' => 'user'], ];}
Laravel Sushi 州
让我们深入了解一些我和其他人使用过的真实示例。我将介绍的最基本的一个是创建州列表或表格。 Ken 和 Facundo 提到了这个用例,但我本人也使用过。
<?php namespace App\Models; use Sushi\Sushi; class Role extends Model{ use Sushi; protected $rows = [ [ 'id' => 1, 'name' => 'Alabama', 'abbreviation' => 'AL', ], [ 'id' => 2, 'name' => 'Alaska', 'abbreviation' => 'AK', ], [ 'id' => 3, 'name' => 'Arizona', 'abbreviation' => 'AZ', ], [ 'id' => 4, 'name' => 'Arkansas', 'abbreviation' => 'AR', ], [ 'id' => 5, 'name' => 'California', 'abbreviation' => 'CA', ], // ... ];}
注意:'id' 列是可选的。Sushi 可以为每个项目创建自动递增的 ID,但如果项目发生更改(并且缓存被清除),您不能保证项目会获得与之前相同的 ID。如果您要将其他数据与 Sushi 模型关联,最好为每个项目提供一个静态 ID 列。
Laravel Sushi 用于博客、课程和信息产品
另一个方便的用例是用于简单的博客和课程。有时,作为一名开发人员,我需要为博客或课程存储一些页面,但我不需要完整的 CMS。我宁愿保持轻量级,同时将所有内容直接存储在代码中,以便可以通过 Git 同步。
Aaron 提到了他使用这种设置来构建 aaronfrancis.com 上的博客。 Caleb 提到了 Livewire v2 演示平台使用了类似于此的功能
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model;use Sushi\Sushi; class Series extends Model{ use Sushi; public function screencasts() { return $this->hasMany(Screencast::class); } public function getRows() { return [ ['id' => 1, 'order' => 1, 'title' => 'Getting Started'], ['id' => 2, 'order' => 2, 'title' => 'A Basic Form With Validation'], //... ]; }}
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model;use Sushi\Sushi; class Screencast extends Model{ use Sushi; public function series() { return $this->belongsTo(Series::class); } public function getNextAttribute() { return static::find($this->id + 1); } public function getPrevAttribute() { return static::find($this->id - 1); } public function getDurationInSecondsAttribute() { // ... } protected $rows = [ [ 'title' => 'Installation', 'slug' => 'installation', 'description' => "Installing Livewire is so simple, this 2.5 minute video feels like overkill. Composer require, and two little lines added to your layout file, and you are fully set up and ready to rumble!", 'video_url' => 'https://vimeo.com/...', 'code_url' => 'https://github.com/...', 'duration_in_minutes' => '2:32', 'series_id' => 1, ], [ 'title' => 'Data Binding', 'slug' => 'data-binding', 'description' => "The first and most important concept to understand when using Livewire is "data binding". It's the backbone of page reactivity in Livewire, and it'll be your first introduction into how Livewire works under the hood. Mandatory viewing.", 'video_url' => 'https://vimeo.com/...',, 'code_url' => 'https://github.com/...', 'duration_in_minutes' => '9:11', 'series_id' => 1, ], // ... ];}
如您在此示例中看到的,由于这些是真正的 Eloquent 模型,因此您可以添加关系、getter 和辅助方法,就像您在普通模型上一样。
有了这些模型,您可以在控制器或 Livewire 组件中查询它们,就像查询数据库驱动的模型一样
$series = Series::with(['screencasts'])->orderBy('order')->get();
然后,您可以在 Blade 中循环遍历它们
<div> @foreach($series as $s) <div> <h2>{{ $series->title }}</h2> <div> @foreach($series->screencasts as $screencast) <div> <h3>{{ $screencast->title }}</h3> <p>{{ $screencast->description }}</p> </div> @endforeach </div> </div> @endforeach</div>
您甚至可以使用 Laravel 的路由模型绑定自动查询 Sushi 模型
Route::get('/screencasts/{screencast:slug}');
Caleb 和我使用非常相似的方法来存储 Alpine Components 的组件。我们使用路由模型绑定进行路由,然后使用 Blade 视图显示每个组件的详细信息。
在 Blade 视图中,我们循环遍历组件的变体并使用 @include($variant->view)
来包含单独的硬编码 Blade 视图,其中包含组件的实际代码。
<?php namespace App\Models; use App\Enums\ComponentType;use Illuminate\Database\Eloquent\Model;use Sushi\Sushi; class Component extends Model{ use Sushi; protected $casts = [ 'variants' => 'collection', 'requirements' => 'collection', 'type' => ComponentType::class, ]; public function getRows() { return [ [ 'title' => 'Dropdown', 'slug' => 'dropdown', 'description' => 'How to build a dropdown component using Alpine.js.', 'screencast_id' => 111, 'variants' => json_encode([ ['view' => 'patterns.dropdown'], ]), 'type' => ComponentType::COMPONENT->value, 'is_public' => true, 'is_free' => true, 'requirements' => json_encode([ [ 'name' => 'alpinejs', 'version' => 'v3.x', 'url' => 'https://alpinejs.dev/installation', ], ]), ], [ 'title' => 'Modal', 'slug' => 'modal', 'description' => 'How to build a modal component using Alpine.js.', 'screencast_id' => 222, 'variants' => json_encode([ ['view' => 'patterns.modal'], ]), 'type' => ComponentType::COMPONENT->value, 'is_public' => true, 'is_free' => false, 'requirements' => json_encode([ [ 'name' => 'alpinejs', 'version' => 'v3.x', 'url' => 'https://alpinejs.dev/installation', ], [ 'name' => '@alpinejs/focus', 'version' => 'v3.x', 'url' => 'https://alpinejs.dev/plugins/focus', ], ]), ], // ... ]; }}
如您在此示例中看到的,我们使用了 getRows
方法,而不是设置 $rows
属性。这是为了我们可以使用 json_encode()
函数,并为每个模型的 variants
和 requirements
列使用 JSON 列。您还可以看到,Sushi 支持将属性转换为不同的类型,就像 Laravel 一样。
API 数据源
另一个不错的用例是从 API 源获取数据。 Raúl、Marcel、Adam 和 Caleb 提到了他们使用过的不同的 API 源。
Caleb 向 GitHub Sponsors API 发送请求以确定谁可以访问 Livewire v2 演示,然后映射这些结果以获取他需要的属性并将其格式化为模型的良好模式。这是 Livewire v2 代码库中 Sponsor
模型的简化版本
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model;use Illuminate\Support\Facades\Cache;use Illuminate\Support\Facades\Http;use Illuminate\Support\Str;use Sushi\Sushi; class Sponsor extends Model{ use Sushi; protected $keyType = 'string'; public function user() { return $this->hasOne(User::class, 'github_username', 'username'); } public function getsScreencasts() { // If they sponsor for more than $8, they get access to screencasts. return $this->tier_price_in_cents > 8 * 100; } public function getRows() { return Cache::remember('sponsors', now()->addHour(), function () { return collect($this->fetchSponsors()) ->map(function ($sponsor) { return [ 'id' => $sponsor['sponsorEntity']['id'], 'username' => $sponsor['sponsorEntity']['login'], 'name' => $sponsor['sponsorEntity']['name'], 'email' => $sponsor['sponsorEntity']['email'], // ... ]; }); }); } public function fetchSponsors() { return Http::retry(3, 100) ->withToken( config('services.github.token') )->post('https://api.github.com/graphql', [ 'query' => 'A big ugly GraphQL query' ]); }}
结论
Sushi 是一个非常棒的包,有一些很棒的用例。我相信在这篇文章中我只触及了表面。如果您使用过这个包,请在 Twitter 上告诉我您的使用体验!
TALL 技术栈(Tailwind CSS、Alpine.js、Laravel 和 Livewire)顾问和 designtotailwind.com 的所有者。