Commit 56799641 authored by larowlan's avatar larowlan

Issue #2720233 by dawehner, Wim Leers, catch: Use arguments resolver in...

Issue #2720233 by dawehner, Wim Leers, catch: Use arguments resolver in RequestHandler to have consistent parameter ordering
parent 98f4e57a
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
namespace Drupal\rest; namespace Drupal\rest;
use Drupal\Component\Utility\ArgumentsResolver;
use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface;
...@@ -119,17 +121,17 @@ public function handle(RouteMatchInterface $route_match, Request $request) { ...@@ -119,17 +121,17 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
// Determine the request parameters that should be passed to the resource // Determine the request parameters that should be passed to the resource
// plugin. // plugin.
$route_parameters = $route_match->getParameters(); $argument_resolver = $this->createArgumentResolver($route_match, $unserialized, $request);
$parameters = []; try {
// Filter out all internal parameters starting with "_". $arguments = $argument_resolver->getArguments([$resource, $method]);
foreach ($route_parameters as $key => $parameter) { }
if ($key{0} !== '_') { catch (\RuntimeException $exception) {
$parameters[] = $parameter; @trigger_error('Passing in arguments the legacy way is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Provide the right parameter names in the method, similar to controllers. See https://www.drupal.org/node/2894819', E_USER_DEPRECATED);
} $arguments = $this->getLegacyParameters($route_match, $unserialized, $request);
} }
// Invoke the operation on the resource plugin. // Invoke the operation on the resource plugin.
$response = call_user_func_array([$resource, $method], array_merge($parameters, [$unserialized, $request])); $response = call_user_func_array([$resource, $method], $arguments);
if ($response instanceof CacheableResponseInterface) { if ($response instanceof CacheableResponseInterface) {
// Add rest config's cache tags. // Add rest config's cache tags.
...@@ -139,4 +141,100 @@ public function handle(RouteMatchInterface $route_match, Request $request) { ...@@ -139,4 +141,100 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
return $response; return $response;
} }
/**
* Creates an argument resolver, containing all REST parameters.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param mixed $unserialized
* The unserialized data.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @return \Drupal\Component\Utility\ArgumentsResolver
* An instance of the argument resolver containing information like the
* 'entity' we process and the 'unserialized' content from the request body.
*/
protected function createArgumentResolver(RouteMatchInterface $route_match, $unserialized, Request $request) {
$route = $route_match->getRouteObject();
// Defaults for the parameters defined on the route object need to be added
// to the raw arguments.
$raw_route_arguments = $route_match->getRawParameters()->all() + $route->getDefaults();
$route_arguments = $route_match->getParameters()->all();
$upcasted_route_arguments = $route_arguments;
// For request methods that have request bodies, ResourceInterface plugin
// methods historically receive the unserialized request body as the N+1th
// method argument, where N is the number of route parameters specified on
// the accompanying route. To be able to use the argument resolver, which is
// not based on position but on name and typehint, specify commonly used
// names here. Similarly, those methods receive the original stored object
// as the first method argument.
$route_arguments_entity = NULL;
// Try to find a parameter which is an entity.
foreach ($route_arguments as $value) {
if ($value instanceof EntityInterface) {
$route_arguments_entity = $value;
break;
}
}
if (in_array($request->getMethod(), ['PATCH', 'POST'], TRUE)) {
$upcasted_route_arguments['entity'] = $unserialized;
$upcasted_route_arguments['data'] = $unserialized;
$upcasted_route_arguments['unserialized'] = $unserialized;
$upcasted_route_arguments['original_entity'] = $route_arguments_entity;
}
else {
$upcasted_route_arguments['entity'] = $route_arguments_entity;
}
// Parameters which are not defined on the route object, but still are
// essential for access checking are passed as wildcards to the argument
// resolver.
$wildcard_arguments = [$route, $route_match];
$wildcard_arguments[] = $request;
if (isset($unserialized)) {
$wildcard_arguments[] = $unserialized;
}
return new ArgumentsResolver($raw_route_arguments, $upcasted_route_arguments, $wildcard_arguments);
}
/**
* Provides the parameter usable without an argument resolver.
*
* This creates an list of parameters in a statically defined order.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match
* @param mixed $unserialized
* The unserialized data.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @deprecated in Drupal 8.4.0, will be removed before Drupal 9.0.0. Use the
* argument resolver method instead, see ::createArgumentResolver().
*
* @see https://www.drupal.org/node/2894819
*
* @return array
* An array of parameters.
*/
protected function getLegacyParameters(RouteMatchInterface $route_match, $unserialized, Request $request) {
$route_parameters = $route_match->getParameters();
$parameters = [];
// Filter out all internal parameters starting with "_".
foreach ($route_parameters as $key => $parameter) {
if ($key{0} !== '_') {
$parameters[] = $parameter;
}
}
return array_merge($parameters, [$unserialized, $request]);
}
} }
...@@ -25,7 +25,7 @@ class NoSerializationClassTestResource extends ResourceBase { ...@@ -25,7 +25,7 @@ class NoSerializationClassTestResource extends ResourceBase {
* *
* @return \Drupal\rest\ResourceResponse * @return \Drupal\rest\ResourceResponse
*/ */
public function post(array $data = []) { public function post(array $data) {
return new ResourceResponse($data); return new ResourceResponse($data);
} }
......
...@@ -93,9 +93,9 @@ public function testHandle() { ...@@ -93,9 +93,9 @@ public function testHandle() {
*/ */
class StubRequestHandlerResourcePlugin extends ResourceBase { class StubRequestHandlerResourcePlugin extends ResourceBase {
public function get() {} public function get($example, Request $request) {}
public function post() {} public function post() {}
public function patch() {} public function patch($example_original, Request $request) {}
public function delete() {} public function delete() {}
} }
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