Commit f8f39839 authored by alexpott's avatar alexpott

Issue #2772537 by dawehner, Wim Leers, jacov: REST Views override existing REST GET routes

parent 497bf117
......@@ -14,6 +14,7 @@
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\PathPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
......@@ -347,6 +348,26 @@ public function collectRoutes(RouteCollection $collection) {
}
}
/**
* Determines whether the view overrides the given route.
*
* @param string $view_path
* The path of the view.
* @param \Symfony\Component\Routing\Route $view_route
* The route of the view.
* @param \Symfony\Component\Routing\Route $route
* The route itself.
*
* @return bool
* TRUE, when the view should override the given route.
*/
protected function overrideApplies($view_path, Route $view_route, Route $route) {
$route_formats = explode('|', $route->getRequirement('_format'));
$view_route_formats = explode('|', $view_route->getRequirement('_format'));
return $this->overrideAppliesPathAndMethod($view_path, $view_route, $route)
&& (!$route->hasRequirement('_format') || array_intersect($route_formats, $view_route_formats) != []);
}
/**
* {@inheritdoc}
*/
......
......@@ -226,19 +226,62 @@ public function collectRoutes(RouteCollection $collection) {
return array("$view_id.$display_id" => $route_name);
}
/**
* Determines whether the view overrides the given route.
*
* @param string $view_path
* The path of the view.
* @param \Symfony\Component\Routing\Route $view_route
* The route of the view.
* @param \Symfony\Component\Routing\Route $route
* The route itself.
*
* @return bool
* TRUE, when the view should override the given route.
*/
protected function overrideApplies($view_path, Route $view_route, Route $route) {
return $this->overrideAppliesPathAndMethod($view_path, $view_route, $route)
&& (!$route->hasRequirement('_format') || $route->getRequirement('_format') === 'html');
}
/**
* Determines whether a override for the path and method should happen.
*
* @param string $view_path
* The path of the view.
* @param \Symfony\Component\Routing\Route $view_route
* The route of the view.
* @param \Symfony\Component\Routing\Route $route
* The route itself.
*
* @return bool
* TRUE, when the view should override the given route.
*/
protected function overrideAppliesPathAndMethod($view_path, Route $view_route, Route $route) {
// Find all paths which match the path of the current display..
$route_path = RouteCompiler::getPathWithoutDefaults($route);
$route_path = RouteCompiler::getPatternOutline($route_path);
// Ensure that we don't override a route which is already controlled by
// views.
return !$route->hasDefault('view_id')
&& ('/' . $view_path == $route_path)
// Also ensure that we don't override for example REST routes.
&& (!$route->getMethods() || in_array('GET', $route->getMethods()));
}
/**
* {@inheritdoc}
*/
public function alterRoutes(RouteCollection $collection) {
$view_route_names = array();
$view_path = $this->getPath();
$view_id = $this->view->storage->id();
$display_id = $this->display['id'];
$view_route = $this->getRoute($view_id, $display_id);
foreach ($collection->all() as $name => $route) {
// Find all paths which match the path of the current display..
$route_path = RouteCompiler::getPathWithoutDefaults($route);
$route_path = RouteCompiler::getPatternOutline($route_path);
// Ensure that we don't override a route which is already controlled by
// views. Also ensure that we don't override for example REST routes.
if (!$route->hasDefault('view_id') && ('/' . $view_path == $route_path) && (!$route->getMethods() || in_array('GET', $route->getMethods()))) {
if ($this->overrideApplies($view_path, $view_route, $route)) {
$parameters = $route->compile()->getPathVariables();
// @todo Figure out whether we need to merge some settings (like
......@@ -248,11 +291,7 @@ public function alterRoutes(RouteCollection $collection) {
$original_route = $collection->get($name);
$collection->remove($name);
$view_id = $this->view->storage->id();
$display_id = $this->display['id'];
$route = $this->getRoute($view_id, $display_id);
$path = $route->getPath();
$path = $view_route->getPath();
// Replace the path with the original parameter names and add a mapping.
$argument_map = array();
// We assume that the numeric ids of the parameters match the one from
......@@ -263,17 +302,17 @@ public function alterRoutes(RouteCollection $collection) {
}
// Copy the original options from the route, so for example we ensure
// that parameter conversion options is carried over.
$route->setOptions($route->getOptions() + $original_route->getOptions());
$view_route->setOptions($view_route->getOptions() + $original_route->getOptions());
if ($original_route->hasDefault('_title_callback')) {
$route->setDefault('_title_callback', $original_route->getDefault('_title_callback'));
$view_route->setDefault('_title_callback', $original_route->getDefault('_title_callback'));
}
// Set the corrected path and the mapping to the route object.
$route->setOption('_view_argument_map', $argument_map);
$route->setPath($path);
$view_route->setOption('_view_argument_map', $argument_map);
$view_route->setPath($path);
$collection->add($name, $route);
$collection->add($name, $view_route);
$view_route_names[$view_id . '.' . $display_id] = $name;
}
}
......
......@@ -263,7 +263,7 @@ public function testAlterRoute() {
/**
* Tests the altering of a REST route.
*/
public function testAlterRestRoute() {
public function testAlterPostRestRoute() {
$collection = new RouteCollection();
$route = new Route('test_route', ['_controller' => 'Drupal\Tests\Core\Controller\TestController::content']);
$route->setMethods(['POST']);
......@@ -298,6 +298,45 @@ public function testAlterRestRoute() {
$this->assertEquals('my views title', $route->getDefault('_title'));
}
/**
* Tests the altering of a REST route.
*/
public function testGetRestRoute() {
$collection = new RouteCollection();
$route = new Route('test_route', ['_controller' => 'Drupal\Tests\Core\Controller\TestController::content']);
$route->setMethods(['GET']);
$route->setRequirement('_format', 'json');
$collection->add('test_route', $route);
list($view) = $this->setupViewExecutableAccessPlugin();
$display = [];
$display['display_plugin'] = 'page';
$display['id'] = 'page_1';
$display['display_options'] = [
'path' => 'test_route',
];
$this->pathPlugin->initDisplay($view, $display);
$this->pathPlugin->collectRoutes($collection);
$view_route_names = $this->pathPlugin->alterRoutes($collection);
$this->assertEquals([], $view_route_names);
// Ensure that the test_route is not overridden.
$this->assertCount(2, $collection);
$route = $collection->get('test_route');
$this->assertTrue($route instanceof Route);
$this->assertFalse($route->hasDefault('view_id'));
$this->assertFalse($route->hasDefault('display_id'));
$this->assertSame($collection->get('test_route'), $route);
$route = $collection->get('view.test_id.page_1');
$this->assertTrue($route instanceof Route);
$this->assertEquals('test_id', $route->getDefault('view_id'));
$this->assertEquals('page_1', $route->getDefault('display_id'));
$this->assertEquals('my views title', $route->getDefault('_title'));
}
/**
* Tests the alter route method with preexisting title callback.
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment