使用Symfony2添加的新实体多对一嵌入表单未保存

时间:2012-06-15 13:48:47

标签: symfony annotations one-to-many twig

这与我的earlier question about embedded forms有关。按照建议,我切换到一个twig模板,现在一切都按预期显示,添加新空表单的链接正常工作。问题是,当我尝试保存新记录时,它不起作用(尽管保存了对现有实体的编辑)。

有几点我可能出错了,所以我会随着问题提出问题。

这是一些背景:
研究可以有许多参与者(即研究实体与实体参与者具有OneToMany关系)。在数据库中,每个参与者记录都具有从“研究”列到“研究”表中记录的“study_id”列的外键链接,使其成为关系的拥有方。类中的注释应该反映这种关系。

学习班:

namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
* CRUK\MyBundle\Entity\Study
*
* @ORM\Table(name="study")
* @ORM\Entity
*/
class Study
{
    /**
    * @var integer $id
    *
    * @ORM\Column(name="study_id", type="integer", nullable=false)
    * @ORM\Id
    * @ORM\GeneratedValue(strategy="IDENTITY")
    */
    private $id;

    /**
     * @var string $studyName
     *
     * @ORM\Column(name="study_name", type="string", length=50, nullable=false)
    */
    private $studyName;

    /*
     * @ORM\OneToMany(targetEntity="Participant", mappedBy="study", cascade={"persist"})
     * 
     * @var ArrayCollection $participants
     */
     protected $participants;

     public function __construct() 
    {
        $this->participants = new ArrayCollection();
    }

    public function setParticipants(ArrayCollection $participants)
    {
        foreach($participants as $participant)  {
            $participant->setStudy($this);
        }
        $this->participants = $participants;
    }

    /**
     * @return ArrayCollection A Doctrine ArrayCollection
     */
    public function getParticipants()
    {
        return $this->participants;
    }
}

我的参与者课程:

namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
 * CRUK\SampleTrackingBundle\Entity\Participant
 *
 * @ORM\Table(name="participant")
 * @ORM\Entity
 */
 class Participant
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="participant_id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    ...

   /**
    * @var study
    *
    * @ORM\ManyToOne(targetEntity="Study", inversedBy="participants") 
    * @ORM\JoinColumn(name="study", referencedColumnName="study_id")
    * 
    */
    private $study;

    //setters and getters...
}

首先,这些注释是否正确? (我很确定我直接得到了整个拥有/反对多对一/一对多的关系,但我可能会弄错)

我的控制器:

Class StudyController extends Controller
{
    ...
    public function addParticipantsAction($id)
    {
        $em = $this->getDoctrine()->getEntityManager();

        $entity = $em->getRepository('MyBundle:Study')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Study id='.$id);
        }
        $participantArray = $em->getRepository('MyBundle:Participant')->findByStudy($id);
        //this is supposed to return a Doctrine ArrayCollection, but for some reason, returns an array
        // This needs to be converted to an ArrayCollection
        $participants = new ArrayCollection();
        foreach ($participantArray as $participant) {
           $participants->add($participant);
        }
        $entity->setParticipants($participants);
        $form   = $this->createForm(new StudyType(), $entity);
        $request = $this->getRequest();

        if ('POST' === $request->getMethod()) {
            $form->bindRequest($request);
            if ($form->isValid()) {
                $em->persist($entity);
                $em->flush();
            }
        }

        return $this->render('MyBundle:Study:addParticipants.html.twig', array(
            'form' => $form->createView(),
            'entity' => $entity
        ));
    }
...
}   

此时我不得不问为什么有必要明确地获取参与者的集合并使用它来设置研究实体的集合?在我添加该代码之前,$ entity-> getParticipants()将返回null(即使我知道有几个参与者为研究设置了外键)。我还有另外两个以多对多关系表的表,其中集合似乎只是通过在实体类中使用正确的注释而自动出现。这是多对多映射与多对一映射之间的区别,还是我以某种方式弄乱了注释?

我不确定其余的代码是否会有所帮助,但这里还有更多: 我的学习班级:

class StudyType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('studyName', null, array('label'=> 'Study Name:'))
            ->add('participants', 'collection', array(
                'type'=> new ParticipantType(), 
                'allow_add'=>true,
                'by_reference'=>false
              ));
    }

    public function getName()
    {
        return 'study';
    }

    public function getDefaultOptions(array $options) 
    {
        return array(
            'data_class' => 'MyBundle\Entity\Study',
        );
    }
}

我的嵌入式表单类:

class ParticipantType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
    $builder
        ->add('participantId','text', array('label'=>'Participant ID'))

        ));
    }

    public function getName()
    {
        return 'participant';
    }

    public function getDefaultOptions(array $options) 
    {
      return array(
          'data_class' => 'MyBundle\Entity\Participant',
      );
    }
}    

我的模板:

{% extends 'MyBundle::base.html.twig' %}

{% block body%}
  <form action="{{ path('study_addparticipants', { 'id': entity.id }) }}" method="POST" {{ form_enctype(form) }}>
    <!-- renders global errors -->
    {{ form_errors(form)  }}
    <h2>Study</h2>
    {{ form_label(form.studyName) }}
    {{ form_errors(form.studyName) }}
    {{ form_widget(form.studyName) }}

    <h3>Participants in this study</h3>
    <ul class="participants" data-prototype="{{ form_widget(form.participants.get('prototype')) | e }}">
        {% for participant in form.participants %}
            <li>{{form_row(participant) }}</li>
        {% endfor %}
    </ul>
    {{ form_rest(form) }}
    <button type="submit">Save Changes</button>
  </form> 
{% endblock%}

{% block javascripts %}
   {# parent block includes jQuery #}
   {{ parent() }}

  <script type='text/javascript'>
      jQuery(document).ready(function() {
        // keep track of how many participant fields have been rendered
        var collectionHolder = $('ul.participants');
        var $addLink = $('<a href="#" class="add_participant_link">Add new Participant</a>');
        var $newLinkLi = $('<li></li>'). append($addLink);
        collectionHolder.append($newLinkLi);
        $addLink.on('click', function(e) {
           e.preventDefault();
           addParticipantForm(collectionHolder, $newLinkLi);
        });
      });

      function addParticipantForm(collectionHolder, $newLinkLi) {
         // Get the data-prototype we explained earlier
        var prototype = collectionHolder.attr('data-prototype');

       // Replace '$$name$$' in the prototype's HTML to
       // instead be a number based on the current collection's length.
       var newForm = prototype.replace(/\$\$name\$\$/g, collectionHolder.children().length);

      // Display the form in the page in an li, before the "Add a tag" link li
      var $newFormLi = $('<li></li>').append(newForm);
      $newLinkLi.before($newFormLi);
    }
  </script>

{% endblock %}

因此,表单显示正确,当我单击“添加新参与者”链接时,会附加一个空的参与者表单。保存对研究和现有参与者记录的更改。没有错误,但没有保存任何新的参与者。

我已经阅读了许多与此相似的问题,据我所知,纳入了应该使这项工作的所有内容。我显然错过了一些东西,所以对于如何正确使用这些建议表示赞赏。

非常感谢。

1 个答案:

答案 0 :(得分:2)

感谢@Luke的建议。我通过循环遍历我的研究对象的pariticpants集合并在控制器中单独保存每个问题来解决问题。新的控制器代码:

...
    if ('POST' === $request->getMethod()) {
        $form->bindRequest($request);
        if ($form->isValid()) {
            $em->persist($entity);
            foreach($entity->getParticipants() as $participant) {
              $em->persist($participant);
            }
            // flush once to commit all entities
            $em->flush();
        }
   }
...