Symfony ArrayCollection - symfony

I have 2 entities:
Category and Article with the following relation:
Each Article has One Category.
Each Category may have Many Article.
The problem :
I want to add new category using this form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('submit', SubmitType::class, [
'label' => 'Add a new category'
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Category::class,
]);
}
Here's the error I get when I enter a string value in the field name:
Name Error This value should be of type array|IteratorAggregate.
I've never used ArrayCollection / Relation in symfony but I've tried to change the TextType::class to CollectionType::class it gives me a white square and I can't type anything.
In a more generic question :
How could I validate values in a form without having the error : This value should be of type array|IteratorAggregate.
Category
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\Unique(message="Cette catégorie existe déjà")
*/
private $name;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param $name
* #return Collection|null
*/
public function setName($name): ?Collection
{
return $this->name = $name;
}
Article
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $title;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $image;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $editedAt;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category")
*/
private $category;

Related

Error : Could not determine access type for property "articlesCategories" in class "Articles"

I am using Symfony 3.4.7 . I work with 3 linked entites, Article, Categorie and ArticlesCategories. ArticlesCategories is the relation table.
I would like to add and edit articles. I wish I could Add / Edit articles, given that an article may have several Categories and vice versa. I have attributes specific to the relationship in the relation table , that's why I created the relation entity.
This is the code of an Articles :
/**
* Articles
*
* #ORM\Table(name="articles")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ArticlesRepository")
*/
class Articles
{
/**
* #var string
*
* #ORM\Column(name="code_article", type="string", length=10)
* #ORM\Id
*/
private $codeArticle;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* #var ArticlesCategories
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\ArticlesCategories", mappedBy="codeArticle")
*/
private $articlesCategories;
// getters et setters normaux
...
/**
* Add articlesCategorie
*
* #param ArticlesCategories $articleCategorie
*
* #return Articles
*/
public function addArticlesCategorie(ArticlesCategories $articleCategorie){
$this->articlesCategories[] = $articleCategorie;
$articleCategorie->setCodeArticle($this);
return $this;
}
/**
* remove articlesCategorie
*
* #param ArticlesCategories $articlesCategorie
*/
public function removeArticlesCategorie(ArticlesCategories $articlesCategorie){
$this->articlesCategories->removeElement($articlesCategorie);
}
/**
* Get articlesCategories
*
* #return Collection
*/
public function getArticlesCategories(){
return $this->articlesCategories;
}
public function __toString()
{
return $this->codeArticle;
}
public function __construct()
{
$this->articlesCategories = new ArrayCollection();
}
This is the code of the relation ArticlesCategories:
/**
* ArticlesCategories
*
* #ORM\Table(name="articles_categories")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ArticlesCategoriesRepository")
*/
class ArticlesCategories
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Articles", inversedBy="articlesCategories")
* #ORM\JoinColumn(referencedColumnName="code_article", nullable=false)
*/
private $codeArticle;
/**
* #var string
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Categories")
* #ORM\JoinColumn(referencedColumnName="reference", nullable=false)
*/
private $codeCategorie;
/**
* #var string
*
* #ORM\Column(name="critere_rech_1", type="string", length=45, nullable=true)
*/
private $critereRech1;
/**
* #var string
*
* #ORM\Column(name="critere_rech_2", type="string", length=45, nullable=true)
*/
private $critereRech2;
And my entite Categories has nothing specific.
I generate automatically the crud of my entitie Articles, then I edit the class ArticlesType to have all attributes of my relation ArticlesCategories who are displays. To do this edition I use CollectionType.
This the code of my Form ArticlesType :
class ArticlesType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('codeArticle')
->add('description')
->add('ecotaxe')
->add('qteMaxCde')
->add('publication')
->add('designation')
->add('taxonomie')
->add('referenceStock')
->add('articleRegroupement')
->add('articleAssocie1')
->add('articleAssocie2')
->add('articleAssocie3')
->add('seuilDegressif')
->add('tauxDegressif')
->add('articlesCategories', CollectionType::class, array(
'entry_type' => ArticlesCategoriesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
'label' => 'test',
'attr' => array('class' => 'collection-articlesCategories'),
'auto_initialize' => true
));
}
public function getName(){
return 'Articles';
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Articles'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_articles';
}
}
When I go on the Article edition page that was generates by Symfony, I get the right display.
Edition page
But When I click on the button "Edit", I get this error : Could not determine access type for property "articlesCategories" in class "AppBundle\Entity\Articles".
I don't see where is my mistake.
I hope I am clear.
Thank you for help.
Try putting the following:
public function setArticlesCategories(...) {
...
}

