Odd many-to-many form rendering with symfony and doctrine - symfony

I'm hoping this is possible using some built-in libraries such as the form builder. I have the following three entities. The one in the middle is almost just a regular join table but it has an extra column with an extra piece of data.
Formula --< FormulaColor >-- Color
FormulaColor has the fields: formula, color, and percentage.
The percentage field is to say what percentage a color makes up of a given formula. A very simple example is that a Formula may be 77% red and 33% blue. My problem is that I want to choose the colors for a Formula, and give them a percentage manually using forms. So I would add (or edit) a certain formula and give it say the color violet (20%) green (45%) and yellow (35%). I do not care about being able to add new colors in the formula add/edit view. I just want to be able to select existing colors. I've been playing around with it for hours with collection and entity types, but no luck.
Got any pointers or tips for me? Will I have to do it manually without the form component etc?
Thanks.
formula form type
class FormulaAddEditType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code', null, array(
'label' => 'Code'
))
->add('name', null, array(
'label' => 'Name'
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Prism\Portal\CommonBundle\Entity\Formula'
));
}
public function getName()
{
return 'prism_portal_adminbundle_formulaaddedittype';
}
}
Formula Entity
class Formula
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #Serializer\Expose
*/
private $id;
/**
* #var string $code
*
* #ORM\Column(name="code", type="string", length=50, nullable=true)
*
* #Serializer\Expose
*/
private $code;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=50, nullable=true)
*/
private $name;
/**
* #var datetime $createdOn
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="createdOn", type="datetime", nullable=true)
*/
private $createdOn;
/**
* #var datetime $updatedOn
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="updatedOn", type="datetime", nullable=true)
*/
private $updatedOn;
/**
* #var formulaColors
*
* #ORM\OneToMany(targetEntity="FormulaColor", mappedBy="formula")
*/
private $formulaColors;
public function __construct()
{
$this->formulaColors = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set code
*
* #param string $code
*/
public function setCode($code)
{
$this->code = $code;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set createdOn
*
* #param datetime $createdOn
*/
public function setCreatedOn($createdOn)
{
$this->createdOn = $createdOn;
}
/**
* Get createdOn
*
* #return datetime
*/
public function getCreatedOn()
{
return $this->createdOn;
}
/**
* Set updatedOn
*
* #param datetime $updatedOn
*/
public function setUpdatedOn($updatedOn)
{
$this->updatedOn = $updatedOn;
}
/**
* Get updatedOn
*
* #return datetime
*/
public function getUpdatedOn()
{
return $this->updatedOn;
}
/**
* Add formulaColor
*
* #param FormulaColor $formulaColor
*/
public function addFormulaColor(FormulaColor $formulaColor)
{
$this->formulaColors[] = $formulaColor;
}
/**
* Get formulaColors
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFormulaColors()
{
return $this->formulaColors;
}
}
FormulaColor Entity
/**
* Prism\Portal\CommonBundle\Entity\FormulaColor
*
* #ORM\Table(name="FormulaColor")
* #ORM\Entity
*
* #Serializer\ExclusionPolicy("all")
*/
class FormulaColor
{
/**
* #var integer $formula
*
* #ORM\ManyToOne(targetEntity="Formula", inversedBy="formulaColors")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="formulaId", referencedColumnName="id")
* })
* #ORM\Id
*/
private $formula;
/**
* #var integer color
*
* #ORM\ManyToOne(targetEntity="Color", inversedBy="formulaColors")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="colorId", referencedColumnName="id")
* })
* #ORM\Id
*/
private $color;
/**
* #var decimal percentage
*
* #ORM\Column(type="decimal", precision=5, scale=2, nullable=false)
*/
private $percentage;
/**
* Set percentage
*
* #param decimal $percentage
*/
public function setPercentage($percentage)
{
$this->percentage = $percentage;
}
/**
* Get percentage
*
* #return decimal
*/
public function getPercentage()
{
return $this->percentage;
}
/**
* Set formula
*
* #param Prism\Portal\CommonBundle\Entity\Formula $formula
*/
public function setFormula(\Prism\Portal\CommonBundle\Entity\Formula $formula)
{
$this->formula = $formula;
}
/**
* Get formula
*
* #return Prism\Portal\CommonBundle\Entity\Formula
*/
public function getFormula()
{
return $this->formula;
}
/**
* Set color
*
* #param Prism\Portal\CommonBundle\Entity\Color $color
*/
public function setColor(\Prism\Portal\CommonBundle\Entity\Color $color)
{
$this->color = $color;
}
/**
* Get color
*
* #return Prism\Portal\CommonBundle\Entity\Color
*/
public function getColor()
{
return $this->color;
}
}
Color Entity
/**
* Prism\Portal\CommonBundle\Entity\Color
*
* #ORM\Table(name="Color")
* #ORM\Entity
*
* #Serializer\ExclusionPolicy("all")
*/
class Color
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string $code
*
* #ORM\Column(name="code", type="string", length=50, nullable=true)
*/
private $code;
/**
* #var string $hexColor
*
* #ORM\Column(name="hex_color", type="string", length=50, nullable=true)
*/
private $hexColor;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=50, nullable=true)
*/
private $name;
/**
* #var integer $sortOrder
*
* #ORM\Column(name="sortOrder", type="integer", nullable=true)
*/
private $sortOrder;
/**
* #var datetime $createdOn
*
* #ORM\Column(name="createdOn", type="datetime", nullable=true)
*/
private $createdOn;
/**
* #var datetime $updatedOn
*
* #ORM\Column(name="updatedOn", type="datetime", nullable=true)
*/
private $updatedOn;
/**
* #var $formulaColors
*
* #ORM\OneToMany(targetEntity="FormulaColor", mappedBy="color")
*/
private $formulaColors;
public function __construct()
{
$this->formulaColors = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set code
*
* #param string $code
*/
public function setCode($code)
{
$this->code = $code;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set sortOrder
*
* #param integer $sortOrder
*/
public function setSortOrder($sortOrder)
{
$this->sortOrder = $sortOrder;
}
/**
* Get sortOrder
*
* #return integer
*/
public function getSortOrder()
{
return $this->sortOrder;
}
/**
* Set createdOn
*
* #param datetime $createdOn
*/
public function setCreatedOn($createdOn)
{
$this->createdOn = $createdOn;
}
/**
* Get createdOn
*
* #return datetime
*/
public function getCreatedOn()
{
return $this->createdOn;
}
/**
* Set updatedOn
*
* #param datetime $updatedOn
*/
public function setUpdatedOn($updatedOn)
{
$this->updatedOn = $updatedOn;
}
/**
* Get updatedOn
*
* #return datetime
*/
public function getUpdatedOn()
{
return $this->updatedOn;
}
/**
* Get formulaColors
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFormulaColors()
{
return $this->formulaColors;
}
/**
* addFormulaColors
*
* #param \Prism\Portal\CommonBundle\Entity\FormulaColor $formulaColor
*/
public function addFormulaColor(\Prism\Portal\CommonBundle\Entity\FormulaColor $formulaColor)
{
$this->formulaColors[] = $formulaColor;
}
/**
* Remove formulaColors
*
* #param \Prism\Portal\CommonBundle\Entity\FormulaColor $formulaColors
*/
public function removeFormulaColor(\Prism\Portal\CommonBundle\Entity\FormulaColor $formulaColors)
{
$this->formulaColors->removeElement($formulaColors);
}
/**
* Set hexColor
*
* #param string $hexColor
* #return Color
*/
public function setHexColor($hexColor)
{
$this->hexColor = $hexColor;
return $this;
}
/**
* Get hexColor
*
* #return string
*/
public function getHexColor()
{
return $this->hexColor;
}
}
I also have a ColorAddEditType form
class ColorAddEditType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code', null, array(
'label' => 'Code'
))
->add('name', null, array(
'label' => 'Name'
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Prism\Portal\CommonBundle\Entity\Color'
));
}
public function getName()
{
return 'prism_portal_adminbundle_coloraddedittype';
}
}
I've also updated my code according to Ryan's response.
FormulaColorType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('color', new ColorAddEditType());
$builder->add('percent', 'number');
}
FormulaAddEditType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code', null, array(
'label' => 'Code'
))
->add('name', null, array(
'label' => 'Name'
));
$builder->add('formulaColors', 'collection', array(
'type' => new FormulaColorType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
));
}
and now I'm getting use a transformer / set data_class to null exceptions. When I change the one to null, then it says I can change the other to null. When I do that it basically says to change it back. Is it normal that I should have to set up a data transformer for something like this?
Here is the current error I'm getting:
The form's view data is expected to be an instance of class
Prism\Portal\CommonBundle\Entity\Color, but is an instance of class
Prism\Portal\CommonBundle\Entity\FormulaColor. You can avoid this
error by setting the "data_class" option to null or by adding a view
transformer that transforms an instance of class
Prism\Portal\CommonBundle\Entity\FormulaColor to an instance of
Prism\Portal\CommonBundle\Entity\Color.

