Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/ParamConverter/EntityConverter.php @ 18:af1871eacc83
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:33:08 +0100 |
parents | 1fec387a4317 |
children |
comparison
equal
deleted
inserted
replaced
17:129ea1e6d783 | 18:af1871eacc83 |
---|---|
1 <?php | 1 <?php |
2 | 2 |
3 namespace Drupal\Core\ParamConverter; | 3 namespace Drupal\Core\ParamConverter; |
4 | 4 |
5 use Drupal\Core\Entity\EntityInterface; | 5 use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; |
6 use Drupal\Core\Entity\EntityManagerInterface; | 6 use Drupal\Core\Entity\EntityManagerInterface; |
7 use Drupal\Core\Entity\EntityRepositoryInterface; | |
8 use Drupal\Core\Entity\EntityTypeManagerInterface; | |
7 use Drupal\Core\Entity\RevisionableInterface; | 9 use Drupal\Core\Entity\RevisionableInterface; |
8 use Drupal\Core\Entity\TranslatableRevisionableInterface; | |
9 use Drupal\Core\Language\LanguageInterface; | 10 use Drupal\Core\Language\LanguageInterface; |
10 use Drupal\Core\Language\LanguageManagerInterface; | 11 use Drupal\Core\Plugin\Context\Context; |
12 use Drupal\Core\Plugin\Context\ContextDefinition; | |
11 use Drupal\Core\TypedData\TranslatableInterface; | 13 use Drupal\Core\TypedData\TranslatableInterface; |
12 use Symfony\Component\Routing\Route; | 14 use Symfony\Component\Routing\Route; |
13 | 15 |
14 /** | 16 /** |
15 * Parameter converter for upcasting entity IDs to full objects. | 17 * Parameter converter for upcasting entity IDs to full objects. |
63 * | 65 * |
64 * @see entities_revisions_translations | 66 * @see entities_revisions_translations |
65 */ | 67 */ |
66 class EntityConverter implements ParamConverterInterface { | 68 class EntityConverter implements ParamConverterInterface { |
67 | 69 |
68 /** | 70 use DeprecatedServicePropertyTrait; |
69 * Entity manager which performs the upcasting in the end. | 71 use DynamicEntityTypeParamConverterTrait; |
70 * | 72 |
71 * @var \Drupal\Core\Entity\EntityManagerInterface | 73 /** |
72 */ | 74 * {@inheritdoc} |
73 protected $entityManager; | 75 */ |
74 | 76 protected $deprecatedProperties = [ |
75 /** | 77 'entityManager' => 'entity.manager', |
76 * The language manager. | 78 'languageManager' => 'language_manager', |
77 * | 79 ]; |
78 * @var \Drupal\Core\Language\LanguageManagerInterface | 80 |
79 */ | 81 /** |
80 protected $languageManager; | 82 * Entity type manager which performs the upcasting in the end. |
83 * | |
84 * @var \Drupal\Core\Entity\EntityTypeManagerInterface | |
85 */ | |
86 protected $entityTypeManager; | |
87 | |
88 /** | |
89 * Entity repository. | |
90 * | |
91 * @var \Drupal\Core\Entity\EntityRepositoryInterface | |
92 */ | |
93 protected $entityRepository; | |
81 | 94 |
82 /** | 95 /** |
83 * Constructs a new EntityConverter. | 96 * Constructs a new EntityConverter. |
84 * | 97 * |
85 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | 98 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager |
86 * The entity manager. | 99 * The entity type manager. |
87 * @param \Drupal\Core\Language\LanguageManagerInterface|null $language_manager | 100 * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository |
88 * (optional) The language manager. Defaults to none. | 101 * The entity repository. |
89 */ | 102 * |
90 public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager = NULL) { | 103 * @see https://www.drupal.org/node/2549139 |
91 $this->entityManager = $entity_manager; | 104 * @see https://www.drupal.org/node/2938929 |
92 $this->languageManager = $language_manager; | 105 */ |
106 public function __construct(EntityTypeManagerInterface $entity_type_manager, $entity_repository = NULL) { | |
107 if ($entity_type_manager instanceof EntityManagerInterface) { | |
108 @trigger_error('Passing the entity.manager service to EntityConverter::__construct() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Pass the entity_type.manager service instead. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); | |
109 } | |
110 $this->entityTypeManager = $entity_type_manager; | |
111 | |
112 if (!($entity_repository instanceof EntityRepositoryInterface)) { | |
113 @trigger_error('Calling EntityConverter::__construct() with the $entity_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); | |
114 $entity_repository = \Drupal::service('entity.repository'); | |
115 } | |
116 $this->entityRepository = $entity_repository; | |
93 } | 117 } |
94 | 118 |
95 /** | 119 /** |
96 * {@inheritdoc} | 120 * {@inheritdoc} |
97 */ | 121 */ |
98 public function convert($value, $definition, $name, array $defaults) { | 122 public function convert($value, $definition, $name, array $defaults) { |
99 $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults); | 123 $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults); |
100 $storage = $this->entityManager->getStorage($entity_type_id); | |
101 $entity_definition = $this->entityManager->getDefinition($entity_type_id); | |
102 | |
103 $entity = $storage->load($value); | |
104 | 124 |
105 // If the entity type is revisionable and the parameter has the | 125 // If the entity type is revisionable and the parameter has the |
106 // "load_latest_revision" flag, load the latest revision. | 126 // "load_latest_revision" flag, load the active variant. |
107 if ($entity instanceof RevisionableInterface && !empty($definition['load_latest_revision']) && $entity_definition->isRevisionable()) { | 127 if (!empty($definition['load_latest_revision'])) { |
108 // Retrieve the latest revision ID taking translations into account. | 128 return $this->entityRepository->getActive($entity_type_id, $value); |
109 $langcode = $this->languageManager() | 129 } |
110 ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT) | 130 |
111 ->getId(); | 131 // Do not inject the context repository as it is not an actual dependency: |
112 $entity = $this->getLatestTranslationAffectedRevision($entity, $langcode); | 132 // it will be removed once both the TODOs below are fixed. |
113 } | 133 /** @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface $contexts_repository */ |
114 | 134 $contexts_repository = \Drupal::service('context.repository'); |
115 // If the entity type is translatable, ensure we return the proper | 135 // @todo Consider deprecating the legacy context operation altogether in |
116 // translation object for the current context. | 136 // https://www.drupal.org/node/3031124. |
117 if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) { | 137 $contexts = $contexts_repository->getAvailableContexts(); |
118 $entity = $this->entityManager->getTranslationFromContext($entity, NULL, ['operation' => 'entity_upcast']); | 138 $contexts[EntityRepositoryInterface::CONTEXT_ID_LEGACY_CONTEXT_OPERATION] = |
119 } | 139 new Context(new ContextDefinition('string'), 'entity_upcast'); |
140 // @todo At the moment we do not need the current user context, which is | |
141 // triggering some test failures. We can remove these lines once | |
142 // https://www.drupal.org/node/2934192 is fixed. | |
143 $context_id = '@user.current_user_context:current_user'; | |
144 if (isset($contexts[$context_id])) { | |
145 $account = $contexts[$context_id]->getContextValue(); | |
146 unset($account->_skipProtectedUserFieldConstraint); | |
147 unset($contexts[$context_id]); | |
148 } | |
149 $entity = $this->entityRepository->getCanonical($entity_type_id, $value, $contexts); | |
120 | 150 |
121 return $entity; | 151 return $entity; |
122 } | 152 } |
123 | 153 |
124 /** | 154 /** |
125 * Returns the ID of the latest revision translation of the specified entity. | 155 * Returns the latest revision translation of the specified entity. |
126 * | 156 * |
127 * @param \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface $entity | 157 * @param \Drupal\Core\Entity\RevisionableInterface $entity |
128 * The default revision of the entity being converted. | 158 * The default revision of the entity being converted. |
129 * @param string $langcode | 159 * @param string $langcode |
130 * The language of the revision translation to be loaded. | 160 * The language of the revision translation to be loaded. |
131 * | 161 * |
132 * @return \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface | 162 * @return \Drupal\Core\Entity\RevisionableInterface |
133 * The latest translation-affecting revision for the specified entity, or | 163 * The latest translation-affecting revision for the specified entity, or |
134 * just the latest revision, if the specified entity is not translatable or | 164 * just the latest revision, if the specified entity is not translatable or |
135 * does not have a matching translation yet. | 165 * does not have a matching translation yet. |
166 * | |
167 * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. | |
168 * Use \Drupal\Core\Entity\EntityRepositoryInterface::getActive() instead. | |
136 */ | 169 */ |
137 protected function getLatestTranslationAffectedRevision(RevisionableInterface $entity, $langcode) { | 170 protected function getLatestTranslationAffectedRevision(RevisionableInterface $entity, $langcode) { |
138 $revision = NULL; | 171 @trigger_error('\Drupal\Core\ParamConverter\EntityConverter::getLatestTranslationAffectedRevision() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityRepositoryInterface::getActive() instead.', E_USER_DEPRECATED); |
139 $storage = $this->entityManager->getStorage($entity->getEntityTypeId()); | 172 $data_type = 'language'; |
140 | 173 $context_id_prefix = '@language.current_language_context:'; |
141 if ($entity instanceof TranslatableRevisionableInterface && $entity->isTranslatable()) { | 174 $contexts = [ |
142 /** @var \Drupal\Core\Entity\TranslatableRevisionableStorageInterface $storage */ | 175 $context_id_prefix . LanguageInterface::TYPE_CONTENT => new Context(new ContextDefinition($data_type), $langcode), |
143 $revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $langcode); | 176 $context_id_prefix . LanguageInterface::TYPE_INTERFACE => new Context(new ContextDefinition($data_type), $langcode), |
144 | 177 ]; |
145 // If the latest translation-affecting revision was a default revision, it | 178 $revision = $this->entityRepository->getActive($entity->getEntityTypeId(), $entity->id(), $contexts); |
146 // is fine to load the latest revision instead, because in this case the | 179 // The EntityRepositoryInterface::getActive() method performs entity |
147 // latest revision, regardless of it being default or pending, will always | 180 // translation negotiation, but this used to return an untranslated entity |
148 // contain the most up-to-date values for the specified translation. This | 181 // object as translation negotiation happened later in ::convert(). |
149 // provides a BC behavior when the route is defined by a module always | 182 if ($revision instanceof TranslatableInterface) { |
150 // expecting the latest revision to be loaded and to be the default | 183 $revision = $revision->getUntranslated(); |
151 // revision. In this particular case the latest revision is always going | 184 } |
152 // to be the default revision, since pending revisions would not be | |
153 // supported. | |
154 /** @var \Drupal\Core\Entity\TranslatableRevisionableInterface $revision */ | |
155 $revision = $revision_id ? $this->loadRevision($entity, $revision_id) : NULL; | |
156 if (!$revision || ($revision->wasDefaultRevision() && !$revision->isDefaultRevision())) { | |
157 $revision = NULL; | |
158 } | |
159 } | |
160 | |
161 // Fall back to the latest revisions if no affected revision for the current | |
162 // content language could be found. This is acceptable as it means the | |
163 // entity is not translated. This is the correct logic also on monolingual | |
164 // sites. | |
165 if (!isset($revision)) { | |
166 $revision_id = $storage->getLatestRevisionId($entity->id()); | |
167 $revision = $this->loadRevision($entity, $revision_id); | |
168 } | |
169 | |
170 return $revision; | 185 return $revision; |
171 } | 186 } |
172 | 187 |
173 /** | 188 /** |
174 * Loads the specified entity revision. | 189 * Loads the specified entity revision. |
175 * | 190 * |
176 * @param \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface $entity | 191 * @param \Drupal\Core\Entity\RevisionableInterface $entity |
177 * The default revision of the entity being converted. | 192 * The default revision of the entity being converted. |
178 * @param string $revision_id | 193 * @param string $revision_id |
179 * The identifier of the revision to be loaded. | 194 * The identifier of the revision to be loaded. |
180 * | 195 * |
181 * @return \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface | 196 * @return \Drupal\Core\Entity\RevisionableInterface |
182 * An entity revision object. | 197 * An entity revision object. |
198 * | |
199 * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. | |
183 */ | 200 */ |
184 protected function loadRevision(RevisionableInterface $entity, $revision_id) { | 201 protected function loadRevision(RevisionableInterface $entity, $revision_id) { |
202 @trigger_error('\Drupal\Core\ParamConverter\EntityConverter::loadRevision() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0.', E_USER_DEPRECATED); | |
185 // We explicitly perform a loose equality check, since a revision ID may | 203 // We explicitly perform a loose equality check, since a revision ID may |
186 // be returned as an integer or a string. | 204 // be returned as an integer or a string. |
187 if ($entity->getLoadedRevisionId() != $revision_id) { | 205 if ($entity->getLoadedRevisionId() != $revision_id) { |
188 $storage = $this->entityManager->getStorage($entity->getEntityTypeId()); | 206 /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */ |
207 $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); | |
189 return $storage->loadRevision($revision_id); | 208 return $storage->loadRevision($revision_id); |
190 } | 209 } |
191 else { | 210 else { |
192 return $entity; | 211 return $entity; |
193 } | 212 } |
201 $entity_type_id = substr($definition['type'], strlen('entity:')); | 220 $entity_type_id = substr($definition['type'], strlen('entity:')); |
202 if (strpos($definition['type'], '{') !== FALSE) { | 221 if (strpos($definition['type'], '{') !== FALSE) { |
203 $entity_type_slug = substr($entity_type_id, 1, -1); | 222 $entity_type_slug = substr($entity_type_id, 1, -1); |
204 return $name != $entity_type_slug && in_array($entity_type_slug, $route->compile()->getVariables(), TRUE); | 223 return $name != $entity_type_slug && in_array($entity_type_slug, $route->compile()->getVariables(), TRUE); |
205 } | 224 } |
206 return $this->entityManager->hasDefinition($entity_type_id); | 225 return $this->entityTypeManager->hasDefinition($entity_type_id); |
207 } | 226 } |
208 return FALSE; | 227 return FALSE; |
209 } | |
210 | |
211 /** | |
212 * Determines the entity type ID given a route definition and route defaults. | |
213 * | |
214 * @param mixed $definition | |
215 * The parameter definition provided in the route options. | |
216 * @param string $name | |
217 * The name of the parameter. | |
218 * @param array $defaults | |
219 * The route defaults array. | |
220 * | |
221 * @return string | |
222 * The entity type ID. | |
223 * | |
224 * @throws \Drupal\Core\ParamConverter\ParamNotConvertedException | |
225 * Thrown when the dynamic entity type is not found in the route defaults. | |
226 */ | |
227 protected function getEntityTypeFromDefaults($definition, $name, array $defaults) { | |
228 $entity_type_id = substr($definition['type'], strlen('entity:')); | |
229 | |
230 // If the entity type is dynamic, it will be pulled from the route defaults. | |
231 if (strpos($entity_type_id, '{') === 0) { | |
232 $entity_type_slug = substr($entity_type_id, 1, -1); | |
233 if (!isset($defaults[$entity_type_slug])) { | |
234 throw new ParamNotConvertedException(sprintf('The "%s" parameter was not converted because the "%s" parameter is missing', $name, $entity_type_slug)); | |
235 } | |
236 $entity_type_id = $defaults[$entity_type_slug]; | |
237 } | |
238 return $entity_type_id; | |
239 } | 228 } |
240 | 229 |
241 /** | 230 /** |
242 * Returns a language manager instance. | 231 * Returns a language manager instance. |
243 * | 232 * |
245 * The language manager. | 234 * The language manager. |
246 * | 235 * |
247 * @internal | 236 * @internal |
248 */ | 237 */ |
249 protected function languageManager() { | 238 protected function languageManager() { |
250 if (!isset($this->languageManager)) { | 239 return $this->__get('languageManager'); |
251 $this->languageManager = \Drupal::languageManager(); | |
252 // @todo Turn this into a proper error (E_USER_ERROR) in | |
253 // https://www.drupal.org/node/2938929. | |
254 @trigger_error('The language manager parameter has been added to EntityConverter since version 8.5.0 and will be made required in version 9.0.0 when requesting the latest translation-affected revision of an entity.', E_USER_DEPRECATED); | |
255 } | |
256 return $this->languageManager; | |
257 } | 240 } |
258 | 241 |
259 } | 242 } |