Doctrine cascade persistence never check if object exists Symfony 2

I have a form which save queries. This is organized in the way of a people who ask is saved also this create a threat where is saving the id_question and emailperson. The issue is that the same person does another question. Logically, this report a primary key error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'drj00003#ujaen.es' for key 'PRIMARY'
How do I fix it? In the constructor of class I cannot check if this exists and all registers are generated automatically in cascade without form.
It should be easy but I can't solve this.
class HiloType extends AbstractType{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('consultanteemail', new ConsultanteType(), array ('label' => false))
->add('personalemail', 'entity', array(
'label' => 'Personal de Soporte',
'class' => 'GuiasDocentes\AppBundle\Entity\Personal',
'property' => 'Enunciado',
'by_reference' => 'false',
'query_builder' => function(PersonalRepository $pr) {
$query= $pr->createQueryBuilder('u')
;
return $query;
},
'empty_value' => 'Elige un perfil de consulta:',
))
;
}
...
class Hilo{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \GuiasDocentes\AppBundle\Entity\Personal
*
* #ORM\ManyToOne(targetEntity="Personal", inversedBy="hilos", cascade ={"ALL"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="personalEmail", referencedColumnName="email")
* })
*/
private $personalemail;
/**
* #var \GuiasDocentes\AppBundle\Entity\Consultante
*
* #ORM\ManyToOne(targetEntity="Consultante", inversedBy="hilos", cascade ={"ALL"} )
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="consultanteEmail", referencedColumnName="email")
* })
*/
private $consultanteemail;
/* Customized code */
/**
* #ORM\OneToMany(targetEntity="GuiasDocentes\AppBundle\Entity\Consulta", mappedBy="hiloid")
* #Assert\Valid()
*/
private $consultas;
public function __construct(){
$this->consultas = new ArrayCollection();
}
public function setConsultas (Consulta $consulta){
$this->hilos[]=$consulta;
}
public function addConsulta (\GuiasDocentes\AppBundle\Entity\Consulta $consulta){
$this->hilos[] = $consulta;
}
/* End customized code */
...
class Consultante{
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=50, nullable=false)
* #ORM\Id
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="nombre", type="string", length=30, nullable=true)
*/
private $nombre;
/**
* #var string
*
* #ORM\Column(name="apellidos", type="string", length=50, nullable=true)
*/
private $apellidos;
/* Customized code */
/**
* #ORM\OneToMany(targetEntity="GuiasDocentes\AppBundle\Entity\Hilo", mappedBy="consultanteemail")
* #Assert\Valid()
*/
private $hilos;
public function __construct(){
$this->hilos = new ArrayCollection();
}
public function setHilos (Hilo $hilo){
$this->hilos[]=$hilo;
}
public function addHilo (\GuiasDocentes\AppBundle\Entity\Hilo $hilo){
$this->hilos[] = $hilo;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/* End customize code */
...
public function contactoAction (Request $request){
$session = $request->getSession();
$perfil = $session->get('perfil');
$consultaHasAsignatura = new ConsultaHasAsignatura();
$form = $this->createForm(new ConsultaHasAsignaturaType(), $consultaHasAsignatura);
if ($request->isMethod('POST')){
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($consultaHasAsignatura);
$em->flush();
return $this->redirectToRoute('guias_docentes_app_homepage');
}
}
return $this->render('GuiasDocentesAppBundle:FAQ:contacto.html.twig', array('perfil'=>$perfil,
'form' => $form->createView()
));
}

