从doctrine2中的代理对象获取"true"对象

MrG*_*ass 35 symfony doctrine-orm

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

我已经定制了我的用户对象,因此它们都需要与不同的对象相关联,我称之为城市.这种关系很好.

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

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

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

bel*_*che 15

这不太可能对问题的特定实例有所帮助,因为您依赖于第三方模块,但您可以通过将实体的"获取模式"设置为"EAGER"来阻止延迟加载.

User:
    ManyToOne:
        city:
            fetch: EAGER
Run Code Online (Sandbox Code Playgroud)

这也可以通过注释来处理:

@ManyToOne(targetEntity="city", fetch="EAGER")
@JoinColumn(name="city", referencedColumnName="id")
Run Code Online (Sandbox Code Playgroud)

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

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


Ser*_*huk 12

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


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

$proxyObject->__load();
Run Code Online (Sandbox Code Playgroud)

  • 虽然它可能有用,但这并没有回答OP的问题,因为`Proxy :: __ load()`返回`void`并且`$ proxyObject`在调用`$ proxyObject - > __ load()之后仍然是代理;`所以这个不从代理返回*true*对象. (15认同)

doy*_*y44 7

这是我的解决方案:

语境:

所有的实体都有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());
Run Code Online (Sandbox Code Playgroud)

问题:

如果id属性和getId()方法在一个Trait类中,则此解决方案不起作用

我希望它可以帮助某人


Ala*_*blo 7

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

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

例子:

您有一个 User 实体@OneToOne在 Config 实体上具有双向...

情况1

您要求您的用户:

  • 你得到一个真实的 User 实例
  • $user->config 将包含一个代理

如果稍后您在应用程序的任何部分请求相同的 Config 实体,您将最终获得该代理。

案例二

您请求您的 Config 并且您的用户之前从未被导入:

  • 你会得到一个真实的 Config 实例
  • $config->user 将包含一个代理

如果稍后您在应用程序的任何部分请求相同的 User 实体,您将最终获得该代理。


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

如果您真的需要实体的第二个实例(例如,如果您的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;
}
Run Code Online (Sandbox Code Playgroud)


MrG*_*ass 0

Doctrines 延迟加载非常擅长它的工作,一旦您尝试使用代理对象或其任何属性,它就会将代理对象替换为真实的对象。如果您因代理对象而遇到问题(就像我在问题中所做的那样),您的模型中很可能存在错误。

也就是说,您可以通过告诉它“水合”来让它提取所有相关数据: $query->getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);