doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.94k stars 2.52k forks source link

problem with persisting entities when using doctrine events #5732

Open poolerMF opened 8 years ago

poolerMF commented 8 years ago

when I use doctrine events (postPersist, preUpdate, postUpdate, postFlush) and there are orphanRemovals, it is "impossible" to persist new Entity ...

becouse:

after 3 hours I found only one solution with persister:

$em->persist($object);
$classMetadata = $em->getClassMetadata($newEntity::class);
$em->getUnitOfWork()->computeChangeSet($classMetadata, $object);
$persister->addInsert($object);

I think orphan removals should be cleared after removing (before dispatching onFlushEvent). I'm also using GEDMO softdeleteable, but I don't think it has effect on this problem

ostrolucky commented 8 years ago

This seems to be same issue we are having. We are using preRemove listener which persists new entity. If this listener has been triggered by orphan removal, doctrine doesn't automatically calculate changesets for these new entities and therefore when flush happens, BasicEntityPersister::prepareUpdateData doesn't return any data for BasicEntityPersister::executeInserts and operation fails with this error:

1) AppBundle\Tests\EventListener\DeleteListenerTest::testDeleteListener
Doctrine\DBAL\Exception\DriverException: An exception occurred while executing 'INSERT INTO trash (table_name, object_id, object_name, date, uri, affected_data, user_id) VALUES (?, ?, ?, ?, ?, ?, ?)':

SQLSTATE[HY093]: Invalid parameter number: no parameters were bound

<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:115
<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:116
<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:175
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:281
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1018
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:378
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:356
<redacted>/src/AppBundle/Tests/EventListener/DeleteListenerTest.php:36

Caused by
Doctrine\DBAL\Driver\PDOException: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound

<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:93
<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:165
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:281
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1018
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:378
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:356
<redacted>/src/AppBundle/Tests/EventListener/DeleteListenerTest.php:36

Caused by
PDOException: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound

<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:91
<redacted>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:165
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:281
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1018
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:378
<redacted>/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:356
<redacted>/src/AppBundle/Tests/EventListener/DeleteListenerTest.php:36

Here is the test, but it's for symfony:

<?php

namespace AppBundle\Tests\EventListener;

use AppBundle\Doctrine\Manager\ApplicationFeConfigManager;
use AppBundle\Entity\Application;
use AppBundle\Entity\ApplicationCustomFieldValue;
use AppBundle\Entity\ApplicationFeFormConfig;
use AppBundle\Entity\FormFieldType;
use AppBundle\Entity\Organisation;
use AppBundle\Entity\Trash;
use AppBundle\EventListener\DeleteListener;
use AppBundle\Tests\AppTestCase;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class DeleteListenerTest extends KernelTestCase
{
    /**
     * Tests if there is no error when orphanRemovals is triggered
     */
    public function testDeleteListener()
    {
        static::bootKernel();

        $container = static::$kernel->getContainer();
        /** @var EntityManager $entityManager */
        $entityManager = $container->get('doctrine.orm.entity_manager');

        $application = new Application();

        $formFieldType = new FormFieldType();
        $formFieldType->setLabel('')->setFieldClass('')->setFormType('');
        $entityManager->persist($formFieldType);

        $application->addCustomFieldValue($formFieldType, [1, 2]);
        $entityManager->persist($application);
        $entityManager->flush();

        $application->getCustomFieldsValues()->remove(0);
        $entityManager->flush();
    }
}
lcobucci commented 8 years ago

I think that this comment is also applicable here.

Consider using your own events to manage your entities instead of relying on lifecycle callbacks.

ostrolucky commented 8 years ago

@lcobucci In the talk you referenced, @Ocramius also speaks about soft deletes and to use audit log instead of them. That's exacly what I'm doing with my delete listener. It records the data that is being deleted. I don't really care for lifecycle callbacks, but I want my listenered to be triggered automatically everytime something is going to be deleted. Is there another way? I don't think so, therefore I see this as best option.

lcobucci commented 8 years ago

@gadelat just don't bind your listener to the ORM... use your own event dispatcher or even a command bus middleware to that (you're the one asking to the ORM to delete/update/insert your entities so you have full control of that).

ostrolucky commented 8 years ago

@lcobucci And how am I supposed to handle cascaded deletes then? Am I supposed to iterate over all of them manually? Even in the case this tickets is about, it was triggered automatically by doctrine's orphanRemovals. How else am I supposed to catch that? What's the point of lifecycle callbacks then? I thought it's precisely so I don't have to do this manually.

osamamosaad commented 3 years ago

Finally!! I found someone who faces the same issue 😄, after two days of debugging in my code. Just I am wondering why the issue is not solved since 2016, Isn't that an issue in Doctrine!! Note: I use v 2.9.5 of Doctrine