While doing some work with Serialization and the RNG contrib module, I found myself getting some really cryptic RouteNotFoundException messages. I did a bit more digging and came across #2853211, which appeared to be a very similar case.

What's happening is that EntityReferenceFieldItemNormalizer::normalize is serialising the basic details of the entity reference as target_type and target_uuid properties. However, it also tries to generate a URL for the entity, and sets that as the url property. RNG's entity types don't all have canonical URLs, yet some of the ones that aren't can be referenced via Entity Reference fields. Hence, when Serialization tries to generate the URL, it can't find a canonical route for the entity type, and an exception is thrown.

Symfony\Component\Routing\Exception\RouteNotFoundException: Route "entity.registration_type.canonical" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 190 of /path/to/webroot/core/lib/Drupal/Core/Routing/RouteProvider.php).

To get around this, perhaps something like the following would work to ensure that an entity's serialised url parameter is only generated if it has a canonical URL.

if (\Drupal::service('route.route_provider')->getRouteByName('entity.' . $values['target_type'] . '.canonical') && $url = $entity->url('canonical')) {
  $values['url'] = $url;
}

There's got to be a less hacky and terrible way to do that, but I'll try and put a patch together for a first take at a more sensible hour.

Members fund testing for the Drupal project. Drupal Association Learn more

Comments

geoffreyr created an issue. See original summary.

geoffreyr’s picture

Status: Needs review » Needs work
geoffreyr’s picture

geoffreyr’s picture

Status: Needs work » Needs review
geoffreyr’s picture

Well, that was stupid. Here's another patch, and this time, it should be using the right service name.

geoffreyr’s picture

So get this... RouteProvider::getRouteByName throws an exception if it doesn't exist, but all we really want to do is check its existence. I'm going to use another function to do it without throwing exceptions, and hopefully, we'll be third time lucky.

Status: Needs review » Needs work

The last submitted patch, 6: serialization--entity-ref-field-item-normalizer--canonical-url--3.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

geoffreyr’s picture

OK, now I see what's going on. Switching to interface ought to fix it, I hope.

geoffreyr’s picture

Status: Needs work » Needs review
tstoeckler’s picture

Status: Needs review » Needs work

I think this is a valid bug report, nice find!

You should be able to check $entity->hasLinkTemplate('canonical') instead of going through the route provider.

geoffreyr’s picture

Thanks for the lead @tstoeckler, I'm glad there's an easier way to do it. Have re-uploaded my patch with your suggestion.

geoffreyr’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work
tstoeckler’s picture

Status: Needs work » Postponed (maintainer needs more info)

Oops, I totally missed that the existing code is using Entity::url(), and not Entity::toUrl(). Entity::url() already contains this:

    // While self::toUrl() will throw an exception if the entity has no id,
    // the expected result for a URL is always a string.
    if ($this->id() === NULL || !$this->hasLinkTemplate($rel)) {
      return '';
    }

So thinking about it now, I actually think the existing code is fine. What may have happened in your case is that you provided a link template for your entity type but didn't actually provide a route, for example by not adding the route provider. This is not supported, though: If there is a link template, you have to make sure the route actually exists.

geoffreyr’s picture

Maybe I should submit a PR to the RNG project to sort out that link template issue then... will try that and get back to you.

geoffreyr’s picture

I've been doing a bit more digging into this, and I've also found the same error being fired off by Entity:uriRelationships. Here's a partial backtrace without args:

[12-Feb-2018 00:09:25 Australia/Sydney] Array
(
    [0] => Array
        (
            [file] => /path/to/webroot/core/lib/Drupal/Core/Routing/UrlGenerator.php
            [line] => 421
            [function] => getRouteByName
            [class] => Drupal\Core\Routing\RouteProvider
            [type] => ->
        )

    [1] => Array
        (
            [file] => /path/to/webroot/core/lib/Drupal/Core/Routing/UrlGenerator.php
            [line] => 271
            [function] => getRoute
            [class] => Drupal\Core\Routing\UrlGenerator
            [type] => ->
        )

    [2] => Array
        (
            [file] => /path/to/webroot/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php
            [line] => 105
            [function] => generateFromRoute
            [class] => Drupal\Core\Routing\UrlGenerator
            [type] => ->
        )

    [3] => Array
        (
            [file] => /path/to/webroot/core/lib/Drupal/Core/Url.php
            [line] => 753
            [function] => generateFromRoute
            [class] => Drupal\Core\Render\MetadataBubblingUrlGenerator
            [type] => ->
        )

    [4] => Array
        (
            [file] => /path/to/webroot/core/lib/Drupal/Core/Entity/Entity.php
            [line] => 333
            [function] => toString
            [class] => Drupal\Core\Url
            [type] => ->
        )

    [5] => Array
        (
            [function] => Drupal\Core\Entity\{closure}
            [class] => Drupal\Core\Entity\Entity
            [type] => ->
        )

    [6] => Array
        (
            [file] => /path/to/webroot/core/lib/Drupal/Core/Entity/Entity.php
            [line] => 339
            [function] => array_filter
        )

    [7] => Array
        (
            [file] => /path/to/webroot/core/modules/node/src/Controller/NodeViewController.php
            [line] => 57
            [function] => uriRelationships
            [class] => Drupal\Core\Entity\Entity
            [type] => ->
        )

    [8] => Array
        (
            [function] => view
            [class] => Drupal\node\Controller\NodeViewController
            [type] => ->
        )

    ...

    [24] => Array
        (
            [file] => /path/to/webroot/index.php
            [line] => 19
            [function] => handle
            [class] => Drupal\Core\DrupalKernel
            [type] => ->
        )

)

I've tried to patch the error in place but to no avail. Will investigate further.

geoffreyr’s picture

Figured it out. RNG's Registration Type entity type does have a canonical URL template, but for some reason it explodes whenever you try and generate a canonical URL.
Reported here: https://github.com/dpi/rng/issues/164

I put together an emergency patch to handle these situations which I won't include here, but suffice to say that it does involve catching any RouteNotFoundExceptions.

tstoeckler’s picture

Category: Bug report » Support request
Status: Postponed (maintainer needs more info) » Fixed

So I looked at that module and, indeed: it is providing a canonical link, but it is not providing a canonical route. That is a bug. So I don't think there's anything we can do from the core side. Marking as fixed, please re-open if you think there is anything we can do.

geoffreyr’s picture

Sounds fair enough, will be making further investigations into RNG. Thanks for spending your time on helping me out.

tstoeckler’s picture

No worries. Thanks for reporting back! Also, please do consider if there is any documentation that you think is lacking or incorrect. Best of luck with that module! ;-)