Nested collection fields in Sonata Admin (2.3)
Asked Answered
K

2

24

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:

enter image description here

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:

enter image description here

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:

enter image description here

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 ?

Kazachok answered 7/2, 2016 at 20:34 Comment(5)
give us the block code that it's responsible for this FatalErrorException (line 9772...)Becki
Apparently your $this->getChildFormBuilder($formBuilder, $elementId); method doesn't return a formBuilder, but null instead. Can you share that code?Janus
I don't see anything weird at this point, so I would start by just adding some var_dumps in the getChildFormBuilder, to check the $elementId and the $name variables, and see if they actually match.Janus
@NicoKaag, the problem is they don't match .. But I don't know the reason why they don't match ...Kazachok
nielsv I see that you've accepted chalasr's answer, does that mean that you were are to solve the problem ? If yes what ended up working for you ?Losel
E
7

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.

Ecto answered 26/2, 2016 at 19:5 Comment(5)
Thanks for your answer. I've updated with composer but now I'm getting another error. I've updated my topic (UPDATE 2). Could you take a look?Kazachok
Can you say me where is the method getDashboardGroups and how/where do you call it ?Ecto
I updated my answer with a fix. Don't forget to clear the cache manually by removing all inside the cache directory.Ecto
I've updated my topic one last time, just a little question. Could you take a look?Kazachok
Yes you should, sonata bundles are related, you may have problems with different version. Use the same for at least admin-bundle, user-bundle, doctrine-orm-admin-bundle & block-bundle. I work with them together on 2.4@dev without any problemwEcto
A
1
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;
}
Antepenult answered 24/2, 2016 at 17:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.