Метод репозитория Symfony 2 UniqueEntity не работает при обновлении объекта

Я работаю над каким-то простым скриптом, но не могу понять эту проблему. Итак, вот оно.

/**
 * Booking
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Tons\BookingBundle\Entity\BookingRepository")
 * @UniqueEntity(fields={"room", "since", "till"}, repositoryMethod="getInterferingRoomBookings")
 * @Assert\Callback(methods={"isSinceLessThanTill"}, groups={"search","default"})
 */
class Booking

и метод репозитория

/**
     * Get room state for a certain time period
     *
     * @param array $criteria
     * @return array
     */
    public function getInterferingRoomBookings(array $criteria)
    {
        /** @var $room Room */
        $room = $criteria['room'];
        $builder = $this->getQueryForRoomsBooked($criteria);
        $builder->andWhere("ira.room = :room")->setParameter("room", $room);
        return $builder->getQuery()->getArrayResult();
    }

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

Я пытался добавить ограничение по идентификатору, но при создании сущностей идентификатор равен нулю, поэтому метод репозитория даже не запускается. Также я попытался удалить Entity, а затем воссоздать его. как

$em->remove($entity);
$em->flush();
//-----------
$em->persist($entity);
$em->flush();

но это тоже не работает.

Создать действие

 /**
     * Creates a new Booking entity.
     *
     * @Route("/create", name="booking_create")
     * @Method("POST")
     * @Template("TonsBookingBundle:Booking:new.html.twig")
     */
    public function createAction(Request $request)
    {
        $entity = new Booking();
        $form = $this->createForm(new BookingType(), $entity);
        $form->bind($request);
        if ($form->isValid())
        {
            $em = $this->getDoctrine()->getManager();
            $room = $entity->getRoom();
            if($room->getLocked() && $room->getLockedBy()->getId() === $this->getUser()->getId())
            {
                $entity->setCreatedAt(new \DateTime());
                $entity->setUpdatedAt(new \DateTime());
                $entity->setManager($this->getUser());

                $em->persist($entity);
                $room->setLocked(false);
                $room->setLockedBy(null);
                $room->setLockedAt(null);
                $em->persist($room);
                $em->flush();
                return $this->redirect($this->generateUrl('booking_show', array('id' => $entity->getId())));
            }
            else
            {
                $form->addError(new FormError("Номер в текущий момент не заблокирован или заблокирован другим менеджером"));
                return array(
                    'entity' => $entity,
                    'form' => $form->createView(),
                );
            }
        }

        return array(
            'entity' => $entity,
            'form' => $form->createView(),
        );
    }

Обновить действие

 /**
     * Edits an existing Booking entity.
     *
     * @Route("/edit/{id}/save", name="booking_update")
     * @Method("PUT")
     * @Template("TonsBookingBundle:Booking:edit.html.twig")
     */
    public function updateAction(Request $request, $id)
    {
        /** @var $em EntityManager */
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('TonsBookingBundle:Booking')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Booking entity.');
        }

        $editForm = $this->createForm(new BookingType(), $entity);
        $editForm->bind($request);

        if ($editForm->isValid()) {
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('booking_edit', array('id' => $id)));
        }

        return array(
            'entity' => $entity,
            'form' => $editForm->createView(),
        );
    }

person farghost    schedule 22.04.2013    source источник
comment
Пожалуйста, опубликуйте контроллер createAction.   -  person Lighthart    schedule 22.04.2013
comment
@Lighthart добавил методы, но они стандартны для доктрины: сгенерировать: crud   -  person farghost    schedule 23.04.2013
comment
$room->setLockedAt(null); не хочет у меня работать, не знаю почему. Мое поле имеет значение DateTime, но допускает значение null, ввод ' ' или null дает мне ошибку: аргумент 1, переданный в AppBundle\Entity\User::setLockedAt(), должен быть экземпляром DateTime, заданным null,...   -  person nclsvh    schedule 09.12.2015


Ответы (3)


Я получил это! Я изменил аннотацию на это

/**
 * Booking
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Tons\BookingBundle\Entity\BookingRepository")
 * @UniqueEntity(fields={"id","room", "since", "till"}, repositoryMethod="getInterferingRoomBookings")
 * @UniqueEntity(fields={"room", "since", "till"}, repositoryMethod="getInterferingRoomBookings",groups={"create"})
 * @Assert\Callback(methods={"isSinceLessThanTill"}, groups={"search","default"})
 */
class Booking

Скопируйте BookingType в BookingTypeCreate и добавьте

 public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Tons\BookingBundle\Entity\Booking',
            'validation_groups' => array('create'),
        ));
    }

Для формирования значений по умолчанию. Итак, теперь параметры отличаются при передаче объекта в метод проверки. Я думаю, что это все еще метод обхода.

person farghost    schedule 25.04.2013

Краткий ответ: вы не получаете данные формы в объект, поэтому вы работаете с новым объектом, который не знает, что его комната была указана в форме.

Длинный ответ: получение данных формы и помещение их в сущность позволяет вам манипулировать данными перед обновлением. См. модифицированный контроллер ниже. Ключевая линия

$entity = form->getData(); Что позволяет вам $room=$entity->getRoom();

