ec631416c9bc823fa3d53ca7d5254d21536efb2f
[yaffs-website] / web / core / modules / hal / src / LinkManager / RelationLinkManager.php
1 <?php
2
3 namespace Drupal\hal\LinkManager;
4
5 use Drupal\Core\Cache\Cache;
6 use Drupal\Core\Cache\CacheBackendInterface;
7 use Drupal\Core\Config\ConfigFactoryInterface;
8 use Drupal\Core\Entity\ContentEntityTypeInterface;
9 use Drupal\Core\Entity\EntityManagerInterface;
10 use Drupal\Core\Extension\ModuleHandlerInterface;
11 use Symfony\Component\HttpFoundation\RequestStack;
12
13 class RelationLinkManager extends LinkManagerBase implements RelationLinkManagerInterface {
14
15   /**
16    * @var \Drupal\Core\Cache\CacheBackendInterface;
17    */
18   protected $cache;
19
20   /**
21    * Entity manager.
22    *
23    * @var \Drupal\Core\Entity\EntityManagerInterface
24    */
25   protected $entityManager;
26
27   /**
28    * Module handler service.
29    *
30    * @var \Drupal\Core\Extension\ModuleHandlerInterface
31    */
32   protected $moduleHandler;
33
34   /**
35    * Constructor.
36    *
37    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
38    *   The cache of relation URIs and their associated Typed Data IDs.
39    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
40    *   The entity manager.
41    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
42    *   The module handler service.
43    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
44    *   The config factory service.
45    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
46    *   The request stack.
47    */
48   public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) {
49     $this->cache = $cache;
50     $this->entityManager = $entity_manager;
51     $this->configFactory = $config_factory;
52     $this->moduleHandler = $module_handler;
53     $this->requestStack = $request_stack;
54   }
55
56   /**
57    * {@inheritdoc}
58    */
59   public function getRelationUri($entity_type, $bundle, $field_name, $context = []) {
60     // Per the interface documentation of this method, the returned URI may
61     // optionally also serve as the URL of a documentation page about this
62     // field. However, Drupal does not currently implement such a documentation
63     // page. Therefore, we return a URI assembled relative to the site's base
64     // URL, which is sufficient to uniquely identify the site's entity type +
65     // bundle + field for use in hypermedia formats, but we do not take into
66     // account unclean URLs, language prefixing, or anything else that would be
67     // required for Drupal to be able to respond with content at this URL. If a
68     // module is installed that adds such content, but requires this URL to be
69     // different (e.g., include a language prefix), then the module must also
70     // override the RelationLinkManager class/service to return the desired URL.
71     $uri = $this->getLinkDomain($context) . "/rest/relation/$entity_type/$bundle/$field_name";
72     $this->moduleHandler->alter('hal_relation_uri', $uri, $context);
73     // @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. This
74     // hook is invoked to maintain backwards compatibility
75     // @see https://www.drupal.org/node/2830467
76     $this->moduleHandler->alter('rest_relation_uri', $uri, $context);
77     return $uri;
78   }
79
80   /**
81    * {@inheritdoc}
82    */
83   public function getRelationInternalIds($relation_uri, $context = []) {
84     $relations = $this->getRelations($context);
85     if (isset($relations[$relation_uri])) {
86       return $relations[$relation_uri];
87     }
88     return FALSE;
89   }
90
91   /**
92    * Get the array of relation links.
93    *
94    * Any field can be handled as a relation simply by changing how it is
95    * normalized. Therefore, there is no prior knowledge that can be used here
96    * to determine which fields to assign relation URIs. Instead, each field,
97    * even primitives, are given a relation URI. It is up to the caller to
98    * determine which URIs to use.
99    *
100    * @param array $context
101    *   Context from the normalizer/serializer operation.
102    *
103    * @return array
104    *   An array of typed data IDs keyed by corresponding relation URI. The keys
105    *   are:
106    *   - 'entity_type_id'
107    *   - 'bundle'
108    *   - 'field_name'
109    *   - 'entity_type' (deprecated)
110    *   The values for 'entity_type_id', 'bundle' and 'field_name' are strings.
111    *   The 'entity_type' key exists for backwards compatibility and its value is
112    *   the full entity type object. The 'entity_type' key will be removed before
113    *   Drupal 9.0.
114    *
115    * @see https://www.drupal.org/node/2877608
116    */
117   protected function getRelations($context = []) {
118     $cid = 'hal:links:relations';
119     $cache = $this->cache->get($cid);
120     if (!$cache) {
121       $data = $this->writeCache($context);
122     }
123     else {
124       $data = $cache->data;
125     }
126
127     // @todo https://www.drupal.org/node/2716163 Remove this in Drupal 9.0.
128     foreach ($data as $relation_uri => $ids) {
129       $data[$relation_uri]['entity_type'] = $this->entityManager->getDefinition($ids['entity_type_id']);
130     }
131     return $data;
132   }
133
134   /**
135    * Writes the cache of relation links.
136    *
137    * @param array $context
138    *   Context from the normalizer/serializer operation.
139    *
140    * @return array
141    *   An array of typed data IDs keyed by corresponding relation URI. The keys
142    *   are:
143    *   - 'entity_type_id'
144    *   - 'bundle'
145    *   - 'field_name'
146    *   The values for 'entity_type_id', 'bundle' and 'field_name' are strings.
147    */
148   protected function writeCache($context = []) {
149     $data = [];
150
151     foreach ($this->entityManager->getDefinitions() as $entity_type) {
152       if ($entity_type instanceof ContentEntityTypeInterface) {
153         foreach ($this->entityManager->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
154           foreach ($this->entityManager->getFieldDefinitions($entity_type->id(), $bundle) as $field_definition) {
155             $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName(), $context);
156             $data[$relation_uri] = [
157               'entity_type_id' => $entity_type->id(),
158               'bundle' => $bundle,
159               'field_name' => $field_definition->getName(),
160             ];
161           }
162         }
163       }
164     }
165     // These URIs only change when field info changes, so cache it permanently
166     // and only clear it when the fields cache is cleared.
167     $this->cache->set('hal:links:relations', $data, Cache::PERMANENT, ['entity_field_info']);
168     return $data;
169   }
170
171 }