Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
33.33% |
2 / 6 |
CRAP | |
87.14% |
61 / 70 |
| EntityResolverManager | |
0.00% |
0 / 1 |
|
33.33% |
2 / 6 |
37.60 | |
87.14% |
61 / 70 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| getControllerClass | |
0.00% |
0 / 1 |
9.65 | |
80.00% |
16 / 20 |
|||
| setParametersFromReflection | |
0.00% |
0 / 1 |
9 | |
95.24% |
20 / 21 |
|||
| setParametersFromEntityInformation | |
0.00% |
0 / 1 |
11.02 | |
94.44% |
17 / 18 |
|||
| setRouteOptions | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
| getEntityTypes | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Entity\EntityResolverManager. | |
| */ | |
| namespace Drupal\Core\Entity; | |
| use Drupal\Core\DependencyInjection\ClassResolverInterface; | |
| use Symfony\Component\Routing\Route; | |
| /** | |
| * Sets the entity route parameter converter options automatically. | |
| * | |
| * If controllers of routes with route parameters, type-hint the parameters with | |
| * an entity interface, upcasting is done automatically. | |
| */ | |
| class EntityResolverManager { | |
| /** | |
| * The entity manager. | |
| * | |
| * @var \Drupal\Core\Entity\EntityManagerInterface | |
| */ | |
| protected $entityManager; | |
| /** | |
| * The class resolver. | |
| * | |
| * @var \Drupal\Core\DependencyInjection\ClassResolverInterface | |
| */ | |
| protected $classResolver; | |
| /** | |
| * Constructs a new EntityRouteAlterSubscriber. | |
| * | |
| * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
| * The entity manager. | |
| * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver | |
| * The class resolver. | |
| */ | |
| public function __construct(EntityManagerInterface $entity_manager, ClassResolverInterface $class_resolver) { | |
| $this->entityManager = $entity_manager; | |
| $this->classResolver = $class_resolver; | |
| } | |
| /** | |
| * Gets the controller class using route defaults. | |
| * | |
| * By design we cannot support all possible routes, but just the ones which | |
| * use the defaults provided by core, which are _controller and _form. | |
| * | |
| * Rather than creating an instance of every controller determine the class | |
| * and method that would be used. This is not possible for the service:method | |
| * notation as the runtime container does not allow static introspection. | |
| * | |
| * @see \Drupal\Core\Controller\ControllerResolver::getControllerFromDefinition() | |
| * @see \Drupal\Core\Controller\ClassResolver::getInstanceFromDefinition() | |
| * | |
| * @param array $defaults | |
| * The default values provided by the route. | |
| * | |
| * @return string|null | |
| * Returns the controller class, otherwise NULL. | |
| */ | |
| protected function getControllerClass(array $defaults) { | |
| $controller = NULL; | |
| if (isset($defaults['_controller'])) { | |
| $controller = $defaults['_controller']; | |
| } | |
| if (isset($defaults['_form'])) { | |
| $controller = $defaults['_form']; | |
| // Check if the class exists and if so use the buildForm() method from the | |
| // interface. | |
| if (class_exists($controller)) { | |
| return array($controller, 'buildForm'); | |
| } | |
| } | |
| if (strpos($controller, ':') === FALSE) { | |
| if (method_exists($controller, '__invoke')) { | |
| return array($controller, '__invoke'); | |
| } | |
| if (function_exists($controller)) { | |
| return $controller; | |
| } | |
| return NULL; | |
| } | |
| $count = substr_count($controller, ':'); | |
| if ($count == 1) { | |
| // Controller in the service:method notation. Get the information from the | |
| // service. This is dangerous as the controller could depend on services | |
| // that could not exist at this point. There is however no other way to | |
| // do it, as the container does not allow static introspection. | |
| list($class_or_service, $method) = explode(':', $controller, 2); | |
| return array($this->classResolver->getInstanceFromDefinition($class_or_service), $method); | |
| } | |
| elseif (strpos($controller, '::') !== FALSE) { | |
| // Controller in the class::method notation. | |
| return explode('::', $controller, 2); | |
| } | |
| return NULL; | |
| } | |
| /** | |
| * Sets the upcasting information using reflection. | |
| * | |
| * @param string|array $controller | |
| * A PHP callable representing the controller. | |
| * @param \Symfony\Component\Routing\Route $route | |
| * The route object to populate without upcasting information. | |
| * | |
| * @return bool | |
| * Returns TRUE if the upcasting parameters could be set, FALSE otherwise. | |
| */ | |
| protected function setParametersFromReflection($controller, Route $route) { | |
| $entity_types = $this->getEntityTypes(); | |
| $parameter_definitions = $route->getOption('parameters') ?: array(); | |
| $result = FALSE; | |
| if (is_array($controller)) { | |
| list($instance, $method) = $controller; | |
| $reflection = new \ReflectionMethod($instance, $method); | |
| } | |
| else { | |
| $reflection = new \ReflectionFunction($controller); | |
| } | |
| $parameters = $reflection->getParameters(); | |
| foreach ($parameters as $parameter) { | |
| $parameter_name = $parameter->getName(); | |
| // If the parameter name matches with an entity type try to set the | |
| // upcasting information automatically. Therefore take into account that | |
| // the user has specified some interface, so the upcasting is intended. | |
| if (isset($entity_types[$parameter_name])) { | |
| $entity_type = $entity_types[$parameter_name]; | |
| $entity_class = $entity_type->getClass(); | |
| if (($reflection_class = $parameter->getClass()) && (is_subclass_of($entity_class, $reflection_class->name) || $entity_class == $reflection_class->name)) { | |
| $parameter_definitions += array($parameter_name => array()); | |
| $parameter_definitions[$parameter_name] += array( | |
| 'type' => 'entity:' . $parameter_name, | |
| ); | |
| $result = TRUE; | |
| } | |
| } | |
| } | |
| if (!empty($parameter_definitions)) { | |
| $route->setOption('parameters', $parameter_definitions); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * Sets the upcasting information using the _entity_* route defaults. | |
| * | |
| * Supports the '_entity_view' and '_entity_form' route defaults. | |
| * | |
| * @param \Symfony\Component\Routing\Route $route | |
| * The route object. | |
| */ | |
| protected function setParametersFromEntityInformation(Route $route) { | |
| if ($entity_view = $route->getDefault('_entity_view')) { | |
| list($entity_type) = explode('.', $entity_view, 2); | |
| } | |
| elseif ($entity_form = $route->getDefault('_entity_form')) { | |
| list($entity_type) = explode('.', $entity_form, 2); | |
| } | |
| if (isset($entity_type) && isset($this->getEntityTypes()[$entity_type])) { | |
| $parameter_definitions = $route->getOption('parameters') ?: array(); | |
| // First try to figure out whether there is already a parameter upcasting | |
| // the same entity type already. | |
| foreach ($parameter_definitions as $info) { | |
| if (isset($info['type'])) { | |
| // The parameter types are in the form 'entity:$entity_type'. | |
| list(, $parameter_entity_type) = explode(':', $info['type'], 2); | |
| if ($parameter_entity_type == $entity_type) { | |
| return; | |
| } | |
| } | |
| } | |
| if (!isset($parameter_definitions[$entity_type])) { | |
| $parameter_definitions[$entity_type] = array(); | |
| } | |
| $parameter_definitions[$entity_type] += array( | |
| 'type' => 'entity:' . $entity_type, | |
| ); | |
| if (!empty($parameter_definitions)) { | |
| $route->setOption('parameters', $parameter_definitions); | |
| } | |
| } | |
| } | |
| /** | |
| * Set the upcasting route objects. | |
| * | |
| * @param \Symfony\Component\Routing\Route $route | |
| * The route object to add the upcasting information onto. | |
| */ | |
| public function setRouteOptions(Route $route) { | |
| if ($controller = $this->getControllerClass($route->getDefaults())) { | |
| // Try to use reflection. | |
| if ($this->setParametersFromReflection($controller, $route)) { | |
| return; | |
| } | |
| } | |
| // Try to use _entity_* information on the route. | |
| $this->setParametersFromEntityInformation($route); | |
| } | |
| /** | |
| * Gets the list of all entity types. | |
| * | |
| * @return \Drupal\Core\Entity\EntityTypeInterface[] | |
| */ | |
| protected function getEntityTypes() { | |
| if (!isset($this->entityTypes)) { | |
| $this->entityTypes = $this->entityManager->getDefinitions(); | |
| } | |
| return $this->entityTypes; | |
| } | |
| } |