Buy
Buy

Now that we've added the yearsStudied field to each GenusScientist, I'm not too sure that checkboxes make sense anymore. I mean, if I want to show that a User studies a Genus, I need to select a User, but I also need to tell the system how many years they have studied. How should this form look now?

Here's an idea, and one that works really well the form system: embed a collection of GenusScientist subforms at the bottom, one for each user that studies this Genus. Each subform will have a User drop-down and a "Years Studied" text box. We'll even add the ability to add or delete subforms via JavaScript, so that we can add or delete GenusScientist rows.

Creating the Embedded Sub-Form

Step one: we need to build a form class that represents just that little embedded GenusScientist form. Inside your Form directory, I'll press Command+N - but you can also right-click and go to "New" - and select "Form". Call it GenusScientistEmbeddedForm. Bah, remove that getName() method - that's not needed in modern versions of Symfony:

... lines 1 - 2
namespace AppBundle\Form;
... lines 4 - 8
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GenusScientistEmbeddedForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
... lines 17 - 26
}
public function configureOptions(OptionsResolver $resolver)
{
... lines 31 - 33
}
}

Yay!

In configureOptions(), add $resolver->setDefaults() with the classic data_class set to GenusScientist::class:

... lines 1 - 4
use AppBundle\Entity\GenusScientist;
... lines 6 - 12
class GenusScientistEmbeddedForm extends AbstractType
{
... lines 15 - 28
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => GenusScientist::class
]);
}
... lines 35 - 36
}

We will ultimately embed this form into our main genus form... but at this point... you can't tell: this form looks exactly like any other. And it will ultimately give us a GenusScientist object.

For the fields, we need two: user and yearsStudied:

... lines 1 - 12
class GenusScientistEmbeddedForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', EntityType::class, [
... lines 19 - 23
])
->add('yearsStudied')
;
}
... lines 28 - 36
}

We do not need a genus dropdown field: instead, we'll automatically set that property to whatever Genus we're editing right now.

The user field should be an EntityType dropdown. In fact, let's go to GenusFormType and steal the options from the genusScientists field - it'll be almost identical. Set this to EntityType::class and then paste the options:

... lines 1 - 5
use AppBundle\Entity\User;
use AppBundle\Repository\UserRepository;
... lines 8 - 12
class GenusScientistEmbeddedForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', EntityType::class, [
'class' => User::class,
'choice_label' => 'email',
'query_builder' => function(UserRepository $repo) {
return $repo->createIsScientistQueryBuilder();
}
])
... line 25
;
}
... lines 28 - 36
}

And make sure you re-type the last r in User and auto-complete it to get the use statement on top. Do the same for UserRepository. The only thing that's different is that this will be a drop-down for just one User, so remove the multiple and expanded options.

Embedding Using CollectionType

This form is now perfect. Time to embed! Remember, our goal is still to modify the genusScientists property on Genus, so our form field will still be called genusScientists. But clear out all of the options and set the type to CollectionType::class. Set its entry_type option to GenusScientistEmbeddedForm::class:

... lines 1 - 11
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
... lines 13 - 18
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
... lines 24 - 46
->add('genusScientists', CollectionType::class, [
'entry_type' => GenusScientistEmbeddedForm::class
])
;
}
... lines 52 - 58
}

Before we talk about this, let's see what it looks like! Refresh!

Woh! This Genus is related to four GenusScientists... which you can see because it built an embedded form for each one! Awesome! Well, it's mostly ugly right now, but it works, and it's free!

Try updating one, like 26 to 27 and hit Save. It even saves!

Rendering the Collection... Better

But let's clean this up - because the form looks awful... even by my standards.

Open the template: app/Resources/views/admin/genus/_form.html.twig:

{{ form_start(genusForm) }}
... lines 2 - 21
{{ form_row(genusForm.genusScientists) }}
... lines 23 - 24
{{ form_end(genusForm) }}

This genusScientists field is not and actual field anymore: it's an array of fields. In fact, each of those field is itself composed of more sub-fields. What we have is a fairly complex form tree, which is something we talked about in our Form Theming Tutorial.