Symfony + Sonata, OneToMany Relation

Could anyone give me a hand please?
I need to implement the list of books and readers using Sonata admin bundle.
The problem is that I get empty column of read books for each reader in the list.
Forein keys are written okay to the DB.
Please see the code:
ReadersAdmin Class
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('book',
'entity',
array(
'class' => 'AppBundle:Books',
'property' => 'name',
'multiple' => true,
'expanded' => true
)
);
}
/**
* #param \Sonata\AdminBundle\Show\ShowMapper $showMapper
*
* #return void
*/
protected function configureShowField(ShowMapper $showMapper)
{
$showMapper
->add('name')
->add('book')
;
}
/**
* #param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper
*
* #return void
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('name')
->add('book')
->add('_action', 'input', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
ReadersRelations Entity
class ReadersRelations
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Books
*
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Books", inversedBy="ReadersRelations")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $book;
/**
* #var Readers
*
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Readers", inversedBy="ReadersRelations")
* #ORM\JoinColumn(name="reader_id", referencedColumnName="id")
*/
private $reader;
Books Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Books
*
* #ORM\Entity
* #ORM\Table(name="Books")
*/
class Books
{
/**
* #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="author", type="string", length=255)
*
*/
private $author;
/**
* #var ReadersRelations
*
* #ORM\OneToMany(targetEntity="\AppBundle\Entity\ReadersRelations" , mappedBy="Books" , cascade={"all"})
*/
private $readers;
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Books
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set author
*
* #param string $author
* #return Books
*/
public function setAuthor($author)
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* #return string
*/
public function getAuthor()
{
return $this->author;
}
}
Readers Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\ReadersRelations;
/**
* Readers
*
* #ORM\Entity
* #ORM\Table(name="Readers")
*/
class Readers
{
/**
* #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 ReadersRelations
*
* #ORM\OneToMany(targetEntity="\AppBundle\Entity\ReadersRelations" , mappedBy="Readers" , cascade={"all"})
*/
private $book;
private $books;
public function __construct()
{
$this->book = new ArrayCollection();
$this->books = new ArrayCollection();
}
public function __toString()
{
return $this->name;
}
public function getBook()
{
$books = new ArrayCollection();
foreach($books as $p)
{
$books[] = $p->getBook();
}
return $books;
}
public function setBook($books)
{
foreach($books as $p)
{
$po = new ReadersRelations();
$po->setBook($p);
$po->setReader($this);
$this->addPo($po);
}
}
public function addPo($ProductOrder)
{
$this->book[] = $ProductOrder;
}
public function removePo($ProductOrder)
{
return $this->book->removeElement($ProductOrder);
}
class Readers
{
/**
* #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 ReadersRelations
*
* #ORM\OneToMany(targetEntity="\AppBundle\Entity\ReadersRelations" , mappedBy="Readers" , cascade={"all"})
*/
private $book;
private $books;
public function __construct()
{
$this->book = new ArrayCollection();
$this->books = new ArrayCollection();
}
public function __toString()
{
return $this->name;
}
public function getBook()
{
$books = new ArrayCollection();
foreach($books as $p)
{
$books[] = $p->getBook();
}
return $books;
}
public function setBook($books)
{
foreach($books as $p)
{
$po = new ReadersRelations();
$po->setBook($p);
$po->setReader($this);
$this->addPo($po);
}
}
public function addPo($ProductOrder)
{
$this->book[] = $ProductOrder;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Readers
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
So, have you problem for store data using your relationship ? Or juste for get data in list ?
If it's for save data, try to implement Sonata type collection or Sonata type model field types for register new related entries.
Then, if you have any problems, we will check if your entities setters have missing actions.

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

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');
}

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