Подменяем EntityManager

1.Создаем класс My\EntityManager

use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query;

class EntityManager extends \Doctrine\ORM\EntityManager
{
    /**
     * @param array|Connection  $connection
     * @param Configuration     $config
     * @param EventManager|null $eventManager
     *
     * @return EntityManager
     *
     * @throws ORMException
     */
    public static function create($connection, Configuration $config, EventManager $eventManager = null)
    {
        if (!$config->getMetadataDriverImpl()) {
            throw ORMException::missingMappingDriverImpl();
        }

        $connection = static::createConnection($connection, $config, $eventManager);

        // порождаем самого себя
        return new EntityManager($connection, $config, $connection->getEventManager());
    }

    /**
     * Например принудительный HINT_REFRESH:
     * 
     * @param string $dql
     *
     * @return Query|mixed
     */
    public function createQuery($dql = '')
    {
        // устанавливаем принудительное обновление запрашиваемых объектов
        $query = parent::createQuery($dql);
        $query->setHint(Query::HINT_REFRESH, true);

        return $query;
    }
    
    /**
     * {@inheritDoc}
     */
    public function close(): void
    {
        // ничего не делаем

        // Если база данных возвращает ошибку выполнения SQL, то Doctrine выбрасывает исключение и 
        // закрывает EntityManager + закрывает подключение к базе данных.
        // Это поведение мы считаем неверным, поэтому переопределяем его.
    }
}

2. Подменяем

простой вариант:

parameters:
    doctrine.orm.entity_manager.class: My\EntityManager

сложный вариант:

    doctrine.orm.entity_manager_db_entity_manager:
        class: My\EntityManager
        factory: ['My\EntityManager', 'create']
        arguments: ['@doctrine.dbal.connection_db_connection', '@doctrine.orm.entity_manager_db_configuration',]
        public: true
        lazy: true
        autowire: false
        autoconfigure: false
        tags:
            - {name: 'container.preload', class: 'Doctrine\ORM\Proxy\Autoloader', priority: 100}

Готово.

Тонкости HINT_REFRESH

Внимание, даже если мы используем Query::HINT_REFRESH, то это не влияет на то, как доктрина смотрит на строки - если доктрина вытащила из таблицы строку с данными и ассоциировала ее с определенным php-классом, то далее она уже не меняет эту ассоциацию (хранится эта ассоциация в UnitOfWork->identityMap).

Когда это важно? А вот когда используется DiscriminatorMap, при этом мы пишем запросы используя createNativeQuery, имею ввиду что-то вроде этого:

$this->rsm = new ResultSetMappingBuilder($this->getEntityManager());
$this->rsm->addRootEntityFromClassMetadata(Document::class, 't');
        
$sql = '
            SELECT
                *
            FROM document
            WHERE available = 1';
$this->getEntityManager()->createNativeQuery($sql, $this->rsm)->getResult();

Фактически, такой запрос вернет объекты класса Document которые конечно являются частичными от полной версии объектов, например таких, которые используя DiscriminatorMap являются расширениями таблицы document. Получается, что если Вы используете такую магию у Вас есть 2 выхода:

  1. использовать запросы с createNativeQuery после того, как Вы вытянули все данные обычными запросами
  2. после createNativeQuery вызывать $entityManager->clear(); который сбросит UnitOfWork (в том числе identityMap)
  3. не использовать createNativeQuery и гидрировать объекты каким-то другим способом

Я для себя пока что выбирают такую стратегию:

  1. делаю все нужные мне createNativeQuery запросы
  2. вызываю $entityManager->clear(); ведь объекты созданные createNativeQuery не являются полноценными
  3. делаю обычные запросы, которые порождают настоящие объекты, которые уже обрабатываются UnitOfWork

Удачки.


03.08.2010 09:24

Комментарии

Комментариев пока нет.


Copyrights © 2007 - 2026 YaPro.Ru