在我的网络应用程序中,用户可以将文档
或电子邮件
上传到 channel
。
channel 还可以有 document_tags
和 email_tags
,所有 上传的文档/电子邮件应该自动继承。
此外,document_tags
和email_tags
将有不同的描述:tag_descriptions
。因此,例如,如果我们有一个文档,上传到具有标签的 channel :animals (id = 1)
和 pets (id = 2)
Document #55
已上传至Channel #8
。Document #55
将自动继承具有document_tags.channel_id = 55
的标签(这可以通过以下关系访问:$channel->文档标签
)。在本例中是动物
和宠物
。- 现在用户应该能够在
tag_descriptions
中为animals
和pets
设置唯一的描述,例如:
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.
现在在上面的数据库设计中,上传的文件#55
,有标签:animals
和pets
相关联,但是这两个标签还有独特的描述,对于特定的文档是独一无二的。
如果我上传另一个文档或电子邮件(比如 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)。
最佳答案
我不确定您到底想做什么,但是具有两个多态关系的表没有意义。启用多态关系的表是数据透视表。虽然我知道您想要每个标签和关系类型的唯一描述,但数据透视表应该只有两个外键列,一个表与和相关。
还有一种方法,就是利用多态关系作为对数据透视表的约束。首先,多态关系中的数据透视表应该重命名为“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');
}
}
您不需要“可标记”表模型。
事情是这样的。当你修补......
$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
希望这对您有所帮助。
关于php - Laravel/PHP - 高级多态关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55816088/