如何在创建资源时要求附加相关资源 - Laravel Nova
how to require attaching related resources upon creation of resources - Laravel Nova
我有一个名为 Tree
的模型,它应该关联到 1..n Thing
s。 Thing
s 可以关联到 0..n 个事物。换句话说,这是一个多对多的关系,在创建Tree
时必须选择Thing
。我的 thing_tree
迁移看起来像这样(还有一个 thing_thing
枢轴 table 但这无关紧要):
public function up()
{
Schema::create('thing_tree', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->unsignedBigInteger('tree_id')->nullable();
$table->unsignedBigInteger('thing_id')->nullable();
$table->unique(['tree_id', 'thing_id']);
$table->foreign('tree_id')->references('id')->on('trees')->onDelete('cascade');
$table->foreign('thing_id')->references('id')->on('things')->onDelete('cascade');
});
}
我的 Tree
模型如下所示:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tree extends Model
{
use HasFactory;
protected $guarded = [];
public function path(){
$path = '/trees/' . $this->id;
return $path;
}
public function associatedThings () {
return $this->belongsToMany(Thing::class);
}
}
Thing
模型如下所示:
public function trees()
{
return $this->belongsToMany(Tree::class);
}
public function parentOf (){
return $this->belongsToMany(Thing::class, 'thing_thing', 'parent_id', 'child_id');
}
public function childOf(){
return $this->belongsToMany(Thing::class, 'thing_thing', 'child_id', 'parent_id');
}
最后,Tree
Nova 资源有这些字段:
public function fields(Request $request)
{
return [
ID::make(__('ID'), 'id')->sortable(),
Text::make('name'),
ID::make('user_id')->hideWhenUpdating()->hideWhenCreating(),
Boolean::make('public'),
BelongsToMany::make('Things', 'associatedThings')
];
}
如果没有附加的 Thing
,应该无法创建 Tree
,但创建屏幕如下所示:
我如何在 Nova 中要求这个?
nova 的默认功能无法做到这一点。以下是我将如何以最少的努力去做(你可能想自己为它创建一个自定义字段)——或者至少我是如何解决过去类似问题的:
1.将 nova checkboxes field 添加到您的项目
2。将字段添加到您的 nova 资源中:
// create an array( id => name) of things
$options = Things::all()->groupBy('id')->map(fn($e) => $e->name)->toArray();
// ...
// add checkboxes to your $fields
Checkboxes::make('Things', 'things_checkboxes')->options($options)
3。添加一个验证器,要求things_checkboxes
不为空
4。添加一个观察者 php artisan make:observer CheckboxObserver
,它将通过复选框将模型的关系与给定的 id 数组同步,然后从对象中删除复选框字段(因为它会抛出一个未找到的列),所以像这样:
public function saving($tree)
{
// Note: In my case I would use the checkbox_relations method of the HasCheckboxes trait and loop over all checkbox relations to perform the following and get the respective array keys and relation names
$available_ids = array_unique($tree['things_checkboxes']);
// Attach new ones, remove old ones (Relation name in my case comes from the HasCheckboxes Trait)
$tree->things->sync($available_ids);
// Unset Checkboxes as the Key doesn't exist as column in the Table
unset($tree['things_checkboxes']);
return true;
}
5.如果您想继续使用复选框来处理关系,请在您的观察者中为 retreived
方法反向添加相同的内容。否则,将 ->hideWhenUpdating()
添加到您的复选框字段
我为此添加了一个特征,以便通过复选框轻松地将关系附加到模型:
trait HasCheckboxRelations
{
/**
* Boot the trait
*
* @return void
*/
public static function bootHasCheckboxRelations()
{
static::observe(CheckboxObserver::class);
}
/**
* Defines which relations should be display as checkboxes instead of
* @return CheckboxRelation[]
*/
public static function checkbox_relations()
{
return [];
}
}
并且 checkbox_relations
包含 class CheckboxRelation
的实例数组,它再次包含有关键名、关系名等的信息。
public function __construct(string $relationName, string $relatedClass, string $fieldName, bool $hasOverrides = false, string $relationType = null, array $_fields = [])
此外,我向默认的 nova 资源添加了一个方法 attachCheckboxRelationFields
,当模型使用特征时,它将在 $fields
上调用。
现在,我只需将 HasCheckboxRelations
添加到模型,添加 checkbox_relations
的数组,仅此而已 - 我通过复选框在 nova 资源上建立了 belongsToMany 关系。当然,如果您以这种方式进行管理,您将无法再选择管理数据透视字段——这可能就是为什么 nova 开发人员没有这样做的原因——但对于简单的 belongsToMany 关系,我真的很喜欢使用复选框解决方案默认附加-table。对于带有数据透视字段的数据,您仍然可以使用默认方式。
另请注意,部分代码是即时编写的,因此可能无法开箱即用,但应该传达总体思路。
希望对您有所帮助!
备选
https://github.com/Benjacho/belongs-to-many-field-nova
BelongsToManyField::make('Role Label', 'roles', 'App\Nova\Role'),
我有一个名为 Tree
的模型,它应该关联到 1..n Thing
s。 Thing
s 可以关联到 0..n 个事物。换句话说,这是一个多对多的关系,在创建Tree
时必须选择Thing
。我的 thing_tree
迁移看起来像这样(还有一个 thing_thing
枢轴 table 但这无关紧要):
public function up()
{
Schema::create('thing_tree', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->unsignedBigInteger('tree_id')->nullable();
$table->unsignedBigInteger('thing_id')->nullable();
$table->unique(['tree_id', 'thing_id']);
$table->foreign('tree_id')->references('id')->on('trees')->onDelete('cascade');
$table->foreign('thing_id')->references('id')->on('things')->onDelete('cascade');
});
}
我的 Tree
模型如下所示:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tree extends Model
{
use HasFactory;
protected $guarded = [];
public function path(){
$path = '/trees/' . $this->id;
return $path;
}
public function associatedThings () {
return $this->belongsToMany(Thing::class);
}
}
Thing
模型如下所示:
public function trees()
{
return $this->belongsToMany(Tree::class);
}
public function parentOf (){
return $this->belongsToMany(Thing::class, 'thing_thing', 'parent_id', 'child_id');
}
public function childOf(){
return $this->belongsToMany(Thing::class, 'thing_thing', 'child_id', 'parent_id');
}
最后,Tree
Nova 资源有这些字段:
public function fields(Request $request)
{
return [
ID::make(__('ID'), 'id')->sortable(),
Text::make('name'),
ID::make('user_id')->hideWhenUpdating()->hideWhenCreating(),
Boolean::make('public'),
BelongsToMany::make('Things', 'associatedThings')
];
}
如果没有附加的 Thing
,应该无法创建 Tree
,但创建屏幕如下所示:
我如何在 Nova 中要求这个?
nova 的默认功能无法做到这一点。以下是我将如何以最少的努力去做(你可能想自己为它创建一个自定义字段)——或者至少我是如何解决过去类似问题的:
1.将 nova checkboxes field 添加到您的项目
2。将字段添加到您的 nova 资源中:
// create an array( id => name) of things
$options = Things::all()->groupBy('id')->map(fn($e) => $e->name)->toArray();
// ...
// add checkboxes to your $fields
Checkboxes::make('Things', 'things_checkboxes')->options($options)
3。添加一个验证器,要求things_checkboxes
不为空
4。添加一个观察者 php artisan make:observer CheckboxObserver
,它将通过复选框将模型的关系与给定的 id 数组同步,然后从对象中删除复选框字段(因为它会抛出一个未找到的列),所以像这样:
public function saving($tree)
{
// Note: In my case I would use the checkbox_relations method of the HasCheckboxes trait and loop over all checkbox relations to perform the following and get the respective array keys and relation names
$available_ids = array_unique($tree['things_checkboxes']);
// Attach new ones, remove old ones (Relation name in my case comes from the HasCheckboxes Trait)
$tree->things->sync($available_ids);
// Unset Checkboxes as the Key doesn't exist as column in the Table
unset($tree['things_checkboxes']);
return true;
}
5.如果您想继续使用复选框来处理关系,请在您的观察者中为 retreived
方法反向添加相同的内容。否则,将 ->hideWhenUpdating()
添加到您的复选框字段
我为此添加了一个特征,以便通过复选框轻松地将关系附加到模型:
trait HasCheckboxRelations
{
/**
* Boot the trait
*
* @return void
*/
public static function bootHasCheckboxRelations()
{
static::observe(CheckboxObserver::class);
}
/**
* Defines which relations should be display as checkboxes instead of
* @return CheckboxRelation[]
*/
public static function checkbox_relations()
{
return [];
}
}
并且 checkbox_relations
包含 class CheckboxRelation
的实例数组,它再次包含有关键名、关系名等的信息。
public function __construct(string $relationName, string $relatedClass, string $fieldName, bool $hasOverrides = false, string $relationType = null, array $_fields = [])
此外,我向默认的 nova 资源添加了一个方法 attachCheckboxRelationFields
,当模型使用特征时,它将在 $fields
上调用。
现在,我只需将 HasCheckboxRelations
添加到模型,添加 checkbox_relations
的数组,仅此而已 - 我通过复选框在 nova 资源上建立了 belongsToMany 关系。当然,如果您以这种方式进行管理,您将无法再选择管理数据透视字段——这可能就是为什么 nova 开发人员没有这样做的原因——但对于简单的 belongsToMany 关系,我真的很喜欢使用复选框解决方案默认附加-table。对于带有数据透视字段的数据,您仍然可以使用默认方式。
另请注意,部分代码是即时编写的,因此可能无法开箱即用,但应该传达总体思路。
希望对您有所帮助!
备选
https://github.com/Benjacho/belongs-to-many-field-nova
BelongsToManyField::make('Role Label', 'roles', 'App\Nova\Role'),