我在Doctrine 2.1中有这种行为,我正在寻找一个很好的“解决方法”。问题如下:
我有一个用户实体:
/**
* @Entity(repositoryClass="Application\Entity\Repository\UserRepository")
* @HasLifecycleCallbacks
*/
class User extends AbstractEntity
{
/**
*
* @var integer
*
* @Column(type="integer",nullable=false)
* @Id
* @GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
*
* @var \DateTime
* @Column(type="datetime",nullable=false)
*/
protected $insertDate;
/**
*
* @var string
* @Column(type="string", nullable=false)
*/
protected $username;
/**
*
* @ManyToOne(targetEntity="UserGroup", cascade={"merge"})
*/
protected $userGroup;
}
usergroup 实体:
/**
* @Entity
*/
class UserGroup extends AbstractEntity
{
/**
*
* @var integer
*
* @Column(type="integer",nullable=false)
* @Id
* @GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
*
* @var string
* @Column(type="string",nullable=false)
*/
protected $name;
}
如果我实例化一个用户对象(使用Zend_Auth执行此操作),Zend_Auth会自动将其置于会话中。
然而问题是,我将它从下一页的会话中拉回来,然后用户类中的数据被完美加载但不在userGroup关联中。如果我将cascade = {“merge”}添加到用户对象的注释中,则加载userGroup对象但数据为空。如果您转储如下内容:
$user->userGroup->name
你会得到NULL。问题是在用户对象保存在会话中之前没有加入用户组实体的数据,因此将返回空的初始化对象。如果我这样做:
echo $user->userGroup->name;
在我将会话中的用户对象存储到会话之前,如果我尝试访问$ user-> userGroup-> name变量,则会成功保存关联userGroup的所有数据并且不会在下一页上返回NULL。 / p>
有没有一种简单的方法来解决这个问题?我可以在用户类中手动加载userGroup对象/关联与生命周期回调@onLoad吗?有什么建议吗?
答案 0 :(得分:3)
您的问题是mjh_ca回答的内容与AbstractEntity
实施问题的结合。
由于您显示以这种方式访问实体字段:
$user->userGroup->name;
我认为您的AbstractEntity
基类使用__get()
和__set()
魔术方法而不是正确的getter和setter:
function getUserGroup()
{
return $this->userGroup;
}
function setUserGroup(UserGroup $userGroup)
{
$this->userGroup = $userGroup;
}
你基本上打破了延迟加载:
“......无论何时访问尚未初始化的代理对象的公共属性,返回值都将为null.Doctrine无法挂钩到此进程并神奇地使实体延迟加载。”
来源:Doctrine Best Practices: Don't Use Public Properties on Entities
您应该以这种方式访问字段:
$user->getUserGroup()->getName();
问题的第二部分正如mjh_ca所写的那样 - Zend_Auth
将实体与实体管理器分离,以便在会话中存储它时进行存储。在关联上设置cascade={"merge"}
将不起作用,因为它是分离的实际实体。您必须将反序列化的User
实体合并到实体管理器中。
$detachedIdentity = Zend_Auth::getInstance()->getIdentity();
$identity = $em->merge($detachedIdentity);
问题是,如何干净利落地做到这一点。您可以考虑为__wakeup()
实体实施User
魔术方法,但这也违反了学说最佳做法......
来源:Implementing Wakeup or Clone
由于我们讨论的是Zend_Auth
,您可以扩展Zend_Auth
并覆盖getIdentity()
函数,以便它可以识别实体。
use Doctrine\ORM\EntityManager,
Doctrine\ORM\UnitOfWork;
class My_Auth extends \Zend_Auth
{
protected $_entityManager;
/**
* override otherwise self::$_instance
* will still create an instance of Zend_Auth
*/
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
public function getEntityManager()
{
return $this->_entityManager;
}
public function setEntityManager(EntityManager $entityManager)
{
$this->_entityManager = $entityManager;
}
public function getIdentity()
{
$storage = $this->getStorage();
if ($storage->isEmpty()) {
return null;
}
$identity = $storage->read();
$em = $this->getEntityManager();
if(UnitOfWork::STATE_DETACHED === $em->getUnitOfWork()->getEntityState($identity))
{
$identity = $em->merge($identity);
}
return $identity;
}
}
然后在你的Bootstrap中添加_init
函数:
public function _initAuth()
{
$this->bootstrap('doctrine');
$em = $this->getResource('doctrine')->getEntityManager();
$auth = My_Auth::getInstance();
$auth->setEntityManager($em);
}
此时调用$user->getUserGroup()->getName();
应按预期工作。
答案 1 :(得分:1)
当您将实体存储到会话中时(通过Zend_Auth或其他方式),对象将被序列化,并且在随后检索和反序列化时不再由Doctrine维护。尝试将实体合并回EntityManager。见http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html