/**
 * Creates a new Booking entity.
 *
 * @Route("/create", name="booking_create")
 * @Method("POST")
 * @Template("TonsBookingBundle:Booking:new.html.twig")
 */
public function createAction(Request $request)
{
    $entity = new Booking();
    $form = $this->createForm(new BookingType(), $entity);
    $form->bind($request);
$entity = $form->getData();  // Lighthart's addition
    if ($form->isValid())
    {
        $em = $this->getDoctrine()->getManager();
        $room = $entity->getRoom();
        if($room->getLocked() && $room->getLockedBy()->getId() === $this->getUser()->getId())
        {
            $entity->setCreatedAt(new \DateTime());
            $entity->setUpdatedAt(new \DateTime());
            $entity->setManager($this->getUser());

            $em->persist($entity);
            $room->setLocked(false);
            $room->setLockedBy(null);
            $room->setLockedAt(null);
            $em->persist($room);
            $em->flush();
            return $this->redirect($this->generateUrl('booking_show', array('id' => $entity->getId())));
        }
        else
        {
            $form->addError(new FormError("Номер в текущий момент не заблокирован или заблокирован другим менеджером"));
            return array(
                'entity' => $entity,
                'form' => $form->createView(),
            );
        }
    }

    return array(
        'entity' => $entity,
        'form' => $form->createView(),
    );
}
person Lighthart    schedule 23.04.2013
comment
Нет, к сожалению, это не так. Вы говорите о методе создания, но он запускается только при создании нового объекта, метод редактирования вызывает updateAction(). Во всяком случае, я пробовал это, и это не сработало. Я полагаю, что bind($request) выполняет задание по заполнению данных формы в сущность. $room на самом деле получает правильное значение. В методе обновления вы получаете объект из базы данных, а затем заполняете его данными формы и при запуске проверки. uniqueEntity терпит неудачу, потому что метод возвращает ту же сущность, которая уже есть в базе данных. - person farghost; 23.04.2013
comment
По умолчанию в symfony новые вызовы создаются и редактируются, обновляются вызовы. В любом случае вам может понадобиться та же форма->getData(). - person Lighthart; 23.04.2013
comment
да, но $form-›getData() уже вызывается внутри $form-›bind(request); поэтому вам не нужен еще один getData. Сущность уже правильно отображена из формы. Проверка на самом деле терпит неудачу, потому что сущность не может быть сохранена, потому что сущность, которую я хочу сохранить, уже находится в базе данных. Вы знаете, потому что это одна и та же сущность. - person farghost; 24.04.2013
comment
Я не могу найти какой-либо вызов getData(), назначенный базовому объекту в form.php, поэтому я не уверен, что ваше утверждение о данных, записываемых в объект, может быть поддержано (на самом деле я прочитал код с совершенно другой интерпретацией) . На данный момент я не могу сказать, недовольны ли вы обновлением или созданием. Просьба уточнить. Мое следующее предположение состоит в том, что ваш тип формы немного глуповат, но я не могу знать, если это также не является кодом Symfony по умолчанию. - person Lighthart; 24.04.2013
comment
Типы форм по умолчанию, создание работает нормально, но обновление утверждает, что объект нарушает ограничение в функции uniqueEntity - getInterferingRoomBookings (массив $criteria). Которые возвращают массив бронирований для этой комнаты и где период бронирования пересекается. Таким образом, он не может обновить запись из-за нарушения ограничения, даже если это тот же объект, который нарушает его, и тот, который вы хотите обновить. - person farghost; 24.04.2013
comment
Вау, я очень плохо понял проблему. Почему бы вам просто не удалить комнату из массива, который возвращается при проверке? - person Lighthart; 24.04.2013
comment
Мне нужно это ограничение для защиты от перекрестных бронирований. Если я просто удалю бронирование из массива, эти ограничения бессмысленны. Мне нужно выяснить, является ли объект-нарушитель и редактируемый объект одним и тем же, но я действительно не могу понять это только путем проверки критериев. если я изменю критерии, чтобы они содержали идентификатор объекта, то проверка не срабатывает в методе создания, поэтому у меня будет большая проблема. - person farghost; 25.04.2013
comment
я думаю, что я понял это как-то. - person farghost; 25.04.2013
comment
Большой. Надеюсь, я лишь немного замедлил вас. - person Lighthart; 25.04.2013

Передайте дополнительный идентификатор (может быть token) в уникальное поле проверки (например, loginName) (fields={"token", "loginName"}) в метод хранилища. Сам id не работает, он равен нулю при создании объекта, а метод репозитория не выполняется. Я создаю token в методе конструктора. Маркер необходим для идентификации объекта при создании или обновлении.

/**
  * @ORM\Table(name="anbieterregistrierung")
  * @ORM\Entity(repositoryClass="AnbieterRegistrierungRepository")
  * @UniqueEntity(
  *      fields={"token", "loginName"},
  *      repositoryMethod="findUniqueEntity"
  * )
  */

затем в классе репозитория вы редактируете WHERE DQL:

public function findUniqueEntity(array $parameter)
    {
        $loginName = $parameter['loginName'];
        $token = $parameter['token'];
.
.
.
. . . WHERE anbieterregistrierung.loginName = :loginName AND anbieterregistrierung.token <> :token
.
.
.
}
person Master B.    schedule 17.06.2014