Skip to content

Commit df34732

Browse files
committed
Merge pull request #122 from symfony-cmf/orm
[WIP] ORM based dynamic routing
2 parents 41fc3a1 + 48194cb commit df34732

File tree

13 files changed

+318
-14
lines changed

13 files changed

+318
-14
lines changed

CmfRoutingBundle.php

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Symfony\Cmf\Bundle\RoutingBundle;
44

55
use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass;
6+
use Symfony\Cmf\Bundle\CoreBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
67
use Symfony\Component\DependencyInjection\Definition;
78
use Symfony\Component\HttpKernel\Bundle\Bundle;
89
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -33,10 +34,39 @@ public function build(ContainerBuilder $container)
3334
realpath(__DIR__ . '/Resources/config/doctrine-model') => 'Symfony\Cmf\Bundle\RoutingBundle\Model',
3435
realpath(__DIR__ . '/Resources/config/doctrine-phpcr') => 'Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr',
3536
),
36-
array('cmf_routing.manager_name')
37+
array('cmf_routing.dynamic.persistence.phpcr.manager_name'),
38+
'cmf_routing.backend_type_phpcr'
3739
)
3840
);
3941
}
42+
43+
if (class_exists('Symfony\Cmf\Bundle\CoreBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass')) {
44+
$container->addCompilerPass($this->buildBaseOrmCompilerPass());
45+
$container->addCompilerPass(
46+
DoctrineOrmMappingsPass::createXmlMappingDriver(
47+
array(
48+
realpath(__DIR__ . '/Resources/config/doctrine-model') => 'Symfony\Cmf\Bundle\RoutingBundle\Model',
49+
realpath(__DIR__ . '/Resources/config/doctrine-orm') => 'Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Orm',
50+
),
51+
array('cmf_routing.dynamic.persistence.orm.manager_name'),
52+
'cmf_routing.backend_type_orm'
53+
)
54+
);
55+
}
56+
}
57+
58+
private function buildBaseOrmCompilerPass()
59+
{
60+
$arguments = array(array(realpath(__DIR__ . '/Resources/config/doctrine-base')), '.orm.xml');
61+
$locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator', $arguments);
62+
$driver = new Definition('Doctrine\ORM\Mapping\Driver\XmlDriver', array($locator));
63+
64+
return new DoctrineOrmMappingsPass(
65+
$driver,
66+
array('Symfony\Component\Routing'),
67+
array('cmf_routing.dynamic.persistence.orm.manager_name'),
68+
'cmf_routing.backend_type_orm'
69+
);
4070
}
4171

