Yii中实现数据与标签多对多关联需三张表(article/tag/article_tag),在Article/Tag模型中定义hasMany+viaTable关系,封装setTags/clearTags及findOrCreateByName方法确保标签去重与自动创建。

在 Yii 框架中实现数据与标签的多对多关联,核心在于正确设计数据库结构、定义 ActiveRecord 关系,并封装便捷的增删查操作。不依赖第三方扩展也能高效完成。
数据库表结构设计
需三张表:主数据表(如 article)、标签表(tag)、中间关联表(article_tag)。
- article:id, title, content, created_at...
-
tag:id, name, slug, created_at(建议加唯一索引 on
slug) - article_tag:article_id, tag_id(联合主键或添加唯一索引防止重复)
ActiveRecord 关系定义
在 Article 模型中声明:
public function getTags()
{
return $this->hasMany(Tag::class, ['id' => 'tag_id'])
->viaTable('article_tag', ['article_id' => 'id']);
}
public function setTags($tagNames)
{
$this->clearTags();
foreach ((array)$tagNames as $name) {
if (empty(trim($name))) continue;
$tag = Tag::findOrCreateByName(trim($name));
if ($tag) {
$this->link('tags', $tag);
}
}
}
public function clearTags()
{
ArticleTag::deleteAll(['article_id' => $this->id]);
}
在 Tag 模型中补充反向关系:
public function getArticles()
{
return $this->hasMany(Article::class, ['id' => 'article_id'])
->viaTable('article_tag', ['tag_id' => 'id']);
}
标签自动创建与去重逻辑
避免重复插入相同标签,推荐在 Tag 模型中封装安全创建方法:
public static function findOrCreateByName($name)
{
$slug = \yii\helpers\Inflector::slug($name);
$model = self::findOne(['slug' => $slug]);
if (!$model) {
$model = new self(['name' => $name, 'slug' => $slug]);
$model->save();
}
return $model;
}
这样传入 "Yii Framework" 或 "yii framework" 都会映射到同一个 slug yii-framework,保证语义统一。
使用示例:保存带标签的文章
控制器中可直接使用:
$article = new Article();
$article->title = 'Yii 标签系统实践';
$article->content = '...';
if ($article->load(Yii::$app->request->post()) && $article->save()) {
$article->setTags(Yii::$app->request->post('Article')['tags']); // 如 ['yii', 'php', 'cms']
Yii::$app->session->setFlash('success', '保存成功');
}
查询带标签的文章列表时,用 with('tags') 预加载提升性能:
$articles = Article::find()
->with('tags')
->where(['status' => 1])
->all();
// 视图中 $article->tags 是 Tag 对象数组
不复杂但容易忽略的是中间表的数据一致性维护——务必在删除文章或标签时同步清理 article_tag 记录,可通过 ActiveRecord 的 afterDelete 回调或事务包裹处理。


















