Sonata Admin Bundle nested list or has child list

Post (parent) to display comments (child) list.

Creating list of rows on sonata admin bundle is fairly easily with there documentation. But when you want to go bit advance on creating sub list of parent entity, then you would be scratching your head.  Luckily this post will help you overcome this very easily.


Assuming you already have PostAdmin and CommentAdmin class to display list individually, if you don’t then please create them first.

We will tell our child class, who is his father (parent). 

// .. YourBundle/Admin/CommentAdmin.php

class CommentAdmin extends Admin
{
    protected $parentAssociationMapping = 'post'; 
    ...
}

Now we can create services for parent and child, if you already have created the services, then please make sure you add call method on parent service. 

// .. service.yml

admin.comment:
    class: Sonata\YourBundle\Admin\CommentAdmin
    arguments: [~, Sonata\YourBundle\Entity\Comment,~]
    tags:
        - {name: sonata.admin, manager_type: orm, group: "Content"}
admin.post:
    class: Sonata\YourBundle\Admin\PostAdmin
    arguments: [~, Sonata\YourBundle\Entity\Post, ~]
    tags:
        - {name: sonata.admin, manager_type: orm, group: "Content"}
    calls:
        - [addChild, ['@admin.comment']]

That’s it!

We can now add links on post list page, but you can add link on any page as you like. 

Create template to add link

// ..YourBundle/Resources/views/CRUD/post_comments.html.twig

<a class="btn btn-sm btn-default" href="{{ path('OUR_NEW_CHILD_ROUTE_ID', {'id': object.id }) }}">Comments</a>

Replace OUR_NEW_CHILD_ROUTE_ID with comment list route id, which you can find it with following command line.

$ php app/console debug:route

Now we are ready to add link on post list page for each post. 

// ..YourBundle/Admin/PostAdmin.php

class PostAdmin extends Admin
{
    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper->add('_action', 'actions', array(
            'actions' => array(
                'show' => array(),
                'edit' => array(),
                'comments' => array('template' => 'YourBundle:CRUD:post_comments.html.twig')
            )
        ))
    }
}

That’s it you are done. Now all post are linked properly with its child comments as oneToMany relationship. 

SonataAdminBundle has so much more to offer, which can be hidden if you are new to it, but once you learn it. Then creating admin area would take you few days or less then week depending on the size of your application. 

Symfony 3 should I upgrade or stay with Symfony 2

Everyone is excited about Symfony 3, including myself :)

Symfony 3 or Symfony 2?

Symfony 2.8 is exactly same as Symfony 3. But wait, then what’s the difference?

Symfony 3 mostly have new directory structure for logs, cache and console. Also all the previous deprecated functions are removed.

Should I upgrade then, if its same?

No!!!, Even tho it is exactly same, but knowing all the deprecated functions are removed on Symfony 3, you should ask yourself.

Are you going to be using third party bundles?

and thous bundles are update-to-date with Symfony 3. Most likely you will get the answer no.

having third party bundles on Symfony 3 will break your application, If it was using any of the deprecated function.

Soon we can upgrade when everyone upgrades their bundles.

Updated – Wed 17 Feb

You can also check your vendor packages, if they are using any old deprecated functions, check following link for more details – http://symfony.com/blog/paving-the-way-for-symfony-3-with-the-deprecation-detector-tool

Complete list of changes

Symfony3 Container Dependency Injection

It’s 2016, you can use trait which will help you extend same class with multiple libraries.

I keep seeing container class get extended into another class, which then extends to another, before you know it you have extended 5 different classes just to use libraries.

What if you can include 5 different libraries into 1 object without extending? Well you can use it with php5.4 trait feature.

Container dependency injection trait class

    
    namespace iBasit\ToolsBundle\Utils\Lib;
    
    use Doctrine\Bundle\DoctrineBundle\Registry;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    
    trait Container
    {
        private $container;
    
        public function setContainer (ContainerInterface $container)
        {
            $this->container = $container;
        }
    
        /**
         * Shortcut to return the Doctrine Registry service.
         *
         * @return Registry
         *
         * @throws \LogicException If DoctrineBundle is not available
         */
        protected function getDoctrine()
        {
            if (!$this->container->has('doctrine')) {
                throw new \LogicException('The DoctrineBundle is not registered in your application.');
            }
    
            return $this->container->get('doctrine');
        }
    
        /**
         * Get a user from the Security Token Storage.
         *
         * @return mixed
         *
         * @throws \LogicException If SecurityBundle is not available
         *
         * @see TokenInterface::getUser()
         */
        protected function getUser()
        {
            if (!$this->container->has('security.token_storage')) {
                throw new \LogicException('The SecurityBundle is not registered in your application.');
            }
    
            if (null === $token = $this->container->get('security.token_storage')->getToken()) {
                return;
            }
    
            if (!is_object($user = $token->getUser())) {
                // e.g. anonymous authentication
                return;
            }
    
            return $user;
        }
    
        /**
         * Returns true if the service id is defined.
         *
         * @param string $id The service id
         *
         * @return bool true if the service id is defined, false otherwise
         */
        protected function has ($id)
        {
            return $this->container->has($id);
        }
    
        /**
         * Gets a container service by its id.
         *
         * @param string $id The service id
         *
         * @return object The service
         */
        protected function get ($id)
        {
            if ('request' === $id)
            {
                @trigger_error('The "request" service is deprecated and will be removed in 3.0. Add a typehint for Symfony\\Component\\HttpFoundation\\Request to your controller parameters to retrieve the request instead.', E_USER_DEPRECATED);
            }
    
            return $this->container->get($id);
        }
    
        /**
         * Gets a container configuration parameter by its name.
         *
         * @param string $name The parameter name
         *
         * @return mixed
         */
        protected function getParameter ($name)
        {
            return $this->container->getParameter($name);
        }
    }

Your custom object, which will be service.

    namespace AppBundle\Utils;
    
    use iBasit\ToolsBundle\Utils\Lib\Container;
    
    class myObject
    {
        use Container;
    }

Your object service settings

     myObject: 
            class: AppBundle\Utils\myObject
            calls:
                - [setContainer, ["@service_container"]]

Call your service in controller

    $myObject = $this->get('myObject');

Setup Environment UAT + LIVE – GITHUB (Auto Deploy)

We will cover UAT and Live environments and exclude developer environment.

DEV environment for developer to code and test, before he push his commits to remote repository (github.com),  which will be auto deploy to UAT environment for everyone to test with other developers changes. Note – we wont be covering DEV environment.

UAT environment for testing the site and getting ready to push to live environment after we think all the bugs are fixed.

Live environment to have the latest version of the website.
Continue reading Setup Environment UAT + LIVE – GITHUB (Auto Deploy)