Подтвердить что ты не робот

Получение "истинного" объекта из прокси-объекта в doctrine2

Doctrine использует прокси-объекты для представления связанных объектов, чтобы облегчить ленивую загрузку. Это действительно классная функция, но она вызывает проблему с чем-то, что я пытаюсь выполнить.

Я настроил свой пользовательский объект так, чтобы все они были связаны с другим объектом, который я буду называть городом. Это соотношение работает нормально.

У меня есть форма, которую мой пользователь заполняет для создания другого объекта, улицы. Улица также связана с объектом города. Вместо того, чтобы мой пользователь выбирал город, когда он заполняет форму, я хочу автоматически установить ее, прежде чем я сохраню объект в моей базе данных.

Я попытался использовать $event->setCity($user->getCity()), но поскольку $user- > getCity() возвращает прокси-объект, это порождает ошибку. Есть ли функция, которую я могу вызвать из прокси-объекта, чтобы получить реальный?

Примечание. Я знаю, что могу создать пользовательский запрос с соединением, чтобы заставить доктрину фактически загружать связанный объект, но так как это пользователь (с помощью FOSUserBundle), который будет трудно выполнить правильно.

4b9b3361

Ответ 1

Изменить: Как упоминалось в @flu, этот подход не возвращает "истинный" объект. Однако это может быть полезно в случае, если вам нужны данные из объекта. Затем вы можете просто получить реальный объект из ObjectManager с помощью некоторого идентификатора.


Мы можем использовать метод __load() из интерфейса прокси-сервера

$proxyObject->__load();

Ответ 2

Это вряд ли поможет в конкретном случае для вопроса, поскольку вы полагаетесь на сторонний модуль, но вы можете предотвратить ленивую загрузку, установив режим "выборки" для вашей организации на "EAGER".

User:
    ManyToOne:
        city:
            fetch: EAGER

Это также может обрабатываться аннотациями:

@ManyToOne(targetEntity="city", fetch="EAGER")
@JoinColumn(name="city", referencedColumnName="id")

См. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annref-manytoone

Ни один из других ответов, которые я видел здесь, не работал у меня.

Ответ 3

Вот мое решение:

Контекст:

Все мои объекты имеют свойство id и getId() метод


Решение:

$em = $this->getDoctrine()->getManager();

// 1 -> get the proxy object class name
$proxy_class_name = get_class($proxyObject);

// 2 -> get the real object class name
$class_name = $em->getClassMetadata($proxy_class_name)->rootEntityName;

// 3 -> get the real object
$object = $em->find($class_name, $proxyObject->getId());

Проблема:

Это решение не работает, если свойство id и getId() находятся в классе Trait

Я надеюсь, что это может помочь кому-то

Ответ 4

Вы получаете уникальный экземпляр объекта из Doctrine. Если вы запросите его два раза, вы всегда получите тот же объект.

Как следствие, если ваш объект сначала загружен ленивым (например, через @ManyToOne где-то), этот экземпляр объекта будет прокси.

Пример:

У вас есть пользовательский объект, имеющий двунаправленный @OneToOne в объекте Config...

Случай 1

Запрос пользователя:

  • вы получаете реальный экземпляр пользователя
  • $user- > config будет содержать прокси

Если позже вы попросите один и тот же объект Config в любой части вашего приложения, вы получите этот прокси.

Случай 2

Вы запрашиваете свой Config, и ваш пользователь никогда не импортировался раньше:

  • вы получаете реальный экземпляр Config
  • $config- > пользователь будет содержать прокси

Если позже вы попросите один и тот же пользовательский объект в любой части вашего приложения, вы получите этот прокси.


В целом, запрос снова для одного и того же объекта будет по-прежнему заканчиваться прокси-сервером (который является экземпляром вашего Пользователя в любом случае, поскольку сгенерированный прокси-сервер простирается от него).

Если вам нужен действительно второй экземпляр вашего объекта, который является real (если в вашей логике приложения есть get_class, которую вы не можете заменить на instanceof для пример), вы можете попробовать играть с $em->detach(), но это будет кошмар (и, следовательно, ваше приложение может вести себя с еще большей магией, чем уже доктрина Doctrine).

Решение (исходящее из моей темной стороны, я полагаю) может воссоздать не управляемую сущность вручную.

public function getRealEntity($proxy)
{
    if ($proxy instanceof Doctrine\ORM\Proxy\Proxy) {
        $metadata              = $this->getManager()->getMetadataFactory()->getMetadataFor(get_class($proxy));
        $class                 = $metadata->getName();
        $entity                = new $class();
        $reflectionSourceClass = new \ReflectionClass($proxy);
        $reflectionTargetClass = new \ReflectionClass($entity);
        foreach ($metadata->getFieldNames() as $fieldName) {
            $reflectionPropertySource = $reflectionSourceClass->getProperty($fieldName);
            $reflectionPropertySource->setAccessible(true);
            $reflectionPropertyTarget = $reflectionTargetClass->getProperty($fieldName);
            $reflectionPropertyTarget->setAccessible(true);
            $reflectionPropertyTarget->setValue($entity, $reflectionPropertySource->getValue($proxy));
        }

        return $entity;
    }

    return $proxy;
}

Ответ 5

Это немного неприятное обходное решение:

// $proxyObject = ...

$em->detach($proxyObject);
$entityObject = $em->find(<ENTITY_CLASS>, $proxyObject->getId());

// now you have real entity and not the proxy (entityObject instead of proxyObject)

после этого вы можете заменить ссылку на прокси, если вам нужно иметь ее внутри других объектов

Ответ 6

Доктрина ленивая загрузка очень хороша в своей работе и заменит прокси-объект на реальный, как только вы попытаетесь использовать его или какие-либо его свойства. Если у вас возникли проблемы из-за прокси-объектов (как в моем вопросе), скорее всего, у вас есть ошибка в вашей модели.

Тем не менее, вы можете сказать доктрине, чтобы вытащить все связанные данные, сказав ей "гидратировать": $query->getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);

Ответ 7

Symfony PropertyNormalizer решает эту проблему с помощью метода getParent ReflectionClass. Если вам нужно осмотреть объект, как для нормализатора, а не просто использовать методы ->get, вы сможете использовать аналогичный подход.

Похоже, что прокси имеет родителя фактической сущности, поэтому instanceof все еще работает.

Ответ 9

Если вы сначала использовали $em->getReference, а затем $em->find, то результатом будет _proxy. Я рекомендую использовать:

 createQueryBuilder ... ->getQuery()
    ->setHint (Query::HINT_FORCE_PARTIAL_LOAD, true)
    ->getOneOrNullResult();

Ответ 10

Это преобразует ссылку на реальный объект и выберет все поля.

$entityManager->refresh($proxyObject);