You should be able to do something like this. It's a fairly common pattern. The tricky bit will be the Javascript, but it won't be too bad. You might need to make your numbers add to 100 using Javascript as well.
FormulaType
public function buildForm(FormBuilder $builder, array $options) {
$builder->add('formulaColors', 'collection', array(
'type' => new FormularColorType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
));
}
FormulaColorType
public function buildForm(FormBuilder $builder, array $options) {
$builder->add('color', new ColorType()); // Or similar
$builder->add('percent', 'number');
}

Related

error in symfony for manyToOne and form imbricated

I encountered a problem under symfony3 which has blocked me for a while ... I do not understand this error:
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Project#$participants", got "string" instead.
Here is my entity Project:
class Project
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Participant", mappedBy="project", cascade={"persist", "remove"})
*/
private $participants;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text", nullable=true)
*/
private $content;
/**
* #var string
*
* #ORM\Column(name="place", type="string", length=255, nullable=true)
*/
private $place;
/**
* #var \DateTime
*
* #ORM\Column(name="dateEvent", type="datetime", nullable=true)
*/
private $dateEvent;
/**
* #var boolean
*
* #ORM\Column(name="status", type="boolean")
*/
private $status;
/**
* #var boolean
*
* #ORM\Column(name="in_progress", type="boolean")
*/
private $inProgress;
/**
* #var boolean
*
* #ORM\Column(name="accept_list", type="boolean")
*/
private $acceptList;
/**
* #var boolean
*
* #ORM\Column(name="visible_list", type="boolean")
*/
private $visibleList;
/**
* #var boolean
*
* #ORM\Column(name="many_loop", type="boolean")
*/
private $manyLoop;
/**
* #var string
*
* #ORM\Column(name="text_email", type="text", nullable=true)
*/
private $textEmail;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
public function __construct()
{
$this->run = false;
$this->status = false;
$this->acceptList = true;
$this->visibleList = true;
$this->inProgress = false;
$this->manyLoop = true;
$this->created = new \Datetime('now');
$this->participants = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Project
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Project
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set place
*
* #param string $place
*
* #return Project
*/
public function setPlace($place)
{
$this->place = $place;
return $this;
}
/**
* Get place
*
* #return string
*/
public function getPlace()
{
return $this->place;
}
/**
* Set dateEvent
*
* #param \DateTime $dateEvent
*
* #return Project
*/
public function setDateEvent($dateEvent)
{
$this->dateEvent = $dateEvent;
return $this;
}
/**
* Get dateEvent
*
* #return \DateTime
*/
public function getDateEvent()
{
return $this->dateEvent;
}
/**
* Set status
*
* #param boolean $status
*
* #return Project
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return boolean
*/
public function getStatus()
{
return $this->status;
}
/**
* Set inProgress
*
* #param boolean $inProgress
*
* #return Project
*/
public function setInProgress($inProgress)
{
$this->inProgress = $inProgress;
return $this;
}
/**
* Get inProgress
*
* #return boolean
*/
public function getInProgress()
{
return $this->inProgress;
}
/**
* Set acceptList
*
* #param boolean $acceptList
*
* #return Project
*/
public function setAcceptList($acceptList)
{
$this->acceptList = $acceptList;
return $this;
}
/**
* Get acceptList
*
* #return boolean
*/
public function getAcceptList()
{
return $this->acceptList;
}
/**
* Set visibleList
*
* #param boolean $visibleList
*
* #return Project
*/
public function setVisibleList($visibleList)
{
$this->visibleList = $visibleList;
return $this;
}
/**
* Get visibleList
*
* #return boolean
*/
public function getVisibleList()
{
return $this->visibleList;
}
/**
* Set manyLoop
*
* #param boolean $manyLoop
*
* #return Project
*/
public function setManyLoop($manyLoop)
{
$this->manyLoop = $manyLoop;
return $this;
}
/**
* Get manyLoop
*
* #return boolean
*/
public function getManyLoop()
{
return $this->manyLoop;
}
/**
* Set textEmail
*
* #param string $textEmail
*
* #return Project
*/
public function setTextEmail($textEmail)
{
$this->textEmail = $textEmail;
return $this;
}
/**
* Get textEmail
*
* #return string
*/
public function getTextEmail()
{
return $this->textEmail;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return Project
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return Project
*/
public function setUser(\AppBundle\Entity\User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Add participant
*
* #param \AppBundle\Entity\Participant $participant
*
* #return Project
*/
public function addParticipant(\AppBundle\Entity\Participant $participant)
{
$this->participants[] = $participant;
// On lie l'annonce à la candidature
$participant->setProject($this);
return $this;
}
/**
* Remove participant
*
* #param \AppBundle\Entity\Participant $participant
*/
public function removeParticipant(\AppBundle\Entity\Participant $participant)
{
$this->participants->removeElement($participant);
}
/**
* Get participants
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getParticipants()
{
return $this->participants;
}}
My Form:
$builder ->add('participants', ParticipantType::class);
/**
* {#inheritdoc}
*/
public function configureOptions (OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Project',
'step' => 1,
));
}
Form imbricated :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('lastName', TextType::class, array(
'required' => true,
'label' => 'Nom'
))
->add('firstName', TextType::class, array(
'required' => true,
'label' => 'Prénom'
))
->add('email', EmailType::class, array(
'required' => true,
'label' => 'Adresse mail'
));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('registration')
));
}
So here is to summarize my problem, I do not understand the error that Symfony returns, yet from the examples on the internet I do not see why there is a problem here ...
If someone to a solution would be top, because I blocked on this problem for a long time ...
Thank's !
Make sure that you don't forget to add : use Doctrine\Common\Collections\ArrayCollection or use $this->participants = new \Doctrine\Common\Collections\ArrayCollection();
In your FormType you should configure the participants field as a collection of Participant. It should be done using CollectionType like this:
$builder->add('participants', CollectionType::class, array(
'entry_type' => ParticipantType::class,
'allow_add' => true,
...
));
EDIT
According to your data model, the participants Field is a collection of Participant object. As you can see in the error message you presented, an array is expected. The CollectionType will always return an array of the target type.
That is why I suggest you to consider using a CollectionType in your form type. You can learn more about CollectionType here and about Symfony forms in general here

