Skip to content

Instantly share code, notes, and snippets.

@veewee
Last active June 12, 2016 13:49
Show Gist options
  • Save veewee/9787055 to your computer and use it in GitHub Desktop.
Save veewee/9787055 to your computer and use it in GitHub Desktop.
Doctrine MongoDB Event Subscriber to Remove DBRefs of Deleted Documents
<?php
/**
* Interface ReferencedDocumentInterface
*
*/
interface ReferencedDocumentInterface
{
/**
* @return mixed
*/
public function getId();
/**
* Returns an array with the entity name as key and the field property as value.
*
* @return array
*/
public function getReferingValueFields();
/**
* Returns an array with the entity name as key and the field property as value.
*
* @return array
*/
public function getReferingCollectionFields();
}
<?php
use Doctrine\Common\EventSubscriber;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use Doctrine\ODM\MongoDB\Events;
/**
* Class RemoveReferencesListener
*
*/
class RemoveReferencesListener
implements EventSubscriber
{
/**
* @param LifecycleEventArgs $args
*/
public function preRemove(LifecycleEventArgs $args)
{
$document = $args->getDocument();
$documentManager = $args->getDocumentManager();
if (!($document instanceof ReferencedDocumentInterface)) {
return;
}
foreach ($document->getReferingValueFields() as $targetDocument => $field) {
$fields = is_array($field) ? $field : [$field];
foreach ($fields as $key) {
$this->removeSingleValuedReference($documentManager, $document, $targetDocument, $key);
}
}
foreach ($document->getReferingCollectionFields() as $targetDocument => $field) {
$fields = is_array($field) ? $field : [$field];
foreach ($fields as $key) {
$this->removeCollectionValuedReference($documentManager, $document, $targetDocument, $key);
}
}
}
/**
* @param DocumentManager $documentManager
* @param ReferencedDocumentInterface $document
* @param $targetDocument
* @param $field
*/
protected function removeSingleValuedReference($documentManager, $document, $targetDocument, $field)
{
$id = $document->getId();
$searchField = $this->parseSearchField($field);
$queryBuilder = $documentManager->createQueryBuilder($targetDocument);
$queryBuilder
->update()
->multiple(true)
->field($field)->set(null)
->field($searchField)->equals($id);
$query = $queryBuilder->getQuery();
$query->execute();
}
/**
* @param DocumentManager $documentManager
* @param ReferencedDocumentInterface $document
* @param $targetDocument
* @param $field
*/
protected function removeCollectionValuedReference($documentManager, $document, $targetDocument, $field)
{
$id = $document->getId();
$searchField = $this->parseSearchField($field);
list($field, $matcher) = explode('.', $searchField, 2);
$matcher = $matcher ? $matcher : '$id';
$queryBuilder = $documentManager->createQueryBuilder($targetDocument);
$queryBuilder
->update()
->multiple(true)
->field($field)->pull([
$matcher => $id,
])
->field($searchField)->equals($id);
$query = $queryBuilder->getQuery();
$query->execute();
}
/**
* This method makes it possible to remove references in embedded collections.
* E.g.:
* Embedded reference field: items.$.asset will set items.$.asset to null
* Embedded reference collection: items.$.assets will pull the asset out of the selected embedded collection
*
* @param $field
*
* @return string
*/
protected function parseSearchField($field)
{
return str_replace('.$.', '.', $field) . '.$id';
}
/**
* Returns an array of events this subscriber wants to listen to.
*
* @return array
*/
public function getSubscribedEvents()
{
return [Events::preRemove];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment