Nested collection fields in Sonata Admin (2.3) - php

I'm having problems creating my form for creating a course. This is a part of my database scheme for which I'm trying to create a form:
So which I'm trying to do is create a course where I can create sessions and dates (moment) attached to that session. It should look something like this:
In my CourseAdmin class I have:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', 'text', array('label' => 'Naam'))
->add('description', 'textarea', array('label' => 'Beschrijving'))
->add('materials', 'textarea', array('label' => 'Benodigde materialen'))
->add('numberOfParticipants', 'number', array('label' => 'Aantal deelnembers'))
->add('numberOfDays', 'number', array('label' => 'Aantal dagen'))
->add('price', 'number', array('label' => 'Prijs'))
->add('priceKmo', 'number', array('label' => 'KMO-portefeuille Prijs'))
->add('location', 'sonata_type_model', array('expanded' => true, 'by_reference' => false, 'multiple' => true, 'btn_add' => false))
->add('session', 'sonata_type_collection', array(
'by_reference' => false,
'type_options' => array(
// Prevents the "Delete" option from being displayed
'delete' => false,
'delete_options' => array(
// You may otherwise choose to put the field but hide it
'type' => 'hidden',
// In that case, you need to fill in the options as well
'type_options' => array(
'mapped' => false,
'required' => false,
)
)
)
), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
;
}
In my SessionAdmin class I have:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('type', 'text', array('label' => 'Type opleiding (Dag / Avond)'))
->add('moment', 'sonata_type_collection', array(
'by_reference' => false,
'type_options' => array(
// Prevents the "Delete" option from being displayed
'delete' => false,
'delete_options' => array(
// You may otherwise choose to put the field but hide it
'type' => 'hidden',
// In that case, you need to fill in the options as well
'type_options' => array(
'mapped' => false,
'required' => false,
)
)
)
), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
;
}
And in my MomentAdmin class I have:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('time', 'date', array('label' => 'Datum'))
;
}
The problem in my form is when I try to add a moment (date) to my session I get the following error:
FatalErrorException: Error: Call to a member function getName() on null in /myproject/app/cache/dev/classes.php line 9772
So, I can add a session but when I try to add a moment to my session I'm getting the error ... .
When I look in the file classes.php on rule 9771 and 9772 there is:
$childFormBuilder = $this->getChildFormBuilder($formBuilder, $elementId);
$fieldDescription = $admin->getFormFieldDescription($childFormBuilder->getName());
The $childFormBuilder is null.
When I look at that function than I get this:
public function getChildFormBuilder(FormBuilder $formBuilder, $elementId)
{
foreach (new FormBuilderIterator($formBuilder) as $name => $formBuilder) {
if ($name == $elementId) {
return $formBuilder;
}
}
return;
}
When I do a var_dump of $name and $elementId like this:
public function getChildFormBuilder(FormBuilder $formBuilder, $elementId)
{
foreach (new FormBuilderIterator($formBuilder) as $name => $formBuilder) {
var_dump("name: " . $name);
var_dump("elementId: " . $elementId);
if ($name == $elementId) {
return $formBuilder;
}
}
die;
return;
}
And push on the Add new button like the following picture:
Then I get this output:
name: s56cda71d2daa0_name
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_description
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_materials
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_numberOfParticipants
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_numberOfDays
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_price
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_priceKmo
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_location
elementId: s56cda71d2daa0_session_0_moment
name: s56cda71d2daa0_session
elementId: s56cda71d2daa0_session_0_moment
In all my entities I have a __toString function. An example in my Course entity:
public function __toString()
{
if(!is_null($this->name))
{
return $this->name;
}
else{
return "";
}
}
What could be the problem here? I'm really stuck with this. I've also posted an issue on the github repo of Sonata Admin but no answers ...
My entities:
Course Entity:
<?php
namespace Studyx\EnrolmentBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Course
*
* #ORM\Table(name="course")
* #ORM\Entity
*/
class Course
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=false)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="materials", type="text", nullable=true)
*/
private $materials;
/**
* #var integer
*
* #ORM\Column(name="number_of_participants", type="integer", nullable=true)
*/
private $numberOfParticipants;
/**
* #var integer
*
* #ORM\Column(name="number_of_days", type="integer", nullable=true)
*/
private $numberOfDays;
/**
* #var string
*
* #ORM\Column(name="price", type="decimal", nullable=true)
*/
private $price;
/**
* #var string
*
* #ORM\Column(name="price_kmo", type="decimal", nullable=true)
*/
private $priceKmo;
/**
* #var integer
*
* #ORM\Column(name="ID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Studyx\EnrolmentBundle\Entity\Location", inversedBy="course")
* #ORM\JoinTable(name="course_has_location",
* joinColumns={
* #ORM\JoinColumn(name="course_ID", referencedColumnName="ID")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="location_ID", referencedColumnName="ID")
* }
* )
*/
private $location;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Studyx\EnrolmentBundle\Entity\Session", mappedBy="course")
*/
private $session;
/**
* Add session
*
* #param \Studyx\EnrolmentBundle\Entity\Session $session
* #return Session
*/
public function addSession(\Studyx\EnrolmentBundle\Entity\Session $session)
{
$this->session[] = $session;
return $this;
}
/**
* Remove session
*
* #param \Studyx\EnrolmentBundle\Entity\Session $session
*/
public function removeSession(\Studyx\EnrolmentBundle\Entity\Session $session)
{
$this->session->removeElement($session);
}
/**
* Get session
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSession()
{
return $this->session;
}
/**
* Constructor
*/
public function __construct()
{
$this->location = new \Doctrine\Common\Collections\ArrayCollection();
}
public function __toString()
{
if(!is_null($this->name))
{
return $this->name;
}
else{
return "";
}
}
/**
* Set name
*
* #param string $name
* #return Course
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* #param string $description
* #return Course
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set materials
*
* #param string $materials
* #return Course
*/
public function setMaterials($materials)
{
$this->materials = $materials;
return $this;
}
/**
* Get materials
*
* #return string
*/
public function getMaterials()
{
return $this->materials;
}
/**
* Set numberOfParticipants
*
* #param integer $numberOfParticipants
* #return Course
*/
public function setNumberOfParticipants($numberOfParticipants)
{
$this->numberOfParticipants = $numberOfParticipants;
return $this;
}
/**
* Get numberOfParticipants
*
* #return integer
*/
public function getNumberOfParticipants()
{
return $this->numberOfParticipants;
}
/**
* Set numberOfDays
*
* #param integer $numberOfDays
* #return Course
*/
public function setNumberOfDays($numberOfDays)
{
$this->numberOfDays = $numberOfDays;
return $this;
}
/**
* Get numberOfDays
*
* #return integer
*/
public function getNumberOfDays()
{
return $this->numberOfDays;
}
/**
* Set price
*
* #param string $price
* #return Course
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* #return string
*/
public function getPrice()
{
return $this->price;
}
/**
* Set priceKmo
*
* #param string $priceKmo
* #return Course
*/
public function setPriceKmo($priceKmo)
{
$this->priceKmo = $priceKmo;
return $this;
}
/**
* Get priceKmo
*
* #return string
*/
public function getPriceKmo()
{
return $this->priceKmo;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add location
*
* #param \Studyx\EnrolmentBundle\Entity\Location $location
* #return Course
*/
public function addLocation(\Studyx\EnrolmentBundle\Entity\Location $location)
{
$this->location[] = $location;
return $this;
}
/**
* Remove location
*
* #param \Studyx\EnrolmentBundle\Entity\Location $location
*/
public function removeLocation(\Studyx\EnrolmentBundle\Entity\Location $location)
{
$this->location->removeElement($location);
}
/**
* Get location
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLocation()
{
return $this->location;
}
}
Session Entity:
<?php
namespace Studyx\EnrolmentBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Session
*
* #ORM\Table(name="session")
* #ORM\Entity
*/
class Session
{
/**
* #var string
*
* #ORM\Column(name="type", type="string", length=45, nullable=false)
*/
private $type;
/**
* #var integer
*
* #ORM\Column(name="ID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Studyx\EnrolmentBundle\Entity\Course
*
* #ORM\ManyToOne(targetEntity="Studyx\EnrolmentBundle\Entity\Course", inversedBy="session")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="course_ID", referencedColumnName="ID")
* })
*/
private $course;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Studyx\EnrolmentBundle\Entity\Moment", mappedBy="session")
*/
private $moment;
/**
* Add moment
*
* #param \Studyx\EnrolmentBundle\Entity\Moment $moment
* #return Moment
*/
public function addMoment(\Studyx\EnrolmentBundle\Entity\Moment $moment)
{
$this->moment[] = $moment;
return $this;
}
/**
* Remove moment
*
* #param \Studyx\EnrolmentBundle\Entity\Moment $moment
*/
public function removeMoment(\Studyx\EnrolmentBundle\Entity\Moment $moment)
{
$this->moment->removeElement($moment);
}
/**
* Get moment
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMoment()
{
return $this->moment;
}
public function __toString()
{
if(!is_null($this->type))
{
return $this->type;
}
else{
return "";
}
}
/**
* Set type
*
* #param string $type
* #return Session
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Get type
*
* #return string
*/
public function getType()
{
return $this->type;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set course
*
* #param \Studyx\EnrolmentBundle\Entity\Course $course
* #return Session
*/
public function setCourse(\Studyx\EnrolmentBundle\Entity\Course $course = null)
{
$this->course = $course;
return $this;
}
/**
* Get course
*
* #return \Studyx\EnrolmentBundle\Entity\Course
*/
public function getCourse()
{
return $this->course;
}
}
Moment Entity:
<?php
namespace Studyx\EnrolmentBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Moment
*
* #ORM\Table(name="moment")
* #ORM\Entity
*/
class Moment
{
/**
* #var \DateTime
*
* #ORM\Column(name="time", type="datetime", nullable=false)
*/
private $time;
/**
* #var integer
*
* #ORM\Column(name="ID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Studyx\EnrolmentBundle\Entity\Session
*
* #ORM\ManyToOne(targetEntity="Studyx\EnrolmentBundle\Entity\Session")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="session_ID", referencedColumnName="ID")
* })
*/
private $session;
public function __toString()
{
if(!is_null($this->time))
{
return $this->time;
}
else{
return "";
}
}
/**
* Set time
*
* #param \DateTime $time
* #return Moment
*/
public function setTime($time)
{
$this->time = $time;
return $this;
}
/**
* Get time
*
* #return \DateTime
*/
public function getTime()
{
return $this->time;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set session
*
* #param \Studyx\EnrolmentBundle\Entity\Session $session
* #return Moment
*/
public function setSession(\Studyx\EnrolmentBundle\Entity\Session $session = null)
{
$this->session = $session;
return $this;
}
/**
* Get session
*
* #return \Studyx\EnrolmentBundle\Entity\Session
*/
public function getSession()
{
return $this->session;
}
}
UPDATE:
I've added some var_dumps to my function getChildFormBuilder like this:
public function getChildFormBuilder(FormBuilder $formBuilder, $elementId)
{
foreach (new FormBuilderIterator($formBuilder) as $name => $formBuilder) {
if ($name == $elementId) {
return $formBuilder;
}
}
var_dump(__METHOD__);
var_dump($elementId);
var_dump(debug_backtrace());
return;
}
The result is this:
string 'Sonata\AdminBundle\Admin\AdminHelper::getChildFormBuilder' (length=57)
string 's56cdfa72c4dea_session_0_moment' (length=31)
array (size=8)
0 =>
array (size=7)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/app/cache/dev/classes.php' (length=69)
'line' => int 9774
'function' => string 'getChildFormBuilder' (length=19)
'class' => string 'Sonata\AdminBundle\Admin\AdminHelper' (length=36)
'object' =>
object(Sonata\AdminBundle\Admin\AdminHelper)[339]
protected 'pool' =>
object(Sonata\AdminBundle\Admin\Pool)[104]
...
'type' => string '->' (length=2)
'args' =>
array (size=2)
0 =>
object(Symfony\Component\Form\FormBuilder)[436]
...
1 => &string 's56cdfa72c4dea_session_0_moment' (length=31)
1 =>
array (size=7)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/vendor/sonata-project/admin-bundle/Controller/HelperController.php' (length=110)
'line' => int 95
'function' => string 'appendFormFieldElement' (length=22)
'class' => string 'Sonata\AdminBundle\Admin\AdminHelper' (length=36)
'object' =>
object(Sonata\AdminBundle\Admin\AdminHelper)[339]
protected 'pool' =>
object(Sonata\AdminBundle\Admin\Pool)[104]
...
'type' => string '->' (length=2)
'args' =>
array (size=3)
0 =>
object(Studyx\EnrolmentBundle\Admin\CourseAdmin)[370]
...
1 =>
object(Studyx\EnrolmentBundle\Entity\Course)[415]
...
2 => &string 's56cdfa72c4dea_session_0_moment' (length=31)
2 =>
array (size=5)
'function' => string 'appendFormFieldElementAction' (length=28)
'class' => string 'Sonata\AdminBundle\Controller\HelperController' (length=46)
'object' =>
object(Sonata\AdminBundle\Controller\HelperController)[244]
protected 'twig' =>
object(Twig_Environment)[220]
...
protected 'helper' =>
object(Sonata\AdminBundle\Admin\AdminHelper)[339]
...
protected 'pool' =>
object(Sonata\AdminBundle\Admin\Pool)[104]
...
protected 'validator' =>
object(Symfony\Component\Validator\Validator)[340]
...
'type' => string '->' (length=2)
'args' =>
array (size=1)
0 =>
object(Symfony\Component\HttpFoundation\Request)[6]
...
3 =>
array (size=4)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67)
'line' => int 2957
'function' => string 'call_user_func_array' (length=20)
'args' =>
array (size=2)
0 => &
array (size=2)
...
1 => &
array (size=1)
...
4 =>
array (size=7)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67)
'line' => int 2931
'function' => string 'handleRaw' (length=9)
'class' => string 'Symfony\Component\HttpKernel\HttpKernel' (length=39)
'object' =>
object(Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel)[300]
protected 'container' =>
object(appDevDebugProjectContainer)[304]
...
protected 'dispatcher' =>
object(Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher)[299]
...
protected 'resolver' =>
object(Symfony\Component\HttpKernel\Controller\TraceableControllerResolver)[249]
...
'type' => string '->' (length=2)
'args' =>
array (size=2)
0 =>
object(Symfony\Component\HttpFoundation\Request)[6]
...
1 => &int 1
5 =>
array (size=7)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67)
'line' => int 3060
'function' => string 'handle' (length=6)
'class' => string 'Symfony\Component\HttpKernel\HttpKernel' (length=39)
'object' =>
object(Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel)[300]
protected 'container' =>
object(appDevDebugProjectContainer)[304]
...
protected 'dispatcher' =>
object(Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher)[299]
...
protected 'resolver' =>
object(Symfony\Component\HttpKernel\Controller\TraceableControllerResolver)[249]
...
'type' => string '->' (length=2)
'args' =>
array (size=3)
0 =>
object(Symfony\Component\HttpFoundation\Request)[6]
...
1 => &int 1
2 => &boolean true
6 =>
array (size=7)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67)
'line' => int 2333
'function' => string 'handle' (length=6)
'class' => string 'Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel' (length=73)
'object' =>
object(Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel)[300]
protected 'container' =>
object(appDevDebugProjectContainer)[304]
...
protected 'dispatcher' =>
object(Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher)[299]
...
protected 'resolver' =>
object(Symfony\Component\HttpKernel\Controller\TraceableControllerResolver)[249]
...
'type' => string '->' (length=2)
'args' =>
array (size=3)
0 =>
object(Symfony\Component\HttpFoundation\Request)[6]
...
1 => &int 1
2 => &boolean true
7 =>
array (size=7)
'file' => string '/Applications/MAMP/htdocs/studyx_enrolments/web/app_dev.php' (length=59)
'line' => int 29
'function' => string 'handle' (length=6)
'class' => string 'Symfony\Component\HttpKernel\Kernel' (length=35)
'object' =>
object(AppKernel)[5]
protected 'bundles' =>
array (size=22)
...
protected 'bundleMap' =>
array (size=22)
...
protected 'container' =>
object(appDevDebugProjectContainer)[304]
...
protected 'rootDir' => string '/Applications/MAMP/htdocs/studyx_enrolments/app' (length=47)
protected 'environment' => string 'dev' (length=3)
protected 'debug' => boolean true
protected 'booted' => boolean true
protected 'name' => string 'app' (length=3)
protected 'startTime' => float 1456339594.61
protected 'loadClassCache' =>
array (size=2)
...
'type' => string '->' (length=2)
'args' =>
array (size=1)
0 =>
object(Symfony\Component\HttpFoundation\Request)[6]
...
UPDATE 2:
I've changed the require in my composer.json to "sonata-project/admin-bundle": "^2.4#dev" and updated with composer. But now I'm getting this error:
ContextErrorException: Warning: Illegal string offset 'admin' in
app/cache/dev/classes.php line 10482
The error is in this function:
public function getDashboardGroups()
{
$groups = $this->adminGroups;
foreach ($this->adminGroups as $name => $adminGroup) {
if (isset($adminGroup['items'])) {
foreach ($adminGroup['items'] as $key => $item) {
if (''!= $item['admin']) {
$admin = $this->getInstance($item['admin']);
if ($admin->showIn(Admin::CONTEXT_DASHBOARD)) {
$groups[$name]['items'][$key] = $admin;
} else {
unset($groups[$name]['items'][$key]);
}
}
else {
unset($groups[$name]['items'][$key]);
}
}
}
if (empty($groups[$name]['items'])) {
unset($groups[$name]);
}
}
return $groups;
}
The errors in on line : if (''!= $item['admin']) { .
In my config.yml I have:
sonata_admin:
title: Studyx
title_logo: bundles/studyxenrolment/images/logo.png
templates:
layout: StudyxEnrolmentBundle:Admin:standard_layout.html.twig
edit: StudyxEnrolmentBundle:CRUD:edit.html.twig
user_block: StudyxEnrolmentBundle:Admin:user_block.html.twig
# search: SonataAdminBundle:Core:search.html.twig
# search_result_block: SonataAdminBundle:Block:block_search_result.html.twig
dashboard:
groups:
studyx.admin.group.inschrijvingen:
label: Inschrijvingen
items: ~
item_adds:
- sonata.admin.enrolment
studyx.admin.group.algemeen:
label: Algemeen
items: ~
item_adds:
- sonata.admin.course
- sonata.admin.student
studyx.admin.group.extra:
label: Extra
items: ~
item_adds:
- sonata.admin.location
blocks:
-
position: top
class: col-md-12
type: sonata.admin.block.admin_list
So I think the function getDashboardGroups is called there.
UPDATE 3:
In my composer.json I have now the following:
"sonata-project/block-bundle": "~2.3",
"sonata-project/admin-bundle": "^2.4#dev",
"sonata-project/doctrine-orm-admin-bundle": "2.3.*",
"sonata-project/formatter-bundle": "^2.3"
Should I update them all to ^2.4#dev ?

This error occurs because you have more than two level of nested collection forms, and it's currently not yet supported in any release of sonata-admin.
From #rande (owner) and sonata maintainers at issues #262, #1228, #1327 and #1971 :
This is still not supported for now ....
You can also look at this old PR #1971 which should solve the problem for some use cases only.
The solution I propose you is to implement the fix provided by the last opened PR #2985.
Because the PR is not merged, you need to tell composer to load it rather than the current (not-working as expected) version. (see composer and VCS).
Hope the PR is merged soon.
Until it is, feel free to fix your problem at the moment by using it directly, as several people do.
UPDATE
The Pull Request #3553 has been recently merged and fix the problem of the nested collections at > 2 levels (nested in nested).
To get the fixed release, you must use the dev-master tag of the bundle (at least from the commit 926f159 representing the merge of the PR).
I tried it and it works well with the following requirement:
// composer.json
"require": {
"sonata-project/admin-bundle": "^2.4#dev",
...
},
I hope you can easily upgrade the bundle in order to get the fix.
Update2
Apparently, your composer doesn't take the last changes of the branch.
The fix provided by the PR #2739 has been merged 6 days ago.
To fix this last one (hope), you need to change a very short code block in the AddDepencyCallsCompilerPass located in vendor/sonata-project/admin-bundle/DependencyInjection/Compiler/AddDependencyCallsCompilerPass.
At line 95, replace this line :
$groupDefaults[$resolvedGroupName]['items'][] = $id;
To those ones:
$groupDefaults[$resolvedGroupName]['items'][] = array(
'admin' => $id,
'label' => !empty($attributes['label']) ? $attributes['label'] : '',
'route' => '',
'route_params' => array(),
);
Like it's done by the PR (it's the only needed change to make it working).
I would make the fix manually because it's very new, and after some days/weeks, run the following commands:
composer clear-cache and composer update sonata-project/admin-bundle
You should try it now before adding the fix manually, maybe the changes will be added.
Also, you can use the link about composer and VCS I given at the begin of my answer and require the fix directly. It's at your own appreciation because of it's a solution at the moment.
And last, be patient, the fixes will be merged in stable releases soon.

