xref: /plugin/combo/ComboStrap/Meta/Api/MetadataTabular.php (revision 70bbd7f1f72440223cc13f3495efdcb2b0a11514)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeau
404fd306cSNickeaunamespace ComboStrap\Meta\Api;
504fd306cSNickeau
604fd306cSNickeau
704fd306cSNickeauuse ComboStrap\DataType;
804fd306cSNickeauuse ComboStrap\ExceptionBadArgument;
904fd306cSNickeauuse ComboStrap\ExceptionNotFound;
1004fd306cSNickeauuse ComboStrap\ExceptionRuntime;
1104fd306cSNickeauuse ComboStrap\ExceptionRuntimeInternal;
1204fd306cSNickeauuse ComboStrap\LogUtility;
1304fd306cSNickeauuse ComboStrap\Meta\Field\Aliases;
1404fd306cSNickeauuse ComboStrap\Path;
1504fd306cSNickeauuse ComboStrap\References;
1604fd306cSNickeau
1704fd306cSNickeau/**
1804fd306cSNickeau * Class MetadataTabular
1904fd306cSNickeau * @package ComboStrap
2004fd306cSNickeau * A list of row represented as a list of column
2104fd306cSNickeau * ie an entity with a map that has a key
2204fd306cSNickeau *
2304fd306cSNickeau * The value of a tabular is an array of row (where a row is an associative array)
2404fd306cSNickeau */
2504fd306cSNickeauabstract class MetadataTabular extends Metadata
2604fd306cSNickeau{
2704fd306cSNickeau
2804fd306cSNickeau    /**
2904fd306cSNickeau     * In the array, the identifier may be the persistent one
3004fd306cSNickeau     * or the identifier one
3104fd306cSNickeau     */
3204fd306cSNickeau    const PERSISTENT_NAME = "persistent";
3304fd306cSNickeau    const IDENTIFIER_NAME = "identifier";
3404fd306cSNickeau
3504fd306cSNickeau
3604fd306cSNickeau    /**
3704fd306cSNickeau     * A array of rows where
3804fd306cSNickeau     * - the key is the `identifier value`
3904fd306cSNickeau     * - the value is a list of column metadata
4004fd306cSNickeau     *     * the key is the {@link Metadata::getPersistentName()}
4104fd306cSNickeau     *     * the value is the metadata
4204fd306cSNickeau     *
4304fd306cSNickeau     * @var Metadata[][]
4404fd306cSNickeau     */
4504fd306cSNickeau    protected ?array $rows = null;
4604fd306cSNickeau
4704fd306cSNickeau
4804fd306cSNickeau    /**
4904fd306cSNickeau     * @return array - the rows in associate array format
5004fd306cSNickeau     * where the identifier value is the key
5104fd306cSNickeau     * @throws ExceptionNotFound
5204fd306cSNickeau     */
5304fd306cSNickeau    public function getValue(): array
5404fd306cSNickeau    {
5504fd306cSNickeau        $this->buildCheck();
5604fd306cSNickeau        if ($this->rows === null) {
5704fd306cSNickeau            throw new ExceptionNotFound("No tabular data found.");
5804fd306cSNickeau        }
5904fd306cSNickeau        return $this->rows;
6004fd306cSNickeau    }
6104fd306cSNickeau
6204fd306cSNickeau    public function setValue($value): Metadata
6304fd306cSNickeau    {
6404fd306cSNickeau        if ($value === null) {
6504fd306cSNickeau            return $this;
6604fd306cSNickeau        }
6704fd306cSNickeau        if (!is_array($value)) {
6804fd306cSNickeau            throw new ExceptionRuntime("The data set is not an array (The tabular data is an array of rows)");
6904fd306cSNickeau        }
7004fd306cSNickeau        $keys = array_keys($value);
7104fd306cSNickeau        foreach ($keys as $key) {
7204fd306cSNickeau            if (!is_numeric($key)) {
7304fd306cSNickeau                throw new ExceptionRuntime("The element of the array are not rows. The index ($key) should be numeric and is not");
7404fd306cSNickeau            }
7504fd306cSNickeau        }
7604fd306cSNickeau        $this->rows = $value;
7704fd306cSNickeau        return $this;
7804fd306cSNickeau
7904fd306cSNickeau    }
8004fd306cSNickeau
8104fd306cSNickeau
8204fd306cSNickeau    public function toStoreValue(): ?array
8304fd306cSNickeau    {
8404fd306cSNickeau        $this->buildCheck();
8504fd306cSNickeau        if ($this->rows === null) {
8604fd306cSNickeau            return null;
8704fd306cSNickeau        }
8804fd306cSNickeau        return $this->rowsToStore($this->rows);
8904fd306cSNickeau    }
9004fd306cSNickeau
9104fd306cSNickeau    /**
9204fd306cSNickeau     * /**
9304fd306cSNickeau     * A list of rows that contains a list of metadata
9404fd306cSNickeau     * with their value
9504fd306cSNickeau     * Each row has the key has value
9604fd306cSNickeau     * @return Metadata[][] - an array of rows
9704fd306cSNickeau     * @throws ExceptionNotFound
9804fd306cSNickeau     */
9904fd306cSNickeau    public abstract function getDefaultValue(): array;
10004fd306cSNickeau
10104fd306cSNickeau    public function toStoreDefaultValue(): ?array
10204fd306cSNickeau    {
10304fd306cSNickeau        try {
10404fd306cSNickeau            $defaultRows = $this->getDefaultValue();
10504fd306cSNickeau        } catch (ExceptionNotFound $e) {
10604fd306cSNickeau            return null;
10704fd306cSNickeau        }
10804fd306cSNickeau
10904fd306cSNickeau        return $this->rowsToStore($defaultRows);
11004fd306cSNickeau    }
11104fd306cSNickeau
11204fd306cSNickeau    /**
11304fd306cSNickeau     * The value is:
11404fd306cSNickeau     *   * a string for a unique identifier value
11504fd306cSNickeau     *   * an array of columns or an array of rows
11604fd306cSNickeau     */
11704fd306cSNickeau    public function setFromStoreValueWithoutException($value): Metadata
11804fd306cSNickeau    {
11904fd306cSNickeau
12004fd306cSNickeau        if ($value === null) {
12104fd306cSNickeau            return $this;
12204fd306cSNickeau        }
12304fd306cSNickeau
12404fd306cSNickeau        /**
12504fd306cSNickeau         * Value of the metadata id
12604fd306cSNickeau         */
12704fd306cSNickeau        $identifierMetadataObject = $this->getUidObject();
12804fd306cSNickeau        $identifierPersistentName = $identifierMetadataObject::getPersistentName();
12904fd306cSNickeau
13004fd306cSNickeau        /**
13104fd306cSNickeau         * Single value
13204fd306cSNickeau         */
13304fd306cSNickeau        if (is_string($value)) {
13404fd306cSNickeau            /**
13504fd306cSNickeau             * @var Metadata $identifierMetadata
13604fd306cSNickeau             */
13704fd306cSNickeau            $identifierMetadata = (new $identifierMetadataObject());
138757e8bb4Sgerardnico            $identifierMetadata
139757e8bb4Sgerardnico                ->setResource($this->getResource())
140757e8bb4Sgerardnico                ->setValue($value);
14104fd306cSNickeau            $rowId = $this->getRowId($identifierMetadata);
14204fd306cSNickeau            $this->rows[$rowId] = [$identifierPersistentName => $identifierMetadata];
14304fd306cSNickeau            return $this;
14404fd306cSNickeau        }
14504fd306cSNickeau
14604fd306cSNickeau        /**
14704fd306cSNickeau         * Array
14804fd306cSNickeau         */
14904fd306cSNickeau        if (!is_array($value)) {
15004fd306cSNickeau            LogUtility::msg("The tabular value is nor a string nor an array");
15104fd306cSNickeau            return $this;
15204fd306cSNickeau        }
15304fd306cSNickeau
15404fd306cSNickeau
15504fd306cSNickeau        /**
15604fd306cSNickeau         * List of columns ({@link MetadataFormDataStore form html way}
15704fd306cSNickeau         *
15804fd306cSNickeau         * For example: for {@link Aliases}, a form will have two fields
15904fd306cSNickeau         *  alias-path:
16004fd306cSNickeau         *    0: path1
16104fd306cSNickeau         *    1: path2
16204fd306cSNickeau         *  alias-type:
16304fd306cSNickeau         *    0: redirect
16404fd306cSNickeau         *    1: redirect
16504fd306cSNickeau         *
16604fd306cSNickeau         * or just a list ({@link References}
16704fd306cSNickeau         *   references:
16804fd306cSNickeau         *    0: reference-1
16904fd306cSNickeau         *    1: $colValue-2
17004fd306cSNickeau         */
17104fd306cSNickeau        $keys = array_keys($value);
17204fd306cSNickeau        $firstElement = array_shift($keys);
17304fd306cSNickeau        if (!is_numeric($firstElement)) {
17404fd306cSNickeau            /**
17504fd306cSNickeau             * Check which kind of key is used
17604fd306cSNickeau             * Resistance to the property key !
17704fd306cSNickeau             */
17804fd306cSNickeau            $identifierName = $identifierMetadataObject::getPersistentName();
17904fd306cSNickeau            $identifierNameType = self::PERSISTENT_NAME;
18004fd306cSNickeau            if (!isset($value[$identifierName])) {
18104fd306cSNickeau                $identifierNameType = self::IDENTIFIER_NAME;
18204fd306cSNickeau                $identifierName = $identifierMetadataObject::getName();
18304fd306cSNickeau            }
18404fd306cSNickeau            $identifierValues = $value[$identifierName];
18504fd306cSNickeau            if ($identifierValues === null || $identifierValues === "") {
18604fd306cSNickeau                // No data
18704fd306cSNickeau                return $this;
18804fd306cSNickeau            }
18904fd306cSNickeau            $index = 0;
19004fd306cSNickeau            if (!is_array($identifierValues)) {
19104fd306cSNickeau                // only one value
19204fd306cSNickeau                $identifierValues = [$identifierValues];
19304fd306cSNickeau            }
19404fd306cSNickeau            foreach ($identifierValues as $identifierValue) {
19504fd306cSNickeau                $row = [];
19604fd306cSNickeau                if ($identifierValue === "") {
19704fd306cSNickeau                    // an empty row in the table
19804fd306cSNickeau                    continue;
19904fd306cSNickeau                }
20004fd306cSNickeau                $row[$identifierPersistentName] = MetadataSystem::toMetadataObject($identifierMetadataObject, $this)
20104fd306cSNickeau                    ->setFromStoreValue($identifierValue);
20204fd306cSNickeau                foreach ($this->getChildrenClass() as $childClass) {
20304fd306cSNickeau                    if ($childClass === get_class($identifierMetadataObject)) {
20404fd306cSNickeau                        continue;
20504fd306cSNickeau                    }
20604fd306cSNickeau                    $metadataChildObject = MetadataSystem::toMetadataObject($childClass, $this);
20704fd306cSNickeau                    $name = $metadataChildObject::getPersistentName();
20804fd306cSNickeau                    if ($identifierNameType === self::IDENTIFIER_NAME) {
20904fd306cSNickeau                        $name = $metadataChildObject::getName();
21004fd306cSNickeau                    }
21104fd306cSNickeau                    $childValue = $value[$name];
21204fd306cSNickeau                    if (is_array($childValue)) {
21304fd306cSNickeau                        $childValue = $childValue[$index];
21404fd306cSNickeau                        $index++;
21504fd306cSNickeau                    }
21604fd306cSNickeau                    $metadataChildObject->setFromStoreValue($childValue);
21704fd306cSNickeau                    $row[$metadataChildObject::getPersistentName()] = $metadataChildObject;
21804fd306cSNickeau                }
21904fd306cSNickeau                $this->rows[] = $row;
22004fd306cSNickeau            }
22104fd306cSNickeau
22204fd306cSNickeau            return $this;
22304fd306cSNickeau        }
22404fd306cSNickeau
22504fd306cSNickeau        /**
22604fd306cSNickeau         * List of row (frontmatter, dokuwiki, ...)
22704fd306cSNickeau         */
22804fd306cSNickeau        $childClassesByPersistentName = [];
22904fd306cSNickeau        foreach ($this->getChildrenObject() as $childObject) {
23004fd306cSNickeau            $childClassesByPersistentName[$childObject::getPersistentName()] = $childObject;
23104fd306cSNickeau        }
23204fd306cSNickeau        foreach ($value as $item) {
23304fd306cSNickeau
23404fd306cSNickeau            /**
23504fd306cSNickeau             * By default, the single value is the identifier
23604fd306cSNickeau             *
23704fd306cSNickeau             * (Note that from {@link MetadataFormDataStore}, tabular data
23804fd306cSNickeau             * should be build before via the {@link self::buildFromReadStore()}
23904fd306cSNickeau             * as tabular data is represented as a series of column)
24004fd306cSNickeau             *
24104fd306cSNickeau             */
24204fd306cSNickeau            if (is_string($item)) {
24304fd306cSNickeau                try {
24404fd306cSNickeau                    $identifierMetadata = MetadataSystem::toMetadataObject($identifierMetadataObject, $this)->setFromStoreValue($item);
24504fd306cSNickeau                } catch (ExceptionBadArgument $e) {
24604fd306cSNickeau                    throw ExceptionRuntimeInternal::withMessageAndError("The $identifierMetadataObject should be known", $e);
24704fd306cSNickeau                }
24804fd306cSNickeau                $identifierValue = $this->getRowId($identifierMetadata);
24904fd306cSNickeau                $this->rows[$identifierValue] = [$identifierPersistentName => $identifierMetadata];
25004fd306cSNickeau                continue;
25104fd306cSNickeau            }
25204fd306cSNickeau            if (!is_array($item)) {
25304fd306cSNickeau                LogUtility::msg("The tabular value is not a string nor an array");
25404fd306cSNickeau            }
25504fd306cSNickeau            $row = [];
25604fd306cSNickeau            $idValue = null;
25704fd306cSNickeau            foreach ($item as $colName => $colValue) {
25804fd306cSNickeau                $childClass = $childClassesByPersistentName[$colName];
25904fd306cSNickeau                if ($childClass === null) {
26004fd306cSNickeau                    LogUtility::msg("The column ($colName) does not have a metadata definition");
26104fd306cSNickeau                    continue;
26204fd306cSNickeau                }
26304fd306cSNickeau                $childObject = MetadataSystem::toMetadataObject($childClass, $this);
26404fd306cSNickeau                $childObject->setFromStoreValueWithoutException($colValue);
26504fd306cSNickeau                $row[$childObject::getPersistentName()] = $childObject;
26604fd306cSNickeau                if ($childObject::getPersistentName() === $identifierPersistentName) {
26704fd306cSNickeau                    $idValue = $this->getRowId($childObject);
26804fd306cSNickeau                }
26904fd306cSNickeau            }
27004fd306cSNickeau            if ($idValue === null) {
27104fd306cSNickeau                LogUtility::msg("The value for the identifier ($identifierPersistentName) was not found");
27204fd306cSNickeau                continue;
27304fd306cSNickeau            }
27404fd306cSNickeau            $this->rows[$idValue] = $row;
27504fd306cSNickeau
27604fd306cSNickeau        }
27704fd306cSNickeau        return $this;
27804fd306cSNickeau    }
27904fd306cSNickeau
28004fd306cSNickeau
28104fd306cSNickeau    public function valueIsNotNull(): bool
28204fd306cSNickeau    {
28304fd306cSNickeau        return $this->rows !== null;
28404fd306cSNickeau    }
28504fd306cSNickeau
28604fd306cSNickeau    public
287*70bbd7f1Sgerardnico    function remove(string $identifierValue): MetadataTabular
28804fd306cSNickeau    {
28904fd306cSNickeau        $this->buildCheck();
29004fd306cSNickeau        if ($this->rows === null) {
29104fd306cSNickeau            return $this;
29204fd306cSNickeau        }
29304fd306cSNickeau        unset($this->rows[$identifierValue]);
29404fd306cSNickeau        return $this;
29504fd306cSNickeau    }
29604fd306cSNickeau
29704fd306cSNickeau
29804fd306cSNickeau    public
29904fd306cSNickeau    function has($identifierValue): bool
30004fd306cSNickeau    {
30104fd306cSNickeau        $this->buildCheck();
30204fd306cSNickeau        if ($this->rows === null) {
30304fd306cSNickeau            return false;
30404fd306cSNickeau        }
30504fd306cSNickeau        return isset($this->rows[$identifierValue]);
30604fd306cSNickeau    }
30704fd306cSNickeau
30804fd306cSNickeau    public
30904fd306cSNickeau    function getSize(): int
31004fd306cSNickeau    {
31104fd306cSNickeau        $this->buildCheck();
31204fd306cSNickeau        if ($this->rows === null) {
31304fd306cSNickeau            return 0;
31404fd306cSNickeau        }
31504fd306cSNickeau        return sizeof($this->rows);
31604fd306cSNickeau    }
31704fd306cSNickeau
31804fd306cSNickeau    public function getRow($id): ?array
31904fd306cSNickeau    {
32004fd306cSNickeau        $this->buildCheck();
32104fd306cSNickeau        $normalizedValue = $this->getUidObject()
32204fd306cSNickeau            ->setFromStoreValueWithoutException($id)
32304fd306cSNickeau            ->getValue();
32404fd306cSNickeau        if (is_object($normalizedValue)) {
32504fd306cSNickeau            $normalizedValue = $normalizedValue->__toString();
32604fd306cSNickeau        }
327*70bbd7f1Sgerardnico        return $this->rows[$normalizedValue] ?? null;
32804fd306cSNickeau    }
32904fd306cSNickeau
33004fd306cSNickeau    static public function getDataType(): string
33104fd306cSNickeau    {
33204fd306cSNickeau        return DataType::TABULAR_TYPE_VALUE;
33304fd306cSNickeau    }
33404fd306cSNickeau
33504fd306cSNickeau    /**
33604fd306cSNickeau     * @throws ExceptionNotFound - if the identifier or it's value was not found
33704fd306cSNickeau     * @var Metadata[] $metas
33804fd306cSNickeau     */
33904fd306cSNickeau    public function addRow(array $metas): MetadataTabular
34004fd306cSNickeau    {
34104fd306cSNickeau        $row = [];
34204fd306cSNickeau        $identifier = null;
34304fd306cSNickeau        foreach ($metas as $meta) {
34404fd306cSNickeau            $row[$meta::getPersistentName()] = $meta;
34504fd306cSNickeau            if (get_class($meta) === $this->getUidClass()) {
34604fd306cSNickeau                $identifier = $meta->getValue();
34704fd306cSNickeau            }
34804fd306cSNickeau        }
34904fd306cSNickeau        if ($identifier === null) {
35004fd306cSNickeau            throw new ExceptionNotFound("The identifier value was not found in the row");
35104fd306cSNickeau        }
35204fd306cSNickeau        $this->rows[$identifier] = $row;
35304fd306cSNickeau        return $this;
35404fd306cSNickeau    }
35504fd306cSNickeau
35604fd306cSNickeau    private function rowsToStore(array $defaultRows): array
35704fd306cSNickeau    {
35804fd306cSNickeau        $rowsArray = [];
35904fd306cSNickeau        ksort($defaultRows);
36004fd306cSNickeau        foreach ($defaultRows as $row) {
36104fd306cSNickeau            $rowArray = [];
36204fd306cSNickeau            foreach ($row as $metadata) {
36304fd306cSNickeau                $toStoreValue = $metadata->toStoreValue();
36404fd306cSNickeau                $toDefaultStoreValue = $metadata->toStoreDefaultValue();
36504fd306cSNickeau                if (
36604fd306cSNickeau                    $toStoreValue !== null
36704fd306cSNickeau                    && $toStoreValue !== $toDefaultStoreValue
36804fd306cSNickeau                ) {
36904fd306cSNickeau                    $rowArray[$metadata::getPersistentName()] = $toStoreValue;
37004fd306cSNickeau                }
37104fd306cSNickeau            }
37204fd306cSNickeau            $rowsArray[] = $rowArray;
37304fd306cSNickeau        }
37404fd306cSNickeau        return $rowsArray;
37504fd306cSNickeau    }
37604fd306cSNickeau
37704fd306cSNickeau    /**
37804fd306cSNickeau     * @param Metadata $identifierMetadata
37904fd306cSNickeau     * @return mixed
38004fd306cSNickeau     * Due to dokuwiki, the same id may be a markup or media path
38104fd306cSNickeau     * We build the object from the same id but the url is not the same
38204fd306cSNickeau     * This function takes the metadata, get the value and
38304fd306cSNickeau     * return the identifier suitable for an array
38404fd306cSNickeau     */
38504fd306cSNickeau    private function getRowId(Metadata $identifierMetadata)
38604fd306cSNickeau    {
38704fd306cSNickeau        try {
38804fd306cSNickeau            $identifierValue = $identifierMetadata->getValue(); // normalize if any
38904fd306cSNickeau        } catch (ExceptionNotFound $e) {
39004fd306cSNickeau            throw ExceptionRuntimeInternal::withMessageAndError("The meta identifier ($identifierMetadata) should have a value", $e);
39104fd306cSNickeau        }
39204fd306cSNickeau        if (DataType::isObject($identifierValue)) {
39304fd306cSNickeau            /**
39404fd306cSNickeau             * An object cannot be the key of an array
39504fd306cSNickeau             */
39604fd306cSNickeau            $identifierValue = $identifierValue->__toString();
39704fd306cSNickeau        }
39804fd306cSNickeau        return $identifierValue;
39904fd306cSNickeau    }
40004fd306cSNickeau
40104fd306cSNickeau}
402