2011-11-06 2 views
0

J'ai ce comportement avec Doctrine 2.1 où je suis à la recherche d'une bonne 'solution de contournement'. Le problème est le suivant:Doctrine ne charge pas les associations de la session correctement

J'ai un utilisateur d'entité:

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

Et usergroup entité:

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

Si j'instancier un objet utilisateur (le faire avec Zend_Auth) et Zend_Auth met automatiquement la session. Le problème est cependant que je le retire de la session à la page suivante puis les données de la classe d'utilisateur sont parfaitement chargées mais pas dans l'association userGroup. Si j'ajoute cascade = {"merge"} dans l'annotation de l'objet utilisateur, l'objet userGroup est chargé mais les données sont vides. Si vous videz quelque chose comme:

$user->userGroup->name

Vous obtiendrez NULL de retour. Le problème est qu'aucune donnée de l'entité du groupe d'utilisateurs n'est accessible avant que l'objet utilisateur ne soit enregistré dans la session, de sorte qu'un objet initialisé vide sera renvoyé. Si je fais quelque chose comme:

echo $user->userGroup->name; 

Avant stocker l'objet utilisateur dans la session toutes les données du userGroup de assocication est correctement enregistré et ne reviendra pas NULL à la page suivante si je tente d'accéder à la user- de $ > userGroup-> variable de nom.

Existe-t-il un moyen simple de résoudre ce problème? Puis-je charger manuellement l'objet/l'association userGroup avec un rappel de cycle de vie @onLoad dans la classe d'utilisateurs peut-être? Aucune suggestion?

Répondre

3

Votre problème est une combinaison de ce que mjh_ca a répondu et un problème avec votre implémentation AbstractEntity.

Puisque vous montrez que vous accédez aux champs d'entités de cette façon:

$user->userGroup->name; 

Je suppose que votre classe de base AbstractEntity utilise __get() et __set() méthodes magiques au lieu de getters appropriés et setters:

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

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

Vous brisez essentiellement le chargement paresseux:

"... chaque fois que vous accédez à une propriété publique d'un objet proxy qui n'a pas encore été initialisé, la valeur de retour sera nulle. La doctrine ne peut pas s'accrocher à ce processus et faire par magie la charge paresseuse de l'entité."

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

Vous devriez plutôt accéderez champs de cette façon:

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

La deuxième partie de votre problème est exactement comme mjh_ca écrit - Zend_Auth détache votre entité du gestionnaire d'entités quand il le sérialise pour le stockage dans la session. Le réglage cascade={"merge"} sur votre association ne fonctionnera pas parce que c'est l'entité réelle qui est détachée. Vous devez fusionner l'entité User désérialisée dans l'entité entière. gestionnaire de ty.

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

La question, est de savoir comment le faire proprement. Vous pouvez regarder dans la mise en œuvre d'une méthode magique __wakeup() pour votre entité User, mais qui est aussi contre les pratiques de doctrine ...

Source: Implementing Wakeup or Clone

Puisque nous parlons de Zend_Auth, vous pouvez étendre Zend_Auth et remplacer la getIdentity() fonction afin qu'il soit conscient de l'entité.

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

Et que d'ajouter une fonction _init à votre Bootstrap:

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

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

A ce stade, appelant $user->getUserGroup()->getName(); devrait fonctionner comme prévu.

1

Lorsque vous stockez l'entité dans une session (via Zend_Auth ou autre), l'objet est sérialisé et n'est plus géré par Doctrine lorsqu'il est ensuite récupéré et désérialisé. Essayez de fusionner l'entité dans EntityManager. Voir http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html

+0

J'ai essayé cela mais le problème réel est que l'association userGroup n'est PAS remplie AVANT qu'elle ne soit sérialisée. Cela provoque que merge() ne soit pas affecté même si vous l'appelez directement à l'objet userGroup lui-même. Ce problème se produit lorsque vous n'accédez pas à l'objet userGroup avant qu'il ne soit sérialisé car Doctrine charge les données d'entité uniquement lorsqu'il est appelé. Avez-vous mon problème? Pour l'instant, j'ai une solution de contournement avec echo $ user-> userGroup-> name; mais ce n'est pas gentil. Je cherche quelque chose pour charger manuellement l'entité userGroup. J'espère que tu as compris. –

Questions connexes