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