Fatal error when using a union type on a controller's callback

Created on 14 October 2022, about 2 years ago
Updated 13 February 2024, 10 months ago

Problem/Motivation

Using a union type in a controller's route callback throws a fatal error.

Fatal error: Uncaught Error: Call to undefined method ReflectionUnionType::isBuiltin() in /data/app/core/lib/Drupal/Component/Utility/Reflection.php:21
Stack trace:
#0 /data/app/core/lib/Drupal/Core/Entity/EntityResolverManager.php(143): Drupal\Component\Utility\Reflection::getParameterClassName()
#1 /data/app/core/lib/Drupal/Core/Entity/EntityResolverManager.php(214): Drupal\Core\Entity\EntityResolverManager->setParametersFromReflection()
#2 /data/app/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php(48): Drupal\Core\Entity\EntityResolverManager->setRouteOptions()
#3 [internal function]: Drupal\Core\EventSubscriber\EntityRouteAlterSubscriber->onRoutingRouteAlterSetType()
#4 /data/app/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()
#5 /data/app/core/lib/Drupal/Core/Routing/RouteBuilder.php(189): Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch()
#6 /data/app/core/lib/Drupal/Core/ProxyClass/Routing/RouteBuilder.php(83): Drupal\Core\Routing\RouteBuilder->rebuild()
#7 /data/app/core/includes/common.inc(587): Drupal\Core\ProxyClass\Routing\RouteBuilder->rebuild()

In my case I had a route that had a node parameter originally only accepting a single bundle but when I enhanced it to 2 it started breaking.

Steps to reproduce

This is just following my setup...

Create 2 Content types Article and Blog
Create bundle classes for the above bundles and use hook_entity_bundle_info_alter to wire them up.
Create a route that looks like the following:

my_module.my_route
  path: '/some/path/{node}'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyController::build'
  requirements:
    _entity_access: 'node.view'
    _format: 'json'
  options:
    parameters:
      node:
        type: entity:node
        bundle:
          - article
          - blog

With the controller callback:

public function build(Article|Blog $node): array {

This last bit specifically is what triggers the error. The ReflectionUnionType is returned because of this parameter type.

πŸ› Bug report
Status

Active

Version

11.0 πŸ”₯

Component
RoutingΒ  β†’

Last updated 4 days ago

Created by

πŸ‡¦πŸ‡ΊAustralia acbramley

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡©πŸ‡ͺGermany donquixote

    The Reflection::getParameterClassName() needs to somehow deal with union types.
    But what would be a suitable return value in case of union types?

    A possible backwards compatible solution could be:
    - Let Reflection::getParameterClassName() return NULL, if the type is a union or intersection type.
    - Optionally, add another parameter to control throwing an exception instead of returning NULL. OR introduce another method like "::demandParameterClassName()",
    - Add a new method Reflection::getParameterClassNames() that also supports union types. This one would return NULL for intersection types.
    - Alternatively, a new method could receive the type itself as a parameter, so it could also be used with return types.
    - All the existing calling code should treat the NULL case as if there is no parameter type at all. Gradually, each piece of calling code could be updated to also support union types.

    ---------

    This said: For the example controller, I think it would be better to simply use "Node" or "NodeInterface" as the parameter type, and then within the method call instanceof to work with the more specific bundle classes.
    As a workaround, you could also rename the parameter from `$node` to something like `$article_or_post` where you are sure that no entity type exists with this name.

  • πŸ‡³πŸ‡±Netherlands spadxiii

    I ran into this with a rest controller myself where it had "string|int" as its parameter.

    Added a small change to the if-statement in \Drupal\Component\Utility\Reflection::getParameterClassName:
    method_exists($parameter->getType(), 'isBuiltin')

    I'm not sure how to handle union types in this case, but this patch allowed me to prevent the error from happening :)

Production build 0.71.5 2024