Doctrine orm 初始化实体中的空关系

Doctrine orm 初始化实体中的空关系,doctrine-orm,Doctrine Orm,我有与其他实体具有1:1或1:M关系的实体。但是,所有关系都可以为空 我想将一些操作代理给相关实体。我在下面举个例子。问题是,如果这个关系仍然不存在,我有null,所以我经常检查null,这显然是错误的。我想做的是用空对象水合我的实体。原因: 条令知道应该为字段创建什么实例。所以它应该只提供空实例而不是null 我不想用初始化来填充代码,比如 $object->setSettings(新设置感知) 是否应该代理请求是有争议的,但我想对客户机代码隐藏DB表示。如果我的方向完全错了,请给我指出正确

我有与其他实体具有1:1或1:M关系的实体。但是,所有关系都可以为空

我想将一些操作代理给相关实体。我在下面举个例子。问题是,如果这个关系仍然不存在,我有
null
,所以我经常检查null,这显然是错误的。我想做的是用空对象水合我的实体。原因:

  • 条令知道应该为字段创建什么实例。所以它应该只提供空实例而不是null
  • 我不想用初始化来填充代码,比如
    $object->setSettings(新设置感知)
  • 是否应该代理请求是有争议的,但我想对客户机代码隐藏DB表示。如果我的方向完全错了,请给我指出正确的方向。我可以接受这是模型的责任,而不是实体的责任,但理论总是将实体返回给我
当然,我可以在实体的构造函数中添加初始化,或者提供getter来创建对象的新实例(如果不存在的话)。我不想这样有几个原因:

  • 我不知道物体实际上是如何水合的。我假设这样的初始化应该发生在事件中,而不是在构造函数中
  • 我不想为每个实体编写代码(有时,有人会忘记在getter中添加初始化),而是希望为每个关系自动编写初始化
一些示例代码:

/**
 * SomeObject
 * @ORM\Entity()
 * @ORM\Table(
     name="some_object"
 * )
*/ class SomeObject implements DataTransfer {
 /**
   * @ORM\OneToOne(targetEntity="Settings", mappedBy="SomeObject")
   */
  protected $settings;

  public function getSettings() {
    return $this->settings;
  }

  public function get() {
    $record = new \stdClass();
    $record->id = $this->getId();
    ...

    $settingsObject = $this->getSettings();
    $record->someKey = $settingsObject ? $settingsObject->getSomeKey() : null;
    $record->someOtherKey = $settingsObject ? $settingsObject->getSomeOtherKey() : null;
    return $record;
  }
欢迎提出任何建议,包括黑客理论


p.S.条令ORM版本为2.3。如果这有助于解决问题,我可以升级。

我不会讨论你的代理理论:你的代码,你的设计,我没有足够的知识来发表意见

关于了解条令是如何将其实体化的,您可以在
\document\ORM\UnitOfWork::createEntity
中看到它是如何完成的。它似乎没有调用构造函数(使用
\ReflectionClass::newInstanceWithoutConstructor
,这显然不应该使用构造函数),但您可能有兴趣听一听原则的建议

关于初始化
null
属性,即加载后事件应该触发的代码,您应该首先在所有实体上拥有一个超类:而不是
class SomeObject实现数据传输{…}
,您应该拥有
class SomeObject扩展MyEntity{…}
(并让
MyEntity
实现
DataTransfer
以保持您的接口)。这个
MyEntity
类将是一个,它将用
@HasLifecycleCallbacks
注释,并声明一个用
@PostLoad
注释的方法。在这里,您有一个钩子来运行空代码

为了使此代码具有通用性(因为它是从这个超类编码的),您可以依赖Doctrine的实体元数据,它保留关联映射和工作单元确定其低级数据库访问业务所需的所有数据。它应该如下所示:

/** @HasLifecycleCallbacks @MappedSuperclass ... */
public class MyEntity implements DataTransfer {
    ...
    /** @PostLoad */
    public function doPostLoad(\Doctrine\Common\Persistence\Event\LifecycleEventArgs $event) { //the argument is needed here, and is passed only since 2.4! If you don't want to upgrade, you can work around by using event listeners, but it's more complicated to implement ;)
        $em = $event->getEntityManager();
        $this->enableFakeMappings($em);
    }

    private function enableFakeMappings(\Doctrine\ORM\EntityManager $em) {
        $mappings = $em->getClassMetadata(get_class($this))->getAssociationMappings(); //try and dump this $mappings array, it's full o'good things!
        foreach ($mappings as $mapping) {
            if (null === $this->{$mapping['fieldName']}) {
                $class = $mapping['targetEntity'];
                $this->{$mapping['fieldName']} = new $class(); //this could be cached in a static and cloned when needed
            }
        }
    }
}

现在,考虑一个新的实体,它需要在没有空值检查的情况下访问它的属性:你必须为这个工作建立一个合适的构造函数。当你仍然需要实体管理器时,最直接的方法是把EM传递给构造函数。您可以插入服务定位器并从中检索EM。有几种方法,但这是另一种情况。因此,在

MyEntity
中的基本方法:

public function __construct(\Doctrine\ORM\EntityManager $em) {
    $this->enableFakeMappings($em);
}
然而,这样做可能会混淆实体持久化时的原则:它应该如何处理所有这些实例化的空对象?它将级联持久化它们,这不是您想要的(如果是,那么您可以停止读取;)。牺牲级联持久性,一个简单的解决方案是这样的,仍然在您的超类中:

/** @PrePersist */
public function doPrePersist(\Doctrine\Common\Persistence\Event\LifecycleEventArgs $event) {
    $em = $event->getEntityManager();
    $this->disableFakeMappings($em);
}

/** @PreUpdate */
public function doPreUpdate(\Doctrine\Common\Persistence\Event\LifecycleEventArgs $event) {
    $em = $event->getEntityManager();
    $this->disableFakeMappings($em);
}

private function disableFakeMappings(\Doctrine\ORM\EntityManager $em) {
    $uow = $em->getUnitOfWork();
    $mappings = $em->getClassMetadata()->getAssociationMappings();
    foreach ($mappings as $mapping) {
        if (!$this->{$mapping['fieldName']} instanceof MyEntity) {
            continue;
        }
        //"reset" faked associations: assume they're fake if the object is not yet handled by Doctrine, which breaks the cascading auto-persist... risk nothing, gain nothing, heh? ;)
        if (null === $uow->getEntityState($this->{$mapping['fieldName']}, null)) {
            $this->{$mapping['fieldName']} = null;
        }
    }
}
希望这有帮助!:)