从doctrine2中的代理对象获取“true”对象

时间:2011-12-01 07:14:17

标签: symfony doctrine-orm

Doctrine使用代理对象来表示相关对象,以便于延迟加载。这是一个非常酷的功能,但它导致了我想要完成的事情的问题。

我已经定制了我的用户对象,因此他们都需要与另一个对象相关联,我称之为城市。这种关系很好。

我有一个表单,我的用户填写该表单以生成另一个对象street。街道也与城市对象有关。我没有让用户在填写表单时选择城市,而是在将对象保存到数据库之前自动设置它。

我尝试使用$event->setCity($user->getCity()),但由于$ user-> getCity()返回代理对象,因此会产生错误。是否有一个我可以从代理对象调用的函数来获取真实函数?

注意:我知道我可以使用连接创建一个自定义查询来强制学说实际加载相关对象,但由于这是用户(使用FOSUserBundle),这将很难正确执行。

11 个答案:

答案 0 :(得分:14)

这不太可能对问题的特定实例有所帮助,因为您依赖于第三方模块,但您可以通过设置"获取模式"来阻止延迟加载。为您的实体提供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

我在这里看到的其他答案都没有为我工作。

答案 1 :(得分:13)

编辑: 正如@flu所提到的,这种方法不会返回" true"宾语。但是,如果您需要来自对象的数据,它可能很有用。 然后,您可以通过一些标识从ObjectManager获取真实对象。


我们可以使用Proxy接口的<_load()方法

$proxyObject->__load();

答案 2 :(得分:2)

这是我的解决方案:

上下文:

所有我的实体拥有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

,则此解决方案不起作用

我希望它可以帮助某人

答案 3 :(得分:2)

您从Doctrine获得实体的唯一实例。如果您要求两次,您将始终获得相同的对象。

因此,如果您的实体首先是延迟加载的(例如,通过某处的@ManyToOne),则此实体实例将是代理。

示例:

您的用户实体在配置实体上具有双向@OneToOne ...

案例1

您要求您的用户:

  • 您获得了用户
  • 真实实例
  • $ user-&gt; config将包含代理

如果您稍后在应用的任何部分中请求相同的配置实体,那么您最终将使用该代理。

案例2

您请求您的配置,之前从未导入过您的用户:

  • 您获得了一个真实的 Config实例
  • $ config-&gt;用户将包含代理

如果您稍后在应用的任何部分中请求相同的用户实体,那么您最终将使用该代理。

总而言之,再次查询同一个实体仍然会以代理(无论如何都是你的用户的实例,因为生成的代理从它扩展而来)。

如果您真的需要您的实体的第二个实例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;
}

答案 4 :(得分:1)

这个问题有点令人讨厌:

// $proxyObject = ...

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

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

之后,如果需要将代理引用放在其他实体中,可以替换代理引用

答案 5 :(得分:0)

Doctrines延迟加载非常适合它的工作,并且一旦你尝试使用它或它的任何属性,就会用一个代理对象替换它。如果由于代理对象而遇到问题(就像我在我的问题中所做的那样),你很可能在模型中出错。

那就是说,你可以告诉学说通过告诉它“保湿”来拉取所有相关数据: $query->getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);

答案 6 :(得分:0)

Symfony PropertyNormalizer使用ReflectionClass getParent方法解决了这个问题。如果您需要检查对象(如标准化器),而不仅仅是使用->get方法,则应该可以使用类似的方法。

似乎代理具有实际实体的父代,这就是instanceof仍然有效的原因。

答案 7 :(得分:0)

我发现的解决方案是扩展from gekko import GEKKO import numpy as np import matplotlib.pyplot as plt m = GEKKO() # create GEKKO model x = m.Var(value=0, lb=0, ub=400000) # define new variable, initial value=0 y = m.Var(value=0, lb=0, ub=200) # define new variable, initial value=1 z = m.Var(value=0, lb=0) m.Equation(x+y+z==100) m.Obj(1.2*x + y + z) # equations m.solve(disp=False) # solve print("Solution with The GEKKO package") print(x.value, y.value , z.value)# # print solution 并将新类用作复杂实体的Hal提取器参数。

您可以在此处找到代码:

Synergy of proxy object of doctrine and zend expressive hal

可能。

答案 8 :(得分:0)

如果首先使用$em->getReference,然后使用$em->find,则结果将是_proxy。 我建议使用:

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

答案 9 :(得分:0)

@ mustaccio,@ Heather Orr

我遇到了这个问题。感谢您的想法,

这是代码:

// $object is a Proxy class
$objectClass = get_class($object);
$reflectionClass = new \ReflectionClass($objectClass);

// Make sure we are not using a Proxy class
if ($obj instanceof Proxy) {
    $reflClass = $reflClass->getParentClass();
}

另一种方式,如果您只需要阅读注释:

// $object is a Proxy class
$objectClass = get_class($object);
$classMetadata = $this->em->getClassMetadata($objectClass);

foreach ($classMetadata->getReflectionProperties() as $property) {
    $annotation = $this->annotationReader->getPropertyAnnotation($property, YOUR_ANNOTATION::class)
}

与原始问题并不完全相关,但是我希望这对某些人有帮助...

答案 10 :(得分:-3)

这会将引用转换为真实对象并获取所有字段。

$entityManager->refresh($proxyObject);