illuminatech / sync-many-attribute
允许通过数组属性控制 Eloquent 多对多关系
Requires
- illuminate/database: ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0
Requires (Dev)
- illuminate/events: *
- phpunit/phpunit: ^7.5 || ^8.0 || ^9.3 || ^10.5
README
通过数组属性同步 Eloquent 多对多关系
此扩展允许通过数组属性控制 Eloquent 多对多关系。
有关许可证信息,请检查LICENSE文件。
安装
安装此扩展的首选方式是通过 composer。
运行
php composer.phar require --prefer-dist illuminatech/sync-many-attribute
或将其添加到 composer.json 的 require 部分。
"illuminatech/sync-many-attribute": "*"
用法
此扩展允许通过数组属性控制 Eloquent 多对多关系。每个这样的属性匹配特定的 BelongsToMany
关系并接受相关模型 ID 的数组。关系将在模型保存期间自动同步。
注意:通常这种方法意义不大,因为 Eloquent 已经为多对多关系同步提供了流畅的接口。然而,当与第三方 CMS(如 Nova)一起使用时,这可能很有用,在 Nova 中,你对模型保存和后处理的控制较少。此外,它还可以简化控制器代码,通过常规属性大量赋值来删除关系操作。
为了使用此功能,您应将 \Illuminatech\SyncManyAttribute\SyncManyToManyAttribute
特性添加到您的模型类中,并声明 syncManyToManyAttributes()
方法,定义用于关系同步的属性。此方法应返回一个数组,其中每个键是新虚拟属性名,值是要同步的关系名。
例如
<?php use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminatech\SyncManyAttribute\SyncManyToManyAttribute; /** * @property int[] $category_ids * @property int[] $tag_ids */ class Item extends Model { use SyncManyToManyAttribute; protected function syncManyToManyAttributes(): array { return [ 'category_ids' => 'categories', 'tag_ids' => 'tags', ]; } public function categories(): BelongsToMany { return $this->belongsToMany(Category::class); } public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class)->withPivot(['created_at']); } // ... }
用法示例
<?php $item = new Item(); $item->category_ids = Category::query()->pluck('id')->toArray(); // ... $item->save(); // relation `Item::categories()` synchronized automatically $item = $item->fresh(); var_dump($item->category_ids); // outputs array of category IDs like `[1, 3, 8, ...]`
您可以在 HTML 表单输入组合期间使用同步属性。例如
... <select multiple="multiple" name="category_ids[]" id="category_ids"> @foreach ($allCategories as $category) <option value="{{ $category->id }}" @if(in_array($category->id, $item->category_ids)) selected="selected" @endif>{{ $category->name }}</option> @endforeach ... </select>
控制器代码示例
<?php use Illuminate\Http\Request; use App\Http\Controllers\Controller; class KioskController extends Controller { public function store(Request $request) { $validatedData = $request->validate([ 'name' => ['required', 'string'], // ... 'category_ids' => ['required', 'array'], 'category_ids.*' => ['int', 'exists:categories,id'], 'tag_ids' => ['required', 'array'], 'tag_ids.*' => ['int', 'exists:tags,id'], ]); $item = new Item; $item->fill($validatedData); // single assignment covers all many-to-many relations $item->save(); // relation `Item::categories()` synchronized automatically // return response } }
注意:请记住,您需要将多对多同步属性的名称添加到
\Illuminate\Database\Eloquent\Model::$fillable
,以便它们可用于大量赋值。
设置枢轴属性
您可以设置在每次关系同步期间应保存的枢轴属性。为此,您应将同步属性定义为数组,其中键定义关系名称,值定义枢轴属性。可以使用 \Closure
定义特定的枢轴属性值或整个枢轴属性集。例如
<?php use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminatech\SyncManyAttribute\SyncManyToManyAttribute; class Item extends Model { use SyncManyToManyAttribute; protected function syncManyToManyAttributes(): array { return [ 'category_ids' => [ 'categories' => [ 'type' => 'help-content', ], ], 'tag_ids' => [ 'tags' => [ 'attached_at' => function (Item $model) { return now(); } ], ], ]; } public function categories(): BelongsToMany { return $this->belongsToMany(Category::class)->withPivot(['type']); } public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class)->withPivot(['attached_at']); } // ... }
您可以使用 \Illuminatech\SyncManyAttribute\ManyToManyAttribute
以更面向对象的方式创建同步属性定义
<?php use Illuminate\Database\Eloquent\Model; use Illuminatech\SyncManyAttribute\ManyToManyAttribute; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminatech\SyncManyAttribute\SyncManyToManyAttribute; class Item extends Model { use SyncManyToManyAttribute; protected function syncManyToManyAttributes(): array { return [ 'category_ids' => (new ManyToManyAttribute) ->relationName('categories') ->pivotAttributes(['type' => 'help-content']), 'tag_ids' => (new ManyToManyAttribute) ->relationName('tags') ->pivotAttributes([ 'attached_at' => function (Item $model) { return now(); }, ]), ]; } public function categories(): BelongsToMany { return $this->belongsToMany(Category::class)->withPivot(['type']); } public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class)->withPivot(['attached_at']); } // ... }
定义的枢轴属性将在模型保存时自动同步关系期间保存
<?php $item = new Item(); $item->category_ids = Category::query()->pluck('id')->toArray(); // ... $item->save(); // relation `Item::categories()` synchronized automatically $category = $item->categories()->first(); var_dump($category->pivot->type); // outputs 'help-content'
Nova 集成
此扩展的主要优势之一是支持第三方 CMS(如 Nova)。您可以使用同步属性,允许用户直接从创建/更新表单设置多对多关系,而不是从详细信息页面单独操作列表。
您可以为 BelongsToMany
关系创建多选框或复选框列表的输入。像 fourstacks/nova-checkboxes 这样的包可用于此类字段。最终的 Nova 资源可能如下所示
<?php use Laravel\Nova\Resource; use Laravel\Nova\Fields\ID; use Fourstacks\NovaCheckboxes\Checkboxes; class Item extends Resource { public static $model = \App\Models\Item::class; // uses `SyncManyToManyAttribute` for 'categories' public function fields(Request $request) { return [ ID::make()->sortable(), // ... // use single checkbox list input instead of `\Laravel\Nova\Fields\BelongsToMany`: Checkboxes::make(__('Categories'), 'category_ids') ->options(\App\Models\Category::pluck('name', 'id')), ]; } }