summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/navigation/src/EntityRouteHelper.php
blob: 104e08b7fcf59802dcccc781ffa84145b05e113d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<?php

declare(strict_types=1);

namespace Drupal\navigation;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Routing\CurrentRouteMatch;

/**
 * Helper service to detect entity routes.
 *
 * @internal
 */
class EntityRouteHelper {

  const ENTITY_ROUTE_CID = 'navigation_content_entity_paths';

  /**
   * A list of all the link paths of enabled content entities.
   *
   * @var array
   */
  protected array $contentEntityPaths;

  /**
   * EntityRouteHelper constructor.
   *
   * @param \Drupal\Core\Routing\CurrentRouteMatch $routeMatch
   *   The route match.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
   *   The cache backend.
   */
  public function __construct(
    protected CurrentRouteMatch $routeMatch,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected CacheBackendInterface $cacheBackend,
  ) {
  }

  /**
   * Determines if content entity route condition is met.
   *
   * @return bool
   *   TRUE if the content entity route condition is met, FALSE otherwise.
   */
  public function isContentEntityRoute(): bool {
    return array_key_exists($this->routeMatch->getRouteObject()->getPath(), $this->getContentEntityPaths());
  }

  public function getContentEntityFromRoute(): ?ContentEntityInterface {
    $path = $this->routeMatch->getRouteObject()->getPath();
    if (!$entity_type = $this->getContentEntityPaths()[$path] ?? NULL) {
      return NULL;
    }

    $entity = $this->routeMatch->getParameter($entity_type);
    if ($entity instanceof ContentEntityInterface && $entity->getEntityTypeId() === $entity_type) {
      return $entity;
    }

    return NULL;
  }

  /**
   * Returns the paths for the link templates of all content entities.
   *
   * @return array
   *   An array of all content entity type IDs, keyed by the corresponding link
   *   template paths.
   */
  protected function getContentEntityPaths(): array {
    if (isset($this->contentEntityPaths)) {
      return $this->contentEntityPaths;
    }

    $content_entity_paths = $this->cacheBackend->get(static::ENTITY_ROUTE_CID);

    if (isset($content_entity_paths->data)) {
      $this->contentEntityPaths = $content_entity_paths->data;
      return $this->contentEntityPaths;
    }

    $this->contentEntityPaths = $this->doGetContentEntityPaths();
    $this->cacheBackend->set(static::ENTITY_ROUTE_CID, $this->contentEntityPaths, CacheBackendInterface::CACHE_PERMANENT, ['entity_types', 'routes']);

    return $this->contentEntityPaths;
  }

  protected function doGetContentEntityPaths(): array {
    $content_entity_paths = [];
    $entity_types = $this->entityTypeManager->getDefinitions();
    foreach ($entity_types as $entity_type) {
      if ($entity_type->entityClassImplements(ContentEntityInterface::class)) {
        $entity_paths = $this->getContentEntityTypePaths($entity_type);
        $content_entity_paths = array_merge($content_entity_paths, $entity_paths);
      }
    }

    return $content_entity_paths;
  }

  /**
   * Returns the path for the link template for a given content entity type.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   *
   * @return array
   *   Array containing the paths for the given content entity type.
   */
  protected function getContentEntityTypePaths(EntityTypeInterface $entity_type): array {
    $paths = array_filter($entity_type->getLinkTemplates(), fn ($template) => $template !== 'collection', ARRAY_FILTER_USE_KEY);
    if ($this->isLayoutBuilderEntityType($entity_type)) {
      $paths[] = $entity_type->getLinkTemplate('canonical') . '/layout';
    }
    return array_fill_keys($paths, $entity_type->id());
  }

  /**
   * Determines if a given entity type is layout builder relevant or not.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type.
   *
   * @return bool
   *   Whether this entity type is a Layout builder candidate or not
   *
   * @see \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::getEntityTypes()
   */
  protected function isLayoutBuilderEntityType(EntityTypeInterface $entity_type): bool {
    return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasHandlerClass('form', 'layout_builder') && $entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical');
  }

}