-
Notifications
You must be signed in to change notification settings - Fork 156
refactor route provider example #423
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
{ | ||
/** | ||
* 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
@@ -119,7 +176,7 @@ configuration as follows: | |
|
||
.. code-block:: yaml | ||
|
||
# app/config/config.yml | ||
// app/config/config.yml | ||
cmf_routing: | ||
dynamic: | ||
enabled: true | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why "endpoint"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was the name in the old example. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets just call this thing acme_demo.route_provider |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
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.