Symfony error when inserting new entity data

I am a beginner learning Symfony. I have an entity called sale for which I a form for inserting sales data.
The problem is each attempt to insert data results in the following error:
Neither the property "selectedstock" nor one of the methods
"addSelectedstock()"/"removeSelectedstock()", "setSelectedstock()",
"selectedstock()", "__set()" or "__call()" exist and have public
access in class "iCerge\Salesdeck\SalesBundle\Entity\Sale".
My Sale class is as follows:
<?php
namespace iCerge\Salesdeck\SalesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Sales
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="iCerge\Salesdeck\SalesBundle\Entity\SalesRepository")
*/
class Sale
{
private $selectedstock;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var float
*
* #ORM\Column(name="cost", type="float")
*/
private $cost;
/**
* #var float
*
* #ORM\Column(name="profitloss", type="float")
*/
private $profitloss;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity="iCerge\Salesdeck\StockBundle\Entity\Stock", inversedBy="sales")
* #ORM\JoinColumn(name="sid", referencedColumnName="id")
*/
protected $stock;
/**
* #var integer
*
* #ORM\Column(name="number_of_purchases", type="integer")
*/
private $number_of_purchases;
/**
* #var float
*
* #ORM\Column(name="active_buy_price", type="float")
*/
private $active_buy_price;
/**
* #var integer
*
* #ORM\Column(name="sid", type="integer")
*/
private $sid;
/**
* Get sid
*
* #return integer
*/
public function getSid()
{
return $this->sid ? $this->sid : 0;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set cost
*
* #param float $cost
* #return Sales
*/
public function setCost($cost)
{
$this->cost = $cost;
return $this;
}
/**
* Get cost
*
* #return float
*/
public function getCost()
{
return $this->cost;
}
/**
* Set date
*
* #param \DateTime $date
* #return Sales
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set profitloss
*
* #param float $profitloss
* #return Sales
*/
public function setProfitloss($profitloss)
{
$this->profitloss = $profitloss;
return $this;
}
/**
* Get profitloss
*
* #return float
*/
public function getProfitloss()
{
return $this->profitloss;
}
/**
* Set stock
*
* #param \iCerge\Salesdeck\StockBundle\Entity\Stock $stock
* #return Sale
*/
public function setStock(\iCerge\Salesdeck\StockBundle\Entity\Stock $stock = null)
{
$this->stock = $stock;
return $this;
}
/**
* Get stock
*
* #return \iCerge\Salesdeck\StockBundle\Entity\Stock
*/
public function getStock()
{
return $this->stock;
}
/********************/
/**
* Set number_of_purchases
*
* #return integer
*/
public function setNumberOfPurchases($purchases)
{
$this->number_of_purchases = $purchases;
return $this;
}
/**
* Get number_of_purchases
*
* #return integer
*/
public function getNumberOfPurchases()
{
return $this->number_of_purchases;
}
/**
* Set active_buy_price
*
* #return integer
*/
public function setActiveBuyPrice($price)
{
$this->active_buy_price = $price;
return $this;
}
/**
* Get active_buy_price
*
* #return integer
*/
public function getActiveBuyPrice()
{
return $this->active_buy_price;
}
public function getselectedstock()
{
return $this->selectedstock;
}
}
And the following is my Form class:
<?php
namespace iCerge\Salesdeck\SalesBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SalesType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('selectedstock', 'choice', array(
'choices' => $options['allow_extra_fields'],
'required' => true,
'empty_value' => 'Choose a Product',
'empty_data' => null,
'label' => 'Select Item',
// 'mapped'=>false
))
/*->add('test', 'choice', array(
'choices' => $options['allow_extra_fields'],
'required' => false,
'empty_value' => 'Choose your gender',
'empty_data' => null,
'mapped'=>false
))*/
->add('number_of_purchases', 'integer', array('label'=>'Number of purchases'))
->add('cost')
->add('profitloss', 'text', array('label'=>'Profit/Loss'))
->add('date', 'datetime', array('data'=>new \DateTime('now')))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
// 'data_class' => 'iCerge\Salesdeck\SalesBundle\Entity\Sales'
'data_class' => 'iCerge\Salesdeck\SalesBundle\Entity\Sale'
));
}
/**
* #return string
*/
public function getName()
{
return 'icerge_salesdeck_salesbundle_sales';
}
}
What am I doing wrong here?
Update
Screenshot shows dropdown list of available stock.
The form is the sales/add form which requires an item of available stock from the list to be selected.
You don't have a setter for selectedstock, and not related to the error you don't have annotation to mark selectedstock as an orm column, so it won't be persisted.

How to customise form rendering when based on entity field in symfony 2?

I have created a form based on an entity field. This form is composed of checkboxes that allow user to choose an entity (here 'car').
This works fine but I need to customize the rendering to get extra informations. Currently, the only information that is displayed is the 'id' attribute.
In my case, I would like to display extra entity informations in the form such as color, brand etc ...
Does anyone know how to proceed ?
The controller :
public function chooserAction() {
//symfony.com/doc/current/reference/forms/types/entity.html
$cars = $this->getDoctrine()
->getRepository('CarBundle:Car')
->find(1);
$formBuilder = $this->createFormBuilder();
/*
foreach ($cars as $car) {
$formBuilder->add($car->getId() ,'checkbox')->getForm();
}
*/
$formBuilder->add('cars', 'entity', array(
'class' => 'CarBundle:Car',
'property' => 'id',
'expanded' => 'true',
'multiple' => 'true',
));
$formBuilder->add('save', 'submit');
$form = $formBuilder->getForm();
$request = $this->get('request');
$form->handleRequest($request);
if ($form->isValid()) {
echo "ok";
// return $this->redirect($this->generateUrl('car_show', array('id' => $car->getId())));
}
return $this->render('CarBundle:Car:chooser.html.twig', array('form' => $form->createView()));
}
The entity :
<?php
namespace Foobar\CarBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Car
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Foobar\CarBundle\Entity\CarRepository")
*/
class Car
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="brand", type="string", length=255)
*/
private $brand;
/**
* #var string
*
* #ORM\Column(name="color", type="string", length=255)
*/
private $color;
/**
* #var integer
*
* #ORM\Column(name="power", type="integer")
*/
private $power;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Car
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set brand
*
* #param string $brand
* #return Car
*/
public function setBrand($brand)
{
$this->brand = $brand;
return $this;
}
/**
* Get brand
*
* #return string
*/
public function getBrand()
{
return $this->brand;
}
/**
* Set color
*
* #param string $color
* #return Car
*/
public function setColor($color)
{
$this->color = $color;
return $this;
}
/**
* Get color
*
* #return string
*/
public function getColor()
{
return $this->color;
}
/**
* Set power
*
* #param integer $power
* #return Car
*/
public function setPower($power)
{
$this->power = $power;
return $this;
}
/**
* Get power
*
* #return integer
*/
public function getPower()
{
return $this->power;
}
}
The view :
car chooser
{{ form(form) }}
You could create a toString() method in your Car entity.
public function __toString()
{
return '' . $this->getName();
}
If you want more thinks like pictures you could follow that http://symfony.com/doc/current/cookbook/form/form_customization.html

