skip to Main Content

I’ve got a program whose database is synced with an external database (unidirectional, the other program/database is always the sender).

Everytime the sync takes place, I create, update or delete records. The other program always sends all its entities of the same kind (e. g. all Cars) at once.
These records are often used via foreign key constraints inside my database. Creating and updating is fine, but deleting records does not work. I need to delete the records on my side, which are not present on the other side any more.

After my create/update routine, I’ve collected all the records I need to delete. I don’t want to delete the records which are still in use inside other tables and only give a warning, hence the foreign key constraints are needed. I know I could make them nullable or disable foreign key checks or something like that, but this is not what I want.

Walking through these objects and trying to delete them destroys my entity manager when the first exception due to a foreign key constraint is thrown. I know there are constraints, I know they prevent records from being deleted when they are still in use – this is exactly what I want. But I want the entity manager to continue working. I get the infamous "The Entity Manager is closed." message.

I’ve seen a couple of so-called fixes, which all maybe worked on earlier symfony versions (I’m on 6.2), including resetting the entity manager, injecting doctrine and doing something, creating a new entity manager (then the objects are not attached to it any more …). Is there any workaround here?

Basically, I just want to delete a bunch of records one by one and not have my program/entity manager be killed if a record can’t be deleted due to still being used as a foreign key somewhere in the database.

Some made-up example code:

<?php
$cars = $em->getRepository(Car::class)->findAll();

foreach($cars as $car) {
  if($car->shouldBeDeleted()) {
    $em->remove($car);
    $em->flush(); // doesn't matter where it's placed
  }
}

$em->flush(); // doesn't matter where it's placed

Try-Catch let’s me see there’s an error, but doesn’t prevent the entity manager from breaking, of course. A method like $car->isStillUsedSomewhereInTheDatabase() would be okay, too, but only if it can be solved inside doctrine/symfony. I don’t want to maintain a list of possible occurences of my records inside other tables …

2

Answers


  1. Chosen as BEST ANSWER

    I've found a solution via trial-and-error. The key was to get the ManagerRegistry and call resetManager(). This seems to finally reset the manager in a way that you can continue using it.

    // $registry comes from an injected DoctrinePersistenceManagerRegistry
    /* @var $entityManager EntityManager */
    $entityManager = $registry->getManager();
    
    // some stuff
    
    // try-catch is inside a loop which walks through the objects to be deleted
    try {
        if(!$entityManager->isOpen()) {
            $registry->resetManager();
    
            // Refetch the object (or else it'd be detached)
            $objectFromDatbaseToBeDeleted = $entityManager->getRepository(Some::class)->find($objectFromDatbaseToBeDeleted);
        }
    
        $entityManager->remove($objectFromDatbaseToBeDeleted);
        $entityManager->flush();
        $numDeletedObjects++;
    } catch(Exception $e) {
        $numNonDeletedObjects++;
    }
    

  2. You could put your code in a try/catch statement and every time check if the entity Manager is still open. If not, recreate the Entity Manager

    if (!$this->entityManager->isOpen()) {
        $this->entityManager = $this->entityManager->create(
            $this->entityManager->getConnection(),
            $this->entityManager->getConfiguration()
        );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search