douziqian2871 2011-11-06 18:06
浏览 39
已采纳

Doctrine不会正确加载会话中的关联

I'm having this behavior with Doctrine 2.1 where I'm looking for a nice 'workaround'. The problem is as follows:

I have a user Entity:

/**
 * @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;
}

And a usergroup entity:

/**
 * @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;   
}

If I instantiate a user object (doing this with Zend_Auth) and Zend_Auth puts it automatically the session.

The problem is however, that is I pull it back from the session at a next page then the data in the user class is perfectly loaded but not in the userGroup association. If I add cascade={"merge"} into the annotation in the user object the userGroup object IS loaded but the data is empty. If you dump something like:

$user->userGroup->name

You will get NULL back. The problem is no data of the usergroup entity is accesed before the user object is saved in the session so a empty initialized object will be returned. If I do something like:

echo $user->userGroup->name;

Before I store the user object in the session all data of the assocication userGroup is succesfully saved and won't return NULL on the next page if I try to access the $user->userGroup->name variable.

Is there a simple way to fix this? Can I manually load the userGroup object/association with a lifecycle callback @onLoad in the user class maybe? Any suggestions?

  • 写回答

2条回答 默认 最新

  • duanbicheng3345 2012-01-04 01:43
    关注

    Your problem is a combination of what mjh_ca answered and a problem with your AbstractEntity implementation.

    Since you show that you access entity fields in this fashion:

    $user->userGroup->name;
    

    I assume your AbstractEntity base class is using __get() and __set() magic methods instead of proper getters and setters:

    function getUserGroup()
    {
        return $this->userGroup;
    }
    
    function setUserGroup(UserGroup $userGroup)
    {
        $this->userGroup = $userGroup;
    }
    

    You are essentially breaking lazy loading:

    "... whenever you access a public property of a proxy object that hasn’t been initialized yet the return value will be null. Doctrine cannot hook into this process and magically make the entity lazy load."

    Source: Doctrine Best Practices: Don't Use Public Properties on Entities

    You should instead be accessing fields this way:

    $user->getUserGroup()->getName();
    

    The second part of your problem is exactly as mjh_ca wrote - Zend_Auth detaches your entity from the entity manager when it serializes it for storage in the session. Setting cascade={"merge"} on your association will not work because it is the actual entity that is detached. You have to merge the deserialized User entity into the entity manager.

    $detachedIdentity = Zend_Auth::getInstance()->getIdentity();
    $identity = $em->merge($detachedIdentity);
    

    The question, is how to do this cleanly. You could look into implementing a __wakeup() magic method for your User entity, but that is also against doctrine best practices...

    Source: Implementing Wakeup or Clone

    Since we are talking about Zend_Auth, you could extend Zend_Auth and override the getIdentity() function so that it is entity aware.

    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;
        }
    }
    

    And than add an _init function to your Bootstrap:

    public function _initAuth()
    {
        $this->bootstrap('doctrine');
        $em = $this->getResource('doctrine')->getEntityManager();
    
        $auth = My_Auth::getInstance();
        $auth->setEntityManager($em);
    }
    

    At this point calling $user->getUserGroup()->getName(); should work as intended.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 如何实验stm32主通道和互补通道独立输出
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题