3 namespace Drupal\Core\Routing;
5 use Drupal\Core\Database\SchemaObjectExistsException;
6 use Drupal\Core\State\StateInterface;
7 use Symfony\Component\Routing\RouteCollection;
9 use Drupal\Core\Database\Connection;
12 * Dumps Route information to a database table.
14 * @see \Drupal\Core\Routing\RouteProvider
16 class MatcherDumper implements MatcherDumperInterface {
19 * The database connection to which to dump route information.
21 * @var \Drupal\Core\Database\Connection
23 protected $connection;
26 * The routes to be dumped.
28 * @var \Symfony\Component\Routing\RouteCollection
35 * @var \Drupal\Core\State\StateInterface
40 * The name of the SQL table to which to dump the routes.
47 * Construct the MatcherDumper.
49 * @param \Drupal\Core\Database\Connection $connection
50 * The database connection which will be used to store the route
52 * @param \Drupal\Core\State\StateInterface $state
54 * @param string $table
55 * (optional) The table to store the route info in. Defaults to 'router'.
57 public function __construct(Connection $connection, StateInterface $state, $table = 'router') {
58 $this->connection = $connection;
59 $this->state = $state;
61 $this->tableName = $table;
67 public function addRoutes(RouteCollection $routes) {
68 if (empty($this->routes)) {
69 $this->routes = $routes;
72 $this->routes->addCollection($routes);
77 * Dumps a set of routes to the router table in the database.
80 * - provider: The route grouping that is being dumped. All existing
81 * routes with this provider will be deleted on dump.
82 * - base_class: The base class name.
84 * @param array $options
85 * An array of options.
87 public function dump(array $options = []) {
88 // Convert all of the routes into database records.
89 // Accumulate the menu masks on top of any we found before.
90 $masks = array_flip($this->state->get('routing.menu_masks.' . $this->tableName, []));
91 // Delete any old records first, then insert the new ones. That avoids
92 // stale data. The transaction makes it atomic to avoid unstable router
93 // states due to random failures.
94 $transaction = $this->connection->startTransaction();
96 // We don't use truncate, because it is not guaranteed to be transaction
99 $this->connection->delete($this->tableName)
102 catch (\Exception $e) {
103 $this->ensureTableExists();
106 // Split the routes into chunks to avoid big INSERT queries.
107 $route_chunks = array_chunk($this->routes->all(), 50, TRUE);
108 foreach ($route_chunks as $routes) {
109 $insert = $this->connection->insert($this->tableName)->fields([
118 foreach ($routes as $name => $route) {
119 /** @var \Symfony\Component\Routing\Route $route */
120 $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler');
121 /** @var \Drupal\Core\Routing\CompiledRoute $compiled */
122 $compiled = $route->compile();
123 // The fit value is a binary number which has 1 at every fixed path
124 // position and 0 where there is a wildcard. We keep track of all such
125 // patterns that exist so that we can minimize the number of path
126 // patterns we need to check in the RouteProvider.
127 $masks[$compiled->getFit()] = 1;
131 'fit' => $compiled->getFit(),
132 'path' => $route->getPath(),
133 'pattern_outline' => $compiled->getPatternOutline(),
134 'number_parts' => $compiled->getNumParts(),
135 'route' => serialize($route),
137 $insert->values($values);
140 // Insert all new routes.
145 catch (\Exception $e) {
146 $transaction->rollBack();
147 watchdog_exception('Routing', $e);
150 // Sort the masks so they are in order of descending fit.
151 $masks = array_keys($masks);
153 $this->state->set('routing.menu_masks.' . $this->tableName, $masks);
155 $this->routes = NULL;
159 * Gets the routes to match.
161 * @return \Symfony\Component\Routing\RouteCollection
162 * A RouteCollection instance representing all routes currently in the
165 public function getRoutes() {
166 return $this->routes;
170 * Checks if the tree table exists and create it if not.
173 * TRUE if the table was created, FALSE otherwise.
175 protected function ensureTableExists() {
177 if (!$this->connection->schema()->tableExists($this->tableName)) {
178 $this->connection->schema()->createTable($this->tableName, $this->schemaDefinition());
182 catch (SchemaObjectExistsException $e) {
183 // If another process has already created the config table, attempting to
184 // recreate it will throw an exception. In this case just catch the
185 // exception and do nothing.
192 * Defines the schema for the router table.
195 * The schema API definition for the SQL storage table.
199 protected function schemaDefinition() {
201 'description' => 'Maps paths to various callbacks (access, page and title)',
204 'description' => 'Primary Key: Machine name of this route',
205 'type' => 'varchar_ascii',
211 'description' => 'The path for this URI',
217 'pattern_outline' => [
218 'description' => 'The pattern',
225 'description' => 'A numeric representation of how specific the path is.',
231 'description' => 'A serialized Route object',
236 'description' => 'Number of parts in this router path.',
244 'pattern_outline_parts' => ['pattern_outline', 'number_parts'],
246 'primary key' => ['name'],