これは、埋め込みフォームに関する私の以前の質問に関連しています。アドバイスどおり、小枝テンプレートに切り替えたところ、すべてが期待どおりに表示され、新しい空のフォームを追加するためのリンクが正しく機能しています。問題は、新しいレコードを保存しようとしても機能しないことです (ただし、既存のエンティティへの編集は保存されます)。
間違っているかもしれない箇所がいくつかありますので、質問しながら進めていきます。
背景をいくつか
紹介します。調査には多数の参加者を含めることができます (つまり、調査エンティティは、エンティティ参加者と OneToMany の関係を持ちます)。データベースでは、各 Participant レコードには、Study テーブル内のレコードの列 "study" から "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...
}
まず、これらの注釈は正しいですか? (所有/逆多対1/1対多の関係全体を頭の中でまっすぐに理解していると確信していますが、間違っている可能性があります)
私のコントローラー:
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 を返していました (研究用に外部キーが設定された複数の参加者がいることがわかっている場合でも)。エンティティ クラスに正しい注釈を付けるだけで、コレクションが自動的に表示されるように見える、多対多の関係にある他の 2 つのテーブルがあります。これは、多対多のマッピングと多対一のマッピングの違いですか、それとも何らかの形で注釈を台無しにしましたか?
コードの残りの部分が役立つかどうかはわかりませんが、さらにいくつかのコードを示します: My study form class:
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 %}
そのため、フォームは正しく表示され、[新しい参加者を追加] リンクをクリックすると、空の参加者フォームが追加されます。スタディおよび既存の参加者レコードへの変更が保存されます。エラーはありませんが、新しい参加者は保存されません。
私はこれに似た質問をたくさん読みましたが、私が知る限り、これを機能させるすべてのものを組み込んでいます。私は明らかに何かを見逃しているので、これを正しくする方法についての提案をいただければ幸いです。
どうもありがとう。