Symfony persists fail with relation cascade

Hello i have this error when i try to persist my entity :
An exception occurred while executing 'INSERT INTO item_details (item_id, item_type, name, start_date, end_date, description, pre_requisites, picture, acquired_knowledges, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' with params [null, null, "ertret", "09\/01\/2014", "17\/01\/2014", "ter", "trtre", {}, "trtre", null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'item_id' cannot be null
Course entity code :
<?php
namespace Mooc\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Course
*
* #ORM\Table(name="course")
* #ORM\Entity
*/
class Course
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="language_id", type="integer", nullable=false)
*/
private $languageId;
/**
* #var integer
*
* #ORM\Column(name="category_id", type="integer", nullable=false)
*/
private $categoryId;
/**
* #ORM\OneToMany(targetEntity="ItemDetails", mappedBy="course",cascade={"all"})
**/
private $details;
/**
* Constructor
*/
public function __construct()
{
$this->details = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set languageId
*
* #param integer $languageId
* #return Course
*/
public function setLanguageId($languageId)
{
$this->languageId = $languageId;
return $this;
}
/**
* Get languageId
*
* #return integer
*/
public function getLanguageId()
{
return $this->languageId;
}
/**
* Set categoryId
*
* #param integer $categoryId
* #return Course
*/
public function setCategoryId($categoryId)
{
$this->categoryId = $categoryId;
return $this;
}
/**
* Get categoryId
*
* #return integer
*/
public function getCategoryId()
{
return $this->categoryId;
}
/**
* Add details
*
* #param \Mooc\TeacherBundle\Entity\ItemDetails $details
* #return Course
*/
public function addDetail(\Mooc\AdminBundle\Entity\ItemDetails $details)
{
$this->details[] = $details;
return $this;
}
/**
* Add details
*
* #param \Mooc\TeacherBundle\Entity\ItemDetails $details
* #return Course
*/
public function setDetails(\Mooc\AdminBundle\Entity\ItemDetails $details)
{
$this->details[] = $details;
return $this;
}
/**
* Remove details
*
* #param \Mooc\TeacherBundle\Entity\ItemDetails $details
*/
public function removeDetail(\Mooc\AdminBundle\Entity\ItemDetails $details)
{
$this->details->removeElement($details);
}
/**
* Get details
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getDetails()
{
return $this->details;
}
}
Item details entity code :
namespace Mooc\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* ItemDetails
*
* #ORM\Table(name="item_details")
* #ORM\Entity
*/
class ItemDetails
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="item_id", type="integer", nullable=false)
*/
private $itemId;
/**
* #var integer
*
* #ORM\Column(name="item_type", type="integer", nullable=false)
*/
private $itemType;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="start_date", type="string", length=60, nullable=false)
*/
private $startDate;
/**
* #var string
*
* #ORM\Column(name="end_date", type="string", length=60, nullable=false)
*/
private $endDate;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=false)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="pre_requisites", type="text", nullable=false)
*/
private $preRequisites;
/**
* #var string
*
* #ORM\Column(name="picture", type="string", length=255, nullable=false)
*/
private $picture;
/**
* #var string
*
* #ORM\Column(name="acquired_knowledges", type="text", nullable=false)
*/
private $acquiredKnowledges;
/**
* #var integer
*
* #ORM\Column(name="status", type="integer", nullable=false)
*/
private $status;
/**
* #ORM\ManyToOne(targetEntity="Course", inversedBy="details")
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
**/
private $course;
/**
* #ORM\OneToMany(targetEntity="Video", mappedBy="itemDetails",cascade={"all"})
**/
private $video;
/**
* Constructor
*/
public function __construct()
{
$this->video = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set itemId
*
* #param integer $itemId
* #return ItemDetails
*/
public function setItemId($itemId)
{
$this->itemId = $itemId;
return $this;
}
/**
* Get itemId
*
* #return integer
*/
public function getItemId()
{
return $this->itemId;
}
/**
* Set itemType
*
* #param integer $itemType
* #return ItemDetails
*/
public function setItemType($itemType)
{
$this->itemType = $itemType;
return $this;
}
/**
* Get itemType
*
* #return integer
*/
public function getItemType()
{
return $this->itemType;
}
/**
* Set name
*
* #param string $name
* #return ItemDetails
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set startDate
*
* #param string $startDate
* #return ItemDetails
*/
public function setStartDate($startDate)
{
$this->startDate = $startDate;
return $this;
}
/**
* Get startDate
*
* #return string
*/
public function getStartDate()
{
return $this->startDate;
}
/**
* Set endDate
*
* #param string $endDate
* #return ItemDetails
*/
public function setEndDate($endDate)
{
$this->endDate = $endDate;
return $this;
}
/**
* Get endDate
*
* #return string
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* Set description
*
* #param string $description
* #return ItemDetails
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set preRequisites
*
* #param string $preRequisites
* #return ItemDetails
*/
public function setPreRequisites($preRequisites)
{
$this->preRequisites = $preRequisites;
return $this;
}
/**
* Get preRequisites
*
* #return string
*/
public function getPreRequisites()
{
return $this->preRequisites;
}
/**
* Set picture
*
* #param string $picture
* #return ItemDetails
*/
public function setPicture($picture)
{
$this->picture = $picture;
return $this;
}
/**
* Get picture
*
* #return string
*/
public function getPicture()
{
return $this->picture;
}
/**
* Set acquiredKnowledges
*
* #param string $acquiredKnowledges
* #return ItemDetails
*/
public function setAcquiredKnowledges($acquiredKnowledges)
{
$this->acquiredKnowledges = $acquiredKnowledges;
return $this;
}
/**
* Get acquiredKnowledges
*
* #return string
*/
public function getAcquiredKnowledges()
{
return $this->acquiredKnowledges;
}
/**
* Set status
*
* #param integer $status
* #return ItemDetails
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Set course
*
* #param \Mooc\AdminBundle\Entity\Course $course
* #return ItemDetails
*/
public function setCourse(\Mooc\AdminBundle\Entity\Course $course = null)
{
$this->course = $course;
return $this;
}
/**
* Get course
*
* #return \Mooc\AdminBundle\Entity\Course
*/
public function getCourse()
{
return $this->course;
}
/**
* Add details
*
* #param \Mooc\TeacherBundle\Entity\ItemDetails $video
* #return Course
*/
public function addVideo(\Mooc\AdminBundle\Entity\Video $video)
{
$this->details[] = $video;
return $this;
}
/**
* Add details
*
* #param \Mooc\TeacherBundle\Entity\ItemDetails $video
* #return Course
*/
public function setVideo(\Mooc\AdminBundle\Entity\Video $video)
{
$this->video[] = $video;
return $this;
}
/**
* Remove details
*
* #param \Mooc\TeacherBundle\Entity\ItemDetails $video
*/
public function removeVideo(\Mooc\AdminBundle\Entity\Video $video)
{
$this->video->removeElement($video);
}
/**
* Get details
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getVideo()
{
return $this->video;
}
}
Video entity code :
namespace Mooc\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Video
*
* #ORM\Table(name="video")
* #ORM\Entity
*/
class Video
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="course_id", type="integer", nullable=false)
*/
private $courseId;
/**
* #var string
*
* #ORM\Column(name="video", type="string", length=255, nullable=false)
*/
private $video;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=false)
*/
private $description;
/**
* #ORM\ManyToOne(targetEntity="ItemDetails")
* #ORM\JoinColumn(name="course_id", referencedColumnName="id")
**/
private $itemDetails;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set courseId
*
* #param integer $courseId
* #return Video
*/
public function setCourseId($courseId)
{
$this->courseId = $courseId;
return $this;
}
/**
* Get courseId
*
* #return integer
*/
public function getCourseId()
{
return $this->courseId;
}
/**
* Set video
*
* #param string $video
* #return Video
*/
public function setVideo($video)
{
$this->video = $video;
return $this;
}
/**
* Get video
*
* #return string
*/
public function getVideo()
{
return $this->video;
}
/**
* Set description
*
* #param string $description
* #return Video
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set itemDetails
*
* #param \Mooc\AdminBundle\Entity\ItemDetails $itemDetails
* #return Video
*/
public function setItemDetails(\Mooc\AdminBundle\Entity\ItemDetails $itemDetails = null)
{
$this->itemDetails = $itemDetails;
return $this;
}
/**
* Get itemDetails
*
* #return \Mooc\AdminBundle\Entity\ItemDetails
*/
public function getItemDetails()
{
return $this->itemDetails;
}
}
Course form code :
namespace Mooc\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Doctrine\ORM\EntityRepository;
class CourseType extends AbstractType
{
private $languageId;
public function __construct($languageId) {
$this->languageId=$languageId;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$session = new Session();
$languageId=$session->get('languageId');
$builder
->add('languageId', 'entity', array(
'class' => 'MoocAdminBundle:Language',
'property' => 'name',
'preferred_choices' => array((object)$languageId),
))
->add('categoryId', 'entity', array(
'class' => 'MoocAdminBundle:CourseCategory',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.languageId ='.$this->languageId);
},
))
->add('details', new ItemDetailsType())
->add('submit', 'submit', array('attr' => array('class' => 'btn btn-success btn-large')));
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mooc\AdminBundle\Entity\Course'
));
}
/**
* #return string
*/
public function getName()
{
return 'mooc_adminbundle_course';
}
}
ItemDetails form :
namespace Mooc\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ItemDetailsType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('startDate')
->add('endDate')
->add('description')
->add('preRequisites')
->add('picture','file')
->add('acquiredKnowledges')
->add('status')
->add('video', new VideoType())
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mooc\AdminBundle\Entity\ItemDetails'
));
}
/**
* #return string
*/
public function getName()
{
return 'mooc_adminbundle_itemdetails';
}
}
video Form :
<?php
namespace Mooc\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class VideoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('courseId')
->add('video','file')
->add('description','textarea')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mooc\AdminBundle\Entity\Video'
));
}
/**
* #return string
*/
public function getName()
{
return 'mooc_adminbundle_video';
}
}
Code in controller :
public function courseaddAction() {
$locale=$this->get('session')->get('_locale');
$language=$this->get('doctrine.orm.entity_manager')->getRepository('MoocAdminBundle:Language')->findBy(array('locale'=>$locale));
$languageId=$language[0]->getId();
$form=$this->createForm(new CourseType($languageId));
if ($this->getRequest()->isMethod("POST")) {
$form->HandleRequest($this->getRequest());
//if ($form->isValid()) {
$course = $form->getData();
$this->get('mooc.course')->addCourse($course);
//}
}
return $this->render('MoocTeacherBundle:Default:courseadd.html.twig', array('form'=>$form->createView(),'languageId'=>$languageId));
}
Code in service :
public function addCourse($course) {
$details=$course->getDetails();
$this->em->persist($course);
/*
foreach($course->getDetails() as $details) {
$details->setItemType('course');
$this->em->persist($details);
foreach($details->getVideo() as $video) {
$this->em->persist($video);
}
}
*/
$this->em->flush();
}
You need to set ItemId, in example from form:
$builder
->add('name')
->add('startDate')
->add('itemId') //add this line
->add('endDate')
->add('description')
->add('preRequisites')
->add('picture','file')
->add('acquiredKnowledges')
->add('status')
->add('video', new VideoType())
;
or change nullable value to true
/**
* #var integer
*
* #ORM\Column(name="item_id", type="integer", nullable=true) //here nullable to true
*/
private $itemId;

