本文以 Articles belongsToMany Tags
为例,阐述多对多(many to many)关系在CakePHP中的应用实践。
连接表的命名默认以两张主表名中间加下划线的形式,主表名以字母顺序排序,如表 articles
和 tags
,其连接表名为 articles_tags
,而不是 tags_articles
。
连接表基本字段包含其自身的主键及两个外键,如 id
、article_id
和 tag_id
。
在两张主表Table类的 initialize()
方法中进行关联设置,连接表不需要生成Model 、Controller及View。
//ArticlesTable
$this->belongsToMany('Tags', [
'foreignKey' => 'article_id',
'targetForeignKey' => 'tag_id',
'joinTable' => 'articles_tags'
]);
//TagsTable
$this->belongsToMany('Articles', [
'foreignKey' => 'tag_id',
'targetForeignKey' => 'article_id',
'joinTable' => 'articles_tags'
]);
在保存主表数据时,根据选择的关联记录,框架会自动更新连接表中的数据。在删除主表数据时,框架会自动删除相关的连接表中的记录。
//Articles/add.ctp
$this->Form->control('tags._ids', ['label' => 'Tags', 'options' => $tags, 'class' => '']); //默认生成可多选的select
//ArticlesController
...
$this->Articles->save($article); //同时更新连接表中的记录
...
$tags = $this->Articles->Tags->find('list', ['limit' => 200]); //获取select的options数据
...
多对多关系的关联数据查询与其他关系类似,都可以使用 contain
、matching
、joinWith
等从句。
//ArticlesController
$article = $this->Articles->get($id, [
'contain' => ['Tags']
]);
link()
和 unlink()
是专门用来设置和取消多对多关联的一对方法,其效果与使用 save()
一致。
//Link Many To Many Records
$article = $articlesTable->get($article_id);
$tags = $articlesTable->Tags->find('all', [
'conditions' => [
'name LIKE' => '%PHP%'
]
])->toArray();
$articlesTable->Tags->link($article, $tags); //第二个参数必须为数组
//Unlink Many To Many Records
$article = $articlesTable->get($article_id);
$tags = $articlesTable->Tags->find('all', [
'conditions' => [
'name LIKE' => '%CakePHP%'
]
])->toArray();
$articlesTable->Tags->unlink($article, $tags); //第二个参数必须为数组
If you are saving belongsToMany associations you can either use a list of entity data or a list of ids.
//Create 2 new tags
$data = [
'title' => '最受欢迎的PHP开发框架',
'content' => '本文介绍最受欢迎的PHP开发框架Top10',
'tags' => [
['name' => 'TestPHP'],
['name' => 'MyPHP']
]
];
$article = $this->Articles->newEntity($data, [
'associated' => ['Tags'] //不必需
]);
$this->Articles->save($article);
//Link an article with existing tags
$data = [
'title' => '最受欢迎的PHP开发框架',
'content' => '本文介绍最受欢迎的PHP开发框架Top10',
'tags' => [
'_ids' => [1, 2, 3, 4, 5] //会覆盖已有的关联记录
]
];
$article = $this->Articles->newEntity($data);
$this->Articles->save($article);
//The first two will be new objects, and the second two will be references to existing tags.
$data = [
'title' => '最受欢迎的PHP开发框架',
'content' => '本文介绍最受欢迎的PHP开发框架Top10',
'tags' => [
['name' => 'CakePHP'],
['name' => 'ThinkPHP'],
['id' => 1],
['id' => 2]
]
];
$article = $this->Articles->newEntity($data);
$this->Articles->save($article);
Allows you to provide either the alias of the Table instance you want used on the join table, or the instance itself. This makes customizing the join table keys possible, and allows you to customize the behavior of the pivot table.
If you plan on adding extra information to the join/pivot table, or if you need to use join columns outside of the conventions, you will need to define the through option. The through option provides you full control over how the belongsToMany association will be created.
使用 through
配置项,可以向连接表中添加自定义的额外字段,并且框架仍然会自动维护连接表记录。
//连接表:articles_tags_ships
//连接表字段:id | article_id | tag_id | valid_days | level
//ArticlesTagsShipsTable
$this->belongsTo('Articles', [
'foreignKey' => 'article_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Tags', [
'foreignKey' => 'tag_id',
'joinType' => 'INNER'
]);
//ArticlesTable
$this->belongsToMany('Tags', [
'through' => 'ArticlesTagsShips'
]);
//TagsTable
$this->belongsToMany('Articles', [
'through' => 'ArticlesTagsShips'
]);
Each entity in a belongsToMany association has a _joinData property that contains the additional columns on the junction table. This data can be either an array or an Entity instance.
使用 joinData
属性在连接表中保存额外字段值。
//When saving data you can populate the additional columns on the junction table
//by setting data to the _joinData property
$article->tags[0]->_joinData->valid_days = 30;
$article->tags[0]->_joinData->level = '一级';
$articlesTable->save($article);
//The _joinData property can be either an entity, or an array of data
//if you are saving entities built from request data.
$data = [
'title' => '最受欢迎的PHP开发框架',
'content' => '本文介绍最受欢迎的PHP开发框架Top10',
'tags' => [
[
'id' => 1,
'_joinData' => [
'valid_days' => 30,
'level' => '一级'
]
],
// Other tags
]
];
$article = $this->Articles->newEntity($data, [
'associated' => ['Tags._joinData'] //不必需
]);