Doctrine不会更新简单的数组类型字段

时间:2012-11-05 07:20:24

标签: php symfony doctrine doctrine-orm symfony-forms

短篇小说(编辑)

可以存储数组而不是映射的关联。在Symfony2中,使用collection Field Type相当容易。例如,使用此技术,您可以存储填充数组事件字段的文本字段数组。然而,为了更新数组,有一个技巧,@ Vadim Ashikhman在接受的答案中对这个技巧进行了精美的解释。

长篇故事

有时存储数组而不是映射关联是有用且高效的。但是,一旦创建,如果该数组的大小没有改变,更新此数组仍然很复杂吗?

许多人都有similar issue,但没有人找到解决此问题的正确方法。

Store an array

团队可以组织许多活动。这些事件只是使用Doctrine而不是使用OneToMany关联存储在数组中。因此,实体事件不与Doctrine映射。

实体事件(未与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;
    }


}

实体团队

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

}

2 个答案:

答案 0 :(得分:13)

你可以在这里找到答案:

How to force Doctrine to update array type fields?

参考文献:

  

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

答案 1 :(得分: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,
    );

}

可能有一种更好的方法可以做到这一点,我知道这不是一个很好的方法,但除非你(或其他人)找到解决方案,你可以使用它作为临时修复。