Problem/Motivation

If there is a link template with mandatory parameters the url cannot be generated without providing those parameters. If we try to build the url without the mandatory parameters the exception Symfony\Component\Routing\Exception\MissingMandatoryParametersException will be thrown.

The entity predelete hook menu_link_content_entity_predelete introduced in #2350797: Orphaned menu links when nodes are deleted if menu_link_ui is not enabled is the one that introduced the problem by calling \Drupal\Core\Entity\EntityInterface::uriRelationships().

This might occur if the diff module is used and a link template like this is added:
"revisions-diff" = "/custom_entity/{custom_entity}/revision/{left_revision}/{right_revision}/{filter}"

This is a major as it prevents deleting entities having such link templates and there is no workaround for this.

Proposed resolution

\Drupal\Core\Entity\EntityInterface::uriRelationships() should catch exceptions of the type MissingMandatoryParametersException.

Remaining tasks

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

hchonov created an issue. See original summary.

hchonov’s picture

Status: Needs review » Needs work

The last submitted patch, 2: 2924338-2.patch, failed testing. View results

hchonov’s picture

+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -13,6 +13,7 @@
@@ -344,6 +345,12 @@ public function uriRelationships() {

@@ -344,6 +345,12 @@ public function uriRelationships() {
+      catch (MissingMandatoryParametersException $e) {
...
+        return TRUE;

The only thing I am not really sure about is if we should or should not return the link template in case we cannot automatically build the url for it?

Also will menu_link_content_entity_predelete be able to find the menu links without the route parameters?

cspitzlay’s picture

Status: Needs work » Needs review
cspitzlay’s picture

I set the issue back to needs review, the test failure was unrelated and a retest was green.

cburschka’s picture

The only thing I am not really sure about is if we should or should not return the link template in case we cannot automatically build the url for it?

Also will menu_link_content_entity_predelete be able to find the menu links without the route parameters?

I think the answer is no for both. With missing parameters, it's an invalid Url (since it did actually throw an exception while rendering), so it doesn't have a path representation, so it can't actually match any menu link. It doesn't make a practical difference, but it'd probably be better to return FALSE and filter it out.

Edit: Confirmed that if I create an entity type with a revision-revert link relationship, then create an entity and revisions, then create a menu link that points at the revert link for a specific revision, then delete the entity, the orphaned menu link will not be deleted, even after the patch. Whether we need to fix that is a separate issue, but this patch doesn't seem to fix it beyond preventing the crash. There doesn't seem to be a way to find all menu links matching a partially parameterized route (ie a specific entity ID, and any revision ID).

cburschka’s picture

Update to the above: The revision relationship (the canonical link to a specific revision) automatically gets the revision parameter set by the entity, so it's not affected by this bug, and revision-specific menu links should in fact get deleted, assuming that menu_link_content_entity_predelete() is called for each revision of the deleted entity (haven't checked in detail).

So the crash only affects other link templates (such as revision_revert or revision_delete, which are autogenerated when creating the content entity type with DrupalConsole). Since it's hard to imagine a legitimate case where you would have a custom menu link pointing at a revision-deletion form, we probably don't need to go out of our way to find and delete those. At worst, you'll need to remove the orphaned menu link yourself.

asherry’s picture

FileSize
919 bytes

I very much need this patch, but, I agree with cburschka it should really be returning FALSE. I'm just going to post a version with the boolean flipped so that I can use it in my composer file for now.

Thanks cspitzlay!

cburschka’s picture

Status: Needs review » Needs work

This looks ready, now we just need to remove the comment as it no longer applies.

cburschka’s picture

Status: Needs work » Needs review
FileSize
729 bytes

May still need tests. (The original code was added in #2293697-188: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available, but I can't see any tests that specifically cover it specifically.)

slydevil’s picture

Status: Needs review » Reviewed & tested by the community

Patch from #11 works great. I uncovered this issue is the 8.4.3 release, so it should be included in the 8.4 branch as well.

Wim Leers’s picture

+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -335,6 +336,12 @@ public function uriRelationships() {
+        // In the case that a link template contains mandatory parameters we'll
+        // not be able to generate the url for it, but we still want to return
+        // the uri relationship.

"we still want to return the uri relationship" doesn't really make sense.

Plus, there already is a comment above the try. This should instead expand that.

#11 is indeed right that #2293697: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available did not add explicit tests, but that's because certain (functional) tests were failing without this change. If we want explicit test coverage for this now, then we probably want to add a testUriRelationships() to \Drupal\Tests\Core\Entity\EntityUnitTest and add mocks for the different edge cases.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hchonov’s picture

Similar problem has been reported in #2941443: EntityInterface::uriRelationships() should document you can't call it on a new entity, but the work on the problem continues here. An easy test inspired by the referenced issue would be to simply create a new entity and before saving it call uriRelationships() on it, which should fail as the entity doesn't yet have an ID.

hchonov’s picture

Title: Exception thrown when deleting an entity having a link template with non-default mandatory parameters » Entity::uriRelationships() throws exceptions if an URL cannot be generated because of missing mandatory parameters
Component: menu system » entity system
RoSk0’s picture

RoSk0’s picture

Component: entity system » menu_link_content.module
Status: Needs work » Needs review
Related issues: +#2927051: Re-use parent code in RevisionableContentEntityBase::urlRouteParameters()
FileSize
1.85 KB

From my understanding the issue is actually in the way this module tries to clean up links to entity being deleted.
There is no need to loop over entity relationships when we just need a canonical URL for it.
Described approach is Implemented in patch.
Also in my case exception was caused by #2927051: Re-use parent code in RevisionableContentEntityBase::urlRouteParameters().
I think my approach could be treated not as bug fix but probably as improvement?

Status: Needs review » Needs work

The last submitted patch, 18: 2924338-18.patch, failed testing. View results

hchonov’s picture

There is no need to loop over entity relationships when we just need a canonical URL for it.

There might be a menu link to other entity URI relationships than to the canonical one and if we could generate the URL and automatically delete it then we should do it. What we do is best effort. Why do you think that we just need the canonical URL?

You could register a link template pointing to e.g. the settings of an entity something like entity/{entity}/settings and you might have a menu link pointing to it and if you delete the entity we should delete the menu link as well.

Additionally we should prevent ::uriRelationships() from throwing exceptions as otherwise the method is pretty much useless and couldn't be relied on, which then will solve this issue and the other one where the method is called on a new entity as if the method is called on a new entity it will not be able to generate the canonical URL and still throw an exception.

Do you mind moving the issue back to the entity system?

I think that #11 is the way to go. We only need a test coverage for the implemented solution.

RoSk0’s picture

Component: menu_link_content.module » entity system

Thank you for that explanation @hchonov. Now that code actually make sense for me. Much appreciated.

acrollet’s picture

There is an issue with this patch, which is that we can't assume that entities have a canonical URL. (e.g. paragraphs, flagging entities etc.)

hchonov’s picture

There is an issue with this patch, which is that we can't assume that entities have a canonical URL. (e.g. paragraphs, flagging entities etc.)

@acrollet, could you please elaborate on that? I don't understand what is the problem with this? All we do is the prevent throwing exceptions if we could not generate the full URL for an entity link template.

acrollet’s picture

@hchonov, I'd be happy to - the patch in #18 simply calls $entity->toUrl() instead of enumerating the uriRelationships method. The default parameter for that method is 'canonical' - entities that do not define a canonical link template (such as flagging entities) trigger this exception:

Drupal\Core\Entity\Exception\UndefinedLinkTemplateException: No link template 'canonical' found for the 'flagging' entity type in Drupal\Core\Entity\Entity->toUrl() (line 224 of /app/web/core/lib/Drupal/Core/Entity/Entity.php).

hchonov’s picture

Status: Needs work » Needs review
FileSize
729 bytes

@acrollet, in the comments afterwards I've explained that #18 is not the right approach, but #11. I am re-uploading the patch from #11 so that this is more clear. We only need a test coverage.

hchonov’s picture

The last submitted patch, 26: 2924338-26-test-only.patch, failed testing. View results

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

👌

alexpott’s picture

Looking at the exceptions that url generation can throw we have:

   * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
   *   Thrown when the named route does not exist.
   * @throws \Symfony\Component\Routing\Exception\MissingMandatoryParametersException
   *   Thrown when some parameters are missing that are mandatory for the route.
   * @throws \Symfony\Component\Routing\Exception\InvalidParameterException
   *   Thrown when a parameter value for a placeholder is not correct because it
   *   does not match the requirement.

2 of which are now caught here. Thinking about InvalidParameterException I think it makes sense to ignore that one since if that was thrown something would probably be very wrong.

alexpott’s picture

Giving review credit to @Wim Leers for a review that changed the patch.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 7aa16e856b to 8.6.x and f51e1bde98 to 8.5.x. Thanks!

  • alexpott committed 7aa16e8 on 8.6.x
    Issue #2924338 by hchonov, cburschka, RoSk0, asherry, Wim Leers: Entity...

  • alexpott committed f51e1bd on 8.5.x
    Issue #2924338 by hchonov, cburschka, RoSk0, asherry, Wim Leers: Entity...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.