oxyaction/yii2-polymorphic-relation-behavior

建立多态关系

1.0.0 2017-03-12 11:06 UTC

This package is not auto-updated.

Last update: 2024-09-14 20:04:20 UTC


README

此行为有助于轻松建立多态行为。忘掉你在存储中无尽的字典表。

Build Status Latest Stable Version Latest Unstable Version License

问题

通常,你的系统中有些实体与多个实体相关联。例如,Comment 实体可以属于 ArticlePhotoPost 等。你可以为每个实体创建一个评论表(ArticleCommentPhotoCommentPostComment),但这种方法并不好,并且完全不遵循DRY原则。

最佳解决方案是创建单个 Comment 实体表并建立多态关系。comment 表需要有一个 type 列,用于指示评论属于哪个实体类型,以及一个 external_id 列,用作外键列。

class Article extends ActiveRecord
{
    const ARTICLE = 1;
    
    public function behaviors()
    {
        return [
            'polymorphic' => [
                'class' => RelatedPolymorphicBehavior::className(),
                'polyRelations' => [
                    'comments' => Comment::className()
                ],
                'polymorphicType' => self::TYPE_ARTICLE
            ]
        ];
    }
}

class Photo extends ActiveRecord
{
    const PHOTO = 2;

    public function behaviors()
    {
        return [
            'polymorphic' => [
                'class' => RelatedPolymorphicBehavior::className(),
                'polyRelations' => [
                    'comments' => Comment::className()
                ],
                'polymorphicType' => self::TYPE_PHOTO
            ]
        ];
    }
}

然后你可以像往常一样查询关系

$article->comments;
$photo->comments;

选项

拥有多个

简写

...
public function behaviors()
{
    return [
        'polymorphic' => [
            'class' => RelatedPolymorphicBehavior::className(),
            'polyRelations' => [
                'comments' => Comment::className()
            ],
            'polymorphicType' => 'article'
        ]
    ];
}
...

相同于

public function behaviors()
{
    return [
        'polymorphic' => [
            'class' => RelatedPolymorphicBehavior::className(),
            'polyRelations' => [
                'comments' => [
                    'type' => RelatedPolymorphicBehavior::HAS_MANY,
                    'class' => Comment::className(),
                    'pkColumnName' => 'id',
                    'foreignKeyColumnName' => 'external_id',
                    'typeColumnName' => 'type',
                    'deleteRelated' => false,
                ]
            ],
            'polymorphicType' => 'article'
        ]
    ]
}

多对多

我们通常需要以多对多的方式建立多态关系。在这种情况下,typeexternal_id 列将位于连接表中。

public function behaviors()
{
    return [
        'polymorphic' => [
            'class' => RelatedPolymorphicBehavior::className(),
            'polyRelations' => [
                'tags' => [
                    'type' => RelatedPolymorphicBehavior::MANY_MANY,
                    'class' => Tag::className(),
                    'viaTable' => 'taggable_tag',
                ]
            ],
            'polymorphicType' => 'article'
        ]
    ]
}

所有可用选项

public function behaviors()
{
    return [
        'polymorphic' => [
            'class' => RelatedPolymorphicBehavior::className(),
            'polyRelations' => [
                'tags' => [
                    'type' => RelatedPolymorphicBehavior::MANY_MANY,
                    'class' => Tag::className(),
                    'viaTable' => 'taggable_tag',
                    'pkColumnName' => 'id',
                    'foreignKeyColumnName' => 'external_id',
                    'otherKeyColumnName' => 'tag_id',
                    'typeColumnName' => 'type',
                    'relatedPkColumnName' => 'id',

                ]
            ],
            'polymorphicType' => 'photo'
        ]
    ]
}

某些选项可以设置在每个行为或每个关系级别。例如,上面的示例中设置的 polymorphicType 是在行为级别设置的,而 typeColumnName 是在关系级别设置的。如果在行为级别设置了选项,则不需要为多个关系重复设置它,而关系选项将具有优先权。

最终示例

class Article extends ActiveRecord {
    public function behaviors()
    {
        return [
            'polymorphic' => [
                'class' => RelatedPolymorphicBehavior::className(),
                'polyRelations' => [
                    'tags' => [
                        'type' => RelatedPolymorphicBehavior::MANY_MANY,
                        'class' => Tag::className(),
                        'viaTable' => 'taggable_tag',
                    ],
                    'images' => [
                        'type' => RelatedPolymorphicBehavior::HAS_MANY,
                        'class' => Image::className(),
                    ],
                    'comments' => [
                        'type' => RelatedPolymorphicBehavior::HAS_MANY,
                        'class' => Comment::className(),
                    ],
                ],
                'polymorphicType' => 'article',
                'pkColumnName' => 'ID',
                'foreignKeyColumnName' => 'some_external_id',
                'typeColumnName' => 'entity_type',
            ]
        ]
    }
}

这假设 taggable_tagimagecomment 表都有 some_external_identity_type 列,对于 Article 实体,它将填充 article 值,而 Article 的主键列名为 ID

警告

由于多态目标实体的 external_id 链接到多个实体,因此不能使用外键约束,需要在应用程序级别检查数据一致性。