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