1<?php
2
3
4namespace ComboStrap;
5
6
7/**
8 * Class MetadataTabular
9 * @package ComboStrap
10 * A list of row represented as a list of column
11 * ie an entity with a map that has a key
12 *
13 * The value of a tabular is an array of row (where a row is an associative array)
14 */
15abstract class MetadataTabular extends Metadata
16{
17
18    /**
19     * In the array, the identifier may be the persistent one
20     * or the identifier one
21     */
22    const PERSISTENT_NAME = "persistent";
23    const IDENTIFIER_NAME = "identifier";
24
25
26    /**
27     * The rows
28     * @var Metadata[][]
29     * Each row has the key has value
30     */
31    protected $rows;
32
33
34    /**
35     * @return array - the rows in associate array format
36     * where the identifier value is the key
37     */
38    public function getValue(): ?array
39    {
40        $this->buildCheck();
41        if ($this->rows === null) {
42            return null;
43        }
44        return $this->rows;
45    }
46
47    public function setValue($value): Metadata
48    {
49        if ($value === null) {
50            return $this;
51        }
52        if (!is_array($value)) {
53            throw new ExceptionComboRuntime("The data set is not an array (The tabular data is an array of rows)");
54        }
55        $keys = array_keys($value);
56        foreach ($keys as $key) {
57            if (!is_numeric($key)) {
58                throw new ExceptionComboRuntime("The element of the array are not rows. The index ($key) should be numeric and is not");
59            }
60        }
61        $this->rows = $value;
62        return $this;
63
64    }
65
66
67    public function toStoreValue(): ?array
68    {
69        if ($this->rows === null) {
70            return null;
71        }
72
73        $rowsArray = [];
74        ksort($this->rows);
75        foreach ($this->rows as $row) {
76            $rowArray = [];
77            foreach ($row as $metadata) {
78                $toStoreValue = $metadata->toStoreValue();
79                $toDefaultStoreValue = $metadata->toStoreDefaultValue();
80                if (
81                    $toStoreValue !== null
82                    && $toStoreValue !== $toDefaultStoreValue
83                ) {
84                    $rowArray[$metadata::getPersistentName()] = $toStoreValue;
85                }
86            }
87            $rowsArray[] = $rowArray;
88        }
89        return $rowsArray;
90    }
91
92    /**
93     * The value is:
94     *   * a string for a unique identifier value
95     *   * an array of columns or an array of rows
96     */
97    public function buildFromStoreValue($value): Metadata
98    {
99
100        if ($value === null) {
101            return $this;
102        }
103
104        /**
105         * Value of the metadata id
106         */
107        $identifierMetadataObject = $this->getUidObject();
108        $identifierPersistentName = $identifierMetadataObject::getPersistentName();
109
110        /**
111         * Single value
112         */
113        if (is_string($value)) {
114            /**
115             * @var Metadata $identifierMetadata
116             */
117            $identifierMetadata = (new $identifierMetadataObject());
118            $identifierMetadata->setValue($value);
119            $this->rows[$value] = [$identifierPersistentName => $identifierMetadata];
120            return $this;
121        }
122
123        /**
124         * Array
125         */
126        if (!is_array($value)) {
127            LogUtility::msg("The tabular value is nor a string nor an array");
128            return $this;
129        }
130
131
132        /**
133         * List of columns ({@link MetadataFormDataStore form html way}
134         */
135        $keys = array_keys($value);
136        $firstElement = array_shift($keys);
137        if (!is_numeric($firstElement)) {
138            /**
139             * Check which kind of key is used
140             * Resistance to the property key !
141             */
142            $identifierName = $identifierMetadataObject::getPersistentName();
143            $identifierNameType = self::PERSISTENT_NAME;
144            if (!isset($value[$identifierName])) {
145                $identifierNameType = self::IDENTIFIER_NAME;
146                $identifierName = $identifierMetadataObject::getName();
147            }
148            $identifierValues = $value[$identifierName];
149            if ($identifierValues === null || $identifierValues === "") {
150                // No data
151                return $this;
152            }
153            $i = 0;
154            foreach ($identifierValues as $identifierValue) {
155                $row = [];
156                if ($identifierValue === "") {
157                    // an empty row in the table
158                    continue;
159                }
160                $row[$identifierPersistentName] = Metadata::toMetadataObject($identifierMetadataObject, $this)
161                    ->setFromStoreValue($identifierValue);
162                foreach ($this->getChildrenClass() as $childClass) {
163                    if ($childClass === $identifierMetadataObject) {
164                        continue;
165                    }
166                    $metadataChildObject = Metadata::toMetadataObject($childClass, $this);
167                    $name = $metadataChildObject::getPersistentName();
168                    if ($identifierNameType === self::IDENTIFIER_NAME) {
169                        $name = $metadataChildObject::getName();
170                    }
171                    $childValue = $value[$name][$i];
172                    $metadataChildObject->setFromStoreValue($childValue);
173                    $row[$metadataChildObject::getPersistentName()] = $metadataChildObject;
174                }
175                $this->rows[] = $row;
176            }
177
178            return $this;
179        }
180
181        /**
182         * List of row (frontmatter, dokuwiki, ...)
183         */
184        $childClassesByPersistentName = [];
185        foreach ($this->getChildrenObject() as $childObject) {
186            $childClassesByPersistentName[$childObject::getPersistentName()] = $childObject;
187        }
188        foreach ($value as $item) {
189
190            // Single value, this is the identifier
191            if (is_string($item)) {
192                $identifierMetadata = Metadata::toMetadataObject($identifierMetadataObject, $this)
193                    ->setFromStoreValue($item);
194                $identifierValue = $identifierMetadata->getValue(); // normalize if any
195                $this->rows[$identifierValue] = [$identifierPersistentName => $identifierMetadata];
196                continue;
197            }
198            if (!is_array($item)) {
199                LogUtility::msg("The tabular value is not a string nor an array");
200            }
201            $row = [];
202            $idValue = null;
203            foreach ($item as $colName => $colValue) {
204                $childClass = $childClassesByPersistentName[$colName];
205                if ($childClass === null) {
206                    LogUtility::msg("The column ($colName) does not have a metadata definition");
207                    continue;
208                }
209                $childObject = Metadata::toMetadataObject($childClass, $this);
210                $childObject->buildFromStoreValue($colValue);
211                $row[$childObject::getPersistentName()] = $childObject;
212                if ($childObject::getPersistentName() === $identifierPersistentName) {
213                    $idValue = $colValue;
214                }
215            }
216            if ($idValue === null) {
217                LogUtility::msg("The value for the identifier ($identifierPersistentName) was not found");
218                continue;
219            }
220            $this->rows[$idValue] = $row;
221
222        }
223        return $this;
224    }
225
226
227    public function valueIsNotNull(): bool
228    {
229        return $this->rows !== null;
230    }
231
232    public
233    function remove($identifierValue)
234    {
235        $this->buildCheck();
236        if ($this->rows === null) {
237            return;
238        }
239        unset($this->rows[$identifierValue]);
240        return $this;
241    }
242
243
244    public
245    function has($identifierValue): bool
246    {
247        $this->buildCheck();
248        if ($this->rows === null) {
249            return false;
250        }
251        return isset($this->rows[$identifierValue]);
252    }
253
254    public
255    function getSize(): int
256    {
257        $this->buildCheck();
258        if ($this->rows === null) {
259            return 0;
260        }
261        return sizeof($this->rows);
262    }
263
264    public function getRow($id): ?array
265    {
266        $this->buildCheck();
267        $normalizedValue = $this->getUidObject()
268            ->buildFromStoreValue($id)
269            ->getValue();
270        return $this->rows[$normalizedValue];
271    }
272
273    public function getDataType(): string
274    {
275        return DataType::TABULAR_TYPE_VALUE;
276    }
277
278    /**
279     * @var Metadata[] $metas
280     */
281    public function addRow(array $metas): MetadataTabular
282    {
283        $row = [];
284        $identifier = null;
285        foreach ($metas as $meta) {
286            $row[$meta::getPersistentName()] = $meta;
287            if (get_class($meta) === $this->getUidClass()) {
288                $identifier = $meta->getValue();
289            }
290        }
291        if ($identifier === null) {
292            LogUtility::msg("The identifier value was not found in the row");
293            return $this;
294        }
295        $this->rows[$identifier] = $row;
296        return $this;
297    }
298
299}
300