php - Laravel/PHP - 高级多态关系

标签 php laravel eloquent

在我的网络应用程序中,用户可以将文档电子邮件上传到 channel

channel 还可以有 document_tagsemail_tags所有 上传的文档/电子邮件应该自动继承。

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

  1. Document #55 已上传至 Channel #8
  2. Document #55 将自动继承具有 document_tags.channel_id = 55 的标签(这可以通过以下关系访问:$channel->文档标签)。在本例中是动物宠物
  3. 现在用户应该能够在 tag_descriptions 中为 animalspets 设置唯一的描述,例如:

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,有标签:animalspets相关联,但是这两个标签还有独特的描述,对于特定的文档是独一无二的。

如果我上传另一个文档或电子邮件(比如 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)。

最佳答案

我不确定您到底想做什么,但是具有两个多态关系的表没有意义。启用多态关系的表是数据透视表。虽然我知道您想要每个标签和关系类型的唯一描述,但数据透视表应该只有两个外键列,一个表与和相关。
还有一种方法,就是利用多态关系作为对数据透视表的约束。首先,多态关系中的数据透视表应该重命名为“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/

相关文章:

php - 尽管进行了设置,laravel session 仍返回 null

java - Java访问Web服务时URL和URI的区别

mysql - 使用 PHP/Laravel 从 MySQL/MariaDB 获取所有 POI 的方法哪种更快

php - SQL 语法错误 '' 近行方式超出我的文件长度

php - 使用 NULL 值连接多个表以避免重复

Laravel:嵌套 "with()"函数

mysql - Eloquent 中一个查询的多个求和

php - Eloquent 外部 Laravel 将获取模式更改为 PDO::FETCH_ASSOC

php - mb_convert_case 未定义函数(Symfony2 FOS/UserBundle)

php - Yii2:如何在不同的数据库表中登录前端和后端?