To render this in a more controlled way, delete the form_row. Then, add an h3 called "Scientists", a Bootstrap row, and then loop over the fields with for genusScientistForm in genusForm.genusScientists:

{{ form_start(genusForm) }}
... lines 2 - 22
<h3>Scientists</h3>
<div class="row">
{% for genusScientistForm in genusForm.genusScientists %}
... lines 26 - 28
{% endfor %}
</div>
... lines 31 - 32
{{ form_end(genusForm) }}

Yep, we're looping over each of those four embedded forms.

Add a column, and then call form_row(genusScientistForm) to print both the user and yearsStudied fields at once:

{{ form_start(genusForm) }}
... lines 2 - 22
<h3>Scientists</h3>
<div class="row">
{% for genusScientistForm in genusForm.genusScientists %}
<div class="col-xs-4">
{{ form_row(genusScientistForm) }}
</div>
{% endfor %}
</div>
... lines 31 - 32
{{ form_end(genusForm) }}

So this should render the same thing as before, but with a bit more styling. Refresh! Ok, it's better... but what's up with those zero, one, two, three labels?

This genusScientistForm is actually an entire form full of several fields. So, it prints out a label for the entire form... which is zero, one, two, three, and four. That's not helpful!

Instead, print each field by hand. Start with form_errors(genusScientistForm), just in case there are any validation errors that are attached at this form level:

{{ form_start(genusForm) }}
... lines 2 - 22
<h3>Scientists</h3>
<div class="row">
{% for genusScientistForm in genusForm.genusScientists %}
<div class="col-xs-4">
{{ form_errors(genusScientistForm) }}
... lines 28 - 29
</div>
{% endfor %}
</div>
... lines 33 - 34
{{ form_end(genusForm) }}

It's not common, but possible. Then, simply print form_row(genusScientistForm.user) and form_row(genusScientistForm.yearsStudied):

{{ form_start(genusForm) }}
... lines 2 - 22
<h3>Scientists</h3>
<div class="row">
{% for genusScientistForm in genusForm.genusScientists %}
<div class="col-xs-4">
{{ form_errors(genusScientistForm) }}
{{ form_row(genusScientistForm.user) }}
{{ form_row(genusScientistForm.yearsStudied) }}
</div>
{% endfor %}
</div>
... lines 33 - 34
{{ form_end(genusForm) }}

Try it! Much better!

But you know what we can't do yet? We can't actually remove - or add - new scientists. all we can do is edit the existing ones. That's silly! So let's fix it!

