11

I'm using DoctrineExtensions with StofDoctrineExtensionsBundle to get the soft-deleteable behaviour.

It works really well in the frontend of my application.

In the backend i need the option to "hard" delete entities.

I have disabled the filter in my admin controllers (i use SonataAdmin):

$filters = $this->getModelManager()->getEntityManager($this->getClass())->getFilters();

if (array_key_exists('softdeleteable', $filters->getEnabledFilters())) {
    $filters->disable('softdeleteable');
}

This works (soft deleted entities show up in the lists), but when i try to delete it, the entity does get soft-deleted again. How can i force a "hard" delete?

smoove
  • 3,920
  • 3
  • 25
  • 31

4 Answers4

13

You don't need to disable filter - it just used for filtering records on select. You must disable listener instead:

// $em is your EntityManager
foreach ($em->getEventManager()->getListeners() as $eventName => $listeners) {
    foreach ($listeners as $listener) {
        if ($listener instanceof \Gedmo\SoftDeleteable\SoftDeleteableListener) {
            $em->getEventManager()->removeEventListener($eventName, $listener);
        }
    }
}

and then call

$em->remove($entity);
$em->flush();
Dmytro
  • 5,443
  • 2
  • 52
  • 50
  • How do you re-enable it after this? – Jessica May 28 '14 at 17:17
  • @Jessica, you can use `addEventListener` http://api.symfony.com/2.4/Symfony/Bridge/Doctrine/ContainerAwareEventManager.html#method_addEventListener – Dmytro May 29 '14 at 11:34
  • Thanks very much, what I did was store the names of any events I did remove it from, then loop through those and re-add it. – Jessica May 29 '14 at 13:58
  • If your entity manager have more operations, all they will be hard deleted, I personally dont like this alternative. Is useful when you are only performing the delete operation of one entity, but isn't suitable for several operations. (e.g.: $customEntityManager->softDelete($a); $customEntityManager->hardDelete($b); $customEntityManager->flush();) both will be hard deleted. – Gonzalo1987 Sep 21 '20 at 10:04
  • this looks very tedious and like a bad DX – Toskan Apr 21 '22 at 19:29
6

No need to create a listener or anything to HARD delete with softdeleteable enabled.

the original softdeleteable event has this line:

$reflProp = $meta->getReflectionProperty($config['fieldName']);
$oldValue = $reflProp->getValue($object);
if ($oldValue instanceof \Datetime) {
    continue; // want to hard delete
}

All this mean if you:

$entity->setDeletedAt(new \Datetime());
$em->flush();

And then:

$em->remove($entity);
$em->flush();

At that point it will be hard deleted.

If you allready have a valid date inside deletedAt field when you call ->flush() after a ->remove($entity) your entity will be hard deleted

greenseed
  • 509
  • 5
  • 10
  • agreed. Which is a bad behavior though, no? at least not transparent at all. "OMG I CALLED REMOVE TWICE ON MY ENTITY OMG OMG" ... – Toskan Apr 21 '22 at 19:30
3

Not the most graceful way : you always can do a real delete with SQL, it will bypass softdeletable

$em->createQuery("DELETE MyEntity e WHERE e = :et")->setParameter('et',$entity)->execute();
Xmanoux
  • 3,435
  • 4
  • 15
  • 16
  • not graceful? I think just fine, no? just add it to the repo as a function. Way more transparent than double delete -> hard delete – Toskan Apr 21 '22 at 19:31
0

Although this question is a bit old maybe it is useful to someone:

Creating your own event listener might be a better solution:

class SoftDeleteableListener extends BaseSoftDeleteableListener
{

/**
 * @inheritdoc
 */
public function onFlush(EventArgs $args)
{
    $ea = $this->getEventAdapter($args);
    $om = $ea->getObjectManager();
    //return from event listener if you disabled filter: $em->getFilters()->disable('softdeleteable');
    if (!$om->getFilters()->isEnabled('softdeleteable')) {
        return;
    }

    parent::onFlush($args);
}

}

And adding in your config:

gedmo.listener.softdeleteable:
    class: AppBundle\EventListener\SoftDeleteableListener
    tags:
        - { name: doctrine.event_subscriber, connection: default }
    calls:
        - [ setAnnotationReader, [ @annotation_reader ] ]

source: https://github.com/Atlantic18/DoctrineExtensions/issues/1175

joe4
  • 69
  • 6