104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeau 404fd306cSNickeaunamespace ComboStrap\Meta\Store; 504fd306cSNickeau 604fd306cSNickeau 704fd306cSNickeauuse ComboStrap\DatabasePageRow; 804fd306cSNickeauuse ComboStrap\ExceptionBadArgument; 904fd306cSNickeauuse ComboStrap\ExceptionCompile; 1004fd306cSNickeauuse ComboStrap\ExceptionNotExists; 1104fd306cSNickeauuse ComboStrap\ExceptionNotFound; 1204fd306cSNickeauuse ComboStrap\ExceptionRuntime; 1304fd306cSNickeauuse ComboStrap\ExceptionRuntimeInternal; 1404fd306cSNickeauuse ComboStrap\ExceptionSqliteNotAvailable; 1504fd306cSNickeauuse ComboStrap\LogUtility; 1604fd306cSNickeauuse ComboStrap\MarkupPath; 1704fd306cSNickeauuse ComboStrap\Meta\Api\Metadata; 1804fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore; 1904fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStore; 2004fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStoreAbs; 2104fd306cSNickeauuse ComboStrap\Meta\Api\MetadataTabular; 2204fd306cSNickeauuse ComboStrap\ResourceCombo; 2304fd306cSNickeauuse ComboStrap\Sqlite; 2404fd306cSNickeau 2504fd306cSNickeau/** 2604fd306cSNickeau * Class MetadataDbStore 2704fd306cSNickeau * @package ComboStrap 2804fd306cSNickeau * The database store 2904fd306cSNickeau * TODO: {@link DatabasePageRow} should be integrated into MetadataDbStore 3004fd306cSNickeau * A tabular metadata should be created to get all {@link DatabasePageRow::getMetaRecord()} 3104fd306cSNickeau */ 3204fd306cSNickeauclass MetadataDbStore extends MetadataStoreAbs implements MetadataStore 3304fd306cSNickeau{ 3404fd306cSNickeau const CANONICAL = "database"; 3504fd306cSNickeau 3604fd306cSNickeau /** 3704fd306cSNickeau * @var DatabasePageRow[] 3804fd306cSNickeau */ 3904fd306cSNickeau private static array $dbRows = []; 4004fd306cSNickeau private Sqlite $sqlite; 4104fd306cSNickeau 4204fd306cSNickeau /** 4304fd306cSNickeau * @var Metadata - the uid metadata 4404fd306cSNickeau * They are here to throw at construct time 4504fd306cSNickeau */ 4604fd306cSNickeau private Metadata $resourceUidMeta; 4704fd306cSNickeau /** 4804fd306cSNickeau * @var mixed - the uid metadata value 4904fd306cSNickeau * They are here to throw at construct time 5004fd306cSNickeau */ 5104fd306cSNickeau private $resourceUidMetaValue; 5204fd306cSNickeau 5304fd306cSNickeau /** 5404fd306cSNickeau * @throws ExceptionSqliteNotAvailable 5504fd306cSNickeau * @throws ExceptionNotExists - if the resource does not exist in the database 5604fd306cSNickeau */ 5704fd306cSNickeau public function __construct(ResourceCombo $resource) 5804fd306cSNickeau { 5904fd306cSNickeau // sqlite in the constructor to handle only one sqlite exception 6004fd306cSNickeau $this->sqlite = Sqlite::createOrGetSqlite(); 6104fd306cSNickeau 6204fd306cSNickeau // uid of the resoure (the old page id) 6304fd306cSNickeau $this->resourceUidMeta = $resource->getUid(); 6404fd306cSNickeau $persistentName = $this->resourceUidMeta::getPersistentName(); 6504fd306cSNickeau /** 6604fd306cSNickeau * If uid is null, it's not yet in the database 6704fd306cSNickeau * and returns the default or null, or empty array 6804fd306cSNickeau */ 6904fd306cSNickeau $this->resourceUidMetaValue = MetadataDokuWikiStore::getOrCreateFromResource($resource) 7004fd306cSNickeau ->getFromName($persistentName); 7104fd306cSNickeau 7204fd306cSNickeau parent::__construct($resource); 7304fd306cSNickeau } 7404fd306cSNickeau 7504fd306cSNickeau 7604fd306cSNickeau /** 7704fd306cSNickeau * @throws ExceptionNotExists 7804fd306cSNickeau * @throws ExceptionSqliteNotAvailable 7904fd306cSNickeau */ 8004fd306cSNickeau static function getOrCreateFromResource(ResourceCombo $resourceCombo): MetadataStore 8104fd306cSNickeau { 8204fd306cSNickeau return new MetadataDbStore($resourceCombo); 8304fd306cSNickeau } 8404fd306cSNickeau 8504fd306cSNickeau public static function resetAll() 8604fd306cSNickeau { 8704fd306cSNickeau self::$dbRows = []; 8804fd306cSNickeau } 8904fd306cSNickeau 9004fd306cSNickeau public function set(Metadata $metadata) 9104fd306cSNickeau { 9204fd306cSNickeau if ($metadata instanceof MetadataTabular) { 9304fd306cSNickeau 9404fd306cSNickeau $this->syncTabular($metadata); 9504fd306cSNickeau return; 9604fd306cSNickeau } 9704fd306cSNickeau 9804fd306cSNickeau throw new ExceptionRuntime("The metadata ($metadata) is not yet supported on set", self::CANONICAL); 9904fd306cSNickeau 10004fd306cSNickeau } 10104fd306cSNickeau 10204fd306cSNickeau public function get(Metadata $metadata, $default = null) 10304fd306cSNickeau { 10404fd306cSNickeau 10504fd306cSNickeau $resource = $metadata->getResource(); 10604fd306cSNickeau if (!($resource instanceof MarkupPath)) { 10704fd306cSNickeau throw new ExceptionRuntime("The resource type ({$resource->getType()}) is not yet supported for the database metadata store", self::CANONICAL); 10804fd306cSNickeau } 10904fd306cSNickeau 11004fd306cSNickeau if ($metadata instanceof MetadataTabular) { 11104fd306cSNickeau 11204fd306cSNickeau return $this->getDbTabularData($metadata); 11304fd306cSNickeau 11404fd306cSNickeau } else { 11504fd306cSNickeau 11604fd306cSNickeau $pageMetaFromFileSystem = MarkupPath::createPageFromAbsoluteId($resource->getPathObject()->toAbsoluteId()); 11704fd306cSNickeau $fsStore = MetadataDokuWikiStore::getOrCreateFromResource($pageMetaFromFileSystem); 11804fd306cSNickeau $pageMetaFromFileSystem->setReadStore($fsStore); 11904fd306cSNickeau 12004fd306cSNickeau $database = DatabasePageRow::getOrCreateFromPageObject($pageMetaFromFileSystem); 12104fd306cSNickeau if (!$database->exists()) { 12204fd306cSNickeau return null; 12304fd306cSNickeau } 12404fd306cSNickeau return $database->getFromRow($metadata->getName()); 12504fd306cSNickeau 12604fd306cSNickeau } 12704fd306cSNickeau } 12804fd306cSNickeau 12904fd306cSNickeau /** 13004fd306cSNickeau * 13104fd306cSNickeau */ 13204fd306cSNickeau private function syncTabular(MetadataTabular $metadata) 13304fd306cSNickeau { 13404fd306cSNickeau 13504fd306cSNickeau try { 13604fd306cSNickeau $uid = $metadata->getUidObject(); 13704fd306cSNickeau } catch (ExceptionBadArgument $e) { 13804fd306cSNickeau throw new ExceptionRuntimeInternal("The uid class should be defined for the metadata ($metadata)"); 13904fd306cSNickeau } 14004fd306cSNickeau 14104fd306cSNickeau $sourceRows = $metadata->toStoreValue(); 14204fd306cSNickeau if ($sourceRows === null) { 14304fd306cSNickeau return; 14404fd306cSNickeau } 14504fd306cSNickeau 14604fd306cSNickeau $targetRows = $this->getDbTabularData($metadata); 14704fd306cSNickeau foreach ($targetRows as $targetRow) { 14804fd306cSNickeau $targetRowId = $targetRow[$uid::getPersistentName()]; 14904fd306cSNickeau if (isset($sourceRows[$targetRowId])) { 15004fd306cSNickeau unset($sourceRows[$targetRowId]); 15104fd306cSNickeau } else { 15204fd306cSNickeau $this->deleteRow($targetRow, $metadata); 15304fd306cSNickeau } 15404fd306cSNickeau } 15504fd306cSNickeau 15604fd306cSNickeau foreach ($sourceRows as $sourceRow) { 15704fd306cSNickeau $this->addRow($sourceRow, $metadata); 15804fd306cSNickeau } 15904fd306cSNickeau 16004fd306cSNickeau } 16104fd306cSNickeau 16204fd306cSNickeau 16304fd306cSNickeau /** 16404fd306cSNickeau * @param array $row 16504fd306cSNickeau * @param Metadata $metadata 16604fd306cSNickeau * @return void 16704fd306cSNickeau */ 16804fd306cSNickeau private function addRow(array $row, Metadata $metadata): void 16904fd306cSNickeau { 17004fd306cSNickeau 17104fd306cSNickeau /** 17204fd306cSNickeau * Add the id 17304fd306cSNickeau */ 17404fd306cSNickeau $resourceCombo = $metadata->getResource(); 17504fd306cSNickeau $resourceUidObject = $resourceCombo->getUidObject(); 17604fd306cSNickeau try { 17704fd306cSNickeau $uidValue = $resourceUidObject->getValue(); 17804fd306cSNickeau } catch (ExceptionNotFound $e) { 17904fd306cSNickeau // not yet in db 18004fd306cSNickeau return; 18104fd306cSNickeau } 18204fd306cSNickeau 18304fd306cSNickeau $row[$resourceUidObject::getPersistentName()] = $uidValue; 18404fd306cSNickeau 18504fd306cSNickeau $tableName = $this->getTableName($metadata); 18604fd306cSNickeau $request = $this->sqlite 18704fd306cSNickeau ->createRequest() 18804fd306cSNickeau ->setTableRow($tableName, $row); 18904fd306cSNickeau try { 19004fd306cSNickeau $request->execute(); 19104fd306cSNickeau } catch (ExceptionCompile $e) { 19204fd306cSNickeau LogUtility::msg("There was a problem during rows insertion for the table ($tableName)" . $e->getMessage()); 19304fd306cSNickeau } finally { 19404fd306cSNickeau $request->close(); 19504fd306cSNickeau } 19604fd306cSNickeau 19704fd306cSNickeau 19804fd306cSNickeau } 19904fd306cSNickeau 20004fd306cSNickeau /** 20104fd306cSNickeau * @param array $row 20204fd306cSNickeau * @param Metadata $metadata 20304fd306cSNickeau */ 20404fd306cSNickeau private function deleteRow(array $row, Metadata $metadata): void 20504fd306cSNickeau { 20604fd306cSNickeau $tableName = $this->getTableName($metadata); 20704fd306cSNickeau $resourceIdAttribute = $metadata->getResource()->getUidObject()::getPersistentName(); 20804fd306cSNickeau $metadataIdAttribute = $metadata->getUidObject()::getPersistentName(); 20904fd306cSNickeau $delete = <<<EOF 21004fd306cSNickeaudelete from $tableName where $resourceIdAttribute = ? and $metadataIdAttribute = ? 21104fd306cSNickeauEOF; 21204fd306cSNickeau 21304fd306cSNickeau $row = [ 214*70bbd7f1Sgerardnico $row[$resourceIdAttribute] ?? null, 215*70bbd7f1Sgerardnico $row[$metadataIdAttribute] ?? null 21604fd306cSNickeau ]; 21704fd306cSNickeau $request = Sqlite::createOrGetSqlite() 21804fd306cSNickeau ->createRequest() 21904fd306cSNickeau ->setQueryParametrized($delete, $row); 22004fd306cSNickeau try { 22104fd306cSNickeau $request->execute(); 22204fd306cSNickeau } catch (ExceptionCompile $e) { 22304fd306cSNickeau LogUtility::msg("There was a problem during the row delete of $tableName. Message: {$e->getMessage()}"); 22404fd306cSNickeau return; 22504fd306cSNickeau } finally { 22604fd306cSNickeau $request->close(); 22704fd306cSNickeau } 22804fd306cSNickeau 22904fd306cSNickeau 23004fd306cSNickeau } 23104fd306cSNickeau 23204fd306cSNickeau 23304fd306cSNickeau /** 23404fd306cSNickeau * @return array - the rows 23504fd306cSNickeau * @var Metadata $metadata 23604fd306cSNickeau */ 23704fd306cSNickeau private function getDbTabularData(Metadata $metadata): array 23804fd306cSNickeau { 23904fd306cSNickeau 24004fd306cSNickeau $uid = $this->resourceUidMeta; 24104fd306cSNickeau $uidValue = $this->resourceUidMetaValue; 24204fd306cSNickeau if ($uidValue === null) { 24304fd306cSNickeau // no yet in the db 24404fd306cSNickeau return []; 24504fd306cSNickeau } 24604fd306cSNickeau 24704fd306cSNickeau $uidAttribute = $uid::getPersistentName(); 24804fd306cSNickeau $children = $metadata->getChildrenObject(); 24904fd306cSNickeau if ($children === null) { 25004fd306cSNickeau throw new ExceptionRuntimeInternal("The children of the tabular metadata ($metadata) should be set to synchronize into the database"); 25104fd306cSNickeau } 25204fd306cSNickeau $attributes = []; 25304fd306cSNickeau foreach ($children as $child) { 25404fd306cSNickeau $attributes[] = $child::getPersistentName(); 25504fd306cSNickeau } 25604fd306cSNickeau $tableName = $this->getTableName($metadata); 25704fd306cSNickeau $query = Sqlite::createSelectFromTableAndColumns($tableName, $attributes); 25804fd306cSNickeau $query = "$query where $uidAttribute = ? "; 25904fd306cSNickeau $res = $this->sqlite 26004fd306cSNickeau ->createRequest() 26104fd306cSNickeau ->setQueryParametrized($query, [$uidValue]); 26204fd306cSNickeau $rows = []; 26304fd306cSNickeau try { 26404fd306cSNickeau $rows = $res 26504fd306cSNickeau ->execute() 26604fd306cSNickeau ->getRows(); 26704fd306cSNickeau } catch (ExceptionCompile $e) { 26804fd306cSNickeau throw new ExceptionRuntimeInternal("An exception has occurred with the $tableName ({$metadata->getResource()}) selection query. Message: {$e->getMessage()}, Query: ($query", self::CANONICAL, 1, $e); 26904fd306cSNickeau } finally { 27004fd306cSNickeau $res->close(); 27104fd306cSNickeau } 27204fd306cSNickeau return $rows; 27304fd306cSNickeau 27404fd306cSNickeau } 27504fd306cSNickeau 27604fd306cSNickeau public function persist() 27704fd306cSNickeau { 27804fd306cSNickeau // there is no notion of commit in the sqlite plugin 27904fd306cSNickeau } 28004fd306cSNickeau 28104fd306cSNickeau public function isHierarchicalTextBased(): bool 28204fd306cSNickeau { 28304fd306cSNickeau return false; 28404fd306cSNickeau } 28504fd306cSNickeau 28604fd306cSNickeau public function reset() 28704fd306cSNickeau { 28804fd306cSNickeau throw new ExceptionRuntime("To implement"); 28904fd306cSNickeau } 29004fd306cSNickeau 29104fd306cSNickeau public function getFromName(string $name, $default = null) 29204fd306cSNickeau { 29304fd306cSNickeau 29404fd306cSNickeau if ($this->resourceUidMetaValue === null) { 29504fd306cSNickeau // not yet in the db 29604fd306cSNickeau return $default; 29704fd306cSNickeau } 29804fd306cSNickeau 29904fd306cSNickeau $row = $this->getDatabaseRow(); 30004fd306cSNickeau $value = $row->getFromRow($name); 30104fd306cSNickeau if ($value !== null) { 30204fd306cSNickeau return $value; 30304fd306cSNickeau } 30404fd306cSNickeau return $default; 30504fd306cSNickeau } 30604fd306cSNickeau 30704fd306cSNickeau public function setFromPersistentName(string $name, $value, $default = null) 30804fd306cSNickeau { 30904fd306cSNickeau throw new ExceptionRuntime("Not implemented"); 31004fd306cSNickeau } 31104fd306cSNickeau 31204fd306cSNickeau 31304fd306cSNickeau private function getTableName(Metadata $metadata): string 31404fd306cSNickeau { 31504fd306cSNickeau return $metadata->getResource()->getType() . "_" . $metadata::getPersistentName(); 31604fd306cSNickeau 31704fd306cSNickeau } 31804fd306cSNickeau 31904fd306cSNickeau public function getCanonical(): string 32004fd306cSNickeau { 32104fd306cSNickeau return self::CANONICAL; 32204fd306cSNickeau } 32304fd306cSNickeau 32404fd306cSNickeau private function getDatabaseRow(): DatabasePageRow 32504fd306cSNickeau { 32604fd306cSNickeau $mapKey = $this->getResource()->getPathObject()->toAbsoluteId(); 32704fd306cSNickeau $row = self::$dbRows[$mapKey]; 32804fd306cSNickeau if ($row === null) { 32904fd306cSNickeau $page = $this->getResource(); 33004fd306cSNickeau $row = DatabasePageRow::getFromPageObject($page); 33104fd306cSNickeau self::$dbRows[$mapKey] = $row; 33204fd306cSNickeau } 33304fd306cSNickeau return $row; 33404fd306cSNickeau } 33504fd306cSNickeau 33604fd306cSNickeau 33704fd306cSNickeau} 338