public function getChildFormBuilder(FormBuilder $formBuilder, $elementId)
{
foreach (new FormBuilderIterator($formBuilder) as $name => $formBuilder) {
if ($name == $elementId) {
return $formBuilder;
}
}
return;
}
Dumping the name and element id within the loop won't accomplish anything. Your application obviously crashes when there is nothing to iterate - it then walks through the loop, exits and goes to the last line where NULL is returned.
I suggest you dump the element id just after the loop, like the following. The use of debug_backtrace might also help:
public function getChildFormBuilder(FormBuilder $formBuilder, $elementId)
{
foreach (new FormBuilderIterator($formBuilder) as $name => $formBuilder) {
if ($name == $elementId) {
return $formBuilder;
}
}
var_dump(__METHOD__);
var_dump($elementId);
var_dump(debug_backtrace());
return;
}

Related

Symfony CollectionType many to many relation in edit form

I have crud with 3 entities. Meal, Product and ProductsQuantity. Between Meal and ProductQuantity is relation many to many. Adding data is working fine, all data are saving to entities but problem is when in want to edit form. Then I got error:
The form's view data is expected to be an instance of class MealBundle\Entity\ProductsQuantity, but is an instance of class Doctrine\ORM\PersistentCollection. 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 Doctrine\ORM\PersistentCollection to an instance of MealBundle\Entity\ProductsQuantity.
I tried with data_class option to null, and setting fetch="EAGER" on the relation but it doesn't solved the problem.
Meal:
/**
* #Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=255)
*/
protected $name = "";
/**
* #var ProductsQuantity[]|Collection
* #ORM\ManyToMany(targetEntity="ProductsQuantity", inversedBy="meal", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinTable(name="meal_products_quantity_relations",
* joinColumns={#ORM\JoinColumn(name="meal_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="products_quantity_id", referencedColumnName="id")}
* )
*/
protected $productsQuantity;
/**
* Meal constructor.
*/
public function __construct()
{
$this->productsQuantity = new ArrayCollection();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
* #return Meal
*/
public function setId(int $id): Meal
{
$this->id = $id;
return $this;
}
/**
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #param string $name
* #return Meal
*/
public function setName(string $name): Meal
{
$this->name = $name;
return $this;
}
/**
* #return Collection|ProductsQuantity[]
*/
public function getProductsQuantity()
{
return $this->productsQuantity;
}
/**
* #param Collection|ProductsQuantity[] $productsQuantity
* #return Meal
*/
public function setProductsQuantity($productsQuantity)
{
$this->productsQuantity = $productsQuantity;
return $this;
}
ProductQuantity:
/**
* #Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var Product
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productsQuantity")
*/
protected $product;
/**
* #var integer
* #ORM\Column(name="amount", type="integer")
*/
protected $amount;
/**
* #var $meal
* #ORM\ManyToMany(targetEntity="MealBundle\Entity\Meal", mappedBy="productsQuantity")
*/
protected $meal;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
* #return ProductsQuantity
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* #return Product
*/
public function getProduct(): ?Product
{
return $this->product;
}
/**
* #param Product $product
* #return ProductsQuantity
*/
public function setProduct(Product $product): ProductsQuantity
{
$this->product = $product;
return $this;
}
/**
* #return int
*/
public function getAmount(): ?int
{
return $this->amount;
}
/**
* #param int $amount
*/
public function setAmount(int $amount): void
{
$this->amount = $amount;
}
/**
* #return mixed
*/
public function getMeal()
{
return $this->meal;
}
/**
* #param mixed $meal
* #return ProductsQuantity
*/
public function setMeal($meal)
{
$this->meal = $meal;
return $this;
}
Meal form:
class MealType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
parent::buildForm($builder, $options);
/** #var Meal $meal */
$meal = $options['data'];
$data = null;
if(!empty($meal)) {
$data = $meal->getProductsQuantity();
} else {
$data = new ProductsQuantity();
}
$builder
->add('name', TextType::class,[
'label' => 'Nazwa Dania',
'required' => true
])->add('productsQuantity', CollectionType::class, [
'data_class' => null,
'label' => 'Produkty',
'entry_type' => ProductsQuantityType::class,
'allow_add' => true,
'data' => ['productsQuantity' => $data],
'prototype_name' => '__product__',
'entry_options' => [
'allow_extra_fields' => true,
'label' => false
],
'prototype' => true
])->add('addProduct', ButtonType::class, [
'label' => 'Dodaj kolejny produkt',
'attr' => [
'class' => 'btn-default addProductEntry'
]
])->add('submit', SubmitType::class, [
'label' => 'Dodaj'
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired('productsQuantity');
$resolver->setDefaults([
'data_class' => Meal::class
]);
}
}
ProductsQuantity form:
class ProductsQuantityType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('product', EntityType::class,[
'class' => Product::class,
'label' => 'Nazwa produktu',
])
->add('amount', NumberType::class, [
'label' => 'Ilość',
'required' => true,
'attr' => [
'placeholder' => 'ilość'
]
])
->add('removeProduct', ButtonType::class, [
'label' => 'X',
'attr' => [
'class' => 'btn-danger removeProductEntry'
]
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ProductsQuantity::class,
]);
}
}
If I change 'data' => ['productsQuantity' => $data] to 'data' => ['productsQuantity' => new ProductsQuantity()] there is no error but have empty ProductsQuantity part of MealType form. Can anyone tell me how to fix this?

