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

Created on 14 October 2022, over 2 years ago
Updated 3 May 2023, about 2 years 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

9.5

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

Merge Requests

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 :)

  • πŸ‡ΊπŸ‡¦Ukraine voleger Ukraine, Rivne

    The same error appears when generating a proxy class for a class with union types used in definitions.
    php core/scripts/generate-proxy-class.php '\Drupal\Core\Batch\BatchProcessor' "core/lib/Drupal/Core".
    Affected me working on πŸ“Œ Create an interface and initial class for the batch processor service Needs work

  • Pipeline finished with Failed
    16 days ago
    Total: 240s
    #526670
  • πŸ‡¦πŸ‡ΊAustralia acbramley

    Finally getting around to this one, I think for now we just return NULL and don't automate the entity upcasting because I can't think of a good way that you could safely upcast an entity parameter if it's a union type?

    Perhaps only if all of them are a subclass of $entity_type->getClass();?

  • Pipeline finished with Success
    16 days ago
    #526672
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Sorry to be that guy

    Can we clean up the issue summary, appears to be missing section with proposed solution

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    My bad, updated.

Production build 0.71.5 2024