/**
* Removes all links between the passed source entity and each of the provided
* target entities. This method assumes that all passed objects are already persisted
* in the database and that each of them contain a primary key value.
*
* ### Options
*
* Additionally to the default options accepted by `Table::delete()`, the following
* keys are supported:
*
* - cleanProperty: Whether or not to remove all the objects in `$targetEntities` that
* are stored in `$sourceEntity` (default: true)
*
* By default this method will unset each of the entity objects stored inside the
* source entity.
*
* ### Example:
*
* ```
* $article->tags = [$tag1, $tag2, $tag3, $tag4];
* $tags = [$tag1, $tag2, $tag3];
* $articles->association('tags')->unlink($article, $tags);
* ```
*
* `$article->get('tags')` will contain only `[$tag4]` after deleting in the database
*
* @param \Cake\Datasource\EntityInterface $sourceEntity an entity persisted in the source table for
* this association
* @param array $targetEntities list of entities persisted in the target table for
* this association
* @param array|bool $options list of options to be passed to the internal `delete` call,
* or a `boolean`
* @throws \InvalidArgumentException if non persisted entities are passed or if
* any of them is lacking a primary key value
* @return void
*/
public function unlink(EntityInterface $sourceEntity, array $targetEntities, $options = [])
{
if (is_bool($options)) {
$options = ['cleanProperty' => $options];
} else {
$options += ['cleanProperty' => true];
}
$this->_checkPersistenceStatus($sourceEntity, $targetEntities);
$property = $this->property();
$this->junction()->connection()->transactional(function () use($sourceEntity, $targetEntities, $options) {
$links = $this->_collectJointEntities($sourceEntity, $targetEntities);
foreach ($links as $entity) {
$this->_junctionTable->delete($entity, $options);
}
});
$existing = $sourceEntity->get($property) ?: [];
if (!$options['cleanProperty'] || empty($existing)) {
return;
}
$storage = new SplObjectStorage();
foreach ($targetEntities as $e) {
$storage->attach($e);
}
foreach ($existing as $k => $e) {
if ($storage->contains($e)) {
unset($existing[$k]);
}
}
$sourceEntity->set($property, array_values($existing));
$sourceEntity->dirty($property, false);
}