/** * Finds routes that may potentially match the request. * * This may return a mixed list of class instances, but all routes returned * must extend the core symfony route. The classes may also implement * RouteObjectInterface to link to a content document. * * This method may not throw an exception based on implementation specific * restrictions on the url. That case is considered a not found - returning * an empty array. Exceptions are only used to abort the whole request in * case something is seriously broken, like the storage backend being down. * * Note that implementations may not implement an optimal matching * algorithm, simply a reasonable first pass. That allows for potentially * very large route sets to be filtered down to likely candidates, which * may then be filtered in memory more completely. * * @param Request $request A request against which to match. * * @return \Symfony\Component\Routing\RouteCollection with all urls that * could potentially match $request. Empty collection if nothing can * match. * * @todo Should this method's found routes also be included in the cache? */ public function getRouteCollectionForRequest(Request $request) { // The '_system_path' has language prefix stripped and path alias resolved, // whereas getPathInfo() returns the requested path. In Drupal, the request // always contains a system_path attribute, but this component may get // adopted by non-Drupal projects. Some unit tests also skip initializing // '_system_path'. // @todo Consider abstracting this to a separate object. if ($request->attributes->has('_system_path')) { // _system_path never has leading or trailing slashes. $path = '/' . $request->attributes->get('_system_path'); } else { // getPathInfo() always has leading slash, and might or might not have a // trailing slash. $path = rtrim($request->getPathInfo(), '/'); } $collection = $this->getRoutesByPath($path); // Try rebuilding the router if it is necessary. if (!$collection->count() && $this->routeBuilder->rebuildIfNeeded()) { $collection = $this->getRoutesByPath($path); } return $collection; }
/** * Rebuilds routers if necessary. * * @param \Symfony\Component\HttpKernel\Event\PostResponseEvent $event * The event object. */ public function onKernelTerminate(PostResponseEvent $event) { $this->routeBuilder->rebuildIfNeeded(); }
/** * {@inheritdoc} */ public function getRoutesByPattern($pattern) { $path = RouteCompiler::getPatternOutline($pattern); $this->routeBuilder->rebuildIfNeeded(); return $this->getRoutesByPath($path); }
/** * Find all local tasks that appear on a named route. * * @param string $route_name * The route for which to find local tasks. * * @return array * Returns an array of task levels. Each task level contains instances * of local tasks (LocalTaskInterface) which appear on the tab route. * The array keys are the depths and the values are arrays of plugin * instances. */ public function getLocalTasksForRoute($route_name) { if (!isset($this->instances[$route_name])) { $this->instances[$route_name] = array(); if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $route_name)) { $base_routes = $cache->data['base_routes']; $parents = $cache->data['parents']; $children = $cache->data['children']; } else { // Maybe some code asked to rebuild the routes, so rebuild the router // as we rely on having proper existing routes in dynamic local tasks. $this->routeBuilder->rebuildIfNeeded(); $definitions = $this->getDefinitions(); // We build the hierarchy by finding all tabs that should // appear on the current route. $base_routes = array(); $parents = array(); $children = array(); foreach ($definitions as $plugin_id => $task_info) { // Fill in the base_route from the parent to insure consistency. if (!empty($task_info['parent_id']) && !empty($definitions[$task_info['parent_id']])) { $task_info['base_route'] = $definitions[$task_info['parent_id']]['base_route']; // Populate the definitions we use in the next loop. Using a // reference like &$task_info causes bugs. $definitions[$plugin_id]['base_route'] = $definitions[$task_info['parent_id']]['base_route']; } if ($route_name == $task_info['route_name']) { if (!empty($task_info['base_route'])) { $base_routes[$task_info['base_route']] = $task_info['base_route']; } // Tabs that link to the current route are viable parents // and their parent and children should be visible also. // @todo - this only works for 2 levels of tabs. // instead need to iterate up. $parents[$plugin_id] = TRUE; if (!empty($task_info['parent_id'])) { $parents[$task_info['parent_id']] = TRUE; } } } if ($base_routes) { // Find all the plugins with the same root and that are at the top // level or that have a visible parent. foreach ($definitions as $plugin_id => $task_info) { if (!empty($base_routes[$task_info['base_route']]) && (empty($task_info['parent_id']) || !empty($parents[$task_info['parent_id']]))) { // Concat '> ' with root ID for the parent of top-level tabs. $parent = empty($task_info['parent_id']) ? '> ' . $task_info['base_route'] : $task_info['parent_id']; $children[$parent][$plugin_id] = $task_info; } } } $data = array('base_routes' => $base_routes, 'parents' => $parents, 'children' => $children); $this->cacheBackend->set($this->cacheKey . ':' . $route_name, $data, Cache::PERMANENT, $this->cacheTags); } // Create a plugin instance for each element of the hierarchy. foreach ($base_routes as $base_route) { // Convert the tree keyed by plugin IDs into a simple one with // integer depth. Create instances for each plugin along the way. $level = 0; // We used this above as the top-level parent array key. $next_parent = '> ' . $base_route; do { $parent = $next_parent; $next_parent = FALSE; foreach ($children[$parent] as $plugin_id => $task_info) { $plugin = $this->createInstance($plugin_id); $this->instances[$route_name][$level][$plugin_id] = $plugin; // Normally, l() compares the href of every link with the current // path and sets the active class accordingly. But the parents of // the current local task may be on a different route in which // case we have to set the class manually by flagging it active. if (!empty($parents[$plugin_id]) && $route_name != $task_info['route_name']) { $plugin->setActive(); } if (isset($children[$plugin_id])) { // This tab has visible children $next_parent = $plugin_id; } } $level++; } while ($next_parent); } } return $this->instances[$route_name]; }