284 lines
7.6 KiB
PHP
284 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace NoccyLabs\LiteDb;
|
|
|
|
use PDO;
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
class ObjectStore
|
|
{
|
|
/** @var LiteDb */
|
|
private $ldb;
|
|
/** @var PDO The PDO object */
|
|
private $pdo;
|
|
/** @var string The name of the store */
|
|
private $storeName;
|
|
/** @var array Store options */
|
|
private $options = [];
|
|
/** @var array The indexes and their options */
|
|
private $indexes = [];
|
|
/** @var string The name of the primary key column */
|
|
private $primaryKey;
|
|
|
|
// Store option constants
|
|
const OPT_AUTO_INDEX = "auto_index";
|
|
|
|
// Key option constants
|
|
const KEY_UNIQUE = "unique";
|
|
const KEY_GENERATOR = "generator";
|
|
const KEY_AUTO_INCREMENT = "autoIncrement";
|
|
const KEY_PRIMARY = "primary";
|
|
const KEY_LENGTH = "length";
|
|
const KEY_REGENERATE = "regenerate";
|
|
const KEY_PATH = "path";
|
|
const KEY_REQUIRED = "required";
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param LiteDb
|
|
* @param string
|
|
* @param array
|
|
*/
|
|
public function __construct(LiteDb $ldb, string $storeName, array $options)
|
|
{
|
|
$this->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 [];
|
|
}
|
|
} |