Laravel doctrine only returning single attribute from findAll() query

Im using laravel doctrine in a lumen application, in one of my controllers i have an index function that should return all records from a given entity.
the function looks like this
public function index(ServerRequestInterface $request)
{
return $this->showResponse(app()->make('em')->getRepository('App\Entities\showOff')->findAll());
}
which returns data like this
[
{
"nodeType": 'showOff'
},
{
"nodeType": 'showOff'
},
{
"nodeType": 'showOff'
},
{
"nodeType": 'showOff'
}
]
This is only one attribute in the entity.
If i turn the doctrine debugger on is see the executed SQL query like
SELECT t0.type AS type_1, t0.size AS size_2, t0.last_modified AS last_modified_3, t0.hair_cutter AS hair_cutter_4, t0.file_path AS file_path_5, t0.content_url AS content_url_6, t0.embed_url AS embed_url_7, t0.height AS height_8, t0.width AS width_9, t0.player_type AS player_type_10, t0.about AS about_11, t0.award AS award_12, t0.comment AS comment_13, t0.comment_count AS comment_count_14, t0.text AS text_15, t0.thumbnail AS thumbnail_16, t0.version AS version_17, t0.name AS name_18, t0.id AS id_19, t0.nid AS nid_20, t0.node_type AS node_type_21, t0.owner_id AS owner_id_22, t23.enabled AS enabled_24, t23.username AS username_25, t23.email AS email_26, t23.password AS password_27, t23.remember_token AS remember_token_28, t23.name AS name_29, t23.id AS id_30, t23.nid AS nid_31, t23.node_type AS node_type_32, t0.aggregate_rating_id AS aggregate_rating_id_33, t34.rating_count AS rating_count_35, t34.rating_score AS rating_score_36, t34.name AS name_37, t34.id AS id_38, t34.nid AS nid_39, t34.node_type AS node_type_40, t0.author_id AS author_id_41, t42.enabled AS enabled_43, t42.username AS username_44, t42.email AS email_45, t42.password AS password_46, t42.remember_token AS remember_token_47, t42.name AS name_48, t42.id AS id_49, t42.nid AS nid_50, t42.node_type AS node_type_51, t0.translator_id AS translator_id_52, t53.enabled AS enabled_54, t53.username AS username_55, t53.email AS email_56, t53.password AS password_57, t53.remember_token AS remember_token_58, t53.name AS name_59, t53.id AS id_60, t53.nid AS nid_61, t53.node_type AS node_type_62 FROM show_off t0 LEFT JOIN users t23 ON t0.owner_id = t23.id LEFT JOIN aggregate_rating t34 ON t0.aggregate_rating_id = t34.id LEFT JOIN users t42 ON t0.author_id = t42.id LEFT JOIN users t53 ON t0.translator_id = t53.id ;
when ran from the mysql command line it returns all data as it should.
Some where along the line all my attributes are being stripped out.
my entity looks like so
<?php
namespace App\Entities;
use Doctrine\ORM\Mapping as ORM;
use App\Jobs\IndexNewEntitiesJob;
use App\Jobs\UpdateIndexEntitiesJob;
use Doctrine\Common\Collections\ArrayCollection;
use ApiArchitect\Compass\Entities\User;
/**
* Class ShowOff
*
* #package Jkirkby91\DoctrineSchemas
* #author James Kirkby <jkirkby91#gmail.com>
*
* #ORM\Entity
* #ORM\HasLifeCycleCallbacks
* #ORM\Table(name="show_off")
* #ORM\Entity(repositoryClass="App\Repositories\ShowOffRepository")
*/
class ShowOff extends \App\Entities\MediaObject
{
/**
* #ORM\Column(type="string", length=45, nullable=false, unique=false)
*/
protected $type;
/**
* #ORM\Column(type="integer", length=45, nullable=false)
*/
protected $size;
/**
* #ORM\Column(type="datetime", length=45, nullable=false, unique=false)
*/
protected $lastModified;
/**
* #ORM\OneToOne(targetEntity="\ApiArchitect\Compass\Entities\User", fetch="EAGER", cascade={"persist"})
*/
protected $owner;
/**
* #ORM\OneToOne(targetEntity="\App\Entities\HairCutter", fetch="EAGER", cascade={"persist"})
* #ORM\Column(nullable=true, unique=false)
*/
protected $hairCutter;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
protected $filePath;
/**
* HairCutter constructor.
* #param $fileName
* #param $filePath
* #param $owner
* #param $hairCutter
*/
public function __construct($fileName, $filePath, User $owner, $type, $lastModified, $size)
{
$this->name = $fileName;
$this->filePath = $filePath;
$this->owner = $owner;
$this->type = $type;
$this->lastModified = $lastModified;
$this->size = $size;
$this->nodeType = 'showOff';
}
/**
* Gets the value of filePath.
*
* #return mixed
*/
public function getFilePath()
{
return $this->filePath;
}
/**
* Sets the value of filePath.
*
* #param mixed $filePath the file path
*
* #return self
*/
protected function setFilePath($filePath)
{
$this->filePath = $filePath;
return $this;
}
/**
* Gets the value of owner.
*
* #return mixed
*/
public function getOwner()
{
return $this->owner;
}
/**
* Sets the value of owner.
*
* #param mixed $owner the owner
*
* #return self
*/
protected function setOwner(User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Gets the value of hairCutter.
*
* #return mixed
*/
public function getHairCutter()
{
return $this->hairCutter;
}
/**
* Sets the value of hairCutter.
*
* #param mixed $hairCutter the hair cutter
*
* #return self
*/
protected function setHairCutter($hairCutter)
{
$this->hairCutter = $hairCutter;
return $this;
}
}
my config looks like so
<?php
return [
'managers' => [
'default' => [
'dev' => env('APP_DEBUG'),
'meta' => env('DOCTRINE_METADATA', 'annotations'),
'connection' => env('DB_CONNECTION', 'sqlite'),
'namespaces' => [
'app'
],
'paths' => [
env('COMPASS_ENTITIES',base_path('vendor/apiarchitect/compass/src/Entities')),
env('AUTH_ENTITIES',base_path('vendor/apiarchitect/auth/src/Entities')),
env('LOG_ENTITIES',base_path('vendor/apiarchitect/log/src/Entities')),
env('NODE_ENTITIES',base_path('vendor/jkirkby91/lumendoctrinecomponent/src/Entities')),
env('APP_ENTITIES',base_path('/app/Entities')),
],
'repository' => Doctrine\ORM\EntityRepository::class,
'proxies' => [
'namespace' => false,
'path' => storage_path('proxies'),
'auto_generate' => env('DOCTRINE_PROXY_AUTOGENERATE', false)
],
'events' => [
'listeners' => [],
'subscribers' => []
],
'filters' => [],
'mapping_types' => [
'enum' => 'string'
]
]
],
'extensions' => [
LaravelDoctrine\Extensions\Timestamps\TimestampableExtension::class,
LaravelDoctrine\Extensions\SoftDeletes\SoftDeleteableExtension::class,
LaravelDoctrine\Extensions\Loggable\LoggableExtension::class,
LaravelDoctrine\Extensions\Blameable\BlameableExtension::class,
LaravelDoctrine\Extensions\IpTraceable\IpTraceableExtension::class,
LaravelDoctrine\ORM\Extensions\TablePrefix\TablePrefixExtension::class,
'custom_types' => [
'json' => LaravelDoctrine\ORM\Types\Json::class
],
'custom_datetime_functions' => [],
'custom_numeric_functions' => [],
'custom_string_functions' => [],
'logger' => env('DOCTRINE_LOGGER', trrue),
'cache' => [
'default' => env('DOCTRINE_CACHE', 'memcached'),
'namespace' => null,
'second_level' => false,
],
'gedmo' => [
'all_mappings' => false
],
'doctrine_presence_verifier' => true,
];
Does any one know why this is doing this?
You write in your question:
Some where along the line all my attributes are being stripped out.
I suspect something happens with the data inside the showResponse method that you wrap around the result set that is returned from the repository findAll() call.
Try once to separate the result from the showResponse call and check what you get then:
public function index(ServerRequestInterface $request)
{
$result = app()->make('em')->getRepository('App\Entities\ShowOff')->findAll();
var_dump( $result ); // <-- should contain a collection of ShowOff entities.
return $this->showResponse($result); // <-- something happens and returns an array
}

Symfony2 setSelected value of Entity Type

This might wound like a silly question, but I'm stuck. I've the following field in a form of my Symfony2 application:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('match_team1','entity', array(
'class' => 'MyAppBundle:Team',
'expanded' => false,
'multiple' => false,
'label' => 'Team 1',
'mapped' => true,
'property' => 'name'
));
}
Match Class
class Match
{
/**
* #var integer
*/
private $id;
/**
* #var integer
*/
private $match_number;
/**
* #var string
*/
private $match_type;
/**
* #var \DateTime
*/
private $match_date;
/**
* #var string
*/
private $match_status;
/**
* #var string
*/
private $match_team1;
/**
* Set matchTeam1
*
* #param string $matchTeam1
*
* #return Match
*/
public function setMatchTeam1($matchTeam1)
{
$this->match_team1 = $matchTeam1;
return $this;
}
/**
* Get matchTeam1
*
* #return string
*/
public function getMatchTeam1()
{
return $this->match_team1;
}
Edit function:
private function createEditForm(Match $entity)
{
$form = $this->createForm(new MatchType(), $entity, array(
'action' => $this->generateUrl('match_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
$form->add('submit', 'submit', array('label' => 'Update'));
return $form;
}
But when I update the form the selected value from the drop down does not stay. In the database I can see the entry but in the form it's not being displayed. Can anyone help me to set the selected value?

addAction,editAction zend framework2 doctrine2

I am using zendframework 2 and doctrine 2. My addAction doesn't work i don't have any error but when i valid my form no row created in my database !!
i think that i have problem in populating foreign key !
this is my Form:
<?php
// filename : module/Users/src/Users/Form/addForm.php
namespace Vehicules\Form;
use Zend\Form\Form;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
class VehiculeForm extends form implements ObjectManagerAwareInterface
{
protected $objectManager;
public function setObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
public function getObjectManager()
{
return $this->objectManager;
}
//public function init()
public function __construct(ObjectManager $objectManager)
{
parent::__construct('add');
$this->objectManager = $objectManager;
$this->init();
}
public function init(){
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/formdata');
$this->add(array(
'name' => 'matricule',
'attributes' => array(
'type' => 'text',
'required' => true
),
'options' => array(
'label' => 'Matricule',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'carburant',
'options' => array(
'label' => 'Carburant',
'value_options' => array(
'0' => 'Essence',
'1' => 'Gasoil',
'2' => 'Hybride',
),
)
));
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'name' => 'option',
'options' => array(
'label' => 'Options Véhicule',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Vehicules\Entity\optionsvehicule',
'property' => 'libellee',
)));
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'categorie',
'options' => array(
'label' => 'categorie',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Vehicules\Entity\categorie',
'property' => 'idcat',
)
));
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'modele',
'options' => array(
'label' => 'Modèle',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Vehicules\Entity\modele',
'property' => 'nom',
)
));
/*$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'modele',
'options' => array(
'label' => 'Modèle',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Vehicules\Entity\modele',
'property' => 'nom',
'is_method' => true,
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('active' => 1),
// Use key 'orderBy' if using ORM
'orderBy' => array('lastname' => 'ASC'),
// Use key 'sort' if using ODM
'sort' => array('lastname' => 'ASC')
),
),
),
));*/
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'marque',
'options' => array(
'label' => 'Marque',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Vehicules\Entity\marque',
'property' => 'nom',
)
));
$this->add(array(
'name' => 'dateMiseCirculation',
'attributes' => array(
'type' => 'Zend\Form\Element\Date',
),
'options' => array(
'label' => 'Date de Mise en Circulation',
),
));
$this->add(array(
'name' => 'numChasis',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Numero de Chasis',
),
));
$this->add(array(
'name' => "Prix d'achat",
'attributes' => array(
'type' => 'int',
),
'options' => array(
'label' => "Prix d'achat",
),
));
$this->add(array(
'name' => 'concessionnaire',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'concessionnaire',
),
));
$this->add(array(
'name' => 'souslocation',
'attributes' => array(
'type' => 'string',
),
'options' => array(
'label' => 'Sous-location',
),
));
$this->add(array(
'name' => 'remarque',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'remarque',
),
));
$this->add(array(
'name' => 'puisfiscal',
'attributes' => array(
'type' => 'int',
),
'options' => array(
'label' => "puissance fiscale",
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'nbreport',
'options' => array(
'label' => 'Nombre de portes',
'value_options' => array(
'0' => '4',
'1' => '2',
'2' => '5',
'3' => '6',
'4' => '7',
'5' => '7',
),
)
));
$this->add(array(
'name' => 'dernierKm',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Dernier kilométrage',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Valider'
),
));
}}
and this is my Entity Vehicule:
<?php
namespace Vehicules\Entity;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterInterface;
use Zend\InputFilter\Factory as InputFactory;
use Doctrine\ORM\Mapping as ORM;
/**
* Vehicule
*
* #ORM\Table(name="vehicule", uniqueConstraints={#ORM\UniqueConstraint(name="VEHICULE_PK", columns={"idVeh"})}, indexes={#ORM\Index(name="ASSOCIATION11_FK", columns={"idCat"}), #ORM\Index(name="ASSOCIATION13_FK", columns={"idMod"})})
* #ORM\Entity
*/
class Vehicule
{ protected $inputFilter;
/**
* #var integer
*
* #ORM\Column(name="idVeh", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idveh;
/**
* #var string
*
* #ORM\Column(name="matricule", type="string", length=254, nullable=false)
*/
private $matricule;
/**
* #var string
*
* #ORM\Column(name="dateMiseCirculation", type="string", length=254, nullable=true)
*/
private $datemisecirculation;
/**
* #var string
*
* #ORM\Column(name="numChasis", type="string", length=254, nullable=false)
*/
private $numchasis;
/**
* #var string
*
* #ORM\Column(name="carburant", type="string", length=254, nullable=true)
*/
private $carburant;
/**
* #var string
*
* #ORM\Column(name="dernierKm", type="decimal", precision=10, scale=0, nullable=false)
*/
private $dernierkm;
/**
* #var integer
*
* #ORM\Column(name="prixachat", type="integer", precision=10, scale=0, nullable=false)
*/
private $prixachat;
/**
* #var string
*
* #ORM\Column(name="concessionnaire", type="string", length=254, nullable=true)
*/
private $concessionnaire;
/**
* #var integer
*
* #ORM\Column(name="sousLocation", type="smallint", nullable=true)
*/
private $souslocation;
/**
* #var string
*
* #ORM\Column(name="remarque", type="string", length=254, nullable=true)
*/
private $remarque;
/**
* #var integer
*
* #ORM\Column(name="puisFiscal", type="integer", nullable=true)
*/
private $puisfiscal;
/**
* #var integer
*
* #ORM\Column(name="nbrePort", type="integer", nullable=true)
*/
private $nbreport;
/**
* #var \Vehicules\Entity\Categorie
*
* #ORM\ManyToOne(targetEntity="Vehicules\Entity\Categorie")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="idCat", referencedColumnName="idCat")
* })
*/
private $idcat;
/**
* #var \Vehicules\Entity\Modele
*
* #ORM\ManyToOne(targetEntity="Vehicules\Entity\Modele")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="idMod", referencedColumnName="idMod")
* })
*/
private $idmod;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Vehicules\Entity\Optionsvehicule", inversedBy="idveh")
* #ORM\JoinTable(name="veh_option",
* joinColumns={
* #ORM\JoinColumn(name="idVeh", referencedColumnName="idVeh")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="idOptVeh", referencedColumnName="idOptVeh")
* }
* )
*/
private $idoptveh;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Vehicules\Entity\Vehiculestatut", inversedBy="idveh")
* #ORM\JoinTable(name="veh_status",
* joinColumns={
* #ORM\JoinColumn(name="idVeh", referencedColumnName="idVeh")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="idStatut", referencedColumnName="idStatut")
* }
* )
*/
private $idstatut;
/**
* Constructor
*/
public function __construct()
{
$this->idoptveh = new \Doctrine\Common\Collections\ArrayCollection();
$this->idstatut = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get idveh
*
* #return integer
*/
public function getIdveh()
{
return $this->idveh;
}
/**
* Set matricule
*
* #param string $matricule
* #return Vehicule
*/
public function setMatricule($matricule)
{
$this->matricule = $matricule;
return $this;
}
/**
* Get matricule
*
* #return string
*/
public function getMatricule()
{
return $this->matricule;
}
/**
* Set datemisecirculation
*
* #param string $datemisecirculation
* #return Vehicule
*/
public function setDatemisecirculation($datemisecirculation)
{
$this->datemisecirculation = $datemisecirculation;
return $this;
}
/**
* Get datemisecirculation
*
* #return string
*/
public function getDatemisecirculation()
{
return $this->datemisecirculation;
}
/**
* Set numchasis
*
* #param string $numchasis
* #return Vehicule
*/
public function setNumchasis($numchasis)
{
$this->numchasis = $numchasis;
return $this;
}
/**
* Get numchasis
*
* #return string
*/
public function getNumchasis()
{
return $this->numchasis;
}
/**
* Set carburant
*
* #param string $carburant
* #return Vehicule
*/
public function setCarburant($carburant)
{
$this->carburant = $carburant;
return $this;
}
/**
* Get carburant
*
* #return string
*/
public function getCarburant()
{
return $this->carburant;
}
/**
* Set dernierkm
*
* #param string $dernierkm
* #return Vehicule
*/
public function setDernierkm($dernierkm)
{
$this->dernierkm = $dernierkm;
return $this;
}
/**
* Get dernierkm
*
* #return string
*/
public function getDernierkm()
{
return $this->dernierkm;
}
/**
* Set prixachat
*
* #param integer $prixachat
* #return Vehicule
*/
public function setPrixachat($prixachat)
{
$this->prixachat = $prixachat;
return $this;
}
/**
* Get prixachat
*
* #return integer
*/
public function getPrixachat()
{
return $this->prixachat;
}
/**
* Set concessionnaire
*
* #param string $concessionnaire
* #return Vehicule
*/
public function setConcessionnaire($concessionnaire)
{
$this->concessionnaire = $concessionnaire;
return $this;
}
/**
* Get concessionnaire
*
* #return string
*/
public function getConcessionnaire()
{
return $this->concessionnaire;
}
/**
* Set souslocation
*
* #param integer $souslocation
* #return Vehicule
*/
public function setSouslocation($souslocation)
{
$this->souslocation = $souslocation;
return $this;
}
/**
* Get souslocation
*
* #return integer
*/
public function getSouslocation()
{
return $this->souslocation;
}
/**
* Set remarque
*
* #param string $remarque
* #return Vehicule
*/
public function setRemarque($remarque)
{
$this->remarque = $remarque;
return $this;
}
/**
* Get remarque
*
* #return string
*/
public function getRemarque()
{
return $this->remarque;
}
/**
* Set puisfiscal
*
* #param integer $puisfiscal
* #return Vehicule
*/
public function setPuisfiscal($puisfiscal)
{
$this->puisfiscal = $puisfiscal;
return $this;
}
/**
* Get puisfiscal
*
* #return integer
*/
public function getPuisfiscal()
{
return $this->puisfiscal;
}
/**
* Set nbreport
*
* #param integer $nbreport
* #return Vehicule
*/
public function setNbreport($nbreport)
{
$this->nbreport = $nbreport;
return $this;
}
/**
* Get nbreport
*
* #return integer
*/
public function getNbreport()
{
return $this->nbreport;
}
/**
* Set idcat
*
* #param \Vehicules\Entity\Categorie $idcat
* #return Vehicule
*/
public function setIdcat(\Vehicules\Entity\Categorie $idcat = null)
{
$this->idcat = $idcat;
return $this;
}
/**
* Get idcat
*
* #return \Vehicules\Entity\Categorie
*/
public function getIdcat()
{
return $this->idcat;
}
/**
* Set idmod
*
* #param \Vehicules\Entity\Modele $idmod
* #return Vehicule
*/
public function setIdmod(\Vehicules\Entity\Modele $idmod = null)
{
$this->idmod = $idmod;
return $this;
}
/**
* Get idmod
*
* #return \Vehicules\Entity\Modele
*/
public function getIdmod()
{
return $this->idmod;
}
/**
* Add idoptveh
*
* #param \Vehicules\Entity\Optionsvehicule $idoptveh
* #return Vehicule
*/
public function addIdoptveh(\Vehicules\Entity\Optionsvehicule $idoptveh)
{
$this->idoptveh[] = $idoptveh;
return $this;
}
/**
* Remove idoptveh
*
* #param \Vehicules\Entity\Optionsvehicule $idoptveh
*/
public function removeIdoptveh(\Vehicules\Entity\Optionsvehicule $idoptveh)
{
$this->idoptveh->removeElement($idoptveh);
}
/**
* Get idoptveh
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdoptveh()
{
return $this->idoptveh;
}
/**
* Add idstatut
*
* #param \Vehicules\Entity\Vehiculestatut $idstatut
* #return Vehicule
*/
public function addIdstatut(\Vehicules\Entity\Vehiculestatut $idstatut)
{
$this->idstatut[] = $idstatut;
return $this;
}
/**
* Remove idstatut
*
* #param \Vehicules\Entity\Vehiculestatut $idstatut
*/
public function removeIdstatut(\Vehicules\Entity\Vehiculestatut $idstatut)
{
$this->idstatut->removeElement($idstatut);
}
/**
* Get idstatut
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdstatut()
{
return $this->idstatut;
}
public function populate($data) {
$this->setMatricule($data['matricule']) ;
$this->setDatemisecirculation($data['dateMiseCirculation']) ;
$this->setNumchasis($data['numChasis']) ;
$this->setCarburant($data['carburant']) ;
$this->setDernierkm($data['dernierKm']) ;
$this->setPrixachat($data["Prix d'achat"]) ;
$this->setConcessionnaire($data['concessionnaire']) ;
$this->setSouslocation($data['souslocation']) ;
$this->setRemarque($data['remarque']) ;
$this->setPuisfiscal($data['puisfiscal']) ;
$this->setNbreport($data['nbreport']) ;
//$this->addIdoptveh($data['option']) ; /* select................*/
//$this->setIdmod() ; /* select................*/
//$this->addIdstatut() ; /*ghanakhd l option dyal libre */
}
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'matricule',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 4,
'max' => 14,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'option',
'required' => false,
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
public function getArrayCopy()
{
return get_object_vars($this);
}
}
this is my controller VehiculeController:
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* #link http://github.com/zendframework/Vehicules for the canonical source repository
* #copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* #license http://framework.zend.com/license/new-bsd New BSD License
*
*/
namespace Vehicules\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Vehicules\Form\VehiculeForm;
use Vehicules\Entity\Vehicule;
class VehiculesController extends AbstractActionController
{
/**
* #var Doctrine\ORM\EntityManager
*/
protected $_objectManager;
protected function getObjectManager()
{
if (!$this->_objectManager) {
$this->_objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->_objectManager;
}
public function indexAction()
{
$vehicules = $this->getObjectManager()->getRepository('Vehicules\Entity\Vehicule')->findAll();
return new ViewModel(array('vehicules' => $vehicules));
}
public function addAction()
{ $_objectManager=$this->getObjectManager();
$form = new VehiculeForm($_objectManager);
$request = $this->getRequest();
$post = $this->request->getPost();
if ($this->request->isPost()) {
$Vehicule= new Vehicule();
$form->setData($post);
$form->setInputFilter($Vehicule->getInputFilter());
if ($form->isValid()) {
$f=$form->getData();
$Vehicule->populate($f);
$cat = $this->getObjectManager()->getRepository('Vehicules\Entity\categorie')->findAll();
foreach ($cat as $c){
if($c->getIdcat()==$f['categorie'] ){
$Vehicule->setIdcat($c) ;
exit;
}
}
$mod = $this->getObjectManager()->getRepository('Vehicules\Entity\modele')->findAll();
foreach ($mod as $m){
if($m->getNom()==$f['modele'] ){
$Vehicule->setIdmod($m->getIdmod()) ;
exit;
}
}
$objectManager = $this->getObjectManager();
$objectManager->persist($Vehicule);
$objectManager->flush();
$id=$Vehicule->getIdveh();
var_dump($id);
$viewModel = new ViewModel(array('form' =>$form,'donne'=>$id));
return $viewModel;
}
}
$viewModel = new ViewModel(array('form' =>$form));
return $viewModel;
}
public function editAction()
{
$id = (int) $this->getEvent()->getRouteMatch()->getParam('id');
if (!$id) {
return $this->redirect()->toRoute('vehicules/default', array('controller'=>'vehicules','action'=>'add'));
}
$vehicule = $this->getObjectManager()->find('Vehicules\Entity\vehicule', $id);
$objectManager= $this->getObjectManager();
$form = new VehiculeForm($objectManager);
$form->setBindOnValidate(false);
$form->bind($vehicule);
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->post());
if ($form->isValid()) {
$form->bindValues();
$this->getEntityManager()->flush();
// Redirect to list of vehicules
return $this->redirect()->toRoute('vehicules/default', array('controller'=>'vehicules','action'=>'index'));
}
}
return array(
'id' => $id,
'form' => $form,
);
}
public function deleteAction()
{
$id = (int)$this->getEvent()->getRouteMatch()->getParam('idVeh');
if (!$id) {
return $this->redirect()->toRoute('vehicules');
}
$request = $this->getRequest();
if ($request->isPost()) {
$del = $request->post()->get('del', 'No');
if ($del == 'Yes') {
$id = (int)$request->post()->get('id');
$vehicule = $this->getEntityManager()->find('Vehicules\Entity\vehicule', $id);
if ($vehicule) {
$this->getEntityManager()->remove($vehicule);
$this->getEntityManager()->flush();
}
}
// Redirect to list of albums
return $this->redirect()->toRoute('default', array(
'controller' => 'vehicules',
'action' => 'index',
));
}
return array(
'id' => $id,
'vehicule' => $this->getEntityManager()->find('Vehicules\Entity\vehicule', $id)->getArrayCopy()
);
}
}
to populate forgnein key idMod and idCat i tried with this:
$mod = $this->getObjectManager()->getRepository('Vehicules\Entity\modele')->findAll();
foreach ($mod as $m){
if($m->getNom()==$f['modele'] ){
$Vehicule->setIdmod($m->getIdmod()) ;
exit;
}
}
but id doesn't work :/
Your issue is the form/entity hydration. You don't need to reinvent the wheel here as Zend\Form already takes care of getting the data in and out of a form.
Currently your form uses the default ArraySerializable hydrator, so when the form is validated and you fetch the data (using $form->getData()) you are given an array. What you want is a already populated Vehicule entity.
The DoctrineModule\Stdlib\Hydrator\DoctrineObject is a hydrator specifically for Doctrine. If you attach it as the hydrator to your form it will return hydrated entity.
To solve this you need to STOP creating the form using new
$form = new VehiculeForm($_objectManager);
Instead create it via the ServiceManager
$form = $this->getServiceLocator()->get('MyModule\Form\VehiculeForm');
Now create the form from a service factory; allowing the injection of the form dependencies, including the hydrator.
MyModule/Factory/Form/VehiculeFormFactory.php
namespace MyModule\Factory\Form;
use MyModule\Entity;
use MyModule\Form;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
use DoctrineModule\Stdlib\Hydrator;
class VehiculeFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$objectManager = $sl->get('Doctrine\ORM\EntityManager');
$form = new Form\VehiculeForm($objectManager);
$vehicule = new Entity\Vehicule();
// create the hydrator; this could also be done via the
// hydrator manager but create here for the example
$hydrator = new DoctrineObject($objectManager);
$form->setHydrator($hydrator);
$form->setObject($vehicule);
// We can also take care of the input filter here too!
// meaning less code in the controller and only
// one place to modify should it change
$form->setInputFilter($Vehicule->getInputFilter());
return $form;
}
}
Module.php
public function getFormElementConfig()
{
return array(
'factories' => array(
'MyModule\Form\VehiculeForm' => 'MyModule\Factory\Form\VehiculeForm' // path to our new factory
),
);
}
You can now get rid of your populate() method and allot of the controller code.
FooController.php
//...
protected function getVehiculeForm()
{
return $this->getServiceLocator()->get('MyModule\Form\VehiculeForm');
}
public function addAction()
{
$request = $this->getRequest();
$form = $this->getVehiculeForm();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$vehicule = $form->getData(); // returns the entity!
if ($vehicule instanceof Vehicule) {
/// Fully hydrated entity!
var_dump($vehicule);
}
}
}
//...
}
//...

