Skip to content
This repository was archived by the owner on Sep 16, 2021. It is now read-only.
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 143 additions & 35 deletions bundles/routing/dynamic_customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,70 +44,127 @@ following class provides a simple solution using an ODM Repository.

.. code-block:: php

<?php
// src/Acme/DemoBundle/Repository/RouteProvider.php
namespace Acme\DemoBundle\Repository;

use Doctrine\ODM\PHPCR\DocumentRepository;
use Doctrine\Bundle\PHPCRBundle\ManagerRegistry;
use Doctrine\ODM\PHPCR\DocumentManager;
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route;
use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter;
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route as SymfonyRoute;
use Symfony\Component\Routing\RouteCollection;

class RouteProvider extends DocumentRepository implements RouteProviderInterface
class RouteProvider implements RouteProviderInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the "story" of this provider? the old example was loading documents and creating routes on the fly - this one just looks like a copy of the default provider.

{
/**
* This method is used to find routes matching the given URL.
* @var ManagerRegistry
*/
public function findManyByUrl($url)
{
// for simplicity we retrieve one route
$document = $this->findOneBy(array(
'url' => $url,
));
private $managerRegistry;

$pattern = $document->getUrl(); // e.g. "/this/is/a/url"

$collection = new RouteCollection();
/**
* To get the Document Manager out of the registry.
*
* @return DocumentManager
*/
private function getDocumentManager()
{
return $this->managerRegistry->getManager();
}

// create a new Route and set our document as
// a default (so that we can retrieve it from the request)
$route = new SymfonyRoute($pattern, array(
'document' => $document,
));
public function __construct(ManagerRegistry $managerRegistry)
{
$this->managerRegistry = $managerRegistry;
}

// add the route to the RouteCollection using
// a unique ID as the key.
$collection->add('my_route_'.uniqid(), $route);
public function getRouteCollectionForRequest(Request $request)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the most crucial point to make here is that this should not do complicated route matching operations, but just load routes that could potentially match the request. the real matching is being done by the symfony routing component later.

can we find some simpler example than the candiates thing? maybe something very simple that just creates routes on the fly, instead of loading from doctrine (as that is already implemented)

{
$path = $request->getPathInfo();
$candidates = $this->getCandidates($path);

$routeCandidates = $this->getDocumentManager()->findMany(null, $candidates);
$routeCollection = new RouteCollection();

$count = 0;
foreach ($routeCandidates as $candidate) {
$count++;
if ($candidate instanceof Route) {
$defaults = $candidate->getDefaults();
$defaults[DynamicRouter::CONTENT_KEY] = $candidate->getContent();
$routeCollection->add(
'my_route_'.$count,
new SymfonyRoute($candidate->getPath(), $defaults)
);
}
}

return $collection;
return $routeCollection;
}

/**
* This method is used to generate URLs, e.g. {{ path('foobar') }}.
* {@inheritDoc}
*/
public function getRouteByName($name, $params = array())
public function getRouteByName($name, $parameters = array())
{
$document = $this->findOneBy(array(
'name' => $name,
));
/** @var Route $route */
$route = $this->getDocumentManager()->find(null, $name);

if ($route) {
$route = new SymfonyRoute($route->getPattern(), array(
'document' => $document,
));
$defaults = $route->getDefaults();
$defaults[DynamicRouter::CONTENT_KEY] = $route->getContent();
$route = new SymfonyRoute($route->getPath(), $defaults);
}

return $route;
}

public function getRoutesByNames($names, $parameters = array())
{

}

/**
* Method to to create the paths to look for the current route.
*
* @param string
*/
private function getCandidates($path)
{
// add your route base paths
$prefixes = array(
'/cms/routes',
);

$result = array();
foreach ($prefixes as $prefix) {
$result[] = $prefix.$path;
}

return $result;
}
}

.. tip::

As you may have noticed we return a ``RouteCollection`` object - why not
The ``RouteProviderInteface`` will force to implement the shown above.
As you may have noticed we return in ``getRouteCollectionForRequest``
and ``getRoutesByNames`` a ``RouteCollection`` object - why not
return a single ``Route``? The Dynamic Router allows us to return many
*candidate* routes, in other words, routes that *might* match the incoming
URL. This is important to enable the possibility of matching *dynamic*
routes, ``/page/{page_id}/edit`` for example. In our example we match the
given URL exactly and only ever return a single ``Route``.
routes, ``/page/{page_id}/edit`` for example.

If you set some defaults for your route (template, controller, etc.), they will
be added as options to the Symfony route. As you may have noticed the example
added the mapped document with a specific key ``DynamicRouter::CONTENT_KEY``
to the defaults array. By doing this you will find the current document in
the requests parameter bag in
``$parameterBag[...]['my_route_1'][DynamicRouter::CONTENT_KEY]``
to manipulate it in listeners for example. But most important part is:
The document will be injected to your action by adding a parameter with
that name.

Replacing the Default CMF Route Provider
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -119,7 +176,7 @@ configuration as follows:

.. code-block:: yaml

# app/config/config.yml
// app/config/config.yml
cmf_routing:
dynamic:
enabled: true
Expand Down Expand Up @@ -150,7 +207,58 @@ configuration as follows:

Where ``acme_demo.provider.endpoint`` is the service ID of your route
provider. See `Creating and configuring services in the container`_ for
information on creating custom services.
information on creating custom services. In our example the service definition
will look like this:

.. configuration-block::

.. code-block:: yaml

parameters:
acme_demo.provider.endpoint.class: Acme\DemoBundle\Repository\RouteProvider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why "endpoint"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was the name in the old example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just call this thing acme_demo.route_provider


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parameters:
    acme_demo.provider.endpoint.class: Acme\DemoBundle\Repository\RouteProvider

services:
    acme_demo.provider.endpoint:
        class: "%acme_demo.provider.endpoint.class%"
        calls:
            - [setDocumentRegistry, ["@doctrine_phpcr"]]
use Symfony\Component\DependencyInjection\Reference;

$container->setParameter(
    'acme_demo.provider.endpoint.class',
    'Acme\DemoBundle\Repository\RouteProvider'
);

$container
    ->register('acme_demo.provider.endpoint', '%acme_demo.provider.endpoint.class%')
    ->addMethodCall('setDocumentRegistry', array(
        new Reference('doctrine_phpcr'),
    ))
;

Btw, why don't you use constructor injection?

services:
acme_demo.provider.endpoint:
class: "%acme_demo.provider.endpoint.class%"
arguments: ["@doctrine_phpcr"]

.. code-block:: xml

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="acme_demo.provider.endpoint.class">Acme\Repository\RouteProvider</parameter>
</parameters>

<services>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should add a new line before this

<service id="acme_demo.provider.endpoint" class="%acme_demo.provider.endpoint.class%">
<argument type="service" id="doctrine_phpcr"/>
</service>
</services>

</container>


.. code-block:: php

$container->setParameter(
'acme_demo.provider.endpoint.class',
'Acme\DemoBundle\Repository\RouteProvider'
);

$container
->register('acme_demo.provider.endpoint', '%acme_demo.provider.endpoint.class%')
->addArgument(new Reference('doctrine_phpcr'))
;

As you can see the ``DocumentRegistry`` of Doctrine PHPCR-ODM is injected.
This will provide the document manger the provider needs to query
the persistence implementation. As the RouteProvider is a Symfony solution
you can inject what you want - you should somehow return a ``Route`` or
``RouteCollection`` in your providers methods - it is up to you.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think you can chose whether to return a Route or a RouteCollection, can you? i think the DynamicRouter is expecting one or the other. i also don't like the "you should somehow" part. Maybe formulate it like this?:

Here we inject the Doctrine Manager Registry into the RouteProvider service. As the RouteProvider is a Symfony service, you can inject whatever you need. The only important thing is that the provider returns a Route object resp. the RouteCollection for a Request.


.. _`Creating and configuring services in the container`: http://symfony.com/doc/current/book/service_container.html#creating-configuring-services-in-the-container/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a new line

.. _`PHPCR-ODM`: http://www.doctrine-project.org/projects/phpcr-odm.html