Задать вопрос
@nuclear

Insert в событии postPersist?

Мне нужно добавлять сущность после добавления других сущностей. Код (аналогичный для postUpdate):

public function postPersist(LifecycleEventArgs $args) {<br>
    $entity = $args->getEntity(); $em = $args->getEntityManager();<br><br>
    if($entity instanceof FeedItemInterface) {<br>
        $feed = new FeedEntity();<br>
        $feed->setTitle($entity->getFeedTitle());<br>
        $feed->setEntity($entity->getFeedEntityId());<br>
        $feed->setType($entity->getFeedType());<br><br>
        $em->persist($feed);<br>
        $em->flush();<br>
    }<br>
}<br>


Но получаю ошибку:

Integrity constraint violation: 1062 Duplicate entry '30-2' for key 'PRIMARY'


И в логе реально два инсерта:

INSERT INTO interview_scientificdirection (interview_id, scientificdirection_id) VALUES (?, ?) ([30,2])<br>
INSERT INTO interview_scientificdirection (interview_id, scientificdirection_id) VALUES (?, ?) ([30,2])<br>


scientificdirection это Many to Many связь с таблицей после который мы добавляем сущность.

Все дело происходит в Sonata Admin и там это связь указана как:

-&gt;add('scientificDirections', 'sonata_type_model')
  • Вопрос задан
  • 3344 просмотра
Подписаться 2 Оценить Комментировать
Решения вопроса 1
BoShurik
@BoShurik Куратор тега Symfony
Symfony developer
Я делал так.
class LogListener implements EventSubscriber
{
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function getSubscribedEvents()
    {
        return array(Events::postUpdate, Events::postPersist, Events::preRemove);
    }

    public function postUpdate(LifecycleEventArgs $eventArgs)
    {
        $this->log($eventArgs, Log::ACTION_UPDATE);
    }

    public function postPersist(LifecycleEventArgs $eventArgs)
    {
        $this->log($eventArgs, Log::ACTION_PERSIST);
    }

    public function preRemove(LifecycleEventArgs $eventArgs)
    {
        $this->log($eventArgs, Log::ACTION_REMOVE);
    }

    private function log(LifecycleEventArgs $eventArgs, $action)
    {
        $token = $this->container->get('security.context')->getToken();
        if (!$token) {
            return;
        }

        $user = $token->getUser();
        if (!($user instanceof User)) {
            return;
        }

        $entity = $eventArgs->getEntity();
        if ($entity instanceof Log) {
            return;
        }

        $request = $this->container->get('request');

        $log = new Log();
        $log->setAction($action);
        $log->setUser($user);
        $log->setIp($request->server->get('REMOTE_ADDR'));
        $log->setEntityClass(get_class($entity));
        $log->setEntityId($entity->getId());

        $em = $this->container->get('doctrine')->getEntityManager();

        $meta = $em->getClassMetadata(get_class($log));
        $conn = $em->getConnection();

        foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) {
            if (!$meta->isIdentifier($fieldName)) {
                $columns[] = $fieldName;
                $values[] = ':' . $fieldName;
            }
        }

        $insertSql = 'INSERT INTO ' . $meta->getQuotedTableName($conn->getDatabasePlatform())
            . ' (' . implode(', ', $columns) . ') '
            . 'VALUES (' . implode(', ', $values) . ')';

        $stmt = $conn->prepare($insertSql);

        $fields = $meta->getFieldNames();
        $association = $meta->getAssociationNames();

        foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) {
            if (!$meta->isIdentifier($fieldName)) {
                $value = $reflProp->getValue($log);
                if (in_array($fieldName, $fields)) {
                    $mapping = $meta->getFieldMapping($fieldName);
                    $stmt->bindValue($meta->getColumnName($fieldName), $value, $mapping['type']);
                } else if (in_array($fieldName, $association)) {
                    $classMeta = $em->getClassMetadata($meta->getAssociationTargetClass($fieldName));
                    list($classId) = $classMeta->getIdentifier();

                    $mapping = $classMeta->getFieldMapping($classId);

                    $associationIdProperty = $classMeta->getReflectionProperty($classId);
                    $associationValue = $associationIdProperty->getValue($value);

                    $stmt->bindValue($meta->getColumnName($fieldName), $associationValue, $mapping['type']);
                } else {
                    throw new \Exception('Exception in log listener');
                }
            }
        }

        $stmt->execute();
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
denver
@denver
Ну, допустим что FeedEntity implements FeedItemInterface, тогда это будет бесконечный цикл, а выполнится только дважды из-за уникального ключа. Да и не правильно флашить в персисте — flush() начинает и заканчивает большую транзакцию, и подозреваю что это не правильно делать середине контроллера, в момент одного из персистов.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы