a95c14b30e3b06d27ac68397f29cb3faeeb20b82
[yaffs-website] / Map.php
1 <?php
2
3 namespace Drupal\Core\TypedData\Plugin\DataType;
4
5 use Drupal\Core\TypedData\TypedData;
6 use Drupal\Core\TypedData\ComplexDataInterface;
7
8 /**
9  * The "map" data type.
10  *
11  * The "map" data type represent a simple complex data type, e.g. for
12  * representing associative arrays. It can also serve as base class for any
13  * complex data type.
14  *
15  * By default there is no metadata for contained properties. Extending classes
16  * may want to override MapDataDefinition::getPropertyDefinitions() to define
17  * it.
18  *
19  * @ingroup typed_data
20  *
21  * @DataType(
22  *   id = "map",
23  *   label = @Translation("Map"),
24  *   definition_class = "\Drupal\Core\TypedData\MapDataDefinition"
25  * )
26  */
27 class Map extends TypedData implements \IteratorAggregate, ComplexDataInterface {
28
29   /**
30    * The data definition.
31    *
32    * @var \Drupal\Core\TypedData\ComplexDataDefinitionInterface
33    */
34   protected $definition;
35
36   /**
37    * An array of values for the contained properties.
38    *
39    * @var array
40    */
41   protected $values = [];
42
43   /**
44    * The array of properties.
45    *
46    * @var \Drupal\Core\TypedData\TypedDataInterface[]
47    */
48   protected $properties = [];
49
50   /**
51    * {@inheritdoc}
52    */
53   public function getValue() {
54     // Update the values and return them.
55     foreach ($this->properties as $name => $property) {
56       $definition = $property->getDataDefinition();
57       if (!$definition->isComputed()) {
58         $value = $property->getValue();
59         // Only write NULL values if the whole map is not NULL.
60         if (isset($this->values) || isset($value)) {
61           $this->values[$name] = $value;
62         }
63       }
64     }
65     return $this->values;
66   }
67
68   /**
69    * Overrides \Drupal\Core\TypedData\TypedData::setValue().
70    *
71    * @param array|null $values
72    *   An array of property values.
73    */
74   public function setValue($values, $notify = TRUE) {
75     if (isset($values) && !is_array($values)) {
76       throw new \InvalidArgumentException("Invalid values given. Values must be represented as an associative array.");
77     }
78     $this->values = $values;
79
80     // Update any existing property objects.
81     foreach ($this->properties as $name => $property) {
82       $value = isset($values[$name]) ? $values[$name] : NULL;
83       $property->setValue($value, FALSE);
84       // Remove the value from $this->values to ensure it does not contain any
85       // value for computed properties.
86       unset($this->values[$name]);
87     }
88     // Notify the parent of any changes.
89     if ($notify && isset($this->parent)) {
90       $this->parent->onChange($this->name);
91     }
92   }
93
94   /**
95    * {@inheritdoc}
96    */
97   public function getString() {
98     $strings = [];
99     foreach ($this->getProperties() as $property) {
100       $strings[] = $property->getString();
101     }
102     // Remove any empty strings resulting from empty items.
103     return implode(', ', array_filter($strings, '\Drupal\Component\Utility\Unicode::strlen'));
104   }
105
106   /**
107    * {@inheritdoc}
108    */
109   public function get($property_name) {
110     if (!isset($this->properties[$property_name])) {
111       $value = NULL;
112       if (isset($this->values[$property_name])) {
113         $value = $this->values[$property_name];
114       }
115       // If the property is unknown, this will throw an exception.
116       $this->properties[$property_name] = $this->getTypedDataManager()->getPropertyInstance($this, $property_name, $value);
117     }
118     return $this->properties[$property_name];
119   }
120
121   /**
122    * {@inheritdoc}
123    */
124   public function set($property_name, $value, $notify = TRUE) {
125     // Separate the writing in a protected method, such that onChange
126     // implementations can make use of it.
127     $this->writePropertyValue($property_name, $value);
128     $this->onChange($property_name, $notify);
129     return $this;
130   }
131
132   /**
133    * Writes the value of a property without handling changes.
134    *
135    * Implementations of onChange() should use this method instead of set() in
136    * order to avoid onChange() being triggered again.
137    *
138    * @param string $property_name
139    *   The name of the property to be written.
140    * @param $value
141    *   The value to set.
142    */
143   protected function writePropertyValue($property_name, $value) {
144     if ($this->definition->getPropertyDefinition($property_name)) {
145       $this->get($property_name)->setValue($value, FALSE);
146     }
147     else {
148       // Just set the plain value, which allows adding a new entry to the map.
149       $this->values[$property_name] = $value;
150     }
151   }
152
153   /**
154    * {@inheritdoc}
155    */
156   public function getProperties($include_computed = FALSE) {
157     $properties = [];
158     foreach ($this->definition->getPropertyDefinitions() as $name => $definition) {
159       if ($include_computed || !$definition->isComputed()) {
160         $properties[$name] = $this->get($name);
161       }
162     }
163     return $properties;
164   }
165
166   /**
167    * {@inheritdoc}
168    */
169   public function toArray() {
170     $values = [];
171     foreach ($this->getProperties() as $name => $property) {
172       $values[$name] = $property->getValue();
173     }
174     return $values;
175   }
176
177   /**
178    * {@inheritdoc}
179    */
180   public function getIterator() {
181     return new \ArrayIterator($this->getProperties());
182   }
183
184   /**
185    * {@inheritdoc}
186    */
187   public function isEmpty() {
188     foreach ($this->properties as $property) {
189       $definition = $property->getDataDefinition();
190       if (!$definition->isComputed() && $property->getValue() !== NULL) {
191         return FALSE;
192       }
193     }
194     if (isset($this->values)) {
195       foreach ($this->values as $name => $value) {
196         if (isset($value) && !isset($this->properties[$name])) {
197           return FALSE;
198         }
199       }
200     }
201     return TRUE;
202   }
203
204   /**
205    * Magic method: Implements a deep clone.
206    */
207   public function __clone() {
208     foreach ($this->properties as $name => $property) {
209       $this->properties[$name] = clone $property;
210       $this->properties[$name]->setContext($name, $this);
211     }
212   }
213
214   /**
215    * {@inheritdoc}
216    *
217    * @param bool $notify
218    *   (optional) Whether to forward the notification to the parent. Defaults to
219    *   TRUE. By passing FALSE, overrides of this method can re-use the logic
220    *   of parent classes without triggering notification.
221    */
222   public function onChange($property_name, $notify = TRUE) {
223     // Notify the parent of changes.
224     if ($notify && isset($this->parent)) {
225       $this->parent->onChange($this->name);
226     }
227   }
228
229   /**
230    * {@inheritdoc}
231    */
232   public function applyDefaultValue($notify = TRUE) {
233     // Apply the default value of all properties.
234     foreach ($this->getProperties() as $property) {
235       $property->applyDefaultValue(FALSE);
236     }
237     return $this;
238   }
239
240 }