在我的Web应用程序中,用户可以将documents
或emails
上传到channels
。
然后,频道可以具有document_tags
和email_tags
,所有个自动上传的文档/电子邮件应该继承。
此外,document_tags
和email_tags
将具有不同的描述:tag_descriptions
。因此,例如,如果我们有一个文档,该文档上载到具有以下标签的频道:animals (id = 1)
和pets (id = 2)
Document #55
被提升为Channel #8
。Document #55
将自动继承具有document_tags.channel_id = 55
的标签(可以通过以下关系访问:$channel->documenttags
)。在这种情况下,animals
和pets
。 animals
中的pets
和tag_descriptions
的小枝设置唯一的描述,例如: tag_descriptions
id | taggable_type | taggable_id | typeable_type | typeable_id | description
1 | App\DocumentTag | 1 | App\Document | 55 | My unique description for animals.
2 | App\DocumentTag | 2 | App\Document | 55 | My unique description for pets.
现在,在上述数据库设计中,上载的document #55
具有以下标签:animals
和pets
,但是,这两个标签具有唯一的描述,对于特定的< strong>文档。
如果我上载另一个文档或电子邮件(例如email #20
),那么我想它看起来像:
tag_descriptions
:
id | taggable_type | taggable_id | typeable_type | typeable_id | description
1 | App\DocumentTag | 1 | App\Document | 55 | My unique description for animals.
2 | App\DocumentTag | 2 | App\Document | 55 | My unique description for pets.
3 | App\EmailTag | 1 | App\Email | 20 | Another unique description for animals.
4 | App\EmailTag | 2 | App\Email | 20 | Yet another unique description for pets.
现在email #20
还具有标签animals
和pets
,但是在这种情况下,用户可以为标签设置唯一的描述。
现在我的问题是:
以上设计可行吗,并且在Laravel / PHP中被认为是最佳实践吗?我有点不确定如何构造代码,因为TagDescription
模型会突然具有两个多态关系(taggable
和typeable
),而我找不到文档中支持此功能的所有内容。
此外,我不确定是否可以使用上述设计通过特定的上载文档访问唯一说明,例如:
//In my Document.php model:
public function tagdescriptions()
{
return $this->morphMany(TagDescription::class, 'typeable');
}
然后使用它,例如:$document->tagdescriptions
。
最后但并非最不重要的一点-我有点不确定如何为特定的taggable_id / taggable_type和唯一的电子邮件/文档保存唯一的标签描述。 (typeable_id和typeable_type)。
答案 0 :(得分:1)
我不确定您要做什么,但是具有两个多态关系的表没有意义。启用多态关系的表是数据透视表。虽然我知道您想要每种标记和关系类型的唯一描述,但数据透视表应该仅包含两个外键列,其中一个表与之相关。
还有另一种方法,就是将多态关系用作对数据透视表的约束。首先,在多态关系中的数据透视表中应重命名为“ taggables”。您不需要“ email_tag”表和“ document_tag”表,可以使用称为“ tags”的表。要获得每个标签的唯一描述,可以将描述添加到表“ tabblables”中。
迁移文件如下所示。...
public function up()
{
Schema::create('taggables', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('tag_id');
$table->unsignedInteger('taggable_id');
$table->string('taggable_type');
$table->string('description');
$table->timestamps();
});
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('documents', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('emails', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
这是您在电子邮件和文档模型中需要做的事情。
class Email extends Model
{
/**
* Get all of the tags.
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->withPivot('description');
}
}
class Document extends Model
{
/**
* Get all of the tag descriptions.
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->withPivot('description');
}
}
“ withPivot”函数将返回查询中指定的列的值。
这是您在标签模型中需要做的。
class Tag extends Model
{
/**
* Get all of the tag descriptions.
*/
public function documents()
{
return $this->morphByMany(Document::class, 'taggable');
}
/**
* Get all of the tag descriptions.
*/
public function emails()
{
return $this->morphByMany(Email::class, 'taggable');
}
}
您不需要“ Taggables”表模型。
这是怎么回事。当你修补...
$email = App\Email::find(1)->tags;
此查询将运行...
select `tags`.*,
`taggables`.`taggable_id` as `pivot_taggable_id`,
`taggables`.`tag_id` as `pivot_tag_id`,
`taggables`.`taggable_type` as `pivot_taggable_type`,
`taggables`.`description` as `pivot_description`
from `tags`
inner join `taggables` on `tags`.`id` = `taggables`.`tag_id`
where `taggables`.`taggable_id` = 1
and `taggables`.`taggable_type` = 'App\Email'
您看到的是多态关系的约束可以查询唯一描述。总结起来,您不能在数据透视表中放置两个以上的外键关系,但是可以将约束添加到数据透视表中。
我认为将其添加到您的AppServiceProvider.php文件看起来更干净...
public function boot()
{
Relation::morphMap([
'email' => Email::class,
'document' => Document::class
]);
}
https://laravel.com/docs/5.8/eloquent-relationships#polymorphic-relationships
这将使您可以将“电子邮件”或“文档”的值存储为可标记类型。
我认为这看起来最干净了……
$tag = Tag::firstOrNew(["name"=>"#tag"]);
$tag->name = "#tag";
$document = new Document();
$document->name = "new doc";
$document->save();
$document->tags()->save($tag,["description"=>"some description"]);
您当然可以使用attach()。 Save()使用attach(),如下所示... https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php#L871
希望这会有所帮助。