Leave a comment!

  • 2018-05-08 Victor Bocharsky

    Hey Dan,

    OK, then you need a clever way to persist those objects. For example, when you iterate over those answers to persist() them, before persisting try to check if some fields to make sure we have some data in them, i.e. you probably need to call persist() manually and do not call this method on empty answers. This should do the trick.

    Cheers!

  • 2018-05-07 Dan Costinel

    Hi Victor,

    Yes, it worked, but the problem was that all 6 Answers entities are saved, the NULL ones too.

    Cheers!

  • 2018-05-01 Dan Costinel

    Hi Victor,

    Ok, thanks for the info. I'll try your suggestions on the test project, and if I'll manage to do it, I'll let you know.

    Have a good day!

  • 2018-05-01 Victor Bocharsky

    Hey Dan,

    So, for new entities you need manually to call persist on each new entity unless you have cascade={"persist"}. Your case is a bit unusual, so let's call persist() on each entity manually - it means you need to persist($question) *and* also iterate over all your answers and call persist() on each of them.

    I think you confuse Doctrine with it, Doctrine works with ArrayCollection if we're talking about OneToMany relation. So, I think you don't need OneToMany but OneToOne relation instead, where you'll add OneToOne annotation for each $answer* field. You can keep getAnswers() if you need all those objects at once, but it should be not OneToMany anymore because for each entity you declare personal field. And then you'll need a setter and getter for each $answer* field as well.

    Cheers!

  • 2018-04-30 Dan Costinel

    1. I can't use form_row, because as you said, I use extra HTML for those fields, and I simplified the post code.
    2. Why exactly 6? So, a question can have up to 6 possible options from which the user can select (one or more being correct). This doesn't mean the admin will fill in all the 6 answers. He can fill in at least 2, and then he can continue filling the 3rd, 4th, 5th, or 6th, depending on the needs. And no, this number won't change with the time. Will remain constant.
    3. Before the need to use embed forms, I set up a relation, OneToMany, between Question and Answer, because a question can have up to 6 answers, as I said above. So I already have that `$question->getAnswers()` method.

    Ok, but let's forget the above code, because I have the feeling it's a bit messy, with a lot of details that doesn't belong to the issue.

    I've created a separate project, just to test the functionality. So, here's a more lighter example:


    ### Entity/Answer.php
    class Answer
    {
    /**
    * @ORM\Column(name="answer", type="text")
    */
    public $answer;

    /**
    * @ORM\Column(name="is_correct", type="boolean")
    */
    public $isCorrect;

    # getters & setters for the two properties
    }


    ### Entity/Question.php
    class Question
    {
    /**
    * @ORM\Column(type="text")
    */
    private $content;

    /**
    * @ORM\Column(type="text")
    */
    private $explication;

    /**
    *@Assert\Type(type="AppBundle\Entity\Answer")
    */
    private $answer1;

    /**
    *@Assert\Type(type="AppBundle\Entity\Answer")
    */
    private $answer2;

    # getters and setters for content and explication

    public function getAnswer1()
    {
    return $this->answer1;
    }

    public function setAnswer(Answer $answer = null)
    {
    $this->answer1 = $answer;

    return $this;
    }

    # same for $this->answer2
    }


    ### Form/AnswerType.php
    $builder
    ->add('answer', CKEditorType::class)
    ->add('isCorrect', CheckboxType::class)
    ;


    ### Form/QuestionType.php
    $builder
    ->add('content', CKEditorType::class)
    ->add('explication', CKEditorType::class)
    ->add('answer1', AnswerType::class)
    ->add('answer2', AnswerType::class)
    ;


    ### Controller/QuestionController.php
    /**
    * Creates a new question entity.
    *
    * @Route("/new", name="question_new")
    * @Method({"GET", "POST"})
    */
    public function newAction(Request $request)
    {
    $question = new Question();
    $form = $this->createForm('AppBundle\Form\QuestionType', $question);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
    $em = $this->getDoctrine()->getManager();
    $em->persist($question);
    $em->flush();

    return $this->redirectToRoute('question_show', array('id' => $question->getId()));
    }

    return $this->render('question/new.html.twig', array(
    'question' => $question,
    'form' => $form->createView(),
    ));
    }

    /**
    * Displays a form to edit an existing question entity.
    *
    * @Route("/{id}/edit", name="question_edit")
    * @Method({"GET", "POST"})
    */
    public function editAction(Request $request, Question $question)
    {
    $deleteForm = $this->createDeleteForm($question);
    $editForm = $this->createForm('AppBundle\Form\QuestionType', $question);
    $editForm->handleRequest($request);

    if ($editForm->isSubmitted() && $editForm->isValid()) {
    $this->getDoctrine()->getManager()->flush();

    return $this->redirectToRoute('question_edit', array('id' => $question->getId()));
    }

    return $this->render('question/edit.html.twig', array(
    'question' => $question,
    'edit_form' => $editForm->createView(),
    'delete_form' => $deleteForm->createView(),
    ));
    }

    Question:
    When creating a new Question, do I need to manually save the Answers all by myself, or Symfony will handle that automatically? In the official docs, they say: `The Category instance is accessible naturally via $task->getCategory() and can be persisted to the database or used however you need.`

    Now, when accessing /question/new and filling in the fields, the question entity is saved, but not the Answers. Why? And when dumping the $question, in createAction: https://imgur.com/a/ABp2Cw4 ; the data is there, but somehow, it is not saved into answers table.

  • 2018-04-30 Victor Bocharsky

    Hey Dan,

    Sorry for delay. First of all, I'd say you can simplify your form with {{ form_row(edit_form. answer1) }}, etc. i.e. use form_row which will render both answer and isCorrect field. Or, actually, you can go further and probably replace all the fields with just {{ form_widget(form) }}. But probably you use some extra HTML markup that you just removed for simplicity in your comment, then it's OK ;) . Oh, and I'd say it looks a bit invalid because you have 6 properties for answers. Why exactly 6? Do you need some more or less in the future? Just think about how permanent this number is. Probably, if this number should be flexible - then you need to refactor your code with collection, i.e. you need only one property called $answers which is Doctrine's ArrayCollection object, and this way you can make a different flexible number of answers for each question.

    But that's just a few tips to you for the future, it does not relates too much to your problem. I see you have AnswerType, but I don't see you use it somewhere. It should be something like:


    ### AppBundle/Form/QuestionType.php
    $builder
    ->add('content', CKEditorType::class)
    ->add('explication', CKEditorType::class)
    ->add('answer1', AnswerType::class)
    ->add('answer2', AnswerType::class)
    ->add('answer3', AnswerType::class)
    ->add('answer4', AnswerType::class)
    ->add('answer5', AnswerType::class)
    ->add('answer6', AnswerType::class)
    ;

    This way exactly AnswerType will be used for all the answers, so if in the future you add new properties to the Answer entity - they won't be rendered in this QuestionType form automatically unless you add them to the AnswerType explicitly.

    And finally, the most important I think, is that you need to be sure when you submit this form in create action - all data are written to the DB correctly, it means, that you have a new question row in the DB with sent data and have 6 answer rows as well in the proper table with the data you input in the form. But please, look closer into DB, make sure your answer1_id, answer2_id, etc. are not NULL in the "questions" table, because I think you are loosing these relations somewhere. Please, let me know whether those answer IDs in the "questions" table are NULL or no.

    Cheers!

  • 2018-04-27 Dan Costinel

    Hey Victor,
    Sure thing!
    Here it is:


    ### AppBundle/Entity/Answer.php
    ...
    private $answer;
    private $isCorrect;
    ...




    ### AppBundle/Form/AnswerType.php
    $builder
    ->add('answer', CKEditorType::class)
    ->add('isCorrect', CheckboxType::class)
    ;




    ### AppBundle/Form/QuestionType.php
    $builder
    ->add('content', CKEditorType::class)
    ->add('explication', CKEditorType::class)
    ->add('answer1')
    ->add('answer2')
    ->add('answer3')
    ->add('answer4')
    ->add('answer5')
    ->add('answer6')
    ;




    ### AppBundle/Entity/Question.php
    /**
    * @Assert\Type(type="AppBundle\Entity\Answer")
    */
    private $answer1;

    #...

    /**
    * @Assert\Type(type="AppBundle\Entity\Answer")
    */
    private $answer6;






    Then, in form:
    ### question/edit.html.twig (this is also same as new.html.twig)
    {{ form_start(edit_form) }}
    {{ form_widget(edit_form.answer1.answer) }}
    {{ form_widget(edit_form.answer1.isCorrect) }}

    ....

    {{ form_widget(edit_form.answer6.answer) }}
    {{ form_widget(edit_form.answer6.isCorrect) }}
    {{ form_end(edit_form) }}

  • 2018-04-27 Victor Bocharsky

    Hey Dan,

    Ah, OK, your form type is not complete probably. Do you use CollectionType there for answers? Could you show your form type?

    Cheers!

  • 2018-04-26 Dan Costinel

    Hi Victor,

    Yes, I have a relation between Question (1) and Answers (N). The code for creating a new Question (as well as Answers):



    if ($form->isSubmitted() && $form->isValid()) {
    $em = $this->getDoctrine()->getManager();
    $answers = $request->get('answers');
    $isCorrect = (int)$request->get('correct');
    $question->setQuiz($quiz); #$quiz being defined before

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

    foreach ($answers as $key => $val) {
    $answer = new Answer();
    $answer->setAnswer($val); # in the answers table, I have an answer column too, which is the #Answer1 through Answer6 in the Question Entity in the image above
    $answer->setQuestion($question);
    $answer->setIsCorrect($isCorrect === $key);

    $em->persist($answer);
    }
    $em->flush();

    $this->addFlash('success', 'Question successfully created');

    return $this->redirectToRoute('question_index');
    }

    And yes, when I save a new Question, I have the Answers too properly saved into the answers table.

  • 2018-04-26 Victor Bocharsky

    Hey Dan,

    OK, it makes sense you don't see answer fields in your form, because your answer fields are empty, i.e. equal null. Don't you have configured relations between Question and Answer entities? I mean, your first task is to properly store it in DB, I bet your answer keys in question entity you dumped are NULL in db too, that's why you don't see them in the form. Probably you lost them when inserting into db, double check your create action. Do you call persist() for each Answer entity in create action?

    Cheers!

  • 2018-04-25 Dan Costinel

    Unfortunately, it doesn't work.
    Here's the code for the editAction:


    /**
    * Displays a form to edit an existing question entity.
    *
    * @param Request $request
    * @param Question $question
    * @return Response
    * @Route("/{id}/edit", name="question_edit")
    * @Method({"GET", "POST"})
    */
    public function editAction(Request $request, Question $question)
    {
    $editForm = $this->createForm('AppBundle\Form\QuestionType', $question);
    $editForm->handleRequest($request);

    if ($editForm->isSubmitted() && $editForm->isValid()) {
    $this->getDoctrine()->getManager()->flush();
    }

    return $this->render('question/edit.html.twig', array(
    'question' => $question,
    'edit_form' => $editForm->createView(),
    ));
    }

    When I access the edit url, I get the form, but the answers are not being pre-populated with the data from Answers table (just the Question data is set in edit form fields): https://imgur.com/a/upJy4ut

    If I die $question, after ->handleRequest($request) line, here's what I get: https://imgur.com/a/4dGqqe9 . I'm not sure, but I guess those #answer1 through #answer6, should contain the data saved in Answers table when the Question was initially created.

    If I die $question after $editForm->isSubmitted() && $editForm->isValid(), and I pass some data into some of the Answers fields, I get: https://imgur.com/a/Ua93LDY

  • 2018-04-25 Victor Bocharsky

    Hey Dan,

    Great, then it should work I think. Or let me know if you have any issues with it ;)

    Cheers!

  • 2018-04-25 Dan Costinel

    Hi Victor,
    Thanks again for replying.
    Yes, I'm using one single Question form.
    I'll try your code as soon as I get to my PC, and I'll let you know if it worked or not.
    Cheers!

  • 2018-04-25 Victor Bocharsky

    Hey Dan,

    Glad you solved it by yourself! Do you solve it in a one complex form? Or do you create a several forms for it? Actually, if you need for editing the same form as you used for inserting - just reuse that form, but now you need to pass already existent data to the form, for example, in a controller:


    $question = $this->getDoctrine()
    ->getEntityManager()
    ->getRepository(Question::class)
    ->find($id);
    $form = $this->createForm(YourQuestionForm::class, $question);

    // Then just pass form view to the template and render it.

    As you can see, I pass data as a second argument to createForm(), and this way Form component will render the same form but with actual data, i.e. fill all the fields with current values in DB, so you will be able to edit them and send edited form. Then just handle submitted form as you already to with inserting data, i.e. if form is valid - call flush() and that's it.

    Cheers!

  • 2018-04-24 Dan Costinel

    Hi Victor, thanks for replying.
    Sorry that I've forgot to post an actual question. Anyways, I've solved that one. Indeed you were right, I was able to solve it using forms.

    Now, I would like to be able to edit those kind of form fields (Answer fields inside Question form).
    Here's an image of what I get when submitting the edit form (nothing special in that form, just an usual CRUD Controller-view generated code for editing). https://imgur.com/a/yoc8kE9
    And this would be the question: how to edit a embedded form-collection field?

    At first point, for editing, in the editAction(), I need to somehow get each Answer entity that was previously saved on the Question creation, and attach them into the Question object that is being edited. So if the question_id is 1, and the answers ids are: 1 , 2, 3, 4, then I need to somehow pass the data saved in the answers, into the first four Answer objects added into the Question Entity. I've added each of those 6 Answer objects into the Question Entity like this:


    /**
    * @Assert\Type(type="AppBundle\Entity\Answer")
    */
    private $answer1;

    public function getAnswer1()
    {
    return $this->answer1;
    }

    public function setAnswer1(Answer $answer = null)
    {
    $this->answer1 = $answer;

    return $this;
    }

    I've managed to do that, even though the code is really ugly.

    So, returning to the problem, if we look at the image, we have that QuestionController.php on line 235: there is the edited data for the Question fields + Answer fields,
    Down under, at QuestionController.php on line 237, is the original Answers that were inserted when the Question was created.
    I need to somehow, add the edited Answer fields to those original Answers.
    Any solution? :)
    Thanks, and sorry for this long post.

  • 2018-04-24 Victor Bocharsky

    Hey Dan,

    At the first sight, I think you can do it with Symfony Forms. But what is your question exactly? I don't see a question at all. Have you tried to implement it? Do you have any errors/problems?

    Cheers!

  • 2018-04-23 Dan Costinel

    Hi. I want to be able to display such a form: https://imgur.com/a/NsDQISz .

    In short, it's about a Quiz project, and at this point, I need to let the admin create a new Question, as well as the Answers for the question in the same template.
    For that I have two tables, Question 1-N Answers related:
    -- Question [id, question_text, explication, questionNo, answers]
    -- Answer [ answer_text, isCorrect, question ].
    One problem, is that the admin can define for a question, a variable number of answers. So it can define 'till six answers (in the image I've shown just 3 for simplicity).
    A second problem, is that the answers needs to be a CKEditor, so that the admin can easily apply styles to the text.

    I've followed the docs from http://symfony.com/doc/3.4/....

    I expect the html to be like: https://gist.github.com/dan...

    And then maybe to get beck, after the form is submitted, something like (and let's say the admin adds 3 answers for a question):


    QuestionNo: 1
    Quiz: object
    Question Text: whatever is entered in the editor
    Answer 1 [
    answer_text: whatever is entered in the editor
    isCorrect: 1 or 0, depending if the checkbox next to Answer 1 label is checked or not
    ]
    Answer 2 [
    answer_text: whatever is entered in the editor
    isCorrect: 1 or 0, depending if the checkbox next to Answer 2 label is checked or not
    ]
    Answer 3 [
    answer_text: whatever is entered in the editor
    isCorrect: 1 or 0, depending if the checkbox next to Answer 3 label is checked or not
    ]

    And then to be able to save:
    - in questions table, the QuestionNo, quiz_id, Question text, explication
    - in answers table, the answer_text, isCorrect, and question_id

    Thanks a lot for any suggestion!

  • 2017-11-07 Diego Aguiar

    Ohh, I got you

    There is a way to handle which records to show for a Entity/Collection Type field. Look at this example in the documentation https://symfony.com/doc/cur...
    I believe it's pretty straight forward, but, if you have doubts, let us know!

    Cheers!

  • 2017-11-07 Bettinz

    Hello Diego Aguiar , this is the situation: I've an Entity called Classroom and one called Student, and I've a OneToMany relationship between them.
    Now what I want to do is to provide to a teacher the ability to manage all male and female students of a classroom and order the form alphabetically.
    So I've created a ClassRoomType Form with one field called students (it's a CollectionType) and the entry_type is StudentType.
    The form StudentType include name, age, sex, etc.
    What I want now is to show only male (or female) students and order them by name.
    I haven't find anything like a query builder for collectionType, so I can't understand the way to go. Thanks

  • 2017-11-06 Diego Aguiar

    Hey Bettinz

    I'm not sure if I understood correctly to your question. What would be your inputs and how would like your end result?

    Cheers!

  • 2017-11-06 Bettinz

    Hello, is it possible to filer and order an Entity passed via Collection Type? :-) like a query builder inside the Collection Type.
    I need to order and filter student from one class but I also need to show the Collection Type because I've multiple students and a form to manage them :-)

  • 2017-01-29 weaverryan

    Yo ehymel!

    I think it should look something like this:


    {% for genusScientistForm in genusForm.genusScientists %}
    <div class="col-xs-4 js-genus-scientist-item">
    {# you don't have individual fields to render, you just have the ONE field to render #}
    {{ form_errors(genusScientistForm) }}
    {{ form_label(genusScientistForm, 'Genus Scientist') }}
    {{ form_widget(genusScientistForm) }}
    </div>
    {% endfor %}

    In the tutorial, genusScientistForm is really an embedded form, with sub-parts as you mentioned. But now, genusScientistForm is literally just a single field (EntityType)... without any sub-fields. So, you just need to render it like any other single field :). One simpler option might be something like this:


    {% for genusScientistForm in genusForm.genusScientists %}
    <div class="col-xs-4 js-genus-scientist-item">
    {{ form_row(genusScientistForm, {
    label: 'Genus Scientist')
    } }}
    </div>
    {% endfor %}

    Not tested - but give it a try :).

    Cheers!

  • 2017-01-29 ehymel

    I really really appreciate you helping work this out!!

    It looks like the only problem with your proposed solution is that I don't seem to have access to the sub-parts of the added EntityType when rendering the form. Specifically if I try to get rid of the automatically generated labels ("0", "1", "2", etc) in the way you did it in the lesson, it fails, I assume because twig doesn't now how to get to it:


    {% for genusScientistForm in genusForm.genusScientists %}
    <div class="col-xs-4 js-genus-scientist-item">
    {{ form_errors(genusScientistForm) }}
    {{ form_row(genusScientistForm.user) }} <------------------- this line fails
    </div>
    {% endfor %}

    Thoughts?

  • 2017-01-29 ehymel

    Genius! That works perfectly. Thanks very much.

    Glad to hear you had to think about it for a while :)

  • 2017-01-25 weaverryan

    Hi ehymel!

    Sorry for my slow reply - I needed to wait until I have a few minutes to really think about this :). So, the goal would be to have a collection of embedded forms (with the ability to add a new one, remove existing ones, etc), but each embedded form will have only the single, Scientist drop down. Is that correct?

    So, here's how to do it :). You will STILL have a CollectionType just like before. The only difference is that the "entry_type" (i.e. the form that's used in the collection) *won't* be an entirely different form class (e.g. GenusScientistEmbeddedForm), it will simply be the EntityType field. Something like this:


    $builder
    ->add('genusScientists', CollectionType::class, [
    'entry_type' => EntityType::class,
    'entry_options' => [
    'class' => User::class,
    'choice_label' => 'email'
    ],
    'allow_delete' => true,
    'allow_add' => true,
    ])

    ... and that's it! In your template, rendering is simpler, something more like this:


    {% for genusScientistForm in genusForm.genusScientists %}
    <div class="col-xs-4 js-genus-scientist-item">



    {# you don't have individual fields to render, you just have the ONE field to render #}
    {{ form_row(genusScientistForm) }}
    </div>
    {% endfor %}

    If you were doing this from the *inverse* side instead, then you would just need to make sure to follow the same steps we did here: https://knpuniversity.com/s...

    Let me know if that helps! And cheers!

  • 2017-01-22 ehymel

    In your lesson for using CollectionType, you have changed from ManyToMany to matching OneToMany and ManyToMany with a new entity to manage the relationship. All makes sense. Your embedded form for multiple drop-down boxes uses that new entity to persist changes to the relationship in the db.

    I would like to leave things as a ManyToMany relationship (like in your earlier lessons) but use drop-down boxes like you use in this lesson. However, it doesn't seem to work. I'm listing the entity of the inverse side of the relationship in the embedded form (so I can list choices in the drop-down select field, but when I save it modifies only this entity (inverse side of ManyToMany) rather than the relationship.

    In other words, using examples from your entities: Owning entity; Genus. Inverse side: Scientist. How to embed multiple drop-down list of scientists that in the same way you do when there is a named entity joining the two, but instead with the ManyToMany relationship itself defining things? I've tried the equivalent of adding an embedded form of Scientists drop-down list and embedding that into the Genus edit page. I get multiple drop-down lists, but the correct items are not pre-selected, and changing/saving updates the Scientist list itself rather than the relationship.