ldb = $ldb; $this->pdo = $ldb->dbGetHandle(); $this->storeName = $storeName; $this->initializeStore($options); } /** * Make sure that the schema metadata is up to date */ protected function initializeStore(array $options) { LiteDb::$logger->debug(sprintf("Initializing store %s (options: %s)", $this->storeName, json_encode($options))); if (!$this->ldb->dbHasStore($this->storeName)) { LiteDb::$logger->info("Creating new store {$this->storeName}"); $this->pdo->exec("CREATE TABLE {$this->storeName} (_data text);"); $this->writeMetadata(); } else { // load the store meta $meta = $this->ldb->dbGetStoreMeta($this->storeName); $this->options = $meta['options']; $this->indexes = $meta['indexes']; $this->primaryKey = $meta['primary']; } } protected function writeMetadata() { $meta = [ 'options' => $this->options, 'indexes' => $this->indexes, 'primary' => $this->primaryKey, ]; $this->ldb->dbSetStoreMeta($this->storeName, $meta); } /** * Generate the index data for a newly created index * * @param string The index to generate data for */ protected function generateIndex(string $key) { LiteDb::$logger->notice("(Re)generating index data for key {$key}"); // Check if we can quickly update the column with an SQL update statement // Create the actual column index } /** * Generate the key value for the data * * @param string * @param array */ protected function generateIndexValue(string $key, array $data) { // Check if a generator is set for this index. If so we can resolve the value // through the generator. $generator = array_key_exists(self::KEY_GENERATOR, $this->indexes[$key]) ?$this->indexes[$key] :null; if (is_callable($generator)) { $indexValue = call_user_func($generator, $data, $key); } elseif (array_key_exists($key, $data)) { $indexValue = $data[$key]; } return $indexValue; } /** * Create or update an index column * * @param string * @param array * @return ObjectStore $this */ public function addIndex(string $key, array $options=[]): ObjectStore { $created = !array_key_exists($key, $this->indexes); $regenerate = $options[self::KEY_REGENERATE]??false; // Modify the table if needed, to add the column to hold the index data $this->indexes[$key] = $options; if ($created) { $this->pdo->exec("alter table {$this->storeName} add column {$key};"); if ($options[self::KEY_UNIQUE]??false) { $this->pdo->exec("create unique index {$this->storeName}_{$key} on {$this->storeName}({$key});"); } else { $this->pdo->exec("create index {$this->storeName}_{$key} on {$this->storeName}({$key});"); } } // If the index is being created (doesn't exist) or the regenerate option is // set, (re)generate the index data for any rows in the db. if ($created || $regenerate) { $this->generateIndex($key); } // We want to save this as the primary key if requested if ($options[self::KEY_PRIMARY]??false) { $this->primaryKey = $key; } // Update the store metadata $this->writeMetadata(); return $this; } /** * Delete an index column * * @param string * @return ObjectStore $this */ public function deleteIndex(string $key): ObjectStore { return $this; } /** * Insert a record into the database * * @param array The data to store * @param string|null If set, the value used for the primary key */ public function add(array $data, $keyValue=null) { $cols = [ '_data' => serialize($data) ]; foreach ($this->indexes as $name=>$meta) { $cols[$name] = $this->generateIndexValue($name, $data); } // Insert the record $sql = sprintf( "insert into %s (%s) values (%s);", $this->storeName, join(",", array_keys($cols)), join(",", array_map([$this->pdo, 'quote'], array_values($cols))) ); LiteDb::$logger->debug("exec: {$sql}"); $this->pdo->exec($sql); } /** * Add multiple records to the database. * * @param array The records to store * @param bool If true, the array key will be used as record key */ public function addAll(array $records, bool $useKeys=false) { foreach ($records as $key=>$record) { try { $this->add($record, ($useKeys?$key:null)); } catch (\PDOException $e) { // Silently consume this one } } } /** * Update a record in the database * * @param array The data to store * @param string|null If set, the value used for the primary key */ public function put(array $data, $keyValue=null) { // If data has a primaryKeyValue, attempt to update it // otherwise, call on add() to create the row } /** * Delete an object from its primary key value * * @param string The key value to match against * @param string|null The key to match against, if null uses primary key */ public function delete(string $keyValue, string $key=null) { } /** * Fetch an object from its primary key value * * @param string The key value to match against * @param string|null The key to match against, if null uses primary key */ public function get(string $keyValue, ?string $key=null) { if ($key === null) { $key = $this->primaryKey; } if (!array_key_exists($key, $this->indexes)) { throw new \RuntimeException("Invalid key {$key}"); } $sql = sprintf("select _data from %s where %s=:keyvalue limit 1;", $this->storeName, $key); $query = $this->pdo->query($sql); $query->execute([ ":keyvalue" => $keyValue ]); $result = $query->fetchColumn(0); $blob = unserialize($result); return $blob; } /** * Fetch all objects from the store * * @return array */ public function getAll(): array { return []; } }