Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fluid interface for building routes in PHP
- Loading branch information
1 parent
00dffe7
commit 15ba2e8
Showing
2 changed files
with
665 additions
and
0 deletions.
There are no files selected for viewing
348 changes: 348 additions & 0 deletions
348
src/Symfony/Component/Routing/RouteCollectionBuilder.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Routing; | ||
|
||
use Symfony\Component\Config\Exception\FileLoaderLoadException; | ||
use Symfony\Component\Config\Loader\LoaderInterface; | ||
|
||
/** | ||
* Helps add and import routes into a RouteCollection. | ||
* | ||
* @author Ryan Weaver <ryan@knpuniversity.com> | ||
*/ | ||
class RouteCollectionBuilder | ||
{ | ||
/** | ||
* @var Route[]|RouteCollectionBuilder[] | ||
*/ | ||
private $routes = array(); | ||
|
||
private $loader; | ||
private $defaults = array(); | ||
private $prefix; | ||
private $host; | ||
private $condition; | ||
private $requirements = array(); | ||
private $options = array(); | ||
private $schemes; | ||
private $methods; | ||
|
||
/** | ||
* @param LoaderInterface $loader | ||
*/ | ||
public function __construct(LoaderInterface $loader = null) | ||
{ | ||
$this->loader = $loader; | ||
} | ||
|
||
/** | ||
* Import an external routing resource and returns the RouteCollectionBuilder. | ||
* | ||
* $routes->mount('/blog', $routes->import('blog.yml')); | ||
* | ||
* @param mixed $resource | ||
* @param string $type | ||
* | ||
* @return RouteCollectionBuilder | ||
* | ||
* @throws FileLoaderLoadException | ||
*/ | ||
public function import($resource, $type = null) | ||
{ | ||
/** @var RouteCollection $collection */ | ||
$collection = $this->load($resource, $type); | ||
|
||
// create a builder from the RouteCollection | ||
$builder = $this->createBuilder(); | ||
foreach ($collection->all() as $name => $route) { | ||
$builder->addRoute($route, $name); | ||
} | ||
|
||
foreach ($collection->getResources() as $resource) { | ||
$builder->addResource($resource); | ||
} | ||
|
||
return $builder; | ||
} | ||
|
||
/** | ||
* Adds a route and returns it for future modification. | ||
* | ||
* @param string $path The route path | ||
* @param string $controller The route's controller | ||
* @param string|null $name The name to give this route | ||
* | ||
* @return Route | ||
*/ | ||
public function add($path, $controller, $name = null) | ||
{ | ||
$route = new Route($path); | ||
$route->setDefault('_controller', $controller); | ||
$this->addRoute($route, $name); | ||
|
||
return $route; | ||
} | ||
|
||
/** | ||
* Returns a RouteCollectionBuilder that can be configured and then added with mount(). | ||
* | ||
* @return RouteCollectionBuilder | ||
*/ | ||
public function createBuilder() | ||
{ | ||
return new self($this->loader); | ||
} | ||
|
||
/** | ||
* Add a RouteCollectionBuilder. | ||
* | ||
* @param RouteCollectionBuilder $builder | ||
*/ | ||
public function mount($prefix, RouteCollectionBuilder $builder) | ||
{ | ||
$builder->prefix = trim(trim($prefix), '/'); | ||
$this->routes[] = $builder; | ||
} | ||
|
||
/** | ||
* Adds a Route object to the builder. | ||
* | ||
* @param Route $route | ||
* @param string|null $name | ||
* | ||
* @return $this | ||
*/ | ||
public function addRoute(Route $route, $name = null) | ||
{ | ||
if (null === $name) { | ||
// used as a flag to know which routes will need a name later | ||
$name = '_unnamed_route_'.spl_object_hash($route); | ||
} | ||
|
||
$this->routes[$name] = $route; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets the host on all embedded routes (unless already set). | ||
* | ||
* @param string $pattern | ||
* | ||
* @return $this | ||
*/ | ||
public function setHost($pattern) | ||
{ | ||
$this->host = $pattern; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets a condition on all embedded routes (unless already set). | ||
* | ||
* @param string $condition | ||
* | ||
* @return $this | ||
*/ | ||
public function setCondition($condition) | ||
{ | ||
$this->condition = $condition; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets a default value that will be added to all embedded routes (unless that | ||
* default value is already set. | ||
* | ||
* @param string $key | ||
* @param mixed $value | ||
* | ||
* @return $this | ||
*/ | ||
public function setDefault($key, $value) | ||
{ | ||
$this->defaults[$key] = $value; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets a requirement that will be added to all embedded routes (unless that | ||
* requirement is already set. | ||
* | ||
* @param string $key | ||
* @param mixed $regex | ||
* | ||
* @return $this | ||
*/ | ||
public function setRequirement($key, $regex) | ||
{ | ||
$this->requirements[$key] = $regex; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets an opiton that will be added to all embedded routes (unless that | ||
* option is already set. | ||
* | ||
* @param string $key | ||
* @param mixed $value | ||
* | ||
* @return $this | ||
*/ | ||
public function setOption($key, $value) | ||
{ | ||
$this->options[$key] = $value; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets the schemes on all embedded routes (unless already set). | ||
* | ||
* @param array|string $schemes | ||
* | ||
* @return $this | ||
*/ | ||
public function setSchemes($schemes) | ||
{ | ||
$this->schemes = $schemes; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Sets the methods on all embedded routes (unless already set). | ||
* | ||
* @param array|string $methods | ||
* | ||
* @return $this | ||
*/ | ||
public function setMethods($methods) | ||
{ | ||
$this->methods = $methods; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Creates the final ArrayCollection, returns it, and clears everything. | ||
* | ||
* @return RouteCollection | ||
*/ | ||
public function build() | ||
{ | ||
$routeCollection = new RouteCollection(); | ||
|
||
foreach ($this->routes as $name => $route) { | ||
if ($route instanceof Route) { | ||
$route->setDefaults(array_merge($this->defaults, $route->getDefaults())); | ||
$route->setOptions(array_merge($this->options, $route->getOptions())); | ||
|
||
// we're extra careful here to avoid re-setting deprecated _method and _scheme | ||
foreach ($this->requirements as $key => $val) { | ||
if (!$route->hasRequirement($key)) { | ||
$route->setRequirement($key, $val); | ||
} | ||
} | ||
|
||
if (null !== $this->prefix) { | ||
$route->setPath('/'.$this->prefix.$route->getPath()); | ||
} | ||
|
||
if (!$route->getHost()) { | ||
$route->setHost($this->host); | ||
} | ||
|
||
if (!$route->getCondition()) { | ||
$route->setCondition($this->condition); | ||
} | ||
|
||
if (!$route->getSchemes()) { | ||
$route->setSchemes($this->schemes); | ||
} | ||
|
||
if (!$route->getMethods()) { | ||
$route->setMethods($this->methods); | ||
} | ||
|
||
// auto-generate the route name if it's been marked | ||
if ('_unnamed_route_' === substr($name, 0, 15)) { | ||
$name = $this->generateRouteName($route); | ||
} | ||
|
||
$routeCollection->add($name, $route); | ||
} else { | ||
/* @var self $route */ | ||
$subCollection = $route->build(); | ||
$subCollection->addPrefix($this->prefix); | ||
|
||
$routeCollection->addCollection($subCollection); | ||
} | ||
} | ||
|
||
return $routeCollection; | ||
} | ||
|
||
/** | ||
* Generates a route name based on details of this route. | ||
* | ||
* @return string | ||
*/ | ||
private function generateRouteName(Route $route) | ||
{ | ||
$methods = implode('_', $route->getMethods()).'_'; | ||
|
||
$routeName = $methods.$route->getPath(); | ||
$routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName); | ||
$routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName); | ||
|
||
// Collapse consecutive underscores down into a single underscore. | ||
$routeName = preg_replace('/_+/', '_', $routeName); | ||
|
||
return $routeName; | ||
} | ||
|
||
/** | ||
* Finds a loader able to load an imported resource and loads it. | ||
* | ||
* @param mixed $resource A resource | ||
* @param string|null $type The resource type or null if unknown | ||
* | ||
* @return RouteCollection | ||
* | ||
* @throws FileLoaderLoadException If no loader is found | ||
*/ | ||
private function load($resource, $type = null) | ||
{ | ||
if (null === $this->loader) { | ||
throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.'); | ||
} | ||
|
||
if ($this->loader->supports($resource, $type)) { | ||
return $this->loader->load($resource, $type); | ||
} | ||
|
||
if (null === $resolver = $this->loader->getResolver()) { | ||
throw new FileLoaderLoadException($resource); | ||
} | ||
|
||
if (false === $loader = $resolver->resolve($resource, $type)) { | ||
throw new FileLoaderLoadException($resource); | ||
} | ||
|
||
return $loader->load($resource, $type); | ||
} | ||
} |
Oops, something went wrong.