Laravel / PHP-高级多态关系

时间:2019-04-23 16:58:30

标签: php laravel eloquent

在我的Web应用程序中,用户可以将documentsemails上传到channels

然后,频道可以具有document_tagsemail_tags所有个自动上传的文档/电子邮件应该继承。

此外,document_tagsemail_tags 将具有不同的描述:tag_descriptions。因此,例如,如果我们有一个文档,该文档上载到具有以下标签的频道:animals (id = 1)pets (id = 2)

  1. Document #55被提升为Channel #8
  2. Document #55将自动继承具有document_tags.channel_id = 55的标签(可以通过以下关系访问:$channel->documenttags)。在这种情况下,animalspets
  3. 现在,用户应该能够为animals中的petstag_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具有以下标签:animalspets,但是,这两个标签具有唯一的描述,对于特定的< 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还具有标签animalspets,但是在这种情况下,用户可以为标签设置唯一的描述。

现在我的问题是:

以上设计可行吗,并且在Laravel / PHP中被认为是最佳实践吗?我有点不确定如何构造代码,因为TagDescription模型会突然具有两个多态关系(taggabletypeable),而我找不到文档中支持此功能的所有内容。

此外,我不确定是否可以使用上述设计通过特定的上载文档访问唯一说明,例如:

//In my Document.php model:
public function tagdescriptions()
{
    return $this->morphMany(TagDescription::class, 'typeable');
}

然后使用它,例如:$document->tagdescriptions

最后但并非最不重要的一点-我有点不确定如何为特定的taggable_id / taggable_type和唯一的电子邮件/文档保存唯一的标签描述。 (typeable_id和typeable_type)。

1 个答案:

答案 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

希望这会有所帮助。