c3404b6fa7fc7faeafe0fbc2374409a7198bb707
[yaffs-website] / web / core / lib / Drupal / Core / Entity / EntityConstraintViolationList.php
1 <?php
2
3 namespace Drupal\Core\Entity;
4
5 use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
6 use Drupal\Core\Session\AccountInterface;
7 use Drupal\Core\StringTranslation\StringTranslationTrait;
8 use Symfony\Component\Validator\ConstraintViolation;
9 use Symfony\Component\Validator\ConstraintViolationInterface;
10 use Symfony\Component\Validator\ConstraintViolationList;
11
12 /**
13  * Implements an entity constraint violation list.
14  */
15 class EntityConstraintViolationList extends ConstraintViolationList implements EntityConstraintViolationListInterface {
16
17   use StringTranslationTrait;
18
19   /**
20    * The entity that has been validated.
21    *
22    * @var \Drupal\Core\Entity\FieldableEntityInterface
23    */
24   protected $entity;
25
26   /**
27    * Violations offsets of entity level violations.
28    *
29    * @var int[]|null
30    */
31   protected $entityViolationOffsets;
32
33   /**
34    * Violation offsets grouped by field.
35    *
36    * Keys are field names, values are arrays of violation offsets.
37    *
38    * @var array[]|null
39    */
40   protected $violationOffsetsByField;
41
42   /**
43    * {@inheritdoc}
44    *
45    * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
46    *   The entity that has been validated.
47    * @param array $violations
48    *   The array of violations.
49    */
50   public function __construct(FieldableEntityInterface $entity, array $violations = []) {
51     parent::__construct($violations);
52     $this->entity = $entity;
53   }
54
55   /**
56    * Groups violation offsets by field and entity level.
57    *
58    * Sets the $violationOffsetsByField and $entityViolationOffsets properties.
59    */
60   protected function groupViolationOffsets() {
61     if (!isset($this->violationOffsetsByField)) {
62       $this->violationOffsetsByField = [];
63       $this->entityViolationOffsets = [];
64       foreach ($this as $offset => $violation) {
65         if ($path = $violation->getPropertyPath()) {
66           // An example of $path might be 'title.0.value'.
67           list($field_name) = explode('.', $path, 2);
68           if ($this->entity->hasField($field_name)) {
69             $this->violationOffsetsByField[$field_name][$offset] = $offset;
70           }
71         }
72         else {
73           $this->entityViolationOffsets[$offset] = $offset;
74         }
75       }
76     }
77   }
78
79   /**
80    * {@inheritdoc}
81    */
82   public function getEntityViolations() {
83     $this->groupViolationOffsets();
84     $violations = [];
85     foreach ($this->entityViolationOffsets as $offset) {
86       $violations[] = $this->get($offset);
87     }
88     return new static($this->entity, $violations);
89   }
90
91   /**
92    * {@inheritdoc}
93    */
94   public function getByField($field_name) {
95     return $this->getByFields([$field_name]);
96   }
97
98   /**
99    * {@inheritdoc}
100    */
101   public function getByFields(array $field_names) {
102     $this->groupViolationOffsets();
103     $violations = [];
104     foreach (array_intersect_key($this->violationOffsetsByField, array_flip($field_names)) as $field_name => $offsets) {
105       foreach ($offsets as $offset) {
106         $violations[] = $this->get($offset);
107       }
108     }
109     return new static($this->entity, $violations);
110   }
111
112   /**
113    * {@inheritdoc}
114    */
115   public function filterByFields(array $field_names) {
116     $this->groupViolationOffsets();
117     $new_violations = [];
118     foreach (array_intersect_key($this->violationOffsetsByField, array_flip($field_names)) as $field_name => $offsets) {
119       foreach ($offsets as $offset) {
120         $violation = $this->get($offset);
121         // Take care of composite field violations and re-map them to some
122         // covered field if necessary.
123         if ($violation->getConstraint() instanceof CompositeConstraintBase) {
124           $covered_fields = $violation->getConstraint()->coversFields();
125
126           // Keep the composite field if it covers some remaining field and put
127           // a violation on some other covered field instead.
128           if ($remaining_fields = array_diff($covered_fields, $field_names)) {
129             $message_params = ['%field_name' => $field_name];
130             $violation = new ConstraintViolation(
131               $this->t('The validation failed because the value conflicts with the value in %field_name, which you cannot access.', $message_params),
132               'The validation failed because the value conflicts with the value in %field_name, which you cannot access.',
133               $message_params,
134               $violation->getRoot(),
135               reset($remaining_fields),
136               $violation->getInvalidValue(),
137               $violation->getPlural(),
138               $violation->getCode(),
139               $violation->getConstraint(),
140               $violation->getCause()
141             );
142             $new_violations[] = $violation;
143           }
144         }
145
146         $this->remove($offset);
147       }
148     }
149     foreach ($new_violations as $violation) {
150       $this->add($violation);
151     }
152     return $this;
153   }
154
155   /**
156    * {@inheritdoc}
157    */
158   public function filterByFieldAccess(AccountInterface $account = NULL) {
159     $filtered_fields = [];
160     foreach ($this->getFieldNames() as $field_name) {
161       if (!$this->entity->get($field_name)->access('edit', $account)) {
162         $filtered_fields[] = $field_name;
163       }
164     }
165     return $this->filterByFields($filtered_fields);
166   }
167
168   /**
169    * {@inheritdoc}
170    */
171   public function getFieldNames() {
172     $this->groupViolationOffsets();
173     return array_keys($this->violationOffsetsByField);
174   }
175
176   /**
177    * {@inheritdoc}
178    */
179   public function getEntity() {
180     return $this->entity;
181   }
182
183   /**
184    * {@inheritdoc}
185    */
186   public function add(ConstraintViolationInterface $violation) {
187     parent::add($violation);
188     $this->violationOffsetsByField = NULL;
189     $this->entityViolationOffsets = NULL;
190   }
191
192   /**
193    * {@inheritdoc}
194    */
195   public function remove($offset) {
196     parent::remove($offset);
197     $this->violationOffsetsByField = NULL;
198     $this->entityViolationOffsets = NULL;
199   }
200
201   /**
202    * {@inheritdoc}
203    */
204   public function set($offset, ConstraintViolationInterface $violation) {
205     parent::set($offset, $violation);
206     $this->violationOffsetsByField = NULL;
207     $this->entityViolationOffsets = NULL;
208   }
209
210 }