使用$ em-> find()时未调用Doctrine2 __constructor; ?如何正确加载实体?

时间:2011-01-23 13:09:26

标签: doctrine-orm

我正在学习doctrine2,并且遇到了如何自动调用构造函数的问题。 例如,在我的实体中我有

/**
 * @Entity
 */
class User{
   ....
   public function __construct() {
       exit('in');
   }
}

当我以这种方式得到对象时:

$userObj = $em->find('User', 1);

我从数据库中获取该对象,但从不调用构造函数。 我想在构造函数中加入一些常见的东西,比如验证规则,甚至从教条文档中放入示例代码,如

        $this->comments = new ArrayCollection();

当我在代码中创建新对象以创建像

这样的用户时,这个方法很有用
$user = new User(); //now constructor works just fine

现在,获得实体的“正确”方式是什么?我怀疑每次使用$ user-bj-> __ construct()时使用$ em-> find()时都必须手动调用构造函数。 ?这有点糟糕......或者我应该使用其他东西 - > gt; find()来正确获取单个实体? 我知道我可以使用@PrePersist,我正在使用它来实际进行验证检查等。 我想我可能在这里遗漏了一些东西,或者我试图以一种糟糕的方式使用构造函数。感谢您的任何解释和指导!

5 个答案:

答案 0 :(得分:6)

我非常确定find或类似的东西不应该调用构造函数...

您需要加入@PostLoad事件。

答案 1 :(得分:2)

为什么要调用已经存在的实体的构造函数?当您需要验证它时,您应该在持久化之前完成验证或初始化。因此,当您调用已经存在的实体时,没有必要对其进行验证。 放置验证和其他初始化的正确位置是实体的构造方法。 例如

/**
 * @Entity
 */
class User{
   protected $name;
   public function __construct($name) {
       if (isset($name)) {
           //** validate the name here */
           $this->name=$name;
       } else {
           throw new Exception("no user name set!");
       }
   }
}

根据doctrine2文档,Doctrine2从不调用实体的__construct()方法。 http://www.doctrine-project.org/docs/orm/2.0/en/reference/architecture.html?highlight=construct

答案 2 :(得分:1)

主义使用反射来实例化对象而不调用构造函数。

从PHP 5.4开始,您可以使用反射来实例化一个类而无需 调用构造函数 ReflectionClass :: newInstanceWithoutConstructor

实例化实例的用法如下:

private function buildFactory(string $className) : callable
{
    $reflectionClass = $this->getReflectionClass($className);

    if ($this->isInstantiableViaReflection($reflectionClass)) {
        return [$reflectionClass, 'newInstanceWithoutConstructor'];
    }

    $serializedString = sprintf(
        '%s:%d:"%s":0:{}',
        is_subclass_of($className, Serializable::class) ? self::SERIALIZATION_FORMAT_USE_UNSERIALIZER : self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER,
        strlen($className),
        $className
    );

    $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString);

    return static function () use ($serializedString) {
        return unserialize($serializedString);
    };
}

答案 3 :(得分:0)

Doctrine ORM将“重写”您的类,它将生成一个实现\Doctrine\ORM\Proxy\Proxy

的新类。

它重写了construct方法:

/**
     * @param \Closure $initializer
     * @param \Closure $cloner
     */
    public function __construct($initializer = null, $cloner = null)
    {

        $this->__initializer__ = $initializer;
        $this->__cloner__      = $cloner;
    }

您可以在cache文件夹${CACHE}/doctrine/orm/Proxies内看到它。

答案 4 :(得分:0)

您需要在类上同时使用@ORM\HasLifecycleCallbacks + 在您选择的特定函数上使用@ORM\PostLoad。

当心!如果你把它放在构造函数上它会覆盖加载的数据库数据

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="dossier")
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 */
class Dossier
{
    // ...

    /**
     * The normal constructor stays as usual
     */
    public function __construct()
    {
        $this->takenActions = new ArrayCollection();
        $this->classifications = new ArrayCollection();
        $this->dossierProblems = new ArrayCollection();
        $this->internalNotes = new ArrayCollection();
    }

    /**
     * Triggers after the entity has been loaded in the EntityManager (e.g. Doctrine's ->find() etc...)
     * The constructor does not get called. Some variables still need a default value
     * Must be in combination with "ORM\HasLifecycleCallbacks" on the class
     * 
     * @ORM\PostLoad
     */
    public function postLoadCallback(): void
    {
        // Only put a default value when it has none yet
        if (!$this->dossierProblems)
            $this->dossierProblems = new ArrayCollection();
        if (!$this->internalNotes)
            $this->internalNotes = new ArrayCollection();
    }

    // ...
}