4272
/**
@@ -55,7 +85,7 @@ private function buildBasePhpcrCompilerPass()
5585
return new DoctrinePhpcrMappingsPass(
5686
$driver,
5787
array('Symfony\Component\Routing'),
58-
array('cmf_routing.manager_name'),
88+
array('cmf_routing.dynamic.persistence.phpcr.manager_name'),
5989
'cmf_routing.backend_type_phpcr'
6090
);
6191
}

DependencyInjection/CmfRoutingExtension.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ private function setupDynamicRouter(array $config, ContainerBuilder $container,
9393
$hasProvider = true;
9494
$hasContentRepository = true;
9595
}
96+
97+
if (!empty($config['persistence']['orm']['enabled'])) {
98+
$this->loadOrmProvider($config['persistence']['orm'], $loader, $container);
99+
$hasProvider = true;
100+
}
101+
96102
if (isset($config['route_provider_service_id'])) {
97103
$container->setAlias('cmf_routing.route_provider', $config['route_provider_service_id']);
98104
$hasProvider = true;
@@ -142,10 +148,10 @@ public function loadPhpcrProvider($config, XmlFileLoader $loader, ContainerBuild
142148

143149
$container->setParameter($this->getAlias() . '.backend_type_phpcr', true);
144150

145-
$container->setParameter($this->getAlias() . '.persistence.phpcr.route_basepath', $config['route_basepath']);
146-
$container->setParameter($this->getAlias() . '.persistence.phpcr.content_basepath', $config['content_basepath']);
151+
$container->setParameter($this->getAlias() . '.dynamic.persistence.phpcr.route_basepath', $config['route_basepath']);
152+
$container->setParameter($this->getAlias() . '.dynamic.persistence.phpcr.content_basepath', $config['content_basepath']);
147153

148-
$container->setParameter($this->getAlias() . '.manager_name', $config['manager_name']);
154+
$container->setParameter($this->getAlias() . '.dynamic.persistence.phpcr.manager_name', $config['manager_name']);
149155

150156
$container->setAlias($this->getAlias() . '.route_provider', $this->getAlias() . '.phpcr_route_provider');
151157
$container->setAlias($this->getAlias() . '.content_repository', $this->getAlias() . '.phpcr_content_repository');
@@ -171,6 +177,13 @@ public function loadSonataPhpcrAdmin($config, XmlFileLoader $loader, ContainerBu
171177
$loader->load('admin-phpcr.xml');
172178
}
173179

180+
public function loadOrmProvider($config, XmlFileLoader $loader, ContainerBuilder $container)
181+
{
182+
$container->setParameter($this->getAlias() . '.dynamic.persistence.orm.manager_name', $config['manager_name']);
183+
$container->setParameter($this->getAlias() . '.backend_type_orm', true);
184+
$loader->load('provider_orm.xml');
185+
}
186+
174187
/**
175188
* Returns the base path for the XSD files.
176189
*

DependencyInjection/Configuration.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ public function getConfigTreeBuilder()
7272
->end()
7373
->end()
7474
->end()
75+
->arrayNode('orm')
76+
->children()
77+
->scalarNode('enabled')->defaultNull()->end()
78+
->scalarNode('manager_name')->defaultNull()->end()
79+
->end()
80+
->end()
7581
->end()
7682
->end()
7783
->scalarNode('uri_filter_regexp')->defaultValue('')->end()

Doctrine/DoctrineProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ abstract class DoctrineProvider
4040
public function __construct(ManagerRegistry $managerRegistry, $className = null)
4141
{
4242
$this->managerRegistry = $managerRegistry;
43+
$this->className = $className;
4344
}
4445

4546
/**

Doctrine/Orm/ContentRepository.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Orm;
4+
5+
use Symfony\Cmf\Component\Routing\ContentRepositoryInterface;
6+
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\DoctrineProvider;
7+
8+
/**
9+
* Abstract content repository for ORM
10+
*
11+
* @author teito
12+
*/
13+
class ContentRepository extends DoctrineProvider implements ContentRepositoryInterface
14+
{
15+
/**
16+
* Determine target class and id for this content
17+
*
18+
* @param mixed $identifier as produced by getContentId
19+
*
20+
* @return array with model first element, id second
21+
*/
22+
protected function getModelAndId($identifier)
23+
{
24+
return explode(':', $identifier, 2);
25+
}
26+
27+
/**
28+
* {@inheritDoc}
29+
*/
30+
public function findById($id)
31+
{
32+
list($model, $modelId) = $this->getModelAndId($id);
33+
34+
return $this->getObjectManager()->getRepository($model)->find($modelId);
35+
}
36+
37+
/**
38+
* {@inheritDoc}
39+
*/
40+
public function getContentId($content)
41+
{
42+
if (! is_object($content)) {
43+
return null;
44+
}
45+
46+
try {
47+
$meta = $this->getObjectManager()->getClassMetadata(get_class($content));
48+
$ids = $meta->getIdentifierValues($content);
49+
if (0 !== count($ids)) {
50+
throw new \Exception('Multi identifier values not supported in ' . get_class($content));
51+
}
52+
53+
return implode(':', array(
54+
get_class($content),
55+
reset($ids)
56+
));
57+
} catch (\Exception $e) {
58+
return null;
59+
}
60+
}
61+
}

Doctrine/Orm/Route.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Orm;
4+
5+
use Symfony\Cmf\Bundle\RoutingBundle\Model\Route as RouteModel;
6+
7+
/**
8+
* ORM route version.
9+
* @author matteo caberlotto [email protected]
10+
*/
11+
class Route extends RouteModel
12+
{
13+
/**
14+
* {@inheritDoc}
15+
*/
16+
protected $name;
17+
18+
/**
19+
* {@inheritDoc}
20+
*/
21+
protected $position;
22+
}

