title: Symfony2 multi-level dynamic router
body: <p>I have a current project that has to displays both defined pages with specific entities, what is very easy to manage with Symfony2, and content pages on different layouts, what is - I guess - a bit less common.</p>
  <p>I get in trouble trying to build the routing system.</p>
  <p>For instance, if I have to display a page with some news,
  I would like to update the router of my bundle with a new route like :</p>
  <pre><code>my_bundle_news_page:
      pattern: /news
      defaults:
          _controller: MyBundle:NewsController:indexAction
  </code></pre>
  <p>But how to manage a dynamic router that could have a totally custom URL on many levels ?</p>
  <p>Let's imagine I've got a &quot;Page&quot; Entity, that is self-references for an optionnal &quot;parent-child&quot; relation.
  I don't think I can just use any config YAML file for this specific routing ?!</p>
  <pre><code>my_bundle_custom_page:
      pattern: /{slug}
      defaults:
          _controller: MyBundle:PageController:showAction
  </code></pre>
  <p>This would bind all the first-level pages:</p>
  <blockquote>
  <p>/projects</p>
  <p>/about</p>
  <p>/contact</p>
  <p>/our-project</p>
  </blockquote>
  <p>What about a page that would be displayed with, for instance, a slug like:</p>
  <blockquote>
  <p>/our-project/health</p>
  </blockquote>
  <p>In fact any URL...</p>
  <blockquote>
  <p>/{slug-level1}/{slug-level2}/{slug-level3} etc.</p>
  </blockquote>
  <p>Cause the pages are supposed to change and be updated from webmastering.</p>
  <p>I guess the best way would be to have a router that compare the {slug} with a database field (entity property)</p>
  <p>I read in <a href="http://symfony.com/doc/current/cmf/cookbook/using_a_custom_route_repository.html" rel="nofollow noreferrer">the Symfony-CMF doc</a> that it is possible to write a service based a route provider:</p>
  <pre><code>namespace MyBundle\Routing;
  
  use Symfony\Component\Routing\RouteCollection;
  use Symfony\Component\Routing\Route as SymfonyRoute;
  
  use MyBundle\Entity\PageRepository;
  
  class RouteProvider extends PageRepository {
      public function findPageBySlug($slug)
      {
          // Find a page by slug property
          $page = $this-&gt;findOneBySlug($slug);
  
          if (!$page) {
              // Maybe any custom Exception
              throw $this-&gt;createNotFoundException('The page you are looking for does not exists.');
          }
  
          $pattern = $page-&gt;getUrl(); // e.g. &quot;/first-level/second-level/third-level&quot;
  
          $collection = new RouteCollection();
  
          // create a new Route and set our page as a default
          // (so that we can retrieve it from the request)
          $route = new SymfonyRoute($pattern, array(
              'page' =&gt; $page,
          ));
  
          // add the route to the RouteCollection using a unique ID as the key.
          $collection-&gt;add('page_'.uniqid(), $route);
  
          return $collection;
      }
  }
  </code></pre>
  <p>But how to set it up as a service ? Are there some requirements ?
  How could this kind of thing work, does it add a route to the RouteCollection when request is called ?</p>
  <p>And will I be able to bind any route in this way ?</p>
  <p>EDIT : services.yml of my bundle</p>
  <pre><code>parameters:
      cmf_routing.matcher.dummy_collection.class: Symfony\Component\Routing\RouteCollection
      cmf_routing.matcher.dummy_context.class: Symfony\Component\Routing\RequestContext
      cmf_routing.generator.class: Symfony\Cmf\Bundle\RoutingBundle\Routing\ContentAwareGenerator
      cmf_routing.nested_matcher.class:  Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
      cmf_routing.url_matcher.class:  Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
      fsbcms.chain_router.class: Symfony\Cmf\Component\Routing\ChainRouter
      fsbcms.route_provider.class: FSB\CMSBundle\Routing\RouteProvider
      fsbcms.dynamic_router.class: Symfony\Cmf\Component\Routing\DynamicRouter
      fsbcms.route_entity.class: null
  
  services:
      fsbcms.router:
          class: %fsbcms.chain_router.class%
          arguments:
              - &quot;@logger&quot;
          calls:
              - [setContext, [&quot;router.request_context&quot;]]
      fsbcms.route_provider:
          class: &quot;%fsbcms.route_provider.class%&quot;
          arguments:
              - &quot;@doctrine&quot;
      cmf_routing.matcher.dummy_collection:
          class: &quot;%cmf_routing.matcher.dummy_collection.class%&quot;
          public: &quot;false&quot;
      cmf_routing.matcher.dummy_context:
          class: &quot;%cmf_routing.matcher.dummy_context.class%&quot;
          public: false
      cmf_routing.generator:
          class: &quot;%cmf_routing.generator.class%&quot;
          arguments:
              - &quot;@fsbcms.route_provider&quot;
              - &quot;@logger&quot;
          calls:
              - [setContainer, [&quot;service_container&quot;]]
              - [setContentRepository, [&quot;cmf_routing.content_repository&quot;]]
      cmf_routing.url_matcher:
          class: &quot;%cmf_routing.url_matcher.class%&quot;
          arguments: [&quot;@cmf_routing.matcher.dummy_collection&quot;, &quot;@cmf_routing.matcher.dummy_context&quot;]
      cmf_routing.nested_matcher:
          class: &quot;%cmf_routing.nested_matcher.class%&quot;
          arguments: [&quot;@fsbcms.route_provider&quot;]
          calls:
              - [setFinalMatcher, [&quot;cmf_routing.url_matcher&quot;]]
      fsbcms.dynamic_router:
          class: &quot;%fsbcms.dynamic_router.class%&quot;
          arguments:
              - &quot;@router.request_context&quot;
              - &quot;@cmf_routing.nested_matcher&quot;
              - &quot;@cmf_routing.generator&quot;
          tags:
              - { name: router, priority: 300 }
  </code></pre>