我正在学习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,我正在使用它来实际进行验证检查等。 我想我可能在这里遗漏了一些东西,或者我试图以一种糟糕的方式使用构造函数。感谢您的任何解释和指导!
答案 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();
}
// ...
}