Search code examples
phpsymfonysonata-adminsonata

Sonata Admin Bundle - RouteCompiler error because of placeholder longer than 32 char


I have just updated my packages using composer, and since then Symfony throws an error. Before the update, my application was running fine.

Error:

Variable name "childChildChildChildChildChildChildId" cannot be longer than 32 characters in route pattern "/admin/project/rest/member/{id}/shelves_member/{childId}/contributor/{childChildId}/series/{childChildChildId}/shelves_series/{childChildChildChildId}/post/{childChildChildChildChildId}/tag/{childChildChildChildChildChildId}/event/{childChildChildChildChildChildChildId}/edit". Please use a shorter name.

Explanation:

It appears that the admins are nested pretty deeply in the admin.yml, which makes the dynamically-generated 'childchild...' placeholder longer than 32 characters, which in turn throws an error since the RoutingCompiler.php from Symfony has a limit for placeholders defined by the constant VARIABLE_MAXIMUM_LENGTH to 32 char.

I haven't found anything in the sonata documentation regarding custom placeholders or setting a maximum length for them.

Right now, the only way I found to get the application to work after updating is to change this constant to 64 characters, but I'm pretty sure it's not a good idea as it could break things elsewhere.

(edit: added stacktrace)

Stacktrace

 in vendor/symfony/symfony/src/Symfony/Component/Routing/RouteCompiler.php at line 116  -

                }
                if (strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) {
                    throw new \DomainException(sprintf('Variable name "%s" cannot be longer than %s characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern));
                }
                if ($isSeparator && strlen($precedingText) > 1) {

at RouteCompiler ::compilePattern (object(Route), '/admin/project/rest/member/{id}/shelves_member/{childId}/contributor/{childChildId}/series/{childChildChildId}/shelves_series/{childChildChildChildId}/post/{childChildChildChildChildId}/tag/{childChildChildChildChildChildId}/event/{childChildChildChildChildChildChildId}/edit', false)
in vendor/symfony/symfony/src/Symfony/Component/Routing/RouteCompiler.php at line 65  +
at RouteCompiler ::compile (object(Route))
in vendor/symfony/symfony/src/Symfony/Component/Routing/Route.php at line 594  +
at Route ->compile ()
in vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php at line 387  +
at PhpMatcherDumper ->groupRoutesByHostRegex (object(RouteCollection))
in vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php at line 125  +
at PhpMatcherDumper ->compileRoutes (object(RouteCollection), true)
in vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php at line 96  +
at PhpMatcherDumper ->generateMatchMethod (true)
in vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php at line 76  +
at PhpMatcherDumper ->dump (array('class' => 'appDevDebugProjectContainerUrlMatcher', 'base_class' => 'Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher'))
in vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php at line 306  +
at Router ->Symfony\Component\Routing\{closure} (object(ResourceCheckerConfigCache))
at call_user_func (object(Closure), object(ResourceCheckerConfigCache))
in vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php at line 46  +
at ResourceCheckerConfigCacheFactory ->cache ('/var/www/serverv2/app/cache/dev/appDevDebugProjectContainerUrlMatcher.php', object(Closure))
in vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php at line 308  +
at Router ->getMatcher ()
in vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php at line 256  +
at Router ->matchRequest (object(Request))
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php at line 157  +
at RouterListener ->onKernelRequest (object(GetResponseEvent), 'kernel.request', object(TraceableEventDispatcher))
at call_user_func (array(object(RouterListener), 'onKernelRequest'), object(GetResponseEvent), 'kernel.request', object(TraceableEventDispatcher))
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php at line 61  +
at WrappedListener ->__invoke (object(GetResponseEvent), 'kernel.request', object(ContainerAwareEventDispatcher))
at call_user_func (object(WrappedListener), object(GetResponseEvent), 'kernel.request', object(ContainerAwareEventDispatcher))
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php at line 184  +
at EventDispatcher ->doDispatch (array(object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener)), 'kernel.request', object(GetResponseEvent))
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php at line 46  +
at EventDispatcher ->dispatch ('kernel.request', object(GetResponseEvent))
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php at line 133  +
at TraceableEventDispatcher ->dispatch ('kernel.request', object(GetResponseEvent))
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php at line 116  +
at HttpKernel ->handleRaw (object(Request), '1')
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php at line 57  +
at HttpKernel ->handle (object(Request), '1', true)
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php at line 67  +
at ContainerAwareHttpKernel ->handle (object(Request), '1', true)
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php at line 183  +
at Kernel ->handle (object(Request))
in web/app_dev.php at line 32  +
Sonata packages

$ composer show --latest 'sonata-project/*' sonata-project/admin-bundle 3.34.1 3.34.1 The missing Symfony Admin Generator sonata-project/block-bundle 3.12.1 3.12.1 Symfony SonataBlockBundle sonata-project/cache 1.0.7 1.0.7 Cache library sonata-project/core-bundle 3.9.1 3.9.1 Symfony SonataCoreBundle sonata-project/datagrid-bundle 2.3.1 2.3.1 Symfony SonataDatagridBundle sonata-project/doctrine-extensions 1.0.2 1.0.2 Doctrine2 behavioral extensions sonata-project/doctrine-mongodb-admin-bundle 3.1.1 3.1.1 Symfony Sonata / Integrate Doctrine MongoDB ODM into the SonataAdminBundle sonata-project/easy-extends-bundle 2.5.0 2.5.0 Symfony SonataEasyExtendsBundle sonata-project/exporter 1.8.0 1.8.0 Lightweight Exporter library sonata-project/formatter-bundle 3.4.1 3.4.1 Symfony SonataFormatterBundle sonata-project/google-authenticator 1.1.0 1.1.0 Library to integrate Google Authenticator into a PHP project sonata-project/user-bundle 3.6.0 3.6.0 Symfony SonataUserBundle

Symfony packages

$ composer show --latest 'symfony*' symfony/monolog-bundle 2.11.1 v3.2.0 Symfony MonologBundle symfony/phpunit-bridge v3.1.5 v4.0.8 Symfony PHPUnit Bridge symfony/polyfill-apcu v1.7.0 v1.7.0 Symfony polyfill backporting apcu_* functions to lower PHP versions symfony/polyfill-intl-icu v1.7.0 v1.7.0 Symfony polyfill for intl's ICU-related data and classes symfony/polyfill-mbstring v1.7.0 v1.7.0 Symfony polyfill for the Mbstring extension symfony/polyfill-php54 v1.7.0 v1.7.0 Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions symfony/polyfill-php55 v1.7.0 v1.7.0 Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions symfony/polyfill-php56 v1.7.0 v1.7.0 Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions symfony/polyfill-php70 v1.7.0 v1.7.0 Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions symfony/polyfill-util v1.7.0 v1.7.0 Symfony utilities for portability of PHP codes symfony/security-acl v3.0.1 v3.0.1 Symfony Security Component - ACL (Access Control List) symfony/swiftmailer-bundle v2.3.11 v3.0.3 Symfony SwiftmailerBundle symfony/symfony v2.8.38 v3.4.8 The Symfony PHP framework

PHP version

$ php -v PHP 5.6.33-0+deb8u1 (cli) (built: Jan 5 2018 15:46:26)

Thanks for your help!


Solution

  • I ran into the same problem and after reading through the issue you added to github I resolved this as follows:

    First I created my own AbstractAdmin that extends Sonata's.

    All my Sonata admin classes then extend my abstract admin instead.

    In my AbstractAdmin I override the getIdParameter() method to force Sonata to use a shorter placeholder - 'c' instead of 'child':

    <?php
    namespace App\Admin;
    
    use Sonata\AdminBundle\Admin\AbstractAdmin as SonataAbstractAdmin;
    
    abstract class AbstractAdmin extends SonataAbstractAdmin
    {
        public function getIdParameter()
        {
            $parameter = 'id';
    
            for ($i = 0; $i < $this->getChildDepth(); ++$i) {
                $parameter = sprintf('c%s', ucfirst($parameter));
            }
    
            return $parameter;
        }
    }
    

    Note that this does not allow for unlimited nesting - just for a lot more.

    There's no way around the overall limit of 32 chars per placeholder that's been imposed by Symfony.

    As you pointed out yourself, it's a constant that could be overwritten but that's probably not a good idea.

    For reference: https://github.com/sonata-project/SonataAdminBundle/issues/5072