3 namespace Drupal\Core\Plugin\Context;
5 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
6 use Drupal\Core\TypedData\TypedDataTrait;
9 * Defines a class for context definitions.
11 class ContextDefinition implements ContextDefinitionInterface {
13 use DependencySerializationTrait {
14 __sleep as traitSleep;
15 __wakeup as traitWakeup;
21 * The data type of the data.
29 * The human-readable label.
37 * The human-readable description.
40 * The description, or NULL if no description is available.
42 protected $description;
45 * Whether the data is multi-valued, i.e. a list of data items.
49 protected $isMultiple = FALSE;
52 * Determines whether a data value is required.
55 * Whether a data value is required.
57 protected $isRequired = TRUE;
64 protected $defaultValue;
67 * An array of constraints.
71 protected $constraints = [];
74 * An EntityContextDefinition instance, for backwards compatibility.
76 * If this context is created with a data type that starts with 'entity:',
77 * this property will be an instance of EntityContextDefinition, and certain
78 * methods of this object will delegate to their overridden counterparts in
79 * $this->entityContextDefinition.
81 * This property should be kept private so that it is only accessible to this
82 * class for backwards compatibility reasons. It will be removed in Drupal 9.
85 * Constructing a context definition for an entity type (i.e., the data type
86 * begins with 'entity:') is deprecated in Drupal 8.6.0. Instead, use
87 * the static factory methods of EntityContextDefinition to create context
88 * definitions for entity types, or the static ::create() method of this
89 * class for any other data type. See https://www.drupal.org/node/2976400
90 * for more information.
92 * @see ::__construct()
95 * @see ::getConstraintObjects()
96 * @see ::getSampleValues()
97 * @see ::initializeEntityContextDefinition()
98 * @see https://www.drupal.org/node/2932462
99 * @see https://www.drupal.org/node/2976400
101 * @var \Drupal\Core\Plugin\Context\EntityContextDefinition
103 private $entityContextDefinition;
106 * Creates a new context definition.
108 * @param string $data_type
109 * The data type for which to create the context definition. Defaults to
113 * The created context definition object.
115 public static function create($data_type = 'any') {
122 * Constructs a new context definition object.
124 * @param string $data_type
125 * The required data type.
126 * @param string|null $label
127 * The label of this context definition for the UI.
128 * @param bool $required
129 * Whether the context definition is required.
130 * @param bool $multiple
131 * Whether the context definition is multivalue.
132 * @param string|null $description
133 * The description of this context definition for the UI.
134 * @param mixed $default_value
135 * The default value of this definition.
137 public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL, $default_value = NULL) {
138 $this->dataType = $data_type;
139 $this->label = $label;
140 $this->isRequired = $required;
141 $this->isMultiple = $multiple;
142 $this->description = $description;
143 $this->defaultValue = $default_value;
145 if (strpos($data_type, 'entity:') === 0 && !($this instanceof EntityContextDefinition)) {
146 @trigger_error('Constructing a ContextDefinition object for an entity type is deprecated in Drupal 8.6.0. Use ' . __NAMESPACE__ . '\EntityContextDefinition instead. See https://www.drupal.org/node/2976400 for more information.', E_USER_DEPRECATED);
147 $this->initializeEntityContextDefinition();
154 public function getDataType() {
155 return $this->dataType;
161 public function setDataType($data_type) {
162 $this->dataType = $data_type;
169 public function getLabel() {
176 public function setLabel($label) {
177 $this->label = $label;
184 public function getDescription() {
185 return $this->description;
191 public function setDescription($description) {
192 $this->description = $description;
199 public function isMultiple() {
200 return $this->isMultiple;
206 public function setMultiple($multiple = TRUE) {
207 $this->isMultiple = $multiple;
214 public function isRequired() {
215 return $this->isRequired;
221 public function setRequired($required = TRUE) {
222 $this->isRequired = $required;
229 public function getDefaultValue() {
230 return $this->defaultValue;
236 public function setDefaultValue($default_value) {
237 $this->defaultValue = $default_value;
244 public function getConstraints() {
245 // If the backwards compatibility layer is present, delegate to that.
246 if ($this->entityContextDefinition) {
247 return $this->entityContextDefinition->getConstraints();
250 // @todo Apply defaults.
251 return $this->constraints;
257 public function getConstraint($constraint_name) {
258 // If the backwards compatibility layer is present, delegate to that.
259 if ($this->entityContextDefinition) {
260 return $this->entityContextDefinition->getConstraint($constraint_name);
263 $constraints = $this->getConstraints();
264 return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
270 public function setConstraints(array $constraints) {
271 // If the backwards compatibility layer is present, delegate to that.
272 if ($this->entityContextDefinition) {
273 $this->entityContextDefinition->setConstraint();
276 $this->constraints = $constraints;
283 public function addConstraint($constraint_name, $options = NULL) {
284 // If the backwards compatibility layer is present, delegate to that.
285 if ($this->entityContextDefinition) {
286 $this->entityContextDefinition->addConstraint($constraint_name, $options);
289 $this->constraints[$constraint_name] = $options;
296 public function getDataDefinition() {
297 if ($this->isMultiple()) {
298 $definition = $this->getTypedDataManager()->createListDataDefinition($this->getDataType());
301 $definition = $this->getTypedDataManager()->createDataDefinition($this->getDataType());
305 throw new \Exception("The data type '{$this->getDataType()}' is invalid");
307 $definition->setLabel($this->getLabel())
308 ->setDescription($this->getDescription())
309 ->setRequired($this->isRequired());
310 $constraints = $definition->getConstraints() + $this->getConstraints();
311 $definition->setConstraints($constraints);
318 public function isSatisfiedBy(ContextInterface $context) {
319 $definition = $context->getContextDefinition();
320 // If the data types do not match, this context is invalid unless the
321 // expected data type is any, which means all data types are supported.
322 if ($this->getDataType() != 'any' && $definition->getDataType() != $this->getDataType()) {
326 // Get the value for this context, either directly if possible or by
327 // introspecting the definition.
328 if ($context->hasContextValue()) {
329 $values = [$context->getContextData()];
331 elseif ($definition instanceof self) {
332 if ($this->entityContextDefinition) {
333 $values = $this->entityContextDefinition->getSampleValues();
336 $values = $definition->getSampleValues();
343 $validator = $this->getTypedDataManager()->getValidator();
344 foreach ($values as $value) {
345 $constraints = array_values($this->getConstraintObjects());
346 $violations = $validator->validate($value, $constraints);
347 foreach ($violations as $delta => $violation) {
348 // Remove any violation that does not correspond to the constraints.
349 if (!in_array($violation->getConstraint(), $constraints)) {
350 $violations->remove($delta);
353 // If a value has no violations then the requirement is satisfied.
354 if (!$violations->count()) {
363 * Returns typed data objects representing this context definition.
365 * This should return as many objects as needed to reflect the variations of
366 * the constraints it supports.
368 * @yield \Drupal\Core\TypedData\TypedDataInterface
369 * The set of typed data object.
371 protected function getSampleValues() {
372 yield $this->getTypedDataManager()->create($this->getDataDefinition());
376 * Extracts an array of constraints for a context definition object.
378 * @return \Symfony\Component\Validator\Constraint[]
379 * A list of applied constraints for the context definition.
381 protected function getConstraintObjects() {
382 // If the backwards compatibility layer is present, delegate to that.
383 if ($this->entityContextDefinition) {
384 return $this->entityContextDefinition->getConstraintObjects();
387 $constraint_definitions = $this->getConstraints();
389 $validation_constraint_manager = $this->getTypedDataManager()->getValidationConstraintManager();
391 foreach ($constraint_definitions as $constraint_name => $constraint_definition) {
392 $constraints[$constraint_name] = $validation_constraint_manager->create($constraint_name, $constraint_definition);
399 * Implements magic __sleep() method.
401 public function __sleep() {
402 return array_diff($this->traitSleep(), ['entityContextDefinition']);
406 * Implements magic __wakeup() method.
408 public function __wakeup() {
409 $this->traitWakeup();
411 if (strpos($this->getDataType(), 'entity:') === 0) {
412 $this->initializeEntityContextDefinition();
417 * Initializes $this->entityContextDefinition for backwards compatibility.
419 * This method should be kept private so that it is only accessible to this
420 * class for backwards compatibility reasons. It will be removed in Drupal 9.
424 private function initializeEntityContextDefinition() {
425 $this->entityContextDefinition = EntityContextDefinition::create()
426 ->setDataType($this->getDataType())
427 ->setLabel($this->getLabel())
428 ->setRequired($this->isRequired())
429 ->setMultiple($this->isMultiple())
430 ->setDescription($this->getDescription())
431 ->setConstraints($this->getConstraints())
432 ->setDefaultValue($this->getDefaultValue());