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 // we're extra careful here to avoid re-setting deprecated _method and _scheme
276 foreach ($this->requirements as $key => $val) {
277 if (!$route->hasRequirement($key)) {
278 $route->setRequirement($key, $val);
282 if (null !== $this->prefix) {
283 $route->setPath('/'.$this->prefix.$route->getPath());
286 if (!$route->getHost()) {
287 $route->setHost($this->host);
290 if (!$route->getCondition()) {
291 $route->setCondition($this->condition);
294 if (!$route->getSchemes()) {
295 $route->setSchemes($this->schemes);
298 if (!$route->getMethods()) {
299 $route->setMethods($this->methods);
302 // auto-generate the route name if it's been marked
303 if ('_unnamed_route_' === substr($name, 0, 15)) {
304 $name = $this->generateRouteName($route);
307 $routeCollection->add($name, $route);
309 /* @var self $route */
310 $subCollection = $route->build();
311 $subCollection->addPrefix($this->prefix);
313 $routeCollection->addCollection($subCollection);
316 foreach ($this->resources as $resource) {
317 $routeCollection->addResource($resource);
321 return $routeCollection;
325 * Generates a route name based on details of this route.
329 private function generateRouteName(Route $route)
331 $methods = implode('_', $route->getMethods()).'_';
333 $routeName = $methods.$route->getPath();
334 $routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName);
335 $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName);
337 // Collapse consecutive underscores down into a single underscore.
338 $routeName = preg_replace('/_+/', '_', $routeName);
344 * Finds a loader able to load an imported resource and loads it.
346 * @param mixed $resource A resource
347 * @param string|null $type The resource type or null if unknown
349 * @return RouteCollection
351 * @throws FileLoaderLoadException If no loader is found
353 private function load($resource, $type = null)
355 if (null === $this->loader) {
356 throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.');
359 if ($this->loader->supports($resource, $type)) {
360 return $this->loader->load($resource, $type);
363 if (null === $resolver = $this->loader->getResolver()) {
364 throw new FileLoaderLoadException($resource);
367 if (false === $loader = $resolver->resolve($resource, $type)) {
368 throw new FileLoaderLoadException($resource);
371 return $loader->load($resource, $type);