How to make symfony 2 auto select the drop down field in edit action

I have a category table and product table. The categories are listed in product form in a drop down select box. When i edit a product then category saved in product table should be auto selected in category drop down at product edit form (selected="selected").
I have already tried empty_data and empty_value but they does not work.
I am searching for the solution since 2 days but could not find any tutorial or such example. I will be really thankful if someone can refer me a tutorial or can see below code to sort out the problem:
Controller
<?php
// src/Scsp/CmsBundle/Controller/ProductController.php
namespace Scsp\CmsBundle\Controller;
use Scsp\CmsBundle\Entity\CategoryEntity;
use Scsp\CmsBundle\Entity\ProductEntity;
use Scsp\CmsBundle\Form\ProductForm;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\HttpFoundation\Session\Session;
class ProductController extends Controller {
public function editAction($id, $request) {
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('ScspCmsBundle:ProductEntity')->find($id);
if (!$product){
throw $this->createNotFoundException('No product found for id ' . $id);
}
$productForm=new ProductForm($id);
$form = $this->createForm($productForm, $product, array(
'attr' => array('class' => 'form-horizontal')
));
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isValid()) {
$product->setCategoryid(3);
$em->persist($product);
$em->flush();
$this->session->getFlashBag()->add('success', $product->getProducts() . ' successfully edited!');
return $this->redirect($this->generateUrl('scsp_cms_products', array('action' => 'list', 'id' => 0)));
}
return $this->render('ScspCmsBundle:Default:form.html.twig', array(
'form' => $form->createView(),
'page_title' => 'Edit product',
'type' => 'products',
'id' => $id
));
}
}
Product Entity:
<?php
// src/Scsp/CmsBundle/Entity/ProductEntity.php
namespace Scsp\CmsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Scsp\CmsBundle\Repository\ProductRepository")
* #ORM\Table(name="products")
*/
class ProductEntity {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $products;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="CategoryEntity", inversedBy="products")
* #ORM\JoinColumn(name="categoryid", referencedColumnName="id")
*/
protected $categoryid;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set id
*
* #param string $id
* #return Id
*/
public function setId($id) {
$this->id = $id;
return $this;
}
/**
* Set products
*
* #param string $products
* #return ProductEntity
*/
public function setProducts($products)
{
$this->products = $products;
return $this;
}
/**
* Get products
*
* #return string
*/
public function getProducts()
{
return $this->products;
}
/**
* Set categoryid
*
* #param integer $categoryid
* #return ProductEntity
*/
public function setCategoryid($categoryid)
{
$this->categoryid = $categoryid;
return $this;
}
/**
* Get categoryid
*
* #return integer
*/
public function getCategoryid()
{
return $this->categoryid;
}
}
Category Entity:
<?php
// src/Scsp/CmsBundle/Entity/CategoryEntity.php
namespace Scsp\CmsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Scsp\CmsBundle\Repository\CategoryRepository")
* #ORM\Table(name="categories")
*/
class CategoryEntity {
/**
* #ORM\OneToMany(targetEntity="ProductEntity", mappedBy="categorid")
*/
protected $products;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $categories;
public function __construct(){
$this->products = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set id
*
* #param string $id
* #return Id
*/
public function setId($id) {
$this->id = $id;
return $this;
}
/**
* Set categories
*
* #param string $categories
* #return Categories
*/
public function setCategories($categories) {
$this->categories = $categories;
return $this;
}
/**
* Get categories
*
* #return string
*/
public function getCategories() {
return $this->categories;
}
public function __toString() {
return $this->getCategories();
}
/**
* Add products
*
* #param \Scsp\CmsBundle\Entity\ProductEntity $products
* #return CategoryEntity
*/
public function addProduct(\Scsp\CmsBundle\Entity\ProductEntity $products)
{
$this->products[] = $products;
return $this;
}
/**
* Remove products
*
* #param \Scsp\CmsBundle\Entity\ProductEntity $products
*/
public function removeProduct(\Scsp\CmsBundle\Entity\ProductEntity $products)
{
$this->products->removeElement($products);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Product Form:
<?php
// src/Scsp/CmsBundle/Form/ProductForm.php
namespace Scsp\CmsBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use \Scsp\CmsBundle\Entity\ProductEntity;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormInterface;
class ProductForm extends AbstractType
{
private $id;
public function __construct($id) {
$this->id=$id;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($this->id>0){
$builder->add('id', 'text',
array(
'label' => false,
'attr' => array(
'class' => 'form-control',
'readonly' => 'readonly'
)
)
);
}
$builder
->add('products', 'text',
array(
'label' => false,
'max_length' => 100,
'attr' => array(
'class' => 'form-control',
'autocomplete' => 'off'
)
)
)
->add('categoryid', 'entity', array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'data'=>$builder->getData()->getCategoryId(),
'attr' => array(
)
)
)
->add('save', 'submit',array(
'attr' => array(
'formnovalidate' => 'formnovalidate',
'class' => 'btn btn-primary',
'label' => 'Save Changes'
)
)
)
->getForm();
}
public function getName()
{
return 'products';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'empty_data' => 'Scsp\CmsBundle\Entity\CategoryEntity'
));
}
}
categoryid
is not an entity, so you must to add a property called category in ProductEntity and stablish the relation ManyToOne there, the code:
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="CategoryEntity", inversedBy="products")
* #ORM\JoinColumn(name="categoryid", referencedColumnName="id")
*/
protected $categoryid;
must be replaced by:
/**
* #ORM\Column(name="categoryid", type="integer")
*/
protected $categoryid;
/**
* #var CategoryEntity
* #ORM\ManyToOne(targetEntity="CategoryEntity", inversedBy="products")
* #ORM\JoinColumn(name="categoryid", referencedColumnName="id")
*/
protected $category;
//Here generate the get and the set customized
/**
* #return CategoryEntity
*/
public function getCategory()
{
return $this->category;
}
/**
* #param CategoryEntity $category
* #return $this
*/
public function setCategory($category)
{
//this is the owning side
//all changes will be detected
//in this side by doctrine
$category->addProduct($this);
$this->category= $category;
//The line below is important to set the categoryid
//field in the product table
$this->setCategoryid($category->getId());
return $this;
}
Now in the Product Form replace:
->add('categoryid', 'entity', array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'data'=>$builder->getData()->getCategoryId(),
'attr' => array(
)
)
)
by:
->add('category', 'entity', array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'attr' => array(
)
)
)
That's all, enjoy it!
I see many things wrong in your form type, nonetheless, what are you looking for is the data option in the field definition:
->add('categoryid', 'entity',
array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'data' => $builder->getData()->getCategoryId()
)
)
This data field must contain the select value property, which is the categoryId, at least when using an entity field type as normal. I don't know if this will work with you current setup!
I have a solution for Symfony 1.4.18.
The key is use jQuery.
You can view a detail explanation (including images) in my blog (in Spanish) here: http://symfonyandme.com/2013/10/09/como-usar-select-dependientes-en-symfony-1-4-con-tres-tablas/
And the same solution here: Symfony 1.4 with AJAX jQuery. How can I improve the AJAX's function that contains a select box which depends on another select box?
Then you would work for adjust this example for Symfony 2

Categories