How to pass SensioLabs Insight route check when using SonataAdminBundle

One of the rules SensioLabs Insight checks for is:

A route should always have a valid HTTP method

This will always fail when you use the SonataAdminBundle, because there is no way to set the methods in all routes generated by the admin classes. You can show them with the following command:

$ php app/console router:debug

Which will output those routes:

...
 Name                                     Method Scheme Host Path
 admin_company_backend_user_list          ANY    ANY    ANY  /admin/company/backend/user/list
 admin_company_backend_user_create        ANY    ANY    ANY  /admin/company/backend/user/create
 admin_company_backend_user_batch         ANY    ANY    ANY  /admin/company/backend/user/batch
 admin_company_backend_user_edit          ANY    ANY    ANY  /admin/company/backend/user/{id}/edit
 admin_company_backend_user_delete        ANY    ANY    ANY  /admin/company/backend/user/{id}/delete
 admin_company_backend_user_show          ANY    ANY    ANY  /admin/company/backend/user/{id}/show
 admin_company_backend_user_custom_action ANY    ANY    ANY  /admin/company/backend/user/{id}/custom-action
...

When you dive into the code of the bundle you're going to see why. The method where they build the routes only offers 4 of the 8 available parameters of the Route class:

$this->elements[$this->getCode($name)] = function() use ($pattern, $defaults, $requirements, $options) {
    return new Route($pattern, $defaults, $requirements, $options);
};

They don't implement $host, $schemes, $methods and $condition of the Route constructor:

public function __construct(
    $path, 
    array $defaults = array(), 
    array $requirements = array(),
    array $options = array(), 
    $host = '', 
    $schemes = array(), 
    $methods = array(), 
    $condition = '')
{

So there is no way to add it to any kind of configuration, because they didn't even consider it within the system.

But I still found a way to implement it. I build my own Admin class which extends from Sonata\AdminBundle\Admin\Admin. All my admin classes now extend of it instead of the Sonata Admin class. As part of this class I build a little helper function which adds the methods ["GET", "PUT", "POST", "DELETE"] to all default routes and whichever you like to your custom ones.

<?php

namespace Company\Bundle\AdminBundle\Admin;

use Sonata\AdminBundle\Admin\Admin as SonataAdmin;
use Sonata\AdminBundle\Route\RouteCollection;
use Symfony\Component\Routing\Route;

/**
 * Class Admin
 *
 * @package Company\Bundle\AdminBundle\Admin
 */
class Admin extends SonataAdmin
{
    /**
     * Configure routes
     *
     * @param RouteCollection $collection    Route collection
     * @param array           $customMethods Custom methods
     *  [
     *      'code'  => ['GET', 'POST', ..],
     *      ..
     *  ]
     */
    protected function configureRouteMethods(RouteCollection $collection, array $customMethods = [])
    {
        /** @var Route $route */
        foreach($collection->getElements() as $code => $route) {
            if(in_array($code, array_keys($customMethods))) {
                $route->setMethods($customMethods[$code]);
            } else {
                $route->setMethods(["GET", "PUT", "POST", "DELETE"]);
            }
        }
    }
}

Then in my admin classes I overwrite configureRoutes and call configureRouteMethods (after I added my own custom methods).

/**
 * Configure routes
 *
 * @param RouteCollection $collection Route collection
 */
protected function configureRoutes(RouteCollection $collection)
{
    $collection->add('custom_action', $this->getRouterIdParameter().'/custom-action');

    $customMethods = [
        $collection->getCode('custom_action') => ['GET', 'DELETE']
    ];

    $this->configureRouteMethods($collection, $customMethods);
}

After the integration it will look like this:

...
 Name                                     Method              Scheme Host Path
 admin_company_backend_user_list          GET|PUT|POST|DELETE ANY    ANY  /admin/company/backend/user/list
 admin_company_backend_user_create        GET|PUT|POST|DELETE ANY    ANY  /admin/company/backend/user/create
 admin_company_backend_user_batch         GET|PUT|POST|DELETE ANY    ANY  /admin/company/backend/user/batch
 admin_company_backend_user_edit          GET|PUT|POST|DELETE ANY    ANY  /admin/company/backend/user/{id}/edit
 admin_company_backend_user_delete        GET|PUT|POST|DELETE ANY    ANY  /admin/company/backend/user/{id}/delete
 admin_company_backend_user_show          GET|PUT|POST|DELETE ANY    ANY  /admin/company/backend/user/{id}/show
 admin_company_backend_user_custom_action GET|DELETE          ANY    ANY  /admin/company/backend/user/{id}/custom-action
...

And that way you will pass the check.