41857

Symfony2 Checkbox List

Question:

I have a Many to Many relation in my project but I want to modify my relation table, so I converted it into a two Many to One relation with the join table on another entity.

The problem is that when I try to render a checkbox group of the en two initial entities I'm not able to use then.

Here is my code:

class Professional extends User { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; ... /** * @ORM\OneToMany(targetEntity="TurnsProfessional", mappedBy="professional") */ private $turns; ...

My second entity

class Turn { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; ... /** * @ORM\OneToMany(targetEntity="TurnsProfessional", mappedBy="turn") */ private $professionals; ...

And the join entity

class TurnsProfessional { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; public function __construct() { } /** * @ORM\ManyToOne(targetEntity="Turn", inversedBy="professionals") * @ORM\JoinColumn(name="turn_id", referencedColumnName="id") */ private $turn; /** * @ORM\ManyToOne(targetEntity="Professional", inversedBy="turns") * @ORM\JoinColumn(name="professional_id", referencedColumnName="id") */ private $professional; /** * @ORM\Column(type="boolean") */ private $status = 0; ...

My goal is to create a checkbox list in the Professional form with the turns. I've tried two things:

First I've tried to add in the ProfessionalType the field as a Turn::class:

... ->add('turns', 'entity', array('class' => 'AppBundle:TurnsProfessional', 'property' => 'label', 'multiple' => true, 'expanded' => true, )); ...

The problem here is that the form does not render the checkboxes. Otherwise if I change the class:

... ->add('turns', 'entity', array('class' => 'AppBundle:Turn', 'property' => 'label', 'multiple' => true, 'expanded' => true, )); ...

The form does render all the checkboxes but i get an error on sending it:

<blockquote>

Found entity of type AppBundle\Entity\Turn on association AppBundle\Entity\Professional#turns, but expecting AppBundle\Entity\TurnsProfessional

</blockquote>

I´ve edited the form as Oliver said (on comments):

->add($builder->create('turns', CollectionType::class, array( 'entry_type' => TurnsProfessionalType::class, 'by_reference' => false, )));

With the TurnsProfessionalType

class TurnsProfessionalType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('status') ->add('turn', EntityType::class, array('class' => 'AppBundle:Turn', 'property' => 'label', 'multiple' => true, 'expanded' => true, )); } public function getBlockPrefix() { return 'turns_professional_registration_form'; } public function getName() { return $this->getBlockPrefix(); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => TurnsProfessional::class, )); } }

Now I've rendered it as:

{% for turn in form.turns %} <td><div class="checkbox">{{ form_widget(turn.status)}}<label></label></div></td> {% endfor %}

But I need to put as label the database label of each turn

Does anyone can help? Thank you!

Answer1:

Professional Entity

/** * @var ArrayCollection * * @ORM\OneToMany(targetEntity="AppBundle\Entity\TurnsProfessional", mappedBy="professional", cascade={"persist"}) */ private $turns; public function __construct() { $this->turns = new ArrayCollection(); } /** * Add turn * * @param TurnsProfessional $turns * @return Professional */ public function addTurn(TurnsProfessional $turn) { /* Doctrine only checks the owning side(ManyToOne). So you need to manually call the join entity setter to keep your data in sync. */ $turn->setProfessional($this); $this->turns->add($turn); return $this; } /** * Remove turn * * @param TurnsProfessional $turn * @return Professional */ public function removeTurn(TurnsProfessional $turn) { /* Doctrine only checks the owning side(ManyToOne). So you need to manually call the join entity setter to keep your data in sync. */ $turn->setProfessional(); $this->turns->removeElement($turn); return $this; } /** * Get turns * * @return ArrayCollection */ public function getTurns() { return $this->turns; }

Turn Entity

/** * @var ArrayCollection * * @ORM\OneToMany(targetEntity="AppBundle\Entity\TurnsProfessional", mappedBy="turn", cascade={"persist"}) */ private $professionals; public function __construct() { $this->professionals = new ArrayCollection(); } /** * Add professional * * @param TurnsProfessional $professional * @return Turn */ public function addProfessional(TurnsProfessional $professional) { /* Doctrine only checks the owning side(ManyToOne). So you need to manually call the join entity setter to keep your data in sync. */ $professional->setTurn($this); $this->professionals->add($professional); return $this; } /** * Remove professional * * @param TurnsProfessional $professional * @return Turn */ public function removeProfessional(TurnsProfessional $professional) { /* Doctrine only checks the owning side(ManyToOne). So you need to manually call the join entity setter to keep your data in sync. */ $professional->setTurn(); $this->professional->removeElement($professional); return $this; } /** * Get professionals * * @return ArrayCollection */ public function getProfessionals() { return $this->professionals; }

TurnsProfessional Entity

