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