Doctrine/Orm/RouteProvider.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
namespace Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Orm;
4+
5+
use Symfony\Component\Routing\RouteCollection;
6+
use Symfony\Component\Routing\Exception\RouteNotFoundException;
7+
8+
use Symfony\Component\HttpFoundation\Request;
9+
10+
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
11+
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\DoctrineProvider;
12+
13+
/**
14+
* Provider loading routes from Doctrine
15+
*
16+
* This is <strong>NOT</strong> not a doctrine repository but just the route
17+
* provider for the NestedMatcher. (you could of course implement this
18+
* interface in a repository class, if you need that)
19+
*
20+
21+
*/
22+
class RouteProvider extends DoctrineProvider implements RouteProviderInterface
23+
{
24+
protected function getCandidates($url)
25+
{
26+
$candidates = array();
27+
if ('/' !== $url) {
28+
if (preg_match('/(.+)\.[a-z]+$/i', $url, $matches)) {
29+
$candidates[] = $url;
30+
$url = $matches[1];
31+
}
32+
33+
$part = $url;
34+
while (false !== ($pos = strrpos($part, '/'))) {
35+
$candidates[] = $part;
36+
$part = substr($url, 0, $pos);
37+
}
38+
}
39+
40+
$candidates[] = '/';
41+
42+
return $candidates;
43+
}
44+
45+
/**
46+
* {@inheritDoc}
47+
*/
48+
public function getRouteByName($name, $parameters = array())
49+
{
50+
$route = $this->getRoutesRepository()->findBy(array('name' => $name));
51+
if (!$route) {
52+
throw new RouteNotFoundException("No route found for name '$name'");
53+
}
54+
55+
return $route;
56+
}
57+
58+
public function getRoutesByNames($names, $parameters = array())
59+
{
60+
}
61+
62+
public function getRouteCollectionForRequest(Request $request)
63+
{
64+
$url = $request->getPathInfo();
65+
66+
$candidates = $this->getCandidates($url);
67+
68+
$collection = new RouteCollection();
69+
70+
if (empty($candidates)) {
71+
return $collection;
72+
}
73+
74+
try {
75+
$routes = $this->getRoutesRepository()->findByStaticPrefix($candidates, array('position' => 'ASC'));
76+
77+
foreach ($routes as $key => $route) {
78+
if (preg_match('/.+\.([a-z]+)$/i', $url, $matches)) {
79+
if ($route->getDefault('_format') === $matches[1]) {
80+
continue;
81+
}
82+
83+
$route->setDefault('_format', $matches[1]);
84+
}
85+
// SYMFONY 2.1 COMPATIBILITY: tweak route name
86+
$key = trim(preg_replace('/[^a-z0-9A-Z_.]/', '_', $key), '_');
87+
$collection->add($key, $route);
88+
}
89+
} catch (RepositoryException $e) {
90+
// TODO: how to determine whether this is a relevant exception or not?
91+
// for example, getting /my//test (note the double /) is just an invalid path
92+
// and means another router might handle this.
93+
// but if the PHPCR backend is down for example, we want to alert the user
94+
}
95+
96+
return $collection;
97+
}
98+
99+
protected function getRoutesRepository()
100+
{
101+
return $this->getObjectManager()->getRepository($this->className);
102+
}
103+
}

Resources/config/admin-phpcr.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
</call>
2626

2727
<call method="setContentRoot">
28-
<argument>%cmf_routing.persistence.phpcr.content_basepath%</argument>
28+
<argument>%cmf_routing.dynamic.persistence.phpcr.content_basepath%</argument>
2929
</call>
3030

3131
<call method="setRouteRoot">
32-
<argument>%cmf_routing.persistence.phpcr.route_basepath%</argument>
32+
<argument>%cmf_routing.dynamic.persistence.phpcr.route_basepath%</argument>
3333
</call>
3434
<call method="setControllerResolver">
3535
<argument type="service" id="controller_resolver" />
@@ -47,7 +47,7 @@
4747
</call>
4848

4949
<call method="setRouteRoot">
50-
<argument>%cmf_routing.persistence.phpcr.route_basepath%</argument>
50+
<argument>%cmf_routing.dynamic.persistence.phpcr.route_basepath%</argument>
5151
</call>
5252
</service>
5353

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
4+
5+
<mapped-superclass name="Symfony\Component\Routing\Route">
6+
<field name="host" type="string"/>
7+
<field name="defaults" type="array"/>
8+
<field name="requirements" type="array"/>
9+
<field name="options" type="array"/>
10+
</mapped-superclass>
11+
12+
</doctrine-mapping>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
4+
5+
<mapped-superclass name="Symfony\Cmf\Bundle\RoutingBundle\Model\Route">
6+
<field name="variablePattern" type="string"/>
7+
<field name="addFormatPattern" type="boolean"/>
8+
<field name="staticPrefix" type="string"/>
9+
10+
<indexes>
11+
<index name="name_idx" columns="name"/>
12+
<index name="prefix_idx" columns="staticPrefix"/>
13+
</indexes>
14+
</mapped-superclass>
15+
16+
</doctrine-mapping>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
4+
5+
<entity name="Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Orm\Route" table="orm_routes">
6+
7+
<id name="id" type="integer" column="id">
8+
<generator strategy="AUTO"/>
9+
</id>
10+
11+
<field name="name" type="string" unique="true"/>
12+
<field name="position" type="integer"/>
13+
14+
</entity>
15+
16+
</doctrine-mapping>

0 commit comments

Comments
 (0)