2011-11-06 7 views
0

私はDoctrine 2.1でこの問題を抱えています。ここで私は素晴らしい回避策を探しています。Doctrineはセッションからの関連を正しくロードしません。

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

そしてユーザーグループエンティティ:

は、私は、ユーザーのエンティティがあります。私は(にZend_Authでこれをやって)ユーザオブジェクトをインスタンス化した場合

/** 
* @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はそれを自動的にセッションに設定します。

しかし、問題は次のページでセッションから取り戻され、ユーザークラスのデータは完全に読み込まれますが、userGroup関連ではありません。ユーザーオブジェクトの注釈にcascade = {"merge"}を追加すると、userGroupオブジェクトはロードされますが、データは空です。あなたが何かダンプ場合:

$user->userGroup->name

をあなたはNULLを取り戻すだろう。問題は、ユーザーオブジェクトがセッションに保存される前にユーザーグループエンティティのデータがアクセスされていないため、空の初期化オブジェクトが返されることです。私のような何かを行う場合は、次の

echo $user->userGroup->name; 

を私はセッションでユーザーオブジェクトを保存する前にassocicationのuserGroupのすべてのデータが正常に保存され、私がする$ user-にアクセスしようとした場合、次のページにはNULLを返しません。 > userGroup-> name変数。

これを解決する簡単な方法はありますか?おそらく、ユーザークラスのライフサイクルコールバック@onLoadでuserGroupオブジェクト/関連付けを手動でロードできますか?助言がありますか?

答えて

3

あなたの問題は、mjh_caが答えたものとAbstractEntity実装の問題の組み合わせです。

あなたはこのやり方でエンティティのフィールドにアクセスすることを示しているので:

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

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

$user->userGroup->name; 

私はあなたのAbstractEntityベースクラスではなく、適切なゲッターとセッターの__get()__set()マジックメソッドを使用していると仮定あなたは本質的に遅延ロードを壊しています:

"...まだ初期化されていないプロキシオブジェクトのpublicプロパティにアクセスすると、戻り値はnullになります。 Doctrineはこのプロセスに繋がり、魔法のようにエンティティを遅延ロードすることはできません。「

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

があなたの代わりにこのようにフィールドにアクセスする必要があります。

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

問題の2番目の部分はmjh_caが書いたとおりにある - Zend_Authは、エンティティマネージャからエンティティをデタッチセッション内での保存のためにシリアル化するときに、関連付けられている実際のエンティティであるため、関連付けられているcascade={"merge"}を設定すると、デシリアライズされたUserエンティティをEntiにマージする必要がありますマネージャー。

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

質問は、これをきれいに行う方法です。我々はおよそZend_Authを話しているので、あなたがZend_Authを拡張し、オーバーライドすることができますImplementing Wakeup or Clone

:ソース

あなたのUserエンティティの__wakeup()マジックメソッドを実装するに見ることができるが、それは教義のベストプラクティスに対してもです...

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機能を追加するよりも

:意図したとおりに動作するはず $user->getUserGroup()->getName();を呼び出して、この時点で

public function _initAuth() 
{ 
    $this->bootstrap('doctrine'); 
    $em = $this->getResource('doctrine')->getEntityManager(); 

    $auth = My_Auth::getInstance(); 
    $auth->setEntityManager($em); 
} 

1

(Zend_Authなどを介して)セッションにエンティティを格納すると、そのオブジェクトはシリアル化され、後で検索されてシリアル化されなくなったときにDoctrineによって管理されなくなります。エンティティをEntityManagerにマージしてみてください。 http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html

+0

私はそれを試みましたが、実際の問題はuserGroupの関連付けがシリアル化される前に満たされていないということです。これにより、merge()がuserGroupオブジェクト自体に直接呼び出されても、merge()は影響を与えません。この問題は、Doctrineが呼び出されたときにのみエンティティデータを読み込むため、シリアライズされる前にuserGroupオブジェクトにアクセスしないと発生します。あなたは私の問題を抱えていますか?今のところ私はecho $ user-> userGroup-> nameを使って回避策をとっています。しかしそれは素晴らしいものではありません。私は手動でuserGroupエンティティをロードする何かを探しています。あなたが理解して欲しいです。 –

関連する問題