我有这种行为与原则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;
}
和用户组实体:
/**
* @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;
在我将用户对象存储在会话中之前,关联userGroup的所有数据都已成功保存,并且如果我尝试访问$user->userGroup->name变量,则不会在下一页返回NULL。
有一个简单的方法来解决这个问题吗?我可以手动加载userGroup对象/关联与生命周期回调@onLoad在用户类也许?有什么建议吗?
您的问题是mjh_ca回答的问题和您的AbstractEntity
实现的问题的组合。
既然您显示您以这种方式访问实体字段:
$user->userGroup->name;
我假设你的AbstractEntity
基类使用__get()
和__set()
魔法方法,而不是适当的getter和setter:
function getUserGroup()
{
return $this->userGroup;
}
function setUserGroup(UserGroup $userGroup)
{
$this->userGroup = $userGroup;
}
你实际上打破了延迟加载:
最佳实践:不要在实体上使用公共属性"……当您访问尚未初始化的代理对象的公共属性时,返回值将为空。Doctrine不能挂接到这个进程并神奇地使实体惰性加载。"
你应该这样访问字段:
$user->getUserGroup()->getName();
问题的第二部分与mjh_ca所写的完全相同- Zend_Auth
在将实体序列化以在会话中存储时将实体与实体管理器分离。在关联上设置cascade={"merge"}
将不起作用,因为它是分离的实际实体。您必须将反序列化的User
实体合并到实体管理器中。
$detachedIdentity = Zend_Auth::getInstance()->getIdentity();
$identity = $em->merge($detachedIdentity);
问题是如何干净利落地做到这一点。您可以考虑为您的User
实体实现__wakeup()
魔法方法,但这也违反了理论最佳实践…
来源:实现唤醒或克隆
既然我们谈论的是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;
}
}
然后添加一个_init
函数到你的Bootstrap:
public function _initAuth()
{
$this->bootstrap('doctrine');
$em = $this->getResource('doctrine')->getEntityManager();
$auth = My_Auth::getInstance();
$auth->setEntityManager($em);
}
此时调用$user->getUserGroup()->getName();
应该可以正常工作。
当您将实体存储到会话(通过Zend_Auth或其他方式)时,对象被序列化,并且在随后检索和反序列化时不再由Doctrine维护。尝试将实体合并回EntityManager。见http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html