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