4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Routing;
14 use Symfony\Component\Config\Exception\FileLoaderLoadException;
15 use Symfony\Component\Config\Loader\LoaderInterface;
16 use Symfony\Component\Config\Resource\ResourceInterface;
19 * Helps add and import routes into a RouteCollection.
21 * @author Ryan Weaver <ryan@knpuniversity.com>
23 class RouteCollectionBuilder
26 * @var Route[]|RouteCollectionBuilder[]
28 private $routes = array();
31 private $defaults = array();
35 private $requirements = array();
36 private $options = array();
39 private $resources = array();
42 * @param LoaderInterface $loader
44 public function __construct(LoaderInterface $loader = null)
46 $this->loader = $loader;
50 * Import an external routing resource and returns the RouteCollectionBuilder.
52 * $routes->import('blog.yml', '/blog');
54 * @param mixed $resource
55 * @param string|null $prefix
60 * @throws FileLoaderLoadException
62 public function import($resource, $prefix = '/', $type = null)
64 /** @var RouteCollection $collection */
65 $collection = $this->load($resource, $type);
67 // create a builder from the RouteCollection
68 $builder = $this->createBuilder();
69 foreach ($collection->all() as $name => $route) {
70 $builder->addRoute($route, $name);
73 foreach ($collection->getResources() as $resource) {
74 $builder->addResource($resource);
77 // mount into this builder
78 $this->mount($prefix, $builder);
84 * Adds a route and returns it for future modification.
86 * @param string $path The route path
87 * @param string $controller The route's controller
88 * @param string|null $name The name to give this route
92 public function add($path, $controller, $name = null)
94 $route = new Route($path);
95 $route->setDefault('_controller', $controller);
96 $this->addRoute($route, $name);
102 * Returns a RouteCollectionBuilder that can be configured and then added with mount().
106 public function createBuilder()
108 return new self($this->loader);
112 * Add a RouteCollectionBuilder.
114 * @param string $prefix
115 * @param RouteCollectionBuilder $builder
117 public function mount($prefix, RouteCollectionBuilder $builder)
119 $builder->prefix = trim(trim($prefix), '/');
120 $this->routes[] = $builder;
124 * Adds a Route object to the builder.
126 * @param Route $route
127 * @param string|null $name
131 public function addRoute(Route $route, $name = null)
133 if (null === $name) {
134 // used as a flag to know which routes will need a name later
135 $name = '_unnamed_route_'.spl_object_hash($route);
138 $this->routes[$name] = $route;
144 * Sets the host on all embedded routes (unless already set).
146 * @param string $pattern
150 public function setHost($pattern)
152 $this->host = $pattern;
158 * Sets a condition on all embedded routes (unless already set).
160 * @param string $condition
164 public function setCondition($condition)
166 $this->condition = $condition;
172 * Sets a default value that will be added to all embedded routes (unless that
173 * default value is already set).
176 * @param mixed $value
180 public function setDefault($key, $value)
182 $this->defaults[$key] = $value;
188 * Sets a requirement that will be added to all embedded routes (unless that
189 * requirement is already set).
192 * @param mixed $regex
196 public function setRequirement($key, $regex)
198 $this->requirements[$key] = $regex;
204 * Sets an opiton that will be added to all embedded routes (unless that
205 * option is already set).
208 * @param mixed $value
212 public function setOption($key, $value)
214 $this->options[$key] = $value;
220 * Sets the schemes on all embedded routes (unless already set).
222 * @param array|string $schemes
226 public function setSchemes($schemes)
228 $this->schemes = $schemes;
234 * Sets the methods on all embedded routes (unless already set).
236 * @param array|string $methods
240 public function setMethods($methods)
242 $this->methods = $methods;
248 * Adds a resource for this collection.
250 * @param ResourceInterface $resource
254 private function addResource(ResourceInterface $resource)
256 $this->resources[] = $resource;
262 * Creates the final RouteCollection and returns it.
264 * @return RouteCollection
266 public function build()
268 $routeCollection = new RouteCollection();
270 foreach ($this->routes as $name => $route) {
271 if ($route instanceof Route) {
272 $route->setDefaults(array_merge($this->defaults, $route->getDefaults()));
273 $route->setOptions(array_merge($this->options, $route->getOptions()));
275 foreach ($this->requirements as $key => $val) {
276 if (!$route->hasRequirement($key)) {
277 $route->setRequirement($key, $val);
281 if (null !== $this->prefix) {
282 $route->setPath('/'.$this->prefix.$route->getPath());
285 if (!$route->getHost()) {
286 $route->setHost($this->host);
289 if (!$route->getCondition()) {
290 $route->setCondition($this->condition);
293 if (!$route->getSchemes()) {
294 $route->setSchemes($this->schemes);
297 if (!$route->getMethods()) {
298 $route->setMethods($this->methods);
301 // auto-generate the route name if it's been marked
302 if ('_unnamed_route_' === substr($name, 0, 15)) {
303 $name = $this->generateRouteName($route);
306 $routeCollection->add($name, $route);
308 /* @var self $route */
309 $subCollection = $route->build();
310 $subCollection->addPrefix($this->prefix);
312 $routeCollection->addCollection($subCollection);
315 foreach ($this->resources as $resource) {
316 $routeCollection->addResource($resource);
320 return $routeCollection;
324 * Generates a route name based on details of this route.
328 private function generateRouteName(Route $route)
330 $methods = implode('_', $route->getMethods()).'_';
332 $routeName = $methods.$route->getPath();
333 $routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName);
334 $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName);
336 // Collapse consecutive underscores down into a single underscore.
337 $routeName = preg_replace('/_+/', '_', $routeName);
343 * Finds a loader able to load an imported resource and loads it.
345 * @param mixed $resource A resource
346 * @param string|null $type The resource type or null if unknown
348 * @return RouteCollection
350 * @throws FileLoaderLoadException If no loader is found
352 private function load($resource, $type = null)
354 if (null === $this->loader) {
355 throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.');
358 if ($this->loader->supports($resource, $type)) {
359 return $this->loader->load($resource, $type);
362 if (null === $resolver = $this->loader->getResolver()) {
363 throw new FileLoaderLoadException($resource);
366 if (false === $loader = $resolver->resolve($resource, $type)) {
367 throw new FileLoaderLoadException($resource);
370 return $loader->load($resource, $type);