Doctrine 3 merge alternantive

Viewed 886

I am currently using doctrine merge to "restore" an entity with relationships after retrieving it from the session.

As from doctrine 3, this function will be deprecated so I am wondering if there is any way to keep an entity object in the session for a while before persisting it to the database.

I need this for a multistep form through which my object gets populated.

For now, the only solution i see is storing the entity in a temporary database table but i don't really like this idea because my table will be filled with "junk".

Thanks !

1 Answers

There are two ways that I have found.

The first is to create your own implementation. I have go this way because there were a lot of usages of merge in project. It looks hacky, but works:

class DoctrineMergeService
{
    /**
     * @var EntityManager
     */
    private $em;

    /**
     * @param EntityManager $em
     */
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    /**
     * @param object $entity
     *
     * @return object
     *
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     * @throws \Doctrine\ORM\TransactionRequiredException
     */
    public function merge(object $entity): object
    {
        $mergedEntity = null;
        $className = get_class($entity);
        $identifiers = $this->getIdentifiersFromEntity($entity);
        $entityFromDoctrine = $this->em->find($className, $identifiers);

        if ($entityFromDoctrine) {
            $mergedEntity = $this->mergeEntities($entityFromDoctrine, $entity);
        } else {
            $this->em->persist($entity);
            $mergedEntity = $entity;
        }

        return $mergedEntity;
    }

    /**
     * @param object $entity
     *
     * @return array
     */
    private function getIdentifiersFromEntity(object $entity): array
    {
        $className = get_class($entity);
        $meta = $this->em->getClassMetadata($className);
        $identifiers = $meta->getIdentifierValues($entity);

        return $identifiers;
    }

    /**
     * @param object $first
     * @param object $second
     *
     * @return object
     */
    private function mergeEntities(object $first, object $second): object
    {
        $classNameFirst = get_class($first);
        $metaFirst = $this->em->getClassMetadata($classNameFirst);
        $classNameSecond = get_class($second);
        $metaSecond = $this->em->getClassMetadata($classNameSecond);

        $fieldNames = $metaFirst->getFieldNames();
        foreach ($fieldNames as $fieldName) {
            $secondValue = $metaSecond->getFieldValue($second, $fieldName);
            $metaFirst->setFieldValue($first, $fieldName, $secondValue);
        }

        return $first;
    }
}

The second is to use serializer, not tested:

// this is controller or something like controller
public function save($id)
{
    $serializedJsonFromSession = $this->session->get('serialized_json');

    $doctrine = $this->getDoctrine();
    $entity = $doctrine->getRepository(Entity::class)->find($id);
    if (!$entity) {
        $entity = new Entity();
        $doctrine->persist($entity);
    }

    $serializer->deserialize(
        $serializedJsonFromSession, 
        Entity::class, 
        'json', 
        [AbstractNormalizer::OBJECT_TO_POPULATE => $entity]
    );

    $doctrine->flush();
}
Related