3

I've created a custom content entity with drush generate module-content-entity, which works great. However, I want my custom entities NOT to have a canonical url. Drush automatically creates those in the annotation of the entity class:

/**
 * Defines the chunk entity class.
 *
 * @ContentEntityType(
 *   id = "chunk",
 *   ...
 *   links = {
 *     "add-form" = "/admin/content/chunk/add/{chunk_type}",
 *     "add-page" = "/admin/content/chunk/add",
 *     "canonical" = "/chunk/{chunk}",
 *     "edit-form" = "/admin/content/chunk/{chunk}/edit",
 *     "delete-form" = "/admin/content/chunk/{chunk}/delete",
 *     "collection" = "/admin/content/chunk"
 *   },
 *   ...
 * )
 */

I've tried to remove the canonical entry from the annotation, but this resulted in numerous errors left and right, because a lot of core and contrib modules assume that canonical URLs always exist. In my case most of those errors are coming from other modules calling functions that end up in EntityBase::toUrl, which fails with a WSOD due the missing canonical entry.

Even though this seems to be mandatory (@see EntityTypeInterface), some others - e.g. Paragraphs module - somehow managed to remove the canonical link, without causing errors everywhere.

Can anybody give me some hints how to safely remove the canonical link from my custom content entity? I do not want to turn the canonical links into 403's or 404's, I'd prefer those links not ever being rendered at all (hide "View" primary tab, prevent links when rendering entity labels, etc).

4
  • I don't know what errors you are getting, but I have created multiple entities without canonical links. I even have entities that have no links defined at all. They're only ever used in code, not on the front end.
    – Jaypan
    Commented Jul 8, 2021 at 22:13
  • "a lot of core and contrib modules assume that canonical URLs always exist" - Yes and no, No, because this entity skeleton works. Yes, because the standard entity handlers assume certain properties in the entity definition. Solution: You need your own handlers.
    – 4uk4
    Commented Jul 9, 2021 at 6:51
  • In my case most seem to be originating from EntityBase::toLink / ::toUrl, which is called by other modules and fails with an WSOD when removing the canonical entry from the annotation
    – Hudri
    Commented Jul 9, 2021 at 7:28
  • 1
    Using these methods with a paragraph results in: No link template 'canonical' found for the 'paragraph' entity type
    – 4uk4
    Commented Jul 9, 2021 at 7:56

2 Answers 2

1

Best way to do it is to define your own html route_provider

In your @ContentEntityType definition:

"route_provider" = {
  "html" = "Drupal\chunk\Routing\ChunkHtmlRouteProvider",
}

And the /chunk/src/Routing/ChunkHtmlRouteProvider.php itself:

<?php

namespace Drupal\chunk\Routing;

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;

class ChunkHtmlRouteProvider extends AdminHtmlRouteProvider {

  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
    return $this->getEditFormRoute($entity_type);
  }

}
0

It is actually really easy, except removing all references to canonical from the boilerplate code I just needed to override one single function toUrl in my custom entity class:

src/Entity/MyContentEntity.php

/**
 * Defines the chunk entity class.
 * Note the missing "links = { canonical }" entry in the annotation
 *
 *
 * @ContentEntityType(
 *   id = "chunk",
 *   ...
 *   links = {
 *     "add-form" = "/admin/content/chunk/add/{chunk_type}",
 *     "add-page" = "/admin/content/chunk/add",
 *     "edit-form" = "/admin/content/chunk/{chunk}/edit",
 *     "delete-form" = "/admin/content/chunk/{chunk}/delete",
 *     "collection" = "/admin/content/chunk"
 *   },
 *   ...
 * )
 */
class Chunk extends ContentEntityBase implements ChunkInterface {
  /**
   * this prevents WSOD when 3rd party modules call $entity->toUrl
   */
  public function toUrl($rel = 'canonical', array $options = []) {
    if ($rel == 'canonical') {
      return Url::fromUri('route:<nolink>')->setOptions($options);
    }
    else {
      return parent::toUrl($rel, $options);
    }
  }
}

The other stuff here is only needed if the entity is based on an auto-generated boilerplate code from drush generate or similar:

src/Form/MyContentEntityForm.php

/**
 * The following change is only necessary if you use boilerplate code from "drush generate" or similar
 */
class ChunkForm extends ContentEntityForm {
  public function save(array $form, FormStateInterface $form_state) {
    ...
    // change the following line
    //$form_state->setRedirect('entity.chunk.canonical', ['chunk' => $entity->id()]);
    // to something of your choice
    $form_state->setRedirect('entity.chunk.edit-form', ['chunk' => $entity->id()]);
  }

}

my_module.links.task.yml:

# If existing, remove the following boilerplate code from "drush generate" or similar

entity.chunk.view:
  title: View
  route_name: entity.chunk.canonical
  base_route: entity.chunk.canonical
3
  • Error: Class 'Drupal\my_content_entity\Entity\Url' not found
    – wranvaud
    Commented Dec 16, 2021 at 16:45
  • @wranvaud realise this is old, however thought it might be useful to point out that you will need to add use Drupal\Core\Url; to make sure it's included. Also worth pointing out that if you want to view the entity listing page rather than the edit form when creating a new entity, swap out edit_form for collection in src/Form/MyContentEntityForm.php.
    – kiwimind
    Commented Jun 6, 2022 at 10:02
  • I usually override toUrl method to point to the edit link instead of route:<nolink> Commented Oct 5, 2022 at 9:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.