Symfony 2 - Multiple form fields for one database row

I'm using Symfony 2.1 and Doctrine 2.
I'm dealing with 2 main entities : Place and Feature, with a ManyToMany relationship between them.
There's many features in the database, and to group them by theme the Feature is also related to a FeatureCategory entity with a ManyToOne relationship.
Here's the code of the different entities :
The Place entity
namespace Mv\PlaceBundle\Entity;
…
/**
* Mv\PlaceBundle\Entity\Place
*
* #ORM\Table(name="place")
* #ORM\Entity(repositoryClass="Mv\PlaceBundle\Entity\Repository\PlaceRepository")
* #ORM\HasLifecycleCallbacks
*/
class Place
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
* #Assert\NotBlank
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="\Mv\MainBundle\Entity\Feature")
* #ORM\JoinTable(name="places_features",
* joinColumns={#ORM\JoinColumn(name="place_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="feature_id", referencedColumnName="id")}
* )
*/
private $features;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Place
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Add features
*
* #param \Mv\MainBundle\Entity\Feature $features
* #return Place
*/
public function addFeature(\Mv\MainBundle\Entity\Feature $features)
{
$this->features[] = $features;
echo 'Add "'.$features.'" - Total '.count($this->features).'<br />';
return $this;
}
/**
* Remove features
*
* #param \Mv\MainBundle\Entity\Feature $features
*/
public function removeFeature(\Mv\MainBundle\Entity\Feature $features)
{
$this->features->removeElement($features);
}
/**
* Get features
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFeatures()
{
return $this->features;
}
public function __construct()
{
$this->features = new \Doctrine\Common\Collections\ArrayCollection();
}
The Feature Entity :
namespace Mv\MainBundle\Entity;
…
/**
* #ORM\Entity
* #ORM\Table(name="feature")
* #ORM\HasLifecycleCallbacks
*/
class Feature
{
use KrToolsTraits\PictureTrait;
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="label", type="string", length=255)
* #Assert\NotBlank()
*/
protected $label;
/**
* #ORM\ManyToOne(targetEntity="\Mv\MainBundle\Entity\FeatureCategory", inversedBy="features", cascade={"persist"})
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set label
*
* #param string $label
* #return Feature
*/
public function setLabel($label)
{
$this->label = $label;
return $this;
}
/**
* Get label
*
* #return string
*/
public function getLabel()
{
return $this->label;
}
/**
* Set category
*
* #param Mv\MainBundle\Entity\FeatureCategory $category
* #return Feature
*/
public function setCategory(\Mv\MainBundle\Entity\FeatureCategory $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return Mv\MainBundle\Entity\FeatureCategory
*/
public function getCategory()
{
return $this->category;
}
}
The FeatureCategory entity :
namespace Mv\MainBundle\Entity;
...
/**
* #ORM\Entity
* #ORM\Table(name="feature_category")
*/
class FeatureCategory
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="code", type="string", length=255)
* #Assert\NotBlank()
*/
protected $code;
/**
* #ORM\Column(name="label", type="string", length=255)
* #Assert\NotBlank()
*/
protected $label;
/**
* #ORM\OneToMany(targetEntity="\Mv\MainBundle\Entity\Feature", mappedBy="category", cascade={"persist", "remove"}, orphanRemoval=true)
* #Assert\Valid()
*/
private $features;
public function __construct()
{
$this->features = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set code
*
* #param string $code
* #return Feature
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set label
*
* #param string $label
* #return Feature
*/
public function setLabel($label)
{
$this->label = $label;
return $this;
}
/**
* Get label
*
* #return string
*/
public function getLabel()
{
return $this->label;
}
/**
* Add features
*
* #param \Mv\MainBundle\Entity\Feature $features
*/
public function addFeatures(\Mv\MainBundle\Entity\Feature $features){
$features->setCategory($this);
$this->features[] = $features;
}
/**
* Get features
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFeatures()
{
return $this->features;
}
/*
* Set features
*/
public function setFeatures(\Doctrine\Common\Collections\Collection $features)
{
foreach ($features as $feature)
{
$feature->setCategory($this);
}
$this->features = $features;
}
/**
* Remove features
*
* #param Mv\MainBundle\Entity\Feature $features
*/
public function removeFeature(\Mv\MainBundle\Entity\Feature $features)
{
$this->features->removeElement($features);
}
/**
* Add features
*
* #param Mv\MainBundle\Entity\Feature $features
* #return FeatureCategory
*/
public function addFeature(\Mv\MainBundle\Entity\Feature $features)
{
$features->setCategory($this);
$this->features[] = $features;
}
}
Feature table is already populated, and users won't be able to add features but only to select them in a form collection to link them to the Place.
(The Feature entity is for the moment only linked to Places but will be later related to others entities from my application, and will contain all the features available for all entities)
In the Place form I need to display checkboxes of the features available for a Place, but I need to display them grouped by category.
Example :
Visits (FeatureCategory - code VIS) :
Free (Feature)
Paying (Feature)
Languages spoken (FeatureCategory - code LAN) :
English (Feature)
French (Feature)
Spanish (Feature)
My idea
Use virtual forms in my PlaceType form, like this :
$builder
->add('name')
->add('visit', new FeatureType('VIS'), array(
'data_class' => 'Mv\PlaceBundle\Entity\Place'
))
->add('language', new FeatureType('LAN'), array(
'data_class' => 'Mv\PlaceBundle\Entity\Place'
));
And create a FeatureType virtual form, like this :
class FeatureType extends AbstractType
{
protected $codeCat;
public function __construct($codeCat)
{
$this->codeCat = $codeCat;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('features', 'entity', array(
'class' => 'MvMainBundle:Feature',
'query_builder' => function(EntityRepository $er)
{
return $er->createQueryBuilder('f')
->leftJoin('f.category', 'c')
->andWhere('c.code = :codeCat')
->setParameter('codeCat', $this->codeCat)
->orderBy('f.position', 'ASC');
},
'expanded' => true,
'multiple' => true
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'virtual' => true
));
}
public function getName()
{
return 'features';
}
}
With this solution I get what I want but the bind process doesn't persist all the features. Instead of grouping them, it only keeps me and persist the last group "language", and erases all the previouses features datas. To see it in action, if I check the 5 checkboxes, it gets well into the Place->addFeature() function 5 times, but the length of the features arrayCollection is successively : 1, 2, 1, 2, 3.
Any idea on how to do it another way ? If I need to change the model I'm still able to do it.
What is the best way, reusable on my future other entities also related to Feature, to handle this ?
Thank you guys.
I think your original need is only about templating.
So you should not tweak the form and entity persistence logic to get the desired autogenerated form.
You should go back to a basic form
$builder
->add('name')
->add('features', 'entity', array(
'class' => 'MvMainBundle:Feature',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('f')
//order by category.xxx, f.position
},
'expanded' => true,
'multiple' => true
));
And tweak your form.html.twig

Resources