/** * @var Professional * * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Professional", inversedBy="turns", cascade={"persist"}) */ private $professional; /** * @var Turn * * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Turn", inversedBy="professionals", cascade={"persist"}) */ private $turn; /** * @var bool * * @ORM\Column(name="status", type="boolean") */ private $status = true; //You should set a default value to true, since it is non sense to create some value if it not active /** * Set professional * * @param Professional $professional * @return TurnsProfessional */ public function setProfessional(Professional $professional = null) { /* Using a default null value allows you to unset of the value of the joined entity when removing in inverse side (see removeTurn in Professional entity) */ $this->professional = $professional; return $this; } /** * Set turn * * @param Turn $turn * @return TurnsProfessional */ public function setTurn(Turn $turn = null) { /* Using a default null value allows you to unset of the value of the joined entity when removing in inverse side (see removeProfessional in Turn entity) */ $this->turn = $turn; return $this; }

ProfessionalType

use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use AppBundle\Form\TurnsProfessionalType; class ProfessionalType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('turns', CollectionType::class, array( 'entry_type' => TurnsProfessionalType::class, 'allow_add' => true, 'by_reference' => false, //with by_reference at false you force the call of the setter )) //other fields ; } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\Professional', 'cascade_validation => true //will also validate joined entities )); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'turns_professional_registration_form'; } }

TurnsProfessionalType

use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use AppBundle\Entity\Turn; class TurnsProfessionalType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('turn', EntityType::class, array( 'class' => Turn::class, 'choice_label' => 'label' )); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\TypesProfessional' )); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'turns_professional_registration_form'; } }

twig template

/* By default you will have an empty turns collection which which means you will get no field displayed that's why you need a addTurn button to fill your collection and add fields to your form */ {{ form_start(form) }} <ul class="turns" data-prototype="{{ form_widget(form.turns.vars.prototype)|e('html_attr') }}"> {% for turn in form.turns %} <li>{{ form_row(turn.turn) }}</li> {% endfor %} </ul> {{ form_end(form) }} <script> var $collectionHolder; // setup an "add a turn" link var $addTurnLink = $('<a href="#" class="add_turn_link">Add a turn</a>'); var $newLinkLi = $('<li></li>').append($addTurnLink); jQuery(document).ready(function() { // Get the ul that holds the collection of turns $collectionHolder = $('ul.turns'); // add the "add a turn" anchor and li to the turns ul $collectionHolder.append($newLinkLi); // count the current form inputs we have (e.g. 2), use that as the new // index when inserting a new item (e.g. 2) $collectionHolder.data('index', $collectionHolder.find(':input').length); $addTurnLink.on('click', function(e) { // prevent the link from creating a "#" on the URL e.preventDefault(); // add a new turn form (see next code block) addTurnForm($collectionHolder, $newLinkLi); }); }); function addTurnForm($collectionHolder, $newLinkLi) { // Get the data-prototype explained earlier var prototype = $collectionHolder.data('prototype'); // get the new index var index = $collectionHolder.data('index'); // Replace '__name__' in the prototype's HTML to // instead be a number based on how many items we have var newForm = prototype.replace(/__name__/g, index); // increase the index with one for the next item $collectionHolder.data('index', index + 1); // Display the form in the page in an li, before the "Add a turn" link li var $newFormLi = $('<li></li>').append(newForm); $newLinkLi.before($newFormLi); } </script>

Recommend

  • Symfony2 Checkbox List
  • Where did RU/m go?
  • Combining 2 forms in symfony
  • C# Open listView Item from treeView item location
  • Bind list with unequal columns
  • Finding Documents Array CouchDB
  • Key “prototype” does not exist
  • The module has been successfully installed but then it's not found when imported? - Python
  • CosmosDB SQL query that matches multiple values in an array
  • How to retrieve the auto incremented ID using slick / plainSQL?
  • pow function and long int causing problems
  • using “self” in association declaration (:has_many, :has_one)
  • how to implement RoR many-to-many relationship?`
  • In Linq to Sql How to get all the foreign key fields for an entity
  • Hibernate manyToOne filter on base entity
  • Git: failed to read object … Invalid argument
  • Java catching exceptions and subclases
  • Do stored procedures have the ability to delete a file from the OS?
  • Executing a function that adds columns and populates them dependig on other columns in Pandas
  • Renewing Cookie-session for express.js
  • How to Cache Images Dynamically-Generated with PHP?
  • passing parameter to server in ExtJs
  • Excel VBA Intersect
  • send data back from jsp iterator to struts action class
  • How to get the index of element in the List in c#
  • Allocating a 2D contiguous array within a function
  • how to set to NULL all the filestream varbinary(max) fields?
  • retrieving data from url in iphone
  • Symfony2 - Doctrine - no changeset in post update
  • Two Tables Serving as one Model in Rails
  • Doctrine/Symfony entity generator and generating entity from one table
  • 550 Access denied - Invalid HELO name
  • Why cepheus don't send int without quotes to orion?
  • GAE: Way to get reference to an HttpSession from its ID?
  • Force show.bind execution
  • need help with bizarre java.net.HttpURLConnection behavior