I'm trying to build a very simple rest api with FOS Rest bundle.
The GET and DELETE methods were easy, but I'm struggling with post.
Here's my post method of a very simple entity (only has a "name" and "active" property):
/**
* #param Request $request
* #return array|View
*/
public function postSkillsAction(Request $request)
{
$skill = new Skill();
$form = $this->createForm(SkillType::class, $skill);
$form->submit($request->request->get($form->getName()));
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($skill);
$this->entityManager->flush();
return $this->redirectToRoute('skillset', ['id' => $skill->getId()], Response::HTTP_CREATED);
}
return [
'form' => $form
];
}
And this is my form:
final class SkillType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) : void
{
$builder
->add(
'name', TextType::class, [
'label' => 'fields.name'
])
->add('active', CheckboxType::class, [
'label' => 'fields.active',
'required' => false
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Skill::class,
'csrf_protection' => false
]);
}
/**
* #return string
*/
public function getName() : string
{
return 'skill';
}
}
The problem is that it seems like the form is not filling the entity data, when I execute /api/skills sending by post name and active I get the following SQL error
An exception occurred while executing 'INSERT INTO skill (name, active, created_at, updated_at) VALUES (?, ?, ?, ?)' with params [null, 0, "2017-03-19 19:49:37", "2017-03-19 19:49:37"]
The form data arrives correctly, I've debug and if I do $request->request->get('name') I got the proper value.
I couldn't find any updated example, this one for instance is for symfony 2, although I tried to follow it as much as possible
https://github.com/gimler/symfony-rest-edition/blob/2.7/src/AppBundle/Controller/NoteController.php
UPDATE
If I do a var_dump of var_dump($request->request->all()); I get
array(
'name' => a,
'active' => 1
)
And here's the entity
final class Skill
{
use OptionalDateTimeTrait;
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #var integer
*/
private $active;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
* #return Skill
*/
public function setId(int $id) : self
{
$this->id = $id;
return $this;
}
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #param string $name
* #return self
*/
public function setName(string $name) : self
{
$this->name = $name;
return $this;
}
/**
* Set active
*
* #param boolean $active
*
* #return Skill
*/
public function setActive($active) : self
{
$this->active = $active;
return $this;
}
/**
* Get active
*
* #return boolean
*/
public function getActive()
{
return $this->active;
}
}
As you've told us, when you're calling $request->request->get('name') you get correct value. Please take a look at your code where you pass data to form:
$form->submit($request->request->get($form->getName()));
This line of code means that you're passing to form only those data, which comes as array named as your form (in your case - skill). In this case you should pass through POST such data:
skill[name]=John
skill[active]=1
If you want not to send data through POST using array wrapper, you have to submit to your form whole request:
$form->submit($request->request->all());
Both methods are technically correct, but second is in fact anti-pattern.
Related
The error in question:
Entity of type App\Entity\Nutt is missing an assigned ID for field 'squirrel'.
The identifier generation strategy for this entity requires the ID field to be populated before EntityManager#persist() is called.
If you want automatically generated identifiers instead you need to adjust the metadata mapping accordingly.
I'm perfectly able to call the api POST to add a Squirrel entity into the database.
And using the id of this Squirrel, I can preform the POST call for the Nutt entity with the result being a correctly related record in the Nutt tabel.
What I can't seem to get working, is allowing the Squirrel api call to include the related collection of Nutts I want to insert in the same api call.
What am I doing wrong?
The JSON call:
{
"name": "Jake",
"nutts": [
{
"size": 10,
"color": "blue"
}
]
}
Entity Squirrel
/**
* #ORM\Entity
* #ORM\Table(name="squirrel")
*/
class Squirrel {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
* #Assert\NotBlank()
*
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Nutt", mappedBy="squirrel", cascade={"persist", "remove"})
*/
private $nutts;
public function __construct()
{
$this->nutts = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getNutts(): ?Collection
{
return $this->nutts;
}
public function setNutts(Collection $nutts)
{
foreach ($nutts as $nutt)
{
$this->nutts->add($nutt);
}
}
public function addNutt(Nutt $nutt): Squirrel
{
$this->nutts->add($nutt);
return $this;
}
}
Entity Squirrel Is updated.
setNutts has been changed to:
public function setNutts(Collection $nutts)
{
foreach ($nutts as $nutt)
{
$nutt->setSquirrel($this);
$this->nutts->add($nutt);
}
}
Entity Nutt
/**
* #ORM\Entity
* #ORM\Table(name="nutt")
*/
class Nutt {
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Squirrel", inversedBy="nutts")
* #ORM\Id
*/
private $squirrel;
/**
* #ORM\Column(type="integer")
* #ORM\Id
*/
private $size;
/**
* #ORM\Column(type="text")
* #Assert\NotBlank()
*/
private $color;
/**
* #return Squirrel|null
*/
public function getSquirrel(): ?Squirrel
{
return $this->squirrel;
}
/**
* #param Squirrel|null $squirrel
* #return $this
*/
public function setSquirrel(?Squirrel $squirrel): self
{
$this->squirrel = $squirrel;
return $this;
}
//getters and setters for the rest
}
Entity Nutt has been updated.
Property $squirrel has its id notation removed as it is a relation:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Squirrel", inversedBy="nutts")
*/
private $squirrel;
SquirrelController
/**
* Squirrel controller.
* #Route("/api", name="api_")
*/
class SquirrelController extends AbstractFOSRestController
{
/**
* Lists all Squirrels.
* #Rest\Get("/squirrels")
* #return Response
*/
public function getSquirrelAction()
{
$repository = $this->getDoctrine()->getRepository(Squirrel::class);
$squirrels = $repository->findall();
return $this->handleView($this->view($squirrels));
}
/**
* Create Squirrel.
* #Rest\Post("/squirrel")
*
* #return Response
*/
public function postSquirrelAction(Request $request)
{
$squirrel = new Squirrel();
$form = $this->createForm(SquirrelType::class, $squirrel);
$data = json_decode($request->getContent(), true);
$form->submit($data);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($squirrel);
$em->flush();
return $this->handleView($this->view(['status' => 'ok'], Response::HTTP_CREATED));
}
return $this->handleView($this->view($form->getErrors()));
}
}
And my current focus
The Squirrel Form
class SquirrelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add(
'nutts',
CollectionType::class, [
'entry_type' => NuttType::class,
'allow_add' => true,
'by_reference' => false
])
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Squirrel::class,
'csrf_protection' => false
));
}
}
There is a nutt form but it works fine.
Question has been solved by #mel in a comment
The Id annotation declares the column as a primary key, so it becomes mandatory. But it's not needed in this case, since squirrel is a relation.
The error itself also hints at the field being null when saving the entity, so setSquirrel is not being called.
You can remove the annotation from size as well.
I have an error from symfony 4 "Unable to guess how to get a Doctrine instance from the request information for parameter "person_id", already tried options for related questions which I found on stackoverflow, but all of them suggest to solve this with #paramconverter, but this method has something to do with #route, I don't think thats what I need.
Here is the code in controller:
/**
* #Route("/skill/new/", name="new_skill")
* Method({"GET", "POST"})
* #param Request $request
* #param Person $person_id
* #return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function new(Request $request, Person $person_id) {
$skill = new Skill();
$form = $this->createFormBuilder($skill)
->add('name', TextType::class, array('attr' => array('class' => 'form-control')))
->add('level', TextareaType::class, array(
'attr' => array('class' => 'form-control')
))
->add('save', SubmitType::class, array(
'label' => 'Create',
'attr' => array('class' => 'btn btn-primary mt-3')
))
->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$skill = $form->getData();
$entityManager = $this->getDoctrine()->getManager();
$person = $entityManager->getRepository(Person::class)->find($person_id);
$person->addSkill($skill);
$entityManager->persist($skill);
$entityManager->persist($person);
$entityManager->flush();
return $this->redirectToRoute('skill_list');
}
return $this->render('main/new.html.twig', array(
'form' => $form->createView()
));
}
and from Person entity
class Person
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Skill", mappedBy="person")
*/
private $skills;
public function __construct()
{
$this->skills = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Skill[]
*/
public function getSkills(): Collection
{
return $this->skills;
}
public function addSkill(Skill $skill): self
{
if (!$this->skills->contains($skill)) {
$this->skills[] = $skill;
$skill->setPerson($this);
}
return $this;
}
public function removeSkill(Skill $skill): self
{
if ($this->skills->contains($skill)) {
$this->skills->removeElement($skill);
// set the owning side to null (unless already changed)
if ($skill->getPerson() === $this) {
$skill->setPerson(null);
}
}
return $this;
}
}
With #paramconverter I wrote id param in route like " * #Route("/skill/new/{id}", name="new_skill", but he is giving another error "No route found for "GET /skill/new"
What I am trying to achieve is that when I create new skill it binds to specific person with specific id, so I made ManyToOne assosiation. So, while Im on route "/person/{{ person.id }}", I need to add skill to this specific id, not everyone.
I guess I made a mistake writing person_id on function arguments, but otherwise it cant find this param in entitymanager. How can I solve this?
The problem is inside the Route definition and method signature. Symfony has no way to infer which Person $person_id it should fetch. If you want this to be an entity you should assign a url parameter for the id, e.g.
#Route("/skill/new/{person_id}", name="new_skill")
This will change the URL from http://example.com/skill/new to http://example.com/skill/new/123 where 123 is the id you want to fetch the Person-object for. Now you MUST have a person id in your URL otherwise the route will not match (as you have already noticed). You can make this optional by changing the method signature:
/**
* #Route("/skill/new/{person_id}", name="new_skill")
* Method({"GET", "POST"})
* #param Request $request
* #param Person $person_id
* #return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function new(Request $request, Person $person_id = null) {
By allowing null for $person_id the url parameter should be optional, so you should be able to use http://example.com/skill/skill/new or http://example.com/skill/skill/new/123.
If you don't want the entity and only want a way to optionally fetch it from the URL without explicitly specifying route parameter you could just change the code a bit:
/**
* #Route("/skill/new", name="new_skill")
* Method({"GET", "POST"})
* #param Request $request
* #param Person $person_id
* #return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function new(Request $request) {
$person_id = $request->query->get('person_id');
...
If you now take your existing URL and add a URL parameter it will be read in your action, e.g. http://example.com/skill/new?person_id=1234 will set $person_id to 1234. When you don't specify the parameter it will be null.
Symfony also has debug commands that help you check what routes there are and whether they match:
bin/console debug:router
bin/console router:match /skill/new
I'm trying since some of hour to add a \DateTime validation in a form builder
The Customer entity (Sample):
class Customer
{
/**
* #Assert\NotBlank
* #Assert\Type("\DateTime")
* #MongoDB\Field(type="date")
*/
private $birthDate;
/**
* #return \DateTime
*/
public function getBirthDate() : ? \DateTime
{
return $this->birthDate;
}
/**
* #param int $birthDate
*/
public function setBirthDate(int $birthDate) : self
{
$this->birthDate = (new \DateTime())->setTimeStamp($birthDate);
return $this;
}
}
The form builder is like this (Sample):
class CustomerType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('birthDate', DateTimeType::class, ['data_class' => \DateTime::class]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Customer::class,
'csrf_protection' => false
]);
}
}
When I want to submit the form, I received always a error:
The form's view data is expected to be an instance of class DateTime, but is a(n) array. You can avoid this error by setting the \"data_class\" option to null or by adding a view transformer that transforms a(n) array to an instance of DateTime.
The user is sending the good parameter:
"customer": {
"birthDate": 30459425029458,
}
I tryed to follow this solution without success
Symfony2.1 form date field: Argument 1 passed to ... must be an instance of DateTime
And the view transformer return a null value
$builder->get('birthDate')
->addModelTransformer(new CallbackTransformer(
function ($tagsAsArray) {
// transform the array to a string
return implode(', ', $tagsAsArray);
},
function ($tagsAsString) {
// transform the string back to an array
return explode(', ', $tagsAsString);
}
))
;
I tried to switch data-class option to null but the form send me a no valid value error.
How can I validate a instance of \DateTime in formBuilder?
Thanks in advance
EDIT:
Temporary bad solution:
if(isset($params['customer']['birthDate'])) {
$customer->setBirthDate($params['customer']['birthDate']);
} else {
$this->error = ['message' => 'birthDate missing'];
return false;
}
I'm having a problem with Symfony options resolver where I need to specify a list of defined variables that should be normalized.
The problem is: I don't want to define all these variables again in
$resolver->setDefined();
because I have a list of defined fields in $builder and the same fields are defined in the entity SlotRequest.
Is there a different way of assigning all fields/variables from entity to resolver?
First approach:
$resolver->setDefined([
'date_form','etc..' ]);
But, it pointless because in the real world I have to normalize 10+ variables + 20 fields)
Second approach would be to parse all annotations from the entity 'SlotRequest', and then fill up an array with that object.
$resolver->setDefined($anArrayOfParsedFieldsFromEntity);
Is there a better way of doing this?
An example of using:
In controller:
$form = $this->createForm(new SlotRequestType(), new SlotRequest());
SlotRequestType:
class SlotRequestType extends AbstractType
{
/**
* #var CCriteria
*/
protected $resolved = null;
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date_from',null,['property_path'=>'dateFrom']);
//more fields
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$this->resolved = new \CCriteria();
$resolver->setDefaults(array(
'data_class' => SlotRequest::class,
'allow_extra_fields' => true,
'method' => 'GET'
));
$resolver->setDefined([....]);// the list of fields
$resolver->setNormalizer('date_from', function (Options $options, $value) {
$dateFrom = new \DateTime($value);
$this->resolved->setStartDate($dateFrom->getTimestamp());
return $value;
});
//more normalizers
}
/**
* #return null
*/
public function getName()
{
return null;
}
/**
* #return CCriteria
*/
public function getResolved()
{
return $this->resolved;
}
Entity SlotRequest
<?php
namespace Test/Entity;
class SlotRequest
{
/**
* #var string
* #Assert\NotBlank(message="Parameter [date_from] is missing.")
* #Assert\Type(
* type="string",
* message="The value {{ value }} is not a valid {{ type }}."
* )
* #Assert\Date()
*/
public $dateFrom;
//more fields
}
I have a form for inserting an entity Category. This entity has two other entities that are related to it.
One related entity is an other separate Entity Group. The other entity is itself self-referenced Category that is an array collection that represents preconditions. So far so good, i can persist the main entity with the relations with the correct ORM annotations.
Rough scheme of Category
id : int
title: string
group : Group obj
preconditions : [Category obj, Category obj, ...]
I made an type class for creating the form as described as best-practice in the documentation.
$form = $this->createForm(new CategoryType($em));
Situation
Before i persist the entity, i must initialize it and set the posted datas to it. The posted related objects can’t simply setted to the persisting entity, because they have the wrong datatype. (E.g. the self-referencing collection is posted only as array with id’s, and not an array collection of the choosed items.)
So i catch this raw datas and get separatelly the related entities from the entity manager.
Goal
The inserting entity should be filled automatically with the related entities, whitout get those separately through the entity manager
Question
Is this the meaning of the form component that those related objects are not posted and made available fully? Or what im missing in my implementation?
Is there a way to do this more automated?
On the form class for the ‘preconditions’ property i had to do mapped => false otherwise i recieve an exception that a wrong type was passed. But at the end i want that the form matches all automatically through the mapping, whitout skipping a mapping, and whitout getting the related entities separately from the entity manager.
class CategoryType extends AbstractType
{
public function __construct($em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$qb = $this->em->createQueryBuilder();
$categories = $qb->select('e.id, e.title')
->from('MyvendorCoreBundle:Category', 'e')
->indexBy('e', 'e.id')
->orderBy('e.title')
->getQuery()
->getResult();
$categories_choice = array_map(function ($value) {
return $value['title'];
}, $categories);
$builder->add('title')
->add('group_Id', new GroupType($this->em))
->add('preconditions', 'choice', array(
'choices' => $categories_choice,
'multiple' => true,
'mapped' => false
))
->add('save', 'submit');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Myvendor\CoreBundle\Entity\Category'
));
}
public function getName()
{
return 'category';
}
}
Controller method
public function newAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(new CategoryType($em));
// Repopulating the form after submission
$form->handleRequest($request);
// Prepare a new empty Category
$category = new Category();
if ($form->isValid()) {
/* Catch some raw datas posted from the form */
// Posted precondition category ids to get its entities more later
$precondition_category_ids = $form->get('preconditions')->getData();
// Posted group entity that have only filled the group id in the object
$group_raw = $form->get('group_Id')->getData();
// Get the explicit filled group entity throuth the posted id.
$group = $em->find('MyvendorCoreBundle:Group', $group_raw->getGroupid());
// Fill the prepaired group with the posted datas
$category->setTitle($form->get('title')->getData());
$category->setGroupId($group);
// Adding preconditions
try {
for ($i = 0; count($precondition_category_ids) > $i; $i ++) {
$precondition_category_id = $precondition_category_ids[$i];
if (0 >= $precondition_category_id) { // Retrieving id must be greater than 0
throw new \Exception('Error retrieving precondition id');
}
$precondition_category = $em->find('MyvendorCoreBundle:Category', $precondition_category_id);
if ($precondition_category instanceof Category) {
$category->addPrecondition($precondition_category);
} else {
throw new \Exception('Error retrieving precondition as Myvendor\CoreBundle\Entity\Category');
}
}
$em->persist($category); // Insert the group item with its relations
$em->flush();
echo '<h1 style="color:green">persisted</h1>';
} catch (\Exception $e) {
echo '<h1 style="color:red">' . $e->getMessage() . '</h1>';
}
}
return $this->render('MyvendorCoreBundle:fbm:new.html.twig', array(
'form' => $form->createView()
));
}
GroupType
class GroupType extends AbstractType
{
public function __construct($em){
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$groups = $this->em->createQuery("
SELECT o.groupid, o.descr
FROM MyvendorCoreBundle:Group o
INDEX BY o.groupid
ORDER BY o.descr
")->getResult();
$groups_dropdown = array();
$groups_dropdown = array_map(function($value) { return $value['descr']; }, $groups);
$builder->add('groupid', 'choice', array(
'label' => false,
'choices' => $groups_dropdown,
'attr' => array('style' => 'width: 300px')
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Myvendor\CoreBundle\Entity\Group',
));
}
public function getName()
{
return 'group';
}
}
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
public function __construct()
{
$this->preconditions = new ArrayCollection();
}
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var \Myvendor\CoreBundle\Entity\Group
*
* #Assert\Type(type="Myvendor\CoreBundle\Entity\Group")
* #Assert\Valid()
* #ORM\ManyToOne(targetEntity="Myvendor\CoreBundle\Entity\Group", inversedBy="Category")
* #ORM\JoinColumn(name="group_id", nullable=false, referencedColumnName="groupid")
*/
private $group_Id;
/**
* #var string
* #Assert\NotBlank()
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $title;
/**
* Preconditions are Categorys referencing to an Category.
* For a single Category its empty (which have no subelements).
* A join table holds the references of a main Category to its sub-Categorys (preconditions)
*
* #ORM\ManyToMany(targetEntity="Category")
* #ORM\JoinTable(name="category_precondition",
* joinColumns={#JoinColumn(name="category_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="category_precondition_id", referencedColumnName="id")}
* )
*/
private $preconditions;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Category
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set groupId
*
* #param \Myvendor\CoreBundle\Entity\Group $groupId
*
* #return Category
*/
public function setGroupId(\Myvendor\CoreBundle\Entity\Group $groupId)
{
$this->group_Id = $groupId;
return $this;
}
/**
* Get groupId
*
* #return \Myvendor\CoreBundle\Entity\Group
*/
public function getGroupId()
{
return $this->group_Id;
}
/**
* Add precondition
*
* #param \Myvendor\CoreBundle\Entity\Category $precondition
*
* #return $this
*/
public function addPrecondition(\Myvendor\CoreBundle\Entity\Category $precondition)
{
$this->preconditions[] = $precondition;
return $this;
}
/**
* Get preconditions
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPreconditions()
{
return $this->preconditions;
}
/**
* Group
*
* #ORM\Table(name="group", indexes={#ORM\Index(name="homepage", columns={"homepage"}), #ORM\Index(name="theme", columns={"theme"})})
* #ORM\Entity
*/
class Group
{
/**
* #var string
*
* #ORM\Column(name="descr", type="string", length=60, nullable=true)
*/
private $descr;
/**
* #var integer
*
* #Assert\NotBlank()
* #ORM\Column(name="groupid", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
public $groupid;
/**
* Set descr
*
* #param string $descr
* #return Group
*/
public function setDescr($descr)
{
$this->descr = $descr;
return $this;
}
/**
* Get descr
*
* #return string
*/
public function getDescr()
{
return $this->descr;
}
/**
* Get groupid
*
* #return integer
*/
public function getGroupid()
{
return $this->groupid;
}
}
The solution was that the type of the selecting choice entities, must be not a choicelist, but really an collection type.
So use something like this
->add('preconditions', 'collection', array(
'entry_type' => 'entity',
'entry_options' => array(
'class' => 'MyVendorCoreBundle:EduStructItem',
'choice_label' => 'title'
),
'allow_add' => true,
'allow_delete' => true
))
instead of
->add('preconditions', 'choice', array(
'choices' => $categories_choice,
'multiple' => true,
'mapped' => false
))