Skip to content
Permalink
Browse files

Implementing BelongsToMany::replaceLinks

  • Loading branch information...
lorenzo committed Dec 14, 2013
1 parent ac00e21 commit e3920835223325aa737946690cdbbf53dd79e040
Showing with 98 additions and 0 deletions.
  1. +76 −0 Cake/ORM/Association/BelongsToMany.php
  2. +22 −0 Cake/Test/TestCase/ORM/TableTest.php
@@ -475,6 +475,82 @@ function() use ($sourceEntity, $targetEntities) {
$sourceEntity->dirty($property, false);
}
public function replaceLinks(Entity $sourceEntity, array $targetEntities, array $options = []) {
$primaryKey = (array)$this->source()->primaryKey();
$primaryValue = $sourceEntity->extract($primaryKey);
if (empty($primaryValue)) {
throw new \InvalidArgumentException;
}
$target = $this->target();
$junction = $this->junction();
$foreignKey = (array)$this->foreignKey();
$existing = $junction->find('all')
->where(array_combine($foreignKey, $primaryValue))
->andWHere($this->conditions());
$jointEntities = $this->_collectJointEntities($sourceEntity, $targetEntities);
$inserts = $this->_diffLinks($existing, $jointEntities, $targetEntities);
$jointProperty = $target->association($junction->alias())->property();
$sourceEntity->set($jointProperty, []);
$this->link($sourceEntity, $inserts, $options);
$sourceEntity->set($jointProperty, $targetEntities);
$sourceEntity->dirty($jointProperty, false);
}
protected function _diffLinks($existing, $jointEntities, $targetEntities) {
$junction = $this->junction();
$target = $this->target();
$belongsTo = $junction->association($target->alias());
$foreignKey = (array)$this->foreignKey();
$assocForeignKey = (array)$belongsTo->foreignKey();
$keys = array_merge($foreignKey, $assocForeignKey);
$deletes = $indexed = $present = [];
foreach ($jointEntities as $i => $entity) {
$indexed[$i] = $entity->extract($keys);
$present[$i] = array_values($entity->extract($assocForeignKey));
}
foreach ($existing as $result) {
$result = $result->extract($keys);
$found = false;
foreach ($indexed as $i => $data) {
if ($result === $data) {
unset($indexed[$i]);
$found = true;
break;
}
}
if (!$found) {
$deletes[] = $result;
}
}
if ($deletes) {
$deletes = ['OR' => $deletes];
$deletes[] = $this->conditions();
$junction->deleteAll($deletes);
}
$primary = (array)$target->primaryKey();
foreach ($targetEntities as $k => $entity) {
$key = array_values($entity->extract($primary));
foreach ($present as $i => $data) {
if ($key === $data) {
unset($targetEntities[$k], $present[$i]);
break;
}
}
}
return array_values($targetEntities);
}
/**
* Throws an exception should any of the passed entities is not persisted.
*
@@ -2865,4 +2865,26 @@ public function testUnlinkBelongsToManyPassingJoint() {
$this->assertNull($left->tags);
}
/**
* Integration test to show how to replace records from a belongsToMany
*
* @return void
*/
public function testReplacelinksBelongsToManyMultiple() {
$table = TableRegistry::get('articles');
$table->belongsToMany('tags');
$tagsTable = TableRegistry::get('tags');
$options = ['markNew' => false];
$article = new \Cake\ORM\Entity(['id' => 1,], $options);
$tags[] = new \TestApp\Model\Entity\Tag(['id' => 2], $options);
$tags[] = new \TestApp\Model\Entity\Tag(['id' => 3], $options);
$table->association('tags')->replaceLinks($article, $tags);
$article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first();
$this->assertCount(2, $article->tags);
$this->assertEquals(2, $article->tags[0]->id);
$this->assertEquals(3, $article->tags[1]->id);
}
}

0 comments on commit e392083

Please sign in to comment.
You can’t perform that action at this time.