55336d55c98cb30db0ec2fb0a9b08c802bc6a8f4
[yaffs-website] / web / core / lib / Drupal / Core / Plugin / Context / ContextDefinition.php
1 <?php
2
3 namespace Drupal\Core\Plugin\Context;
4
5 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
6 use Drupal\Core\Entity\ContentEntityStorageInterface;
7 use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
8 use Drupal\Core\Entity\Plugin\Validation\Constraint\BundleConstraint;
9 use Drupal\Core\Entity\Plugin\Validation\Constraint\EntityTypeConstraint;
10 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
11 use Drupal\Core\TypedData\TypedDataTrait;
12
13 /**
14  * Defines a class for context definitions.
15  */
16 class ContextDefinition implements ContextDefinitionInterface {
17
18   use DependencySerializationTrait;
19
20   use TypedDataTrait;
21
22   /**
23    * The data type of the data.
24    *
25    * @var string
26    *   The data type.
27    */
28   protected $dataType;
29
30   /**
31    * The human-readable label.
32    *
33    * @var string
34    *   The label.
35    */
36   protected $label;
37
38   /**
39    * The human-readable description.
40    *
41    * @var string|null
42    *   The description, or NULL if no description is available.
43    */
44   protected $description;
45
46   /**
47    * Whether the data is multi-valued, i.e. a list of data items.
48    *
49    * @var bool
50    */
51   protected $isMultiple = FALSE;
52
53   /**
54    * Determines whether a data value is required.
55    *
56    * @var bool
57    *   Whether a data value is required.
58    */
59   protected $isRequired = TRUE;
60
61   /**
62    * The default value.
63    *
64    * @var mixed
65    */
66   protected $defaultValue;
67
68   /**
69    * An array of constraints.
70    *
71    * @var array[]
72    */
73   protected $constraints = [];
74
75   /**
76    * Creates a new context definition.
77    *
78    * @param string $data_type
79    *   The data type for which to create the context definition. Defaults to
80    *   'any'.
81    *
82    * @return static
83    *   The created context definition object.
84    */
85   public static function create($data_type = 'any') {
86     return new static(
87       $data_type
88     );
89   }
90
91   /**
92    * Constructs a new context definition object.
93    *
94    * @param string $data_type
95    *   The required data type.
96    * @param string|null $label
97    *   The label of this context definition for the UI.
98    * @param bool $required
99    *   Whether the context definition is required.
100    * @param bool $multiple
101    *   Whether the context definition is multivalue.
102    * @param string|null $description
103    *   The description of this context definition for the UI.
104    * @param mixed $default_value
105    *   The default value of this definition.
106    */
107   public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL, $default_value = NULL) {
108     $this->dataType = $data_type;
109     $this->label = $label;
110     $this->isRequired = $required;
111     $this->isMultiple = $multiple;
112     $this->description = $description;
113     $this->defaultValue = $default_value;
114   }
115
116   /**
117    * {@inheritdoc}
118    */
119   public function getDataType() {
120     return $this->dataType;
121   }
122
123   /**
124    * {@inheritdoc}
125    */
126   public function setDataType($data_type) {
127     $this->dataType = $data_type;
128     return $this;
129   }
130
131   /**
132    * {@inheritdoc}
133    */
134   public function getLabel() {
135     return $this->label;
136   }
137
138   /**
139    * {@inheritdoc}
140    */
141   public function setLabel($label) {
142     $this->label = $label;
143     return $this;
144   }
145
146   /**
147    * {@inheritdoc}
148    */
149   public function getDescription() {
150     return $this->description;
151   }
152
153   /**
154    * {@inheritdoc}
155    */
156   public function setDescription($description) {
157     $this->description = $description;
158     return $this;
159   }
160
161   /**
162    * {@inheritdoc}
163    */
164   public function isMultiple() {
165     return $this->isMultiple;
166   }
167
168   /**
169    * {@inheritdoc}
170    */
171   public function setMultiple($multiple = TRUE) {
172     $this->isMultiple = $multiple;
173     return $this;
174   }
175
176   /**
177    * {@inheritdoc}
178    */
179   public function isRequired() {
180     return $this->isRequired;
181   }
182
183   /**
184    * {@inheritdoc}
185    */
186   public function setRequired($required = TRUE) {
187     $this->isRequired = $required;
188     return $this;
189   }
190
191   /**
192    * {@inheritdoc}
193    */
194   public function getDefaultValue() {
195     return $this->defaultValue;
196   }
197
198   /**
199    * {@inheritdoc}
200    */
201   public function setDefaultValue($default_value) {
202     $this->defaultValue = $default_value;
203     return $this;
204   }
205
206   /**
207    * {@inheritdoc}
208    */
209   public function getConstraints() {
210     // @todo Apply defaults.
211     return $this->constraints;
212   }
213
214   /**
215    * {@inheritdoc}
216    */
217   public function getConstraint($constraint_name) {
218     $constraints = $this->getConstraints();
219     return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
220   }
221
222   /**
223    * {@inheritdoc}
224    */
225   public function setConstraints(array $constraints) {
226     $this->constraints = $constraints;
227     return $this;
228   }
229
230   /**
231    * {@inheritdoc}
232    */
233   public function addConstraint($constraint_name, $options = NULL) {
234     $this->constraints[$constraint_name] = $options;
235     return $this;
236   }
237
238   /**
239    * {@inheritdoc}
240    */
241   public function getDataDefinition() {
242     if ($this->isMultiple()) {
243       $definition = $this->getTypedDataManager()->createListDataDefinition($this->getDataType());
244     }
245     else {
246       $definition = $this->getTypedDataManager()->createDataDefinition($this->getDataType());
247     }
248
249     if (!$definition) {
250       throw new \Exception("The data type '{$this->getDataType()}' is invalid");
251     }
252     $definition->setLabel($this->getLabel())
253       ->setDescription($this->getDescription())
254       ->setRequired($this->isRequired());
255     $constraints = $definition->getConstraints() + $this->getConstraints();
256     $definition->setConstraints($constraints);
257     return $definition;
258   }
259
260   /**
261    * {@inheritdoc}
262    */
263   public function isSatisfiedBy(ContextInterface $context) {
264     $definition = $context->getContextDefinition();
265     // If the data types do not match, this context is invalid unless the
266     // expected data type is any, which means all data types are supported.
267     if ($this->getDataType() != 'any' && $definition->getDataType() != $this->getDataType()) {
268       return FALSE;
269     }
270
271     // Get the value for this context, either directly if possible or by
272     // introspecting the definition.
273     if ($context->hasContextValue()) {
274       $values = [$context->getContextData()];
275     }
276     elseif ($definition instanceof static) {
277       $values = $definition->getSampleValues();
278     }
279     else {
280       $values = [];
281     }
282
283     $validator = $this->getTypedDataManager()->getValidator();
284     foreach ($values as $value) {
285       $violations = $validator->validate($value, array_values($this->getConstraintObjects()));
286       // If a value has no violations then the requirement is satisfied.
287       if (!$violations->count()) {
288         return TRUE;
289       }
290     }
291
292     return FALSE;
293   }
294
295   /**
296    * Returns typed data objects representing this context definition.
297    *
298    * This should return as many objects as needed to reflect the variations of
299    * the constraints it supports.
300    *
301    * @yield \Drupal\Core\TypedData\TypedDataInterface
302    *   The set of typed data object.
303    */
304   protected function getSampleValues() {
305     // @todo Move the entity specific logic out of this class in
306     //   https://www.drupal.org/node/2932462.
307     // Get the constraints from the context's definition.
308     $constraints = $this->getConstraintObjects();
309     // If constraints include EntityType, we generate an entity or adapter.
310     if (!empty($constraints['EntityType']) && $constraints['EntityType'] instanceof EntityTypeConstraint) {
311       $entity_type_manager = \Drupal::entityTypeManager();
312       $entity_type_id = $constraints['EntityType']->type;
313       $storage = $entity_type_manager->getStorage($entity_type_id);
314       // If the storage can generate a sample entity we might delegate to that.
315       if ($storage instanceof ContentEntityStorageInterface) {
316         if (!empty($constraints['Bundle']) && $constraints['Bundle'] instanceof BundleConstraint) {
317           foreach ($constraints['Bundle']->bundle as $bundle) {
318             // We have a bundle, we are bundleable and we can generate a sample.
319             yield EntityAdapter::createFromEntity($storage->createWithSampleValues($bundle));
320           }
321           return;
322         }
323       }
324
325       // Either no bundle, or not bundleable, so generate an entity adapter.
326       $definition = EntityDataDefinition::create($entity_type_id);
327       yield new EntityAdapter($definition);
328       return;
329     }
330
331     // No entity related constraints, so generate a basic typed data object.
332     yield $this->getTypedDataManager()->create($this->getDataDefinition());
333   }
334
335   /**
336    * Extracts an array of constraints for a context definition object.
337    *
338    * @return \Symfony\Component\Validator\Constraint[]
339    *   A list of applied constraints for the context definition.
340    */
341   protected function getConstraintObjects() {
342     $constraint_definitions = $this->getConstraints();
343
344     // @todo Move the entity specific logic out of this class in
345     //   https://www.drupal.org/node/2932462.
346     // If the data type is an entity, manually add one to the constraints array.
347     if (strpos($this->getDataType(), 'entity:') === 0) {
348       $entity_type_id = substr($this->getDataType(), 7);
349       $constraint_definitions['EntityType'] = ['type' => $entity_type_id];
350     }
351
352     $validation_constraint_manager = $this->getTypedDataManager()->getValidationConstraintManager();
353     $constraints = [];
354     foreach ($constraint_definitions as $constraint_name => $constraint_definition) {
355       $constraints[$constraint_name] = $validation_constraint_manager->create($constraint_name, $constraint_definition);
356     }
357
358     return $constraints;
359   }
360
361 }