6081a8de16830bb0339599dda15c2cc2657e0fae
[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           // If the first part of the violation property path is not a valid
72           // field name, we're dealing with an entity-level validation.
73           else {
74             $this->entityViolationOffsets[$offset] = $offset;
75           }
76         }
77         else {
78           $this->entityViolationOffsets[$offset] = $offset;
79         }
80       }
81     }
82   }
83
84   /**
85    * {@inheritdoc}
86    */
87   public function getEntityViolations() {
88     $this->groupViolationOffsets();
89     $violations = [];
90     foreach ($this->entityViolationOffsets as $offset) {
91       $violations[] = $this->get($offset);
92     }
93     return new static($this->entity, $violations);
94   }
95
96   /**
97    * {@inheritdoc}
98    */
99   public function getByField($field_name) {
100     return $this->getByFields([$field_name]);
101   }
102
103   /**
104    * {@inheritdoc}
105    */
106   public function getByFields(array $field_names) {
107     $this->groupViolationOffsets();
108     $violations = [];
109     foreach (array_intersect_key($this->violationOffsetsByField, array_flip($field_names)) as $field_name => $offsets) {
110       foreach ($offsets as $offset) {
111         $violations[] = $this->get($offset);
112       }
113     }
114     return new static($this->entity, $violations);
115   }
116
117   /**
118    * {@inheritdoc}
119    */
120   public function filterByFields(array $field_names) {
121     $this->groupViolationOffsets();
122     $new_violations = [];
123     foreach (array_intersect_key($this->violationOffsetsByField, array_flip($field_names)) as $field_name => $offsets) {
124       foreach ($offsets as $offset) {
125         $violation = $this->get($offset);
126         // Take care of composite field violations and re-map them to some
127         // covered field if necessary.
128         if ($violation->getConstraint() instanceof CompositeConstraintBase) {
129           $covered_fields = $violation->getConstraint()->coversFields();
130
131           // Keep the composite field if it covers some remaining field and put
132           // a violation on some other covered field instead.
133           if ($remaining_fields = array_diff($covered_fields, $field_names)) {
134             $message_params = ['%field_name' => $field_name];
135             $violation = new ConstraintViolation(
136               $this->t('The validation failed because the value conflicts with the value in %field_name, which you cannot access.', $message_params),
137               'The validation failed because the value conflicts with the value in %field_name, which you cannot access.',
138               $message_params,
139               $violation->getRoot(),
140               reset($remaining_fields),
141               $violation->getInvalidValue(),
142               $violation->getPlural(),
143               $violation->getCode(),
144               $violation->getConstraint(),
145               $violation->getCause()
146             );
147             $new_violations[] = $violation;
148           }
149         }
150
151         $this->remove($offset);
152       }
153     }
154     foreach ($new_violations as $violation) {
155       $this->add($violation);
156     }
157     return $this;
158   }
159
160   /**
161    * {@inheritdoc}
162    */
163   public function filterByFieldAccess(AccountInterface $account = NULL) {
164     $filtered_fields = [];
165     foreach ($this->getFieldNames() as $field_name) {
166       if (!$this->entity->get($field_name)->access('edit', $account)) {
167         $filtered_fields[] = $field_name;
168       }
169     }
170     return $this->filterByFields($filtered_fields);
171   }
172
173   /**
174    * {@inheritdoc}
175    */
176   public function getFieldNames() {
177     $this->groupViolationOffsets();
178     return array_keys($this->violationOffsetsByField);
179   }
180
181   /**
182    * {@inheritdoc}
183    */
184   public function getEntity() {
185     return $this->entity;
186   }
187
188   /**
189    * {@inheritdoc}
190    */
191   public function add(ConstraintViolationInterface $violation) {
192     parent::add($violation);
193     $this->violationOffsetsByField = NULL;
194     $this->entityViolationOffsets = NULL;
195   }
196
197   /**
198    * {@inheritdoc}
199    */
200   public function remove($offset) {
201     parent::remove($offset);
202     $this->violationOffsetsByField = NULL;
203     $this->entityViolationOffsets = NULL;
204   }
205
206   /**
207    * {@inheritdoc}
208    */
209   public function set($offset, ConstraintViolationInterface $violation) {
210     parent::set($offset, $violation);
211     $this->violationOffsetsByField = NULL;
212     $this->entityViolationOffsets = NULL;
213   }
214
215 }