Доктрина не загружает ассоциации из сессии правильно - PullRequest
0 голосов
/ 06 ноября 2011

У меня такое поведение с 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;
}

И группа пользователей сущность:

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

Прежде чем я сохраню объект пользователя в сеансе, все данные ассоциированной группы пользователей будут успешно сохранены и не будут возвращать NULL на следующей странице, если я попытаюсь обратиться к переменной $ user-> userGroup-> name.

Есть ли простой способ исправить это? Можно ли вручную загрузить объект / ассоциацию группы пользователей с помощью обратного вызова жизненного цикла @onLoad в классе пользователя? Есть предложения?

Ответы [ 2 ]

3 голосов
/ 04 января 2012

Ваша проблема - это сочетание ответа mjh_ca и проблемы с вашей реализацией AbstractEntity.

Поскольку вы показываете, что обращаетесь к полям сущностей следующим образом:

$user->userGroup->name;

Полагаю, ваш базовый класс AbstractEntity использует магические методы __get() и __set() вместо правильных методов получения и установки:

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

Вопрос в том, как сделать это чисто. Вы можете рассмотреть реализацию магического метода __wakeup() для вашей User сущности, но это также противоречит лучшим доктринам ...

Источник: Реализация пробуждения или клонирования

Поскольку речь идет о 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 к вашей начальной загрузке:

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

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

В этот момент вызов $user->getUserGroup()->getName(); должен работать как положено.

1 голос
/ 12 ноября 2011

Когда вы сохраняете сущность в сеансе (через Zend_Auth или иным способом), объект сериализуется и больше не поддерживается Doctrine, когда впоследствии извлекается и не сериализуется.Попробуйте объединить сущность обратно в EntityManager.См. http://www.doctrine -project.org / docs / orm / 2.1 / en / reference / working-with-objects.html

...