diff --git a/Cake/ORM/Association/BelongsToMany.php b/Cake/ORM/Association/BelongsToMany.php index 7d41a84d745..7734dced79c 100644 --- a/Cake/ORM/Association/BelongsToMany.php +++ b/Cake/ORM/Association/BelongsToMany.php @@ -386,6 +386,56 @@ protected function _saveLinks(Entity $sourceEntity, $targetEntities, $options) { return true; } +/** + * Associates the source entity to each of the target entities provided by + * creating links in the junction table. Both the source entity and each of + * the target entities are assumed to be already persisted, if the are marked + * as new or their status is unknown, an exception will be thrown. + * + * When using this method, all entities in `$targetEntities` will be appended to + * the source entity'property corresponding to this association object. + * + * This method does not check link uniqueness. + * + * ###Example: + * + * {{{ + * $newTags = $tags->find('relevant')->execute(); + * $articles->association('tags')->link($article, $newTags); + * }}} + * + * `$article->get('tags')` will contain all tags in `$newTags` after liking + * + * @param \Cake\ORM\Entity $sourceEntity the row belonging to the `source` side + * of this association + * @param array $targetEntities list of entities belonging to the `target` side + * of this association + * @param array $options list of options to be passed to the save method + * @throws \InvalidArgumentException when any of the values in $targetEntities is + * detected to not be already persisted + * @return boolean true on success, false otherwise + */ + public function link(Entity $sourceEntity, array $targetEntities, array $options = []) { + if ($sourceEntity->isNew() !== false) { + $error = __d('cake_dev', 'Source entity needs to be persisted before linking'); + throw new \InvalidArgumentException($error); + } + + $property = $this->property(); + $links = $sourceEntity->get($property) ?: []; + + foreach ($targetEntities as $entity) { + if ($entity->isNew() !== false) { + $error = __d('cake_dev', 'Cannot link not persisted entities'); + throw new \InvalidArgumentException($error); + } + $links[] = $entity; + } + + $sourceEntity->set($property, $links); + return $this->_saveLinks($sourceEntity, $targetEntities, $options); + } + /** * Appends any conditions required to load the relevant set of records in the * target table query given a filter key and some filtering values. diff --git a/Cake/Test/TestApp/Model/Repository/TagsTable.php b/Cake/Test/TestApp/Model/Repository/TagsTable.php index 7315ccf64d4..1776a44a7e1 100644 --- a/Cake/Test/TestApp/Model/Repository/TagsTable.php +++ b/Cake/Test/TestApp/Model/Repository/TagsTable.php @@ -21,7 +21,7 @@ class TagsTable extends Table { public function initialize(array $config) { $this->belongsTo('authors'); - $this->belongsToMany('tags'); + $this->belongsToMany('articles'); $this->hasMany('articlesTags', ['propertyName' => 'extraInfo']); } diff --git a/Cake/Test/TestCase/ORM/TableTest.php b/Cake/Test/TestCase/ORM/TableTest.php index b76dd889d7e..8795a5b325d 100644 --- a/Cake/Test/TestCase/ORM/TableTest.php +++ b/Cake/Test/TestCase/ORM/TableTest.php @@ -2762,4 +2762,40 @@ public function testSaveDeepAssociationOptions() { ])); } +/** + * Integration test for linking entities with belongsToMany + * + * @return void + */ + public function testLinkBelongsToMany() { + $table = TableRegistry::get('articles'); + $table->belongsToMany('tags'); + $tagsTable = TableRegistry::get('tags'); + $options = ['markNew' => false]; + + $article = new \Cake\ORM\Entity([ + 'id' => 1, + ], $options); + + $newTag = new \TestApp\Model\Entity\Tag([ + 'name' => 'Foo' + ]); + $tags[] = new \TestApp\Model\Entity\Tag([ + 'id' => 3 + ], $options); + $tags[] = $newTag; + + $tagsTable->save($newTag); + $table->association('tags')->link($article, $tags); + + $this->assertEquals($article->tags, $tags); + foreach ($tags as $tag) { + $this->assertFalse($tag->isNew()); + } + + $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first(); + $this->assertEquals($article->tags[2]->id, $tags[0]->id); + $this->assertEquals($article->tags[3], $tags[1]); + } + }