Commit 86922ace authored by alexpott's avatar alexpott

Issue #2330363 by dawehner: Enhance the controller resolver to get a route match class.

parent bfe397f9
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Controller;
use Drupal\Core\Routing\RouteMatch;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver;
......@@ -136,7 +137,35 @@ protected function createController($controller) {
* {@inheritdoc}
*/
protected function doGetArguments(Request $request, $controller, array $parameters) {
$arguments = parent::doGetArguments($request, $controller, $parameters);
$attributes = $request->attributes->all();
$arguments = array();
foreach ($parameters as $param) {
if (array_key_exists($param->name, $attributes)) {
$arguments[] = $attributes[$param->name];
}
elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
$arguments[] = $request;
}
elseif ($param->getClass() && ($param->getClass()->name == 'Drupal\Core\Routing\RouteMatchInterface' || is_subclass_of($param->getClass()->name, 'Drupal\Core\Routing\RouteMatchInterface'))) {
$arguments[] = RouteMatch::createFromRequest($request);
}
elseif ($param->isDefaultValueAvailable()) {
$arguments[] = $param->getDefaultValue();
}
else {
if (is_array($controller)) {
$repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
}
elseif (is_object($controller)) {
$repr = get_class($controller);
}
else {
$repr = $controller;
}
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name));
}
}
// The parameter converter overrides the raw request attributes with the
// upcasted objects. However, it keeps a backup copy of the original, raw
......
......@@ -10,7 +10,7 @@
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenDialogCommand;
use Drupal\Core\Page\HtmlPage;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
......@@ -51,14 +51,16 @@ public function __construct(ControllerResolverInterface $controller_resolver, Ti
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param mixed $_content
* A controller definition string, or a callable object/closure.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* AjaxResponse to return the content wrapper in a modal dialog.
*/
public function modal(Request $request, $_content) {
return $this->dialog($request, $_content, TRUE);
public function modal(Request $request, RouteMatchInterface $route_match, $_content) {
return $this->dialog($request, $route_match, $_content, TRUE);
}
/**
......@@ -66,6 +68,8 @@ public function modal(Request $request, $_content) {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param mixed $_content
* A controller definition string, or a callable object/closure.
* @param bool $modal
......@@ -74,7 +78,7 @@ public function modal(Request $request, $_content) {
* @return \Drupal\Core\Ajax\AjaxResponse
* AjaxResponse to return the content wrapper in a dialog.
*/
public function dialog(Request $request, $_content, $modal = FALSE) {
public function dialog(Request $request, RouteMatchInterface $route_match, $_content, $modal = FALSE) {
$page_content = $this->getContentResult($request, $_content);
// Allow controllers to return a HtmlPage or a Response object directly.
......@@ -94,7 +98,7 @@ public function dialog(Request $request, $_content, $modal = FALSE) {
$content = drupal_render($page_content);
drupal_process_attached($page_content);
$title = isset($page_content['#title']) ? $page_content['#title'] : $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
$title = isset($page_content['#title']) ? $page_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
$response = new AjaxResponse();
// Fetch any modal options passed in from data-dialog-options.
$options = $request->request->get('dialogOptions', array());
......@@ -117,7 +121,7 @@ public function dialog(Request $request, $_content, $modal = FALSE) {
}
else {
// Generate a target based on the route id.
$route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME);
$route_name = $route_match->getRouteName();
$target = '#' . drupal_html_id("drupal-dialog-$route_name");
}
}
......
......@@ -13,6 +13,7 @@
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
......@@ -109,13 +110,15 @@ public static function create(ContainerInterface $container) {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Page request object.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param string $plugin_id
* The plugin ID of the mapper.
*
* @return array
* Page render array.
*/
public function itemPage(Request $request, $plugin_id) {
public function itemPage(Request $request, RouteMatchInterface $route_match, $plugin_id) {
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
$mapper = $this->configMapperManager->createInstance($plugin_id);
$mapper->populateFromRequest($request);
......@@ -164,7 +167,7 @@ public function itemPage(Request $request, $plugin_id) {
// Check access for the path/route for editing, so we can decide to
// include a link to edit or not.
$edit_access = $this->accessManager->checkNamedRoute($mapper->getBaseRouteName(), $request->attributes->get('_raw_variables')->all(), $this->account);
$edit_access = $this->accessManager->checkNamedRoute($mapper->getBaseRouteName(), $route_match->getRawParameters()->all(), $this->account);
// Build list of operations.
$operations = array();
......
......@@ -7,7 +7,7 @@
namespace Drupal\rest;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Request;
......@@ -27,15 +27,17 @@ class RequestHandler implements ContainerAwareInterface {
/**
* Handles a web API request.
*
* @param Symfony\Component\HttpFoundation\Request $request
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param \Symfony\Component\HttpFoundation\Request $request
* The HTTP request object.
*
* @return \Symfony\Component\HttpFoundation\Response
* The response object.
*/
public function handle(Request $request) {
public function handle(RouteMatchInterface $route_match, Request $request) {
$plugin = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getDefault('_plugin');
$plugin = $route_match->getRouteObject()->getDefault('_plugin');
$method = strtolower($request->getMethod());
$resource = $this->container
......@@ -87,7 +89,7 @@ public function handle(Request $request) {
// All REST routes are restricted to exactly one format, so instead of
// parsing it out of the Accept headers again, we can simply retrieve the
// format requirement. If there is no format associated, just pick JSON.
$format = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getRequirement('_format') ?: 'json';
$format = $route_match->getRouteObject()->getRequirement('_format') ?: 'json';
try {
$response = call_user_func_array(array($resource, $method), array_merge($parameters, array($unserialized, $request)));
}
......
......@@ -10,7 +10,6 @@
use Drupal\Component\Utility\Xss;
use Drupal\Core\Controller\ControllerBase;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Drupal\Core\Datetime\DateFormatter;
......@@ -132,14 +131,11 @@ public function resetPass($uid, $timestamp, $hash) {
* Displays user profile if user is logged in, or login form for anonymous
* users.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|array
* Returns either a redirect to the user page or the render
* array of the login form.
*/
public function userPage(Request $request) {
public function userPage() {
$user = $this->currentUser();
if ($user->id()) {
$response = $this->redirect('entity.user.canonical', array('user' => $user->id()));
......
......@@ -10,8 +10,8 @@
use Drupal\Component\Utility\String;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\ViewExecutableFactory;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
......@@ -59,12 +59,19 @@ public static function create(ContainerInterface $container) {
}
/**
* Handles a response for a view.
* Handler a response for a given view and display.
*
* @param string $view_id
* The ID of the view
* @param string $display_id
* The ID of the display.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @return null|void
*/
public function handle(Request $request) {
$view_id = $request->attributes->get('view_id');
$display_id = $request->attributes->get('display_id');
public function handle($view_id, $display_id, Request $request, RouteMatchInterface $route_match) {
$entity = $this->storage->load($view_id);
if (empty($entity)) {
throw new NotFoundHttpException(String::format('Page controller for view %id requested, but view was not found.', array('%id' => $view_id)));
......@@ -75,7 +82,7 @@ public function handle(Request $request) {
$view->initHandlers();
$args = array();
$map = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getOption('_view_argument_map', array());
$map = $route_match->getRouteObject()->getOption('_view_argument_map', array());
$arguments_length = count($view->argument);
for ($argument_index = 0; $argument_index < $arguments_length; $argument_index++) {
// Allow parameters be pulled from the request.
......@@ -85,18 +92,11 @@ public function handle(Request $request) {
$attribute = 'arg_' . $argument_index;
if (isset($map[$attribute])) {
$attribute = $map[$attribute];
// First try to get from the original values then on the not converted
// ones.
if ($request->attributes->has('_raw_variables')) {
$arg = $request->attributes->get('_raw_variables')->get($attribute);
}
else {
$arg = $request->attributes->get($attribute);
}
}
if ($arg = $route_match->getRawParameter($attribute)) {
}
else {
$arg = $request->attributes->get($attribute);
$arg = $route_match->getParameter($attribute);
}
if (isset($arg)) {
......
......@@ -7,6 +7,7 @@
namespace Drupal\Tests\views\Unit\Routing;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Tests\UnitTestCase;
use Drupal\views\Routing\ViewPageController;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
......@@ -85,9 +86,10 @@ public function testPageController() {
$request = new Request();
$request->attributes->set('view_id', 'test_page_view');
$request->attributes->set('display_id', 'default');
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route(''));
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/test', ['view_id' => 'test_page_view', 'display_id' => 'default']));
$route_match = RouteMatch::createFromRequest($request);
$output = $this->pageController->handle($request);
$output = $this->pageController->handle($route_match->getParameter('view_id'), $route_match->getParameter('display_id'), $request, $route_match);
$this->assertInternalType('array', $output);
$this->assertEquals(array('#markup' => 'example output'), $output);
}
......@@ -132,9 +134,10 @@ public function testHandleWithArgumentsWithoutOverridden() {
$request->attributes->set('display_id', 'page_1');
// Add the argument to the request.
$request->attributes->set('arg_0', 'test-argument');
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route(''));
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/test/{arg_0}', ['view_id' => 'test_page_view', 'display_id' => 'default']));
$route_match = RouteMatch::createFromRequest($request);
$this->pageController->handle($request);
$this->pageController->handle($route_match->getParameter('view_id'), $route_match->getParameter('display_id'), $request, $route_match);
}
/**
......@@ -179,11 +182,12 @@ public function testHandleWithArgumentsOnOveriddenRoute() {
$request->attributes->set('display_id', 'page_1');
// Add the argument to the request.
$request->attributes->set('parameter', 'test-argument');
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('', array(), array(), array('_view_argument_map' => array(
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/test/{parameter}', ['view_id' => 'test_page_view', 'display_id' => 'default'], [], ['_view_argument_map' => [
'arg_0' => 'parameter',
))));
]]));
$route_match = RouteMatch::createFromRequest($request);
$this->pageController->handle($request);
$this->pageController->handle($route_match->getParameter('view_id'), $route_match->getParameter('display_id'), $request, $route_match);
}
/**
......@@ -231,12 +235,12 @@ public function testHandleWithArgumentsOnOveriddenRouteWithUpcasting() {
$request->attributes->set('test_entity', $this->getMock('Drupal\Core\Entity\EntityInterface'));
$raw_variables = new ParameterBag(array('test_entity' => 'example_id'));
$request->attributes->set('_raw_variables', $raw_variables);
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('', array(), array(), array('_view_argument_map' => array(
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/test/{test_entity}', ['view_id' => 'test_page_view', 'display_id' => 'default'], [], ['_view_argument_map' => [
'arg_0' => 'test_entity',
))));
]]));
$route_match = RouteMatch::createFromRequest($request);
$this->pageController->handle($request);
$this->pageController->handle($route_match->getParameter('view_id'), $route_match->getParameter('display_id'), $request, $route_match);
}
/**
......@@ -251,7 +255,9 @@ public function testHandleWithNotExistingView() {
$request = new Request();
$request->attributes->set('view_id', $random_view_id);
$request->attributes->set('display_id', 'default');
$this->pageController->handle($request);
$route_match = RouteMatch::createFromRequest($request);
$this->pageController->handle($route_match->getParameter('view_id'), $route_match->getParameter('display_id'), $request, $route_match);
}
}
......@@ -11,6 +11,8 @@
use Drupal\Core\DependencyInjection\ClassResolver;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerBuilder;
......@@ -59,7 +61,7 @@ protected function setUp() {
* @see \Drupal\Core\Controller\ControllerResolver::doGetArguments()
*/
public function testGetArguments() {
$controller = function(EntityInterface $entity, $user) {
$controller = function(EntityInterface $entity, $user, RouteMatchInterface $route_match) {
};
$mock_entity = $this->getMockBuilder('Drupal\Core\Entity\Entity')
->disableOriginalConstructor()
......@@ -74,6 +76,7 @@ public function testGetArguments() {
$this->assertEquals($mock_entity, $arguments[0], 'Type hinted variables should use upcasted values.');
$this->assertEquals(1, $arguments[1], 'Not type hinted variables should use not upcasted values.');
$this->assertEquals(RouteMatch::createFromRequest($request), $arguments[2], 'Ensure that the route match object is passed along as well');
}
/**
......@@ -203,12 +206,30 @@ protected function assertCallableController($controller, $class, $output) {
$this->assertSame($output, call_user_func($controller));
}
/**
* Tests getArguments with a route match and a request.
*
* @covers ::getArguments
* @covers ::doGetArguments
*/
public function testGetArgumentsWithRouteMatchAndRequest() {
$request = Request::create('/test');
$mock_controller = new MockController();
$arguments = $this->controllerResolver->getArguments($request, [$mock_controller, 'getControllerWithRequestAndRouteMatch']);
$this->assertEquals([RouteMatch::createFromRequest($request), $request], $arguments);
}
}
class MockController {
public function getResult() {
return 'This is a regular controller.';
}
public function getControllerWithRequestAndRouteMatch(RouteMatchInterface $route_match, Request $request) {
return 'this is another example controller';
}
}
class MockContainerInjection implements ContainerInjectionInterface {
protected $result;
......
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