Подтвердить что ты не робот

Doctrine не обновляет поле типа простого массива

Краткая история (Edit)

Можно сохранить массив вместо сопоставленной сопоставления. В Symfony2 это довольно легко, используя тип поля коллекции. Например, используя этот метод, вы можете сохранить массив текстовых полей, которые заполняют поле событий массива. Однако, чтобы обновить массив, есть трюк, и этот трюк красиво объясняется @Vadim Ashikhman в принятом ответе.

Длинная история

Иногда полезно и более эффективно хранить массив вместо сопоставленной сопоставления. Однако, после создания, остается сложным обновить этот массив, если размер этого массива не изменяется?

У многих людей есть аналогичная проблема, но никто не нашел правильного решения этой проблемы.

Store an array

Команда может организовать множество событий. Эти события просто хранятся в массиве с использованием Doctrine вместо использования ассоциации OneToMany. Следовательно, сущность Событие не отображается с Доктриной.

Событие сущности (не привязано к Doctrine)

<?php

namespace Acme\TestBundle\Entity;

...

class Event
{

    /**
     * @Assert\NotBlank
     */
    private $name;

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }


}

Entity Team

<?php

namespace Acme\TestBundle\Entity;

...

/**
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 * @ORM\Table(name="teams")  
 */
class Team 
{

/**
     * @ORM\Column(type="array")
     * @var array
     */
    protected $events;


    public function addEvent($event)
    {
        if (!in_array($event, $this->events, true)) {
            $this->events[] = $event;
        }

        return $this;
    }

    public function removeEvent($event)
    {
        if (false !== $key = array_search($event, $this->events, true)) {
            unset($this->events[$key]);
            $this->events = array_values($this->events);
        }

        return $this;
    }

    public function getEvents()
    {
        return $this->events;
    }

    public function setEvents(array $events)
    {
        $this->events = array();

        foreach ($events as $event) {
            $this->addEvent($event);
        }

        return $this;
    }

Форма события

<?php
namespace Acme\TestBundle\Form\Type;

...

class EventType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        $builder->add('name', 'text');
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\TestBundle\Entity\Event',
            'cascade_validation' => true,
        ));
    }

    ...
}

Форма команды

<?php

namespace Acme\TestBundle\Form\Type;

...

class TeamType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        $builder->add('events','collection', array(
            'type' => new EventType(),
            'allow_add'   => true,
            'allow_delete' => true,
            'prototype' => true,
            'by_reference' => false,
            'options' => array('data_class' => 'Acme\TestBundle\Entity\Event'),
            )
        );

    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\TestBundle\Entity\Team',
        ));
    }
    ...

}

Контроллер

/**
 * Update a team
 *
 * @Route("update/{team_id}", name="updateTeamFromId")
 * @Template("AcmeTestBundle:Team:teamUpdate.html.twig")
 */
public function updateTeamAction($team_id, Request $request)
{

    $em = $this->getDoctrine()->getManager();

    $repository= $em->getRepository('AcmeTestBundle:Team');

    $team_to_update = $repository->find($team_id);

    $form = $this->createForm(new teamType(), $team_to_update);

    if ($request->getMethod() == 'POST')
    {
        $form->bind($request);

        if ($form->isValid()){

            $em->persist($team_to_update);
            $em->flush();

            return $this->redirect($this->generateUrl('homepage'))  ;
        }
    }

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

}
4b9b3361

Ответ 1

Здесь вы можете найти ответ:

Как заставить Doctrine обновить поля типа массива?

работ:

В Doctrine используется идентичный оператор === для сравнения изменений между старыми и новые значения. Оператор, используемый на одном объекте с разные данные всегда возвращают true. Существует другой способ решить эта проблема, вы можете клонировать объект, который необходимо изменить.

$items = $myEntityObject->getItems(); 
$items[0] = clone $items[0];
$items[0]->setSomething(123); 
$myEntityObject->setItems($items);

Или измените метод setItems()

public function setItems($items) 
{
    if (!empty($items) && $items === $this->items) {
        reset($items);
        $key = key($items);
        $items[$key] = clone $items[$key];
    }
    $this->items = $items; 
}

Ответ 2

Вы можете попробовать это внутри контроллера:

public function updateTeamAction($team_id, Request $request)
{

    $em = $this->getDoctrine()->getManager();

    $repository= $em->getRepository('AcmeTestBundle:Team');

    $team_to_update = $repository->find($team_id);

    $form = $this->createForm(new teamType(), $team_to_update);

    if ($request->getMethod() == 'POST')
    {
        $form->bind($request);

        if ($form->isValid()){
            $events = $team_to_update->getEvents();
            foreach($events as $key => $value){
                $team_to_update->removeEvent($key);
            }
            $em->flush();
            $team_to_update->setEvents($events);
            $em->persist($team_to_update);
            $em->flush();

            return $this->redirect($this->generateUrl('homepage'))  ;
        }
    }

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

}

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