Further Drupal 8.6.4 changes. Some core files were not committed before a commit...
[yaffs-website] / web / core / lib / Drupal / Core / Entity / EntityType.php
1 <?php
2
3 namespace Drupal\Core\Entity;
4
5 use Drupal\Component\Plugin\Definition\PluginDefinition;
6 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
7 use Drupal\Core\Entity\Exception\EntityTypeIdLengthException;
8 use Drupal\Core\StringTranslation\StringTranslationTrait;
9 use Drupal\Core\StringTranslation\TranslatableMarkup;
10
11 /**
12  * Provides an implementation of an entity type and its metadata.
13  *
14  * @ingroup entity_api
15  */
16 class EntityType extends PluginDefinition implements EntityTypeInterface {
17
18   use DependencySerializationTrait;
19   use StringTranslationTrait;
20
21   /**
22    * Indicates whether entities should be statically cached.
23    *
24    * @var bool
25    */
26   protected $static_cache = TRUE;
27
28   /**
29    * Indicates whether the rendered output of entities should be cached.
30    *
31    * @var bool
32    */
33   protected $render_cache = TRUE;
34
35   /**
36    * Indicates if the persistent cache of field data should be used.
37    *
38    * @var bool
39    */
40   protected $persistent_cache = TRUE;
41
42   /**
43    * An array of entity keys.
44    *
45    * @var array
46    */
47   protected $entity_keys = [];
48
49   /**
50    * The unique identifier of this entity type.
51    *
52    * @var string
53    */
54   protected $id;
55
56   /**
57    * The name of the original entity type class.
58    *
59    * This is only set if the class name is changed.
60    *
61    * @var string
62    */
63   protected $originalClass;
64
65   /**
66    * An array of handlers.
67    *
68    * @var array
69    */
70   protected $handlers = [];
71
72   /**
73    * The name of the default administrative permission.
74    *
75    * @var string
76    */
77   protected $admin_permission;
78
79   /**
80    * The permission granularity level.
81    *
82    * The allowed values are respectively "entity_type" or "bundle".
83    *
84    * @var string
85    */
86   protected $permission_granularity = 'entity_type';
87   /**
88    * Link templates using the URI template syntax.
89    *
90    * @var array
91    */
92   protected $links = [];
93
94   /**
95    * The name of a callback that returns the label of the entity.
96    *
97    * @var callable|null
98    *
99    * @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
100    *   Use Drupal\Core\Entity\EntityInterface::label() for complex label
101    *   generation as needed.
102    *
103    * @see \Drupal\Core\Entity\EntityInterface::label()
104    *
105    * @todo Remove usages of label_callback https://www.drupal.org/node/2450793.
106    */
107   protected $label_callback = NULL;
108
109   /**
110    * The name of the entity type which provides bundles.
111    *
112    * @var string
113    */
114   protected $bundle_entity_type = NULL;
115
116   /**
117    * The name of the entity type for which bundles are provided.
118    *
119    * @var string|null
120    */
121   protected $bundle_of = NULL;
122
123   /**
124    * The human-readable name of the entity bundles, e.g. Vocabulary.
125    *
126    * @var string|null
127    */
128   protected $bundle_label = NULL;
129
130   /**
131    * The name of the entity type's base table.
132    *
133    * @var string|null
134    */
135   protected $base_table = NULL;
136
137   /**
138    * The name of the entity type's revision data table.
139    *
140    * @var string|null
141    */
142   protected $revision_data_table = NULL;
143
144   /**
145    * The name of the entity type's revision table.
146    *
147    * @var string|null
148    */
149   protected $revision_table = NULL;
150
151   /**
152    * The name of the entity type's data table.
153    *
154    * @var string|null
155    */
156   protected $data_table = NULL;
157
158   /**
159    * Indicates whether the entity data is internal.
160    *
161    * @var bool
162    */
163   protected $internal = FALSE;
164
165   /**
166    * Indicates whether entities of this type have multilingual support.
167    *
168    * @var bool
169    */
170   protected $translatable = FALSE;
171
172   /**
173    * Indicates whether the revision form fields should be added to the form.
174    *
175    * @var bool
176    */
177   protected $show_revision_ui = FALSE;
178
179   /**
180    * The human-readable name of the type.
181    *
182    * @var string
183    *
184    * @see \Drupal\Core\Entity\EntityTypeInterface::getLabel()
185    */
186   protected $label = '';
187
188   /**
189    * The human-readable label for a collection of entities of the type.
190    *
191    * @var string
192    *
193    * @see \Drupal\Core\Entity\EntityTypeInterface::getCollectionLabel()
194    */
195   protected $label_collection = '';
196
197   /**
198    * The indefinite singular name of the type.
199    *
200    * @var string
201    *
202    * @see \Drupal\Core\Entity\EntityTypeInterface::getSingularLabel()
203    */
204   protected $label_singular = '';
205
206   /**
207    * The indefinite plural name of the type.
208    *
209    * @var string
210    *
211    * @see \Drupal\Core\Entity\EntityTypeInterface::getPluralLabel()
212    */
213   protected $label_plural = '';
214
215   /**
216    * A definite singular/plural name of the type.
217    *
218    * Needed keys: "singular" and "plural".
219    *
220    * @var string[]
221    *
222    * @see \Drupal\Core\Entity\EntityTypeInterface::getCountLabel()
223    */
224   protected $label_count = [];
225
226   /**
227    * A callable that can be used to provide the entity URI.
228    *
229    * @var callable|null
230    */
231   protected $uri_callback = NULL;
232
233   /**
234    * The machine name of the entity type group.
235    */
236   protected $group;
237
238   /**
239    * The human-readable name of the entity type group.
240    */
241   protected $group_label;
242
243   /**
244    * The route name used by field UI to attach its management pages.
245    *
246    * @var string
247    */
248   protected $field_ui_base_route;
249
250   /**
251    * Indicates whether this entity type is commonly used as a reference target.
252    *
253    * This is used by the Entity reference field to promote an entity type in the
254    * add new field select list in Field UI.
255    *
256    * @var bool
257    */
258   protected $common_reference_target = FALSE;
259
260   /**
261    * The list cache contexts for this entity type.
262    *
263    * @var string[]
264    */
265   protected $list_cache_contexts = [];
266
267   /**
268    * The list cache tags for this entity type.
269    *
270    * @var string[]
271    */
272   protected $list_cache_tags = [];
273
274   /**
275    * Entity constraint definitions.
276    *
277    * @var array[]
278    */
279   protected $constraints = [];
280
281   /**
282    * Any additional properties and values.
283    *
284    * @var array
285    */
286   protected $additional = [];
287
288   /**
289    * Constructs a new EntityType.
290    *
291    * @param array $definition
292    *   An array of values from the annotation.
293    *
294    * @throws \Drupal\Core\Entity\Exception\EntityTypeIdLengthException
295    *   Thrown when attempting to instantiate an entity type with too long ID.
296    */
297   public function __construct($definition) {
298     // Throw an exception if the entity type ID is longer than 32 characters.
299     if (mb_strlen($definition['id']) > static::ID_MAX_LENGTH) {
300       throw new EntityTypeIdLengthException('Attempt to create an entity type with an ID longer than ' . static::ID_MAX_LENGTH . " characters: {$definition['id']}.");
301     }
302
303     foreach ($definition as $property => $value) {
304       $this->set($property, $value);
305     }
306
307     // Ensure defaults.
308     $this->entity_keys += [
309       'revision' => '',
310       'bundle' => '',
311       'langcode' => '',
312       'default_langcode' => 'default_langcode',
313       'revision_translation_affected' => 'revision_translation_affected',
314     ];
315     $this->handlers += [
316       'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
317     ];
318     if (isset($this->handlers['storage'])) {
319       $this->checkStorageClass($this->handlers['storage']);
320     }
321
322     // Automatically add the "EntityChanged" constraint if the entity type
323     // tracks the changed time.
324     if ($this->entityClassImplements(EntityChangedInterface::class)) {
325       $this->addConstraint('EntityChanged');
326     }
327     // Automatically add the "EntityUntranslatableFields" constraint if we have
328     // an entity type supporting translatable fields and pending revisions.
329     if ($this->entityClassImplements(ContentEntityInterface::class)) {
330       $this->addConstraint('EntityUntranslatableFields');
331     }
332
333     // Ensure a default list cache tag is set.
334     if (empty($this->list_cache_tags)) {
335       $this->list_cache_tags = [$definition['id'] . '_list'];
336     }
337   }
338
339   /**
340    * {@inheritdoc}
341    */
342   public function get($property) {
343     if (property_exists($this, $property)) {
344       $value = isset($this->{$property}) ? $this->{$property} : NULL;
345     }
346     else {
347       $value = isset($this->additional[$property]) ? $this->additional[$property] : NULL;
348     }
349     return $value;
350   }
351
352   /**
353    * {@inheritdoc}
354    */
355   public function set($property, $value) {
356     if (property_exists($this, $property)) {
357       $this->{$property} = $value;
358     }
359     else {
360       $this->additional[$property] = $value;
361     }
362     return $this;
363   }
364
365   /**
366    * {@inheritdoc}
367    */
368   public function isInternal() {
369     return $this->internal;
370   }
371
372   /**
373    * {@inheritdoc}
374    */
375   public function isStaticallyCacheable() {
376     return $this->static_cache;
377   }
378
379   /**
380    * {@inheritdoc}
381    */
382   public function isRenderCacheable() {
383     return $this->render_cache;
384   }
385
386   /**
387    * {@inheritdoc}
388    */
389   public function isPersistentlyCacheable() {
390     return $this->persistent_cache;
391   }
392
393   /**
394    * {@inheritdoc}
395    */
396   public function getKeys() {
397     return $this->entity_keys;
398   }
399
400   /**
401    * {@inheritdoc}
402    */
403   public function getKey($key) {
404     $keys = $this->getKeys();
405     return isset($keys[$key]) ? $keys[$key] : FALSE;
406   }
407
408   /**
409    * {@inheritdoc}
410    */
411   public function hasKey($key) {
412     $keys = $this->getKeys();
413     return !empty($keys[$key]);
414   }
415
416   /**
417    * {@inheritdoc}
418    */
419   public function getOriginalClass() {
420     return $this->originalClass ?: $this->class;
421   }
422
423   /**
424    * {@inheritdoc}
425    */
426   public function setClass($class) {
427     if (!$this->originalClass && $this->class) {
428       // If the original class is currently not set, set it to the current
429       // class, assume that is the original class name.
430       $this->originalClass = $this->class;
431     }
432
433     return parent::setClass($class);
434   }
435
436   /**
437    * {@inheritdoc}
438    */
439   public function entityClassImplements($interface) {
440     return is_subclass_of($this->getClass(), $interface);
441   }
442
443   /**
444    * {@inheritdoc}
445    */
446   public function isSubclassOf($class) {
447     return $this->entityClassImplements($class);
448   }
449
450   /**
451    * {@inheritdoc}
452    */
453   public function getHandlerClasses() {
454     return $this->handlers;
455   }
456
457   /**
458    * {@inheritdoc}
459    */
460   public function getHandlerClass($handler_type, $nested = FALSE) {
461     if ($this->hasHandlerClass($handler_type, $nested)) {
462       $handlers = $this->getHandlerClasses();
463       return $nested ? $handlers[$handler_type][$nested] : $handlers[$handler_type];
464     }
465   }
466
467   /**
468    * {@inheritdoc}
469    */
470   public function setHandlerClass($handler_type, $value) {
471     $this->handlers[$handler_type] = $value;
472     return $this;
473   }
474
475   /**
476    * {@inheritdoc}
477    */
478   public function hasHandlerClass($handler_type, $nested = FALSE) {
479     $handlers = $this->getHandlerClasses();
480     if (!isset($handlers[$handler_type]) || ($nested && !isset($handlers[$handler_type][$nested]))) {
481       return FALSE;
482     }
483     $handler = $handlers[$handler_type];
484     if ($nested) {
485       $handler = $handler[$nested];
486     }
487     return class_exists($handler);
488   }
489
490   /**
491    * {@inheritdoc}
492    */
493   public function getStorageClass() {
494     return $this->getHandlerClass('storage');
495   }
496
497   /**
498    * {@inheritdoc}
499    */
500   public function setStorageClass($class) {
501     $this->checkStorageClass($class);
502     $this->handlers['storage'] = $class;
503     return $this;
504   }
505
506   /**
507    * Checks that the provided class is compatible with the current entity type.
508    *
509    * @param string $class
510    *   The class to check.
511    */
512   protected function checkStorageClass($class) {
513     // Nothing to check by default.
514   }
515
516   /**
517    * {@inheritdoc}
518    */
519   public function getFormClass($operation) {
520     return $this->getHandlerClass('form', $operation);
521   }
522
523   /**
524    * {@inheritdoc}
525    */
526   public function setFormClass($operation, $class) {
527     $this->handlers['form'][$operation] = $class;
528     return $this;
529   }
530
531   /**
532    * {@inheritdoc}
533    */
534   public function hasFormClasses() {
535     return !empty($this->handlers['form']);
536   }
537
538   /**
539    * {@inheritdoc}
540    */
541   public function hasRouteProviders() {
542     return !empty($this->handlers['route_provider']);
543   }
544
545   /**
546    * {@inheritdoc}
547    */
548   public function getListBuilderClass() {
549     return $this->getHandlerClass('list_builder');
550   }
551
552   /**
553    * {@inheritdoc}
554    */
555   public function setListBuilderClass($class) {
556     $this->handlers['list_builder'] = $class;
557     return $this;
558   }
559
560   /**
561    * {@inheritdoc}
562    */
563   public function hasListBuilderClass() {
564     return $this->hasHandlerClass('list_builder');
565   }
566
567   /**
568    * {@inheritdoc}
569    */
570   public function getViewBuilderClass() {
571     return $this->getHandlerClass('view_builder');
572   }
573
574   /**
575    * {@inheritdoc}
576    */
577   public function setViewBuilderClass($class) {
578     $this->handlers['view_builder'] = $class;
579     return $this;
580   }
581
582   /**
583    * {@inheritdoc}
584    */
585   public function hasViewBuilderClass() {
586     return $this->hasHandlerClass('view_builder');
587   }
588
589   /**
590    * {@inheritdoc}
591    */
592   public function getRouteProviderClasses() {
593     return !empty($this->handlers['route_provider']) ? $this->handlers['route_provider'] : [];
594   }
595
596   /**
597    * {@inheritdoc}
598    */
599   public function getAccessControlClass() {
600     return $this->getHandlerClass('access');
601   }
602
603   /**
604    * {@inheritdoc}
605    */
606   public function setAccessClass($class) {
607     $this->handlers['access'] = $class;
608     return $this;
609   }
610
611   /**
612    * {@inheritdoc}
613    */
614   public function getAdminPermission() {
615     return $this->admin_permission ?: FALSE;
616   }
617
618   /**
619    * {@inheritdoc}
620    */
621   public function getPermissionGranularity() {
622     return $this->permission_granularity;
623   }
624
625   /**
626    * {@inheritdoc}
627    */
628   public function getLinkTemplates() {
629     return $this->links;
630   }
631
632   /**
633    * {@inheritdoc}
634    */
635   public function getLinkTemplate($key) {
636     $links = $this->getLinkTemplates();
637     return isset($links[$key]) ? $links[$key] : FALSE;
638   }
639
640   /**
641    * {@inheritdoc}
642    */
643   public function hasLinkTemplate($key) {
644     $links = $this->getLinkTemplates();
645     return isset($links[$key]);
646   }
647
648   /**
649    * {@inheritdoc}
650    */
651   public function setLinkTemplate($key, $path) {
652     if ($path[0] !== '/') {
653       throw new \InvalidArgumentException('Link templates accepts paths, which have to start with a leading slash.');
654     }
655
656     $this->links[$key] = $path;
657     return $this;
658   }
659
660   /**
661    * {@inheritdoc}
662    */
663   public function getLabelCallback() {
664     return $this->label_callback;
665   }
666
667   /**
668    * {@inheritdoc}
669    */
670   public function setLabelCallback($callback) {
671     $this->label_callback = $callback;
672     return $this;
673   }
674
675   /**
676    * {@inheritdoc}
677    */
678   public function hasLabelCallback() {
679     return isset($this->label_callback);
680   }
681
682   /**
683    * {@inheritdoc}
684    */
685   public function getBundleEntityType() {
686     return $this->bundle_entity_type;
687   }
688
689   /**
690    * {@inheritdoc}
691    */
692   public function getBundleOf() {
693     return $this->bundle_of;
694   }
695
696   /**
697    * {@inheritdoc}
698    */
699   public function getBundleLabel() {
700     // If there is no bundle label defined, try to provide some sensible
701     // fallbacks.
702     if (!empty($this->bundle_label)) {
703       return (string) $this->bundle_label;
704     }
705     elseif ($bundle_entity_type_id = $this->getBundleEntityType()) {
706       return (string) \Drupal::entityTypeManager()->getDefinition($bundle_entity_type_id)->getLabel();
707     }
708     return (string) new TranslatableMarkup('@type_label bundle', ['@type_label' => $this->getLabel()], [], $this->getStringTranslation());
709   }
710
711   /**
712    * {@inheritdoc}
713    */
714   public function getBaseTable() {
715     return $this->base_table;
716   }
717
718   /**
719    * {@inheritdoc}
720    */
721   public function showRevisionUi() {
722     return $this->isRevisionable() && $this->show_revision_ui;
723   }
724
725   /**
726    * {@inheritdoc}
727    */
728   public function isTranslatable() {
729     return !empty($this->translatable);
730   }
731
732   /**
733    * {@inheritdoc}
734    */
735   public function isRevisionable() {
736     // Entity types are revisionable if a revision key has been specified.
737     return $this->hasKey('revision');
738   }
739
740   /**
741    * {@inheritdoc}
742    */
743   public function getRevisionDataTable() {
744     return $this->revision_data_table;
745   }
746
747   /**
748    * {@inheritdoc}
749    */
750   public function getRevisionTable() {
751     return $this->revision_table;
752   }
753
754   /**
755    * {@inheritdoc}
756    */
757   public function getDataTable() {
758     return $this->data_table;
759   }
760
761   /**
762    * {@inheritdoc}
763    */
764   public function getLabel() {
765     return $this->label;
766   }
767
768   /**
769    * {@inheritdoc}
770    */
771   public function getLowercaseLabel() {
772     return mb_strtolower($this->getLabel());
773   }
774
775   /**
776    * {@inheritdoc}
777    */
778   public function getCollectionLabel() {
779     if (empty($this->label_collection)) {
780       $label = $this->getLabel();
781       $this->label_collection = new TranslatableMarkup('@label entities', ['@label' => $label], [], $this->getStringTranslation());
782     }
783     return $this->label_collection;
784   }
785
786   /**
787    * {@inheritdoc}
788    */
789   public function getSingularLabel() {
790     if (empty($this->label_singular)) {
791       $lowercase_label = $this->getLowercaseLabel();
792       $this->label_singular = $lowercase_label;
793     }
794     return $this->label_singular;
795   }
796
797   /**
798    * {@inheritdoc}
799    */
800   public function getPluralLabel() {
801     if (empty($this->label_plural)) {
802       $lowercase_label = $this->getLowercaseLabel();
803       $this->label_plural = new TranslatableMarkup('@label entities', ['@label' => $lowercase_label], [], $this->getStringTranslation());
804     }
805     return $this->label_plural;
806   }
807
808   /**
809    * {@inheritdoc}
810    */
811   public function getCountLabel($count) {
812     if (empty($this->label_count)) {
813       return $this->formatPlural($count, '@count @label', '@count @label entities', ['@label' => $this->getLowercaseLabel()], ['context' => 'Entity type label']);
814     }
815     $context = isset($this->label_count['context']) ? $this->label_count['context'] : 'Entity type label';
816     return $this->formatPlural($count, $this->label_count['singular'], $this->label_count['plural'], ['context' => $context]);
817   }
818
819   /**
820    * {@inheritdoc}
821    */
822   public function getUriCallback() {
823     return $this->uri_callback;
824   }
825
826   /**
827    * {@inheritdoc}
828    */
829   public function setUriCallback($callback) {
830     $this->uri_callback = $callback;
831     return $this;
832   }
833
834   /**
835    * {@inheritdoc}
836    */
837   public function getGroup() {
838     return $this->group;
839   }
840
841   /**
842    * {@inheritdoc}
843    */
844   public function getGroupLabel() {
845     return !empty($this->group_label) ? $this->group_label : $this->t('Other', [], ['context' => 'Entity type group']);
846   }
847
848   /**
849    * {@inheritdoc}
850    */
851   public function getListCacheContexts() {
852     return $this->list_cache_contexts;
853   }
854
855   /**
856    * {@inheritdoc}
857    */
858   public function getListCacheTags() {
859     return $this->list_cache_tags;
860   }
861
862   /**
863    * {@inheritdoc}
864    */
865   public function getConfigDependencyKey() {
866     // Return 'content' for the default implementation as important distinction
867     // is that dependencies on other configuration entities are hard
868     // dependencies and have to exist before creating the dependent entity.
869     return 'content';
870   }
871
872   /**
873    * {@inheritdoc}
874    */
875   public function isCommonReferenceTarget() {
876     return $this->common_reference_target;
877   }
878
879   /**
880    * {@inheritdoc}
881    */
882   public function getConstraints() {
883     return $this->constraints;
884   }
885
886   /**
887    * {@inheritdoc}
888    */
889   public function setConstraints(array $constraints) {
890     $this->constraints = $constraints;
891     return $this;
892   }
893
894   /**
895    * {@inheritdoc}
896    */
897   public function addConstraint($constraint_name, $options = NULL) {
898     $this->constraints[$constraint_name] = $options;
899     return $this;
900   }
901
902   /**
903    * {@inheritdoc}
904    */
905   public function getBundleConfigDependency($bundle) {
906     // If this entity type uses entities to manage its bundles then depend on
907     // the bundle entity.
908     if ($bundle_entity_type_id = $this->getBundleEntityType()) {
909       if (!$bundle_entity = \Drupal::entityManager()->getStorage($bundle_entity_type_id)->load($bundle)) {
910         throw new \LogicException(sprintf('Missing bundle entity, entity type %s, entity id %s.', $bundle_entity_type_id, $bundle));
911       }
912       $config_dependency = [
913         'type' => 'config',
914         'name' => $bundle_entity->getConfigDependencyName(),
915       ];
916     }
917     else {
918       // Depend on the provider of the entity type.
919       $config_dependency = [
920         'type' => 'module',
921         'name' => $this->getProvider(),
922       ];
923     }
924
925     return $config_dependency;
926   }
927
928 }