Sunday, January 26, 2014

SonataAdminBundle: FatalErrorException: Error: Call to a member function add() on a non-object...

The problem usually occurs during updating a data record in the admin/dashboard of SonataAdminBundle.
The cause of the problem is that the add() method, defined in "vendor/sonata-project/doctrine-orm-admin-bundle/Sonata/DoctrineORMAdminBundle/Model/ModelManager.php line 560" received a two dimensional array object instead of a simple array.
Let's take a look at the User.php:
//src/Enstb/Bundle/VisplatBundle/Entity/User.php
public function getRoles()
    {
        return $this->roles->toArray();
    }
The getRoles method is automatically called when updating a user. As you can see, it wraps the roles object with an array, this is required for authentication system of Symfony.
Well, what we can do? One of the possible solutions is making a new name for roles, in our case is rolesCollection, to let us configure a different return type.
//src/Enstb/Bundle/VisplatBundle/Administrator/UserAdmin.php
protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            //...
            ->add('rolesCollection','entity',array(
                'class' => 'EnstbVisplatBundle:Role',
                'property'=>'name',
                'expanded' => true,
                'compound' => true,
                'multiple' => true,
                'by_reference' => false
            ));
    }
Then, the add,remove,get, and set are required:
//src/Enstb/Bundle/VisplatBundle/Entity/User.php
 public function addRolesCollection($role){
        $this->addRole($role);
    }
public function removeRolesCollection($role){
        $this->removeRole($role);
    }
public function getRolesCollection()
    {
        return $this->roles;
    }
public function setRolesCollection($roles)
    {
        if (count($roles) > 0) {
            foreach ($roles as $role) {
                $this->addRole($role);
            }
        }
        return $this;
    }
Finally, DONT forget to verify an existing roles before adding it in order to prevent duplicating roles added into a database.
//src/Enstb/Bundle/VisplatBundle/Entity/User.php
public function addRole(\Enstb\Bundle\VisplatBundle\Entity\Role $role)
    {
        if(!in_array($role,$this->getRoles())){
            // Link each role with the user
            $role->addUser($this);
            $this->roles->add($role);
        }

        return $this;
    }