1*04fd306cSNickeau<?php 2*04fd306cSNickeau 3*04fd306cSNickeau 4*04fd306cSNickeaunamespace ComboStrap\Meta\Store; 5*04fd306cSNickeau 6*04fd306cSNickeau 7*04fd306cSNickeauuse ComboStrap\DatabasePageRow; 8*04fd306cSNickeauuse ComboStrap\ExceptionBadArgument; 9*04fd306cSNickeauuse ComboStrap\ExceptionCompile; 10*04fd306cSNickeauuse ComboStrap\ExceptionNotExists; 11*04fd306cSNickeauuse ComboStrap\ExceptionNotFound; 12*04fd306cSNickeauuse ComboStrap\ExceptionRuntime; 13*04fd306cSNickeauuse ComboStrap\ExceptionRuntimeInternal; 14*04fd306cSNickeauuse ComboStrap\ExceptionSqliteNotAvailable; 15*04fd306cSNickeauuse ComboStrap\LogUtility; 16*04fd306cSNickeauuse ComboStrap\MarkupPath; 17*04fd306cSNickeauuse ComboStrap\Meta\Api\Metadata; 18*04fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore; 19*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStore; 20*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStoreAbs; 21*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataTabular; 22*04fd306cSNickeauuse ComboStrap\ResourceCombo; 23*04fd306cSNickeauuse ComboStrap\Sqlite; 24*04fd306cSNickeau 25*04fd306cSNickeau/** 26*04fd306cSNickeau * Class MetadataDbStore 27*04fd306cSNickeau * @package ComboStrap 28*04fd306cSNickeau * The database store 29*04fd306cSNickeau * TODO: {@link DatabasePageRow} should be integrated into MetadataDbStore 30*04fd306cSNickeau * A tabular metadata should be created to get all {@link DatabasePageRow::getMetaRecord()} 31*04fd306cSNickeau */ 32*04fd306cSNickeauclass MetadataDbStore extends MetadataStoreAbs implements MetadataStore 33*04fd306cSNickeau{ 34*04fd306cSNickeau const CANONICAL = "database"; 35*04fd306cSNickeau 36*04fd306cSNickeau /** 37*04fd306cSNickeau * @var DatabasePageRow[] 38*04fd306cSNickeau */ 39*04fd306cSNickeau private static array $dbRows = []; 40*04fd306cSNickeau private Sqlite $sqlite; 41*04fd306cSNickeau 42*04fd306cSNickeau /** 43*04fd306cSNickeau * @var Metadata - the uid metadata 44*04fd306cSNickeau * They are here to throw at construct time 45*04fd306cSNickeau */ 46*04fd306cSNickeau private Metadata $resourceUidMeta; 47*04fd306cSNickeau /** 48*04fd306cSNickeau * @var mixed - the uid metadata value 49*04fd306cSNickeau * They are here to throw at construct time 50*04fd306cSNickeau */ 51*04fd306cSNickeau private $resourceUidMetaValue; 52*04fd306cSNickeau 53*04fd306cSNickeau /** 54*04fd306cSNickeau * @throws ExceptionSqliteNotAvailable 55*04fd306cSNickeau * @throws ExceptionNotExists - if the resource does not exist in the database 56*04fd306cSNickeau */ 57*04fd306cSNickeau public function __construct(ResourceCombo $resource) 58*04fd306cSNickeau { 59*04fd306cSNickeau // sqlite in the constructor to handle only one sqlite exception 60*04fd306cSNickeau $this->sqlite = Sqlite::createOrGetSqlite(); 61*04fd306cSNickeau 62*04fd306cSNickeau // uid of the resoure (the old page id) 63*04fd306cSNickeau $this->resourceUidMeta = $resource->getUid(); 64*04fd306cSNickeau $persistentName = $this->resourceUidMeta::getPersistentName(); 65*04fd306cSNickeau /** 66*04fd306cSNickeau * If uid is null, it's not yet in the database 67*04fd306cSNickeau * and returns the default or null, or empty array 68*04fd306cSNickeau */ 69*04fd306cSNickeau $this->resourceUidMetaValue = MetadataDokuWikiStore::getOrCreateFromResource($resource) 70*04fd306cSNickeau ->getFromName($persistentName); 71*04fd306cSNickeau 72*04fd306cSNickeau parent::__construct($resource); 73*04fd306cSNickeau } 74*04fd306cSNickeau 75*04fd306cSNickeau 76*04fd306cSNickeau /** 77*04fd306cSNickeau * @throws ExceptionNotExists 78*04fd306cSNickeau * @throws ExceptionSqliteNotAvailable 79*04fd306cSNickeau */ 80*04fd306cSNickeau static function getOrCreateFromResource(ResourceCombo $resourceCombo): MetadataStore 81*04fd306cSNickeau { 82*04fd306cSNickeau return new MetadataDbStore($resourceCombo); 83*04fd306cSNickeau } 84*04fd306cSNickeau 85*04fd306cSNickeau public static function resetAll() 86*04fd306cSNickeau { 87*04fd306cSNickeau self::$dbRows = []; 88*04fd306cSNickeau } 89*04fd306cSNickeau 90*04fd306cSNickeau public function set(Metadata $metadata) 91*04fd306cSNickeau { 92*04fd306cSNickeau if ($metadata instanceof MetadataTabular) { 93*04fd306cSNickeau 94*04fd306cSNickeau $this->syncTabular($metadata); 95*04fd306cSNickeau return; 96*04fd306cSNickeau } 97*04fd306cSNickeau 98*04fd306cSNickeau throw new ExceptionRuntime("The metadata ($metadata) is not yet supported on set", self::CANONICAL); 99*04fd306cSNickeau 100*04fd306cSNickeau } 101*04fd306cSNickeau 102*04fd306cSNickeau public function get(Metadata $metadata, $default = null) 103*04fd306cSNickeau { 104*04fd306cSNickeau 105*04fd306cSNickeau $resource = $metadata->getResource(); 106*04fd306cSNickeau if (!($resource instanceof MarkupPath)) { 107*04fd306cSNickeau throw new ExceptionRuntime("The resource type ({$resource->getType()}) is not yet supported for the database metadata store", self::CANONICAL); 108*04fd306cSNickeau } 109*04fd306cSNickeau 110*04fd306cSNickeau if ($metadata instanceof MetadataTabular) { 111*04fd306cSNickeau 112*04fd306cSNickeau return $this->getDbTabularData($metadata); 113*04fd306cSNickeau 114*04fd306cSNickeau } else { 115*04fd306cSNickeau 116*04fd306cSNickeau $pageMetaFromFileSystem = MarkupPath::createPageFromAbsoluteId($resource->getPathObject()->toAbsoluteId()); 117*04fd306cSNickeau $fsStore = MetadataDokuWikiStore::getOrCreateFromResource($pageMetaFromFileSystem); 118*04fd306cSNickeau $pageMetaFromFileSystem->setReadStore($fsStore); 119*04fd306cSNickeau 120*04fd306cSNickeau $database = DatabasePageRow::getOrCreateFromPageObject($pageMetaFromFileSystem); 121*04fd306cSNickeau if (!$database->exists()) { 122*04fd306cSNickeau return null; 123*04fd306cSNickeau } 124*04fd306cSNickeau return $database->getFromRow($metadata->getName()); 125*04fd306cSNickeau 126*04fd306cSNickeau } 127*04fd306cSNickeau } 128*04fd306cSNickeau 129*04fd306cSNickeau /** 130*04fd306cSNickeau * 131*04fd306cSNickeau */ 132*04fd306cSNickeau private function syncTabular(MetadataTabular $metadata) 133*04fd306cSNickeau { 134*04fd306cSNickeau 135*04fd306cSNickeau try { 136*04fd306cSNickeau $uid = $metadata->getUidObject(); 137*04fd306cSNickeau } catch (ExceptionBadArgument $e) { 138*04fd306cSNickeau throw new ExceptionRuntimeInternal("The uid class should be defined for the metadata ($metadata)"); 139*04fd306cSNickeau } 140*04fd306cSNickeau 141*04fd306cSNickeau $sourceRows = $metadata->toStoreValue(); 142*04fd306cSNickeau if ($sourceRows === null) { 143*04fd306cSNickeau return; 144*04fd306cSNickeau } 145*04fd306cSNickeau 146*04fd306cSNickeau $targetRows = $this->getDbTabularData($metadata); 147*04fd306cSNickeau foreach ($targetRows as $targetRow) { 148*04fd306cSNickeau $targetRowId = $targetRow[$uid::getPersistentName()]; 149*04fd306cSNickeau if (isset($sourceRows[$targetRowId])) { 150*04fd306cSNickeau unset($sourceRows[$targetRowId]); 151*04fd306cSNickeau } else { 152*04fd306cSNickeau $this->deleteRow($targetRow, $metadata); 153*04fd306cSNickeau } 154*04fd306cSNickeau } 155*04fd306cSNickeau 156*04fd306cSNickeau foreach ($sourceRows as $sourceRow) { 157*04fd306cSNickeau $this->addRow($sourceRow, $metadata); 158*04fd306cSNickeau } 159*04fd306cSNickeau 160*04fd306cSNickeau } 161*04fd306cSNickeau 162*04fd306cSNickeau 163*04fd306cSNickeau /** 164*04fd306cSNickeau * @param array $row 165*04fd306cSNickeau * @param Metadata $metadata 166*04fd306cSNickeau * @return void 167*04fd306cSNickeau */ 168*04fd306cSNickeau private function addRow(array $row, Metadata $metadata): void 169*04fd306cSNickeau { 170*04fd306cSNickeau 171*04fd306cSNickeau /** 172*04fd306cSNickeau * Add the id 173*04fd306cSNickeau */ 174*04fd306cSNickeau $resourceCombo = $metadata->getResource(); 175*04fd306cSNickeau $resourceUidObject = $resourceCombo->getUidObject(); 176*04fd306cSNickeau try { 177*04fd306cSNickeau $uidValue = $resourceUidObject->getValue(); 178*04fd306cSNickeau } catch (ExceptionNotFound $e) { 179*04fd306cSNickeau // not yet in db 180*04fd306cSNickeau return; 181*04fd306cSNickeau } 182*04fd306cSNickeau 183*04fd306cSNickeau $row[$resourceUidObject::getPersistentName()] = $uidValue; 184*04fd306cSNickeau 185*04fd306cSNickeau $tableName = $this->getTableName($metadata); 186*04fd306cSNickeau $request = $this->sqlite 187*04fd306cSNickeau ->createRequest() 188*04fd306cSNickeau ->setTableRow($tableName, $row); 189*04fd306cSNickeau try { 190*04fd306cSNickeau $request->execute(); 191*04fd306cSNickeau } catch (ExceptionCompile $e) { 192*04fd306cSNickeau LogUtility::msg("There was a problem during rows insertion for the table ($tableName)" . $e->getMessage()); 193*04fd306cSNickeau } finally { 194*04fd306cSNickeau $request->close(); 195*04fd306cSNickeau } 196*04fd306cSNickeau 197*04fd306cSNickeau 198*04fd306cSNickeau } 199*04fd306cSNickeau 200*04fd306cSNickeau /** 201*04fd306cSNickeau * @param array $row 202*04fd306cSNickeau * @param Metadata $metadata 203*04fd306cSNickeau */ 204*04fd306cSNickeau private function deleteRow(array $row, Metadata $metadata): void 205*04fd306cSNickeau { 206*04fd306cSNickeau $tableName = $this->getTableName($metadata); 207*04fd306cSNickeau $resourceIdAttribute = $metadata->getResource()->getUidObject()::getPersistentName(); 208*04fd306cSNickeau $metadataIdAttribute = $metadata->getUidObject()::getPersistentName(); 209*04fd306cSNickeau $delete = <<<EOF 210*04fd306cSNickeaudelete from $tableName where $resourceIdAttribute = ? and $metadataIdAttribute = ? 211*04fd306cSNickeauEOF; 212*04fd306cSNickeau 213*04fd306cSNickeau $row = [ 214*04fd306cSNickeau $resourceIdAttribute => $row[$resourceIdAttribute], 215*04fd306cSNickeau $metadataIdAttribute => $row[$metadataIdAttribute] 216*04fd306cSNickeau ]; 217*04fd306cSNickeau $request = Sqlite::createOrGetSqlite() 218*04fd306cSNickeau ->createRequest() 219*04fd306cSNickeau ->setQueryParametrized($delete, $row); 220*04fd306cSNickeau try { 221*04fd306cSNickeau $request->execute(); 222*04fd306cSNickeau } catch (ExceptionCompile $e) { 223*04fd306cSNickeau LogUtility::msg("There was a problem during the row delete of $tableName. Message: {$e->getMessage()}"); 224*04fd306cSNickeau return; 225*04fd306cSNickeau } finally { 226*04fd306cSNickeau $request->close(); 227*04fd306cSNickeau } 228*04fd306cSNickeau 229*04fd306cSNickeau 230*04fd306cSNickeau } 231*04fd306cSNickeau 232*04fd306cSNickeau 233*04fd306cSNickeau /** 234*04fd306cSNickeau * @return array - the rows 235*04fd306cSNickeau * @var Metadata $metadata 236*04fd306cSNickeau */ 237*04fd306cSNickeau private function getDbTabularData(Metadata $metadata): array 238*04fd306cSNickeau { 239*04fd306cSNickeau 240*04fd306cSNickeau $uid = $this->resourceUidMeta; 241*04fd306cSNickeau $uidValue = $this->resourceUidMetaValue; 242*04fd306cSNickeau if ($uidValue === null) { 243*04fd306cSNickeau // no yet in the db 244*04fd306cSNickeau return []; 245*04fd306cSNickeau } 246*04fd306cSNickeau 247*04fd306cSNickeau $uidAttribute = $uid::getPersistentName(); 248*04fd306cSNickeau $children = $metadata->getChildrenObject(); 249*04fd306cSNickeau if ($children === null) { 250*04fd306cSNickeau throw new ExceptionRuntimeInternal("The children of the tabular metadata ($metadata) should be set to synchronize into the database"); 251*04fd306cSNickeau } 252*04fd306cSNickeau $attributes = []; 253*04fd306cSNickeau foreach ($children as $child) { 254*04fd306cSNickeau $attributes[] = $child::getPersistentName(); 255*04fd306cSNickeau } 256*04fd306cSNickeau $tableName = $this->getTableName($metadata); 257*04fd306cSNickeau $query = Sqlite::createSelectFromTableAndColumns($tableName, $attributes); 258*04fd306cSNickeau $query = "$query where $uidAttribute = ? "; 259*04fd306cSNickeau $res = $this->sqlite 260*04fd306cSNickeau ->createRequest() 261*04fd306cSNickeau ->setQueryParametrized($query, [$uidValue]); 262*04fd306cSNickeau $rows = []; 263*04fd306cSNickeau try { 264*04fd306cSNickeau $rows = $res 265*04fd306cSNickeau ->execute() 266*04fd306cSNickeau ->getRows(); 267*04fd306cSNickeau } catch (ExceptionCompile $e) { 268*04fd306cSNickeau throw new ExceptionRuntimeInternal("An exception has occurred with the $tableName ({$metadata->getResource()}) selection query. Message: {$e->getMessage()}, Query: ($query", self::CANONICAL, 1, $e); 269*04fd306cSNickeau } finally { 270*04fd306cSNickeau $res->close(); 271*04fd306cSNickeau } 272*04fd306cSNickeau return $rows; 273*04fd306cSNickeau 274*04fd306cSNickeau } 275*04fd306cSNickeau 276*04fd306cSNickeau public function persist() 277*04fd306cSNickeau { 278*04fd306cSNickeau // there is no notion of commit in the sqlite plugin 279*04fd306cSNickeau } 280*04fd306cSNickeau 281*04fd306cSNickeau public function isHierarchicalTextBased(): bool 282*04fd306cSNickeau { 283*04fd306cSNickeau return false; 284*04fd306cSNickeau } 285*04fd306cSNickeau 286*04fd306cSNickeau public function reset() 287*04fd306cSNickeau { 288*04fd306cSNickeau throw new ExceptionRuntime("To implement"); 289*04fd306cSNickeau } 290*04fd306cSNickeau 291*04fd306cSNickeau public function getFromName(string $name, $default = null) 292*04fd306cSNickeau { 293*04fd306cSNickeau 294*04fd306cSNickeau if ($this->resourceUidMetaValue === null) { 295*04fd306cSNickeau // not yet in the db 296*04fd306cSNickeau return $default; 297*04fd306cSNickeau } 298*04fd306cSNickeau 299*04fd306cSNickeau $row = $this->getDatabaseRow(); 300*04fd306cSNickeau $value = $row->getFromRow($name); 301*04fd306cSNickeau if ($value !== null) { 302*04fd306cSNickeau return $value; 303*04fd306cSNickeau } 304*04fd306cSNickeau return $default; 305*04fd306cSNickeau } 306*04fd306cSNickeau 307*04fd306cSNickeau public function setFromPersistentName(string $name, $value, $default = null) 308*04fd306cSNickeau { 309*04fd306cSNickeau throw new ExceptionRuntime("Not implemented"); 310*04fd306cSNickeau } 311*04fd306cSNickeau 312*04fd306cSNickeau 313*04fd306cSNickeau private function getTableName(Metadata $metadata): string 314*04fd306cSNickeau { 315*04fd306cSNickeau return $metadata->getResource()->getType() . "_" . $metadata::getPersistentName(); 316*04fd306cSNickeau 317*04fd306cSNickeau } 318*04fd306cSNickeau 319*04fd306cSNickeau public function getCanonical(): string 320*04fd306cSNickeau { 321*04fd306cSNickeau return self::CANONICAL; 322*04fd306cSNickeau } 323*04fd306cSNickeau 324*04fd306cSNickeau private function getDatabaseRow(): DatabasePageRow 325*04fd306cSNickeau { 326*04fd306cSNickeau $mapKey = $this->getResource()->getPathObject()->toAbsoluteId(); 327*04fd306cSNickeau $row = self::$dbRows[$mapKey]; 328*04fd306cSNickeau if ($row === null) { 329*04fd306cSNickeau $page = $this->getResource(); 330*04fd306cSNickeau $row = DatabasePageRow::getFromPageObject($page); 331*04fd306cSNickeau self::$dbRows[$mapKey] = $row; 332*04fd306cSNickeau } 333*04fd306cSNickeau return $row; 334*04fd306cSNickeau } 335*04fd306cSNickeau 336*04fd306cSNickeau 337*04fd306cSNickeau} 338