Version 1
[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 entities of this type have multilingual support.
159    *
160    * @var bool
161    */
162   protected $translatable = FALSE;
163
164   /**
165    * Indicates whether the revision form fields should be added to the form.
166    *
167    * @var bool
168    */
169   protected $show_revision_ui = FALSE;
170
171   /**
172    * The human-readable name of the type.
173    *
174    * @var string
175    *
176    * @see \Drupal\Core\Entity\EntityTypeInterface::getLabel()
177    */
178   protected $label = '';
179
180   /**
181    * The human-readable label for a collection of entities of the type.
182    *
183    * @var string
184    *
185    * @see \Drupal\Core\Entity\EntityTypeInterface::getCollectionLabel()
186    */
187   protected $label_collection = '';
188
189   /**
190    * The indefinite singular name of the type.
191    *
192    * @var string
193    *
194    * @see \Drupal\Core\Entity\EntityTypeInterface::getSingularLabel()
195    */
196   protected $label_singular = '';
197
198   /**
199    * The indefinite plural name of the type.
200    *
201    * @var string
202    *
203    * @see \Drupal\Core\Entity\EntityTypeInterface::getPluralLabel()
204    */
205   protected $label_plural = '';
206
207   /**
208    * A definite singular/plural name of the type.
209    *
210    * Needed keys: "singular" and "plural".
211    *
212    * @var string[]
213    *
214    * @see \Drupal\Core\Entity\EntityTypeInterface::getCountLabel()
215    */
216   protected $label_count = [];
217
218   /**
219    * A callable that can be used to provide the entity URI.
220    *
221    * @var callable|null
222    */
223   protected $uri_callback = NULL;
224
225   /**
226    * The machine name of the entity type group.
227    */
228   protected $group;
229
230   /**
231    * The human-readable name of the entity type group.
232    */
233   protected $group_label;
234
235   /**
236    * The route name used by field UI to attach its management pages.
237    *
238    * @var string
239    */
240   protected $field_ui_base_route;
241
242   /**
243    * Indicates whether this entity type is commonly used as a reference target.
244    *
245    * This is used by the Entity reference field to promote an entity type in the
246    * add new field select list in Field UI.
247    *
248    * @var bool
249    */
250   protected $common_reference_target = FALSE;
251
252   /**
253    * The list cache contexts for this entity type.
254    *
255    * @var string[]
256    */
257   protected $list_cache_contexts = [];
258
259   /**
260    * The list cache tags for this entity type.
261    *
262    * @var string[]
263    */
264   protected $list_cache_tags = [];
265
266   /**
267    * Entity constraint definitions.
268    *
269    * @var array[]
270    */
271   protected $constraints = [];
272
273   /**
274    * Any additional properties and values.
275    *
276    * @var array
277    */
278   protected $additional = [];
279
280   /**
281    * Constructs a new EntityType.
282    *
283    * @param array $definition
284    *   An array of values from the annotation.
285    *
286    * @throws \Drupal\Core\Entity\Exception\EntityTypeIdLengthException
287    *   Thrown when attempting to instantiate an entity type with too long ID.
288    */
289   public function __construct($definition) {
290     // Throw an exception if the entity type ID is longer than 32 characters.
291     if (Unicode::strlen($definition['id']) > static::ID_MAX_LENGTH) {
292       throw new EntityTypeIdLengthException('Attempt to create an entity type with an ID longer than ' . static::ID_MAX_LENGTH . " characters: {$definition['id']}.");
293     }
294
295     foreach ($definition as $property => $value) {
296       $this->set($property, $value);
297     }
298
299     // Ensure defaults.
300     $this->entity_keys += [
301       'revision' => '',
302       'bundle' => '',
303       'langcode' => '',
304       'default_langcode' => 'default_langcode',
305     ];
306     $this->handlers += [
307       'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
308     ];
309     if (isset($this->handlers['storage'])) {
310       $this->checkStorageClass($this->handlers['storage']);
311     }
312
313     // Automatically add the EntityChanged constraint if the entity type tracks
314     // the changed time.
315     if ($this->entityClassImplements(EntityChangedInterface::class) ) {
316       $this->addConstraint('EntityChanged');
317     }
318
319     // Ensure a default list cache tag is set.
320     if (empty($this->list_cache_tags)) {
321       $this->list_cache_tags = [$definition['id'] . '_list'];
322     }
323   }
324
325   /**
326    * {@inheritdoc}
327    */
328   public function get($property) {
329     if (property_exists($this, $property)) {
330       $value = isset($this->{$property}) ? $this->{$property} : NULL;
331     }
332     else {
333       $value = isset($this->additional[$property]) ? $this->additional[$property] : NULL;
334     }
335     return $value;
336   }
337
338   /**
339    * {@inheritdoc}
340    */
341   public function set($property, $value) {
342     if (property_exists($this, $property)) {
343       $this->{$property} = $value;
344     }
345     else {
346       $this->additional[$property] = $value;
347     }
348     return $this;
349   }
350
351   /**
352    * {@inheritdoc}
353    */
354   public function isStaticallyCacheable() {
355     return $this->static_cache;
356   }
357
358   /**
359    * {@inheritdoc}
360    */
361   public function isRenderCacheable() {
362     return $this->render_cache;
363   }
364
365   /**
366    * {@inheritdoc}
367    */
368   public function isPersistentlyCacheable() {
369     return $this->persistent_cache;
370   }
371
372   /**
373    * {@inheritdoc}
374    */
375   public function getKeys() {
376     return $this->entity_keys;
377   }
378
379   /**
380    * {@inheritdoc}
381    */
382   public function getKey($key) {
383     $keys = $this->getKeys();
384     return isset($keys[$key]) ? $keys[$key] : FALSE;
385   }
386
387   /**
388    * {@inheritdoc}
389    */
390   public function hasKey($key) {
391     $keys = $this->getKeys();
392     return !empty($keys[$key]);
393   }
394
395   /**
396    * {@inheritdoc}
397    */
398   public function getOriginalClass() {
399     return $this->originalClass ?: $this->class;
400   }
401
402   /**
403    * {@inheritdoc}
404    */
405   public function setClass($class) {
406     if (!$this->originalClass && $this->class) {
407       // If the original class is currently not set, set it to the current
408       // class, assume that is the original class name.
409       $this->originalClass = $this->class;
410     }
411
412     return parent::setClass($class);
413   }
414
415   /**
416    * {@inheritdoc}
417    */
418   public function entityClassImplements($interface) {
419     return is_subclass_of($this->getClass(), $interface);
420   }
421
422   /**
423    * {@inheritdoc}
424    */
425   public function isSubclassOf($class) {
426     return $this->entityClassImplements($class);
427   }
428
429   /**
430    * {@inheritdoc}
431    */
432   public function getHandlerClasses() {
433     return $this->handlers;
434   }
435
436   /**
437    * {@inheritdoc}
438    */
439   public function getHandlerClass($handler_type, $nested = FALSE) {
440     if ($this->hasHandlerClass($handler_type, $nested)) {
441       $handlers = $this->getHandlerClasses();
442       return $nested ? $handlers[$handler_type][$nested] : $handlers[$handler_type];
443     }
444   }
445
446   /**
447    * {@inheritdoc}
448    */
449   public function setHandlerClass($handler_type, $value) {
450     $this->handlers[$handler_type] = $value;
451     return $this;
452   }
453
454   /**
455    * {@inheritdoc}
456    */
457   public function hasHandlerClass($handler_type, $nested = FALSE) {
458     $handlers = $this->getHandlerClasses();
459     if (!isset($handlers[$handler_type]) || ($nested && !isset($handlers[$handler_type][$nested]))) {
460       return FALSE;
461     }
462     $handler = $handlers[$handler_type];
463     if ($nested) {
464       $handler = $handler[$nested];
465     }
466     return class_exists($handler);
467   }
468
469   /**
470    * {@inheritdoc}
471    */
472   public function getStorageClass() {
473     return $this->getHandlerClass('storage');
474   }
475
476   /**
477    * {@inheritdoc}
478    */
479   public function setStorageClass($class) {
480     $this->checkStorageClass($class);
481     $this->handlers['storage'] = $class;
482     return $this;
483   }
484
485   /**
486    * Checks that the provided class is compatible with the current entity type.
487    *
488    * @param string $class
489    *   The class to check.
490    */
491   protected function checkStorageClass($class) {
492     // Nothing to check by default.
493   }
494
495   /**
496    * {@inheritdoc}
497    */
498   public function getFormClass($operation) {
499     return $this->getHandlerClass('form', $operation);
500   }
501
502   /**
503    * {@inheritdoc}
504    */
505   public function setFormClass($operation, $class) {
506     $this->handlers['form'][$operation] = $class;
507     return $this;
508   }
509
510   /**
511    * {@inheritdoc}
512    */
513   public function hasFormClasses() {
514     return !empty($this->handlers['form']);
515   }
516
517   /**
518    * {@inheritdoc}
519    */
520   public function hasRouteProviders() {
521     return !empty($this->handlers['route_provider']);
522   }
523
524   /**
525    * {@inheritdoc}
526    */
527   public function getListBuilderClass() {
528     return $this->getHandlerClass('list_builder');
529   }
530
531   /**
532    * {@inheritdoc}
533    */
534   public function setListBuilderClass($class) {
535     $this->handlers['list_builder'] = $class;
536     return $this;
537   }
538
539   /**
540    * {@inheritdoc}
541    */
542   public function hasListBuilderClass() {
543     return $this->hasHandlerClass('list_builder');
544   }
545
546   /**
547    * {@inheritdoc}
548    */
549   public function getViewBuilderClass() {
550     return $this->getHandlerClass('view_builder');
551   }
552
553   /**
554    * {@inheritdoc}
555    */
556   public function setViewBuilderClass($class) {
557     $this->handlers['view_builder'] = $class;
558     return $this;
559   }
560
561   /**
562    * {@inheritdoc}
563    */
564   public function hasViewBuilderClass() {
565     return $this->hasHandlerClass('view_builder');
566   }
567
568   /**
569    * {@inheritdoc}
570    */
571   public function getRouteProviderClasses() {
572     return !empty($this->handlers['route_provider']) ? $this->handlers['route_provider'] : [];
573   }
574
575   /**
576    * {@inheritdoc}
577    */
578   public function getAccessControlClass() {
579     return $this->getHandlerClass('access');
580   }
581
582   /**
583    * {@inheritdoc}
584    */
585   public function setAccessClass($class) {
586     $this->handlers['access'] = $class;
587     return $this;
588   }
589
590   /**
591    * {@inheritdoc}
592    */
593   public function getAdminPermission() {
594     return $this->admin_permission ?: FALSE;
595   }
596
597   /**
598    * {@inheritdoc}
599    */
600   public function getPermissionGranularity() {
601     return $this->permission_granularity;
602   }
603
604   /**
605    * {@inheritdoc}
606    */
607   public function getLinkTemplates() {
608     return $this->links;
609   }
610
611   /**
612    * {@inheritdoc}
613    */
614   public function getLinkTemplate($key) {
615     $links = $this->getLinkTemplates();
616     return isset($links[$key]) ? $links[$key] : FALSE;
617   }
618
619   /**
620    * {@inheritdoc}
621    */
622   public function hasLinkTemplate($key) {
623     $links = $this->getLinkTemplates();
624     return isset($links[$key]);
625   }
626
627   /**
628    * {@inheritdoc}
629    */
630   public function setLinkTemplate($key, $path) {
631     if ($path[0] !== '/') {
632       throw new \InvalidArgumentException('Link templates accepts paths, which have to start with a leading slash.');
633     }
634
635     $this->links[$key] = $path;
636     return $this;
637   }
638
639   /**
640    * {@inheritdoc}
641    */
642   public function getLabelCallback() {
643     return $this->label_callback;
644   }
645
646   /**
647    * {@inheritdoc}
648    */
649   public function setLabelCallback($callback) {
650     $this->label_callback = $callback;
651     return $this;
652   }
653
654   /**
655    * {@inheritdoc}
656    */
657   public function hasLabelCallback() {
658     return isset($this->label_callback);
659   }
660
661   /**
662    * {@inheritdoc}
663    */
664   public function getBundleEntityType() {
665     return $this->bundle_entity_type;
666   }
667
668   /**
669    * {@inheritdoc}
670    */
671   public function getBundleOf() {
672     return $this->bundle_of;
673   }
674
675   /**
676    * {@inheritdoc}
677    */
678   public function getBundleLabel() {
679     return (string) $this->bundle_label;
680   }
681
682   /**
683    * {@inheritdoc}
684    */
685   public function getBaseTable() {
686     return $this->base_table;
687   }
688
689   /**
690    * {@inheritdoc}
691    */
692   public function showRevisionUi() {
693     return $this->isRevisionable() && $this->show_revision_ui;
694   }
695
696   /**
697    * {@inheritdoc}
698    */
699   public function isTranslatable() {
700     return !empty($this->translatable);
701   }
702
703   /**
704    * {@inheritdoc}
705    */
706   public function isRevisionable() {
707     // Entity types are revisionable if a revision key has been specified.
708     return $this->hasKey('revision');
709   }
710
711   /**
712    * {@inheritdoc}
713    */
714   public function getRevisionDataTable() {
715     return $this->revision_data_table;
716   }
717
718   /**
719    * {@inheritdoc}
720    */
721   public function getRevisionTable() {
722     return $this->revision_table;
723   }
724
725   /**
726    * {@inheritdoc}
727    */
728   public function getDataTable() {
729     return $this->data_table;
730   }
731
732   /**
733    * {@inheritdoc}
734    */
735   public function getLabel() {
736     return $this->label;
737   }
738
739   /**
740    * {@inheritdoc}
741    */
742   public function getLowercaseLabel() {
743     return Unicode::strtolower($this->getLabel());
744   }
745
746   /**
747    * {@inheritdoc}
748    */
749   public function getCollectionLabel() {
750     if (empty($this->label_collection)) {
751       $label = $this->getLabel();
752       $this->label_collection = new TranslatableMarkup('@label entities', ['@label' => $label], [], $this->getStringTranslation());
753     }
754     return $this->label_collection;
755   }
756
757   /**
758    * {@inheritdoc}
759    */
760   public function getSingularLabel() {
761     if (empty($this->label_singular)) {
762       $lowercase_label = $this->getLowercaseLabel();
763       $this->label_singular = $lowercase_label;
764     }
765     return $this->label_singular;
766   }
767
768   /**
769    * {@inheritdoc}
770    */
771   public function getPluralLabel() {
772     if (empty($this->label_plural)) {
773       $lowercase_label = $this->getLowercaseLabel();
774       $this->label_plural = new TranslatableMarkup('@label entities', ['@label' => $lowercase_label], [], $this->getStringTranslation());
775     }
776     return $this->label_plural;
777   }
778
779   /**
780    * {@inheritdoc}
781    */
782   public function getCountLabel($count) {
783     if (empty($this->label_count)) {
784       return $this->formatPlural($count, '@count @label', '@count @label entities', ['@label' => $this->getLowercaseLabel()], ['context' => 'Entity type label']);
785     }
786     $context = isset($this->label_count['context']) ? $this->label_count['context'] : 'Entity type label';
787     return $this->formatPlural($count, $this->label_count['singular'], $this->label_count['plural'], ['context' => $context]);
788   }
789
790   /**
791    * {@inheritdoc}
792    */
793   public function getUriCallback() {
794     return $this->uri_callback;
795   }
796
797   /**
798    * {@inheritdoc}
799    */
800   public function setUriCallback($callback) {
801     $this->uri_callback = $callback;
802     return $this;
803   }
804
805   /**
806    * {@inheritdoc}
807    */
808   public function getGroup() {
809     return $this->group;
810   }
811
812
813   /**
814    * {@inheritdoc}
815    */
816   public function getGroupLabel() {
817     return !empty($this->group_label) ? $this->group_label : $this->t('Other', [], ['context' => 'Entity type group']);
818   }
819
820   /**
821    * {@inheritdoc}
822    */
823   public function getListCacheContexts() {
824     return $this->list_cache_contexts;
825   }
826
827   /**
828    * {@inheritdoc}
829    */
830   public function getListCacheTags() {
831     return $this->list_cache_tags;
832   }
833
834   /**
835    * {@inheritdoc}
836    */
837   public function getConfigDependencyKey() {
838     // Return 'content' for the default implementation as important distinction
839     // is that dependencies on other configuration entities are hard
840     // dependencies and have to exist before creating the dependent entity.
841     return 'content';
842   }
843
844   /**
845    * {@inheritdoc}
846    */
847   public function isCommonReferenceTarget() {
848     return $this->common_reference_target;
849   }
850
851   /**
852    * {@inheritdoc}
853    */
854   public function getConstraints() {
855     return $this->constraints;
856   }
857
858   /**
859    * {@inheritdoc}
860    */
861   public function setConstraints(array $constraints) {
862     $this->constraints = $constraints;
863     return $this;
864   }
865
866   /**
867    * {@inheritdoc}
868    */
869   public function addConstraint($constraint_name, $options = NULL) {
870     $this->constraints[$constraint_name] = $options;
871     return $this;
872   }
873
874   /**
875    * {@inheritdoc}
876    */
877   public function getBundleConfigDependency($bundle) {
878     // If this entity type uses entities to manage its bundles then depend on
879     // the bundle entity.
880     if ($bundle_entity_type_id = $this->getBundleEntityType()) {
881       if (!$bundle_entity = \Drupal::entityManager()->getStorage($bundle_entity_type_id)->load($bundle)) {
882         throw new \LogicException(sprintf('Missing bundle entity, entity type %s, entity id %s.', $bundle_entity_type_id, $bundle));
883       }
884       $config_dependency = [
885         'type' => 'config',
886         'name' => $bundle_entity->getConfigDependencyName(),
887       ];
888     }
889     else {
890       // Depend on the provider of the entity type.
891       $config_dependency = [
892         'type' => 'module',
893         'name' => $this->getProvider(),
894       ];
895     }
896
897     return $config_dependency;
898   }
899
900 }