Initial commit
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
/composer.lock
 | 
			
		||||
/vendor
 | 
			
		||||
							
								
								
									
										53
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
# LiteDB: An IndexedDB/MongoDb-inspired wrapper around SQLite
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
To use LiteDb, pass the path to the database file, the current schema version
 | 
			
		||||
and an upgrade handler. This makes it easy to upgrade the database as new
 | 
			
		||||
stores and indexes are needed.
 | 
			
		||||
 | 
			
		||||
### Create (or upgrade) a store
 | 
			
		||||
 | 
			
		||||
Use the `createStore()` method to create or upgrade a store. If the store exists,
 | 
			
		||||
the instance will be returned so the same fluid code style can be used.
 | 
			
		||||
 | 
			
		||||
    $db->createStore("foo")
 | 
			
		||||
        ->addIndex("bar");
 | 
			
		||||
 | 
			
		||||
### Create (insert) data
 | 
			
		||||
 | 
			
		||||
Create records using the `add()` or `addAll()` methods on the store.
 | 
			
		||||
 | 
			
		||||
### Update data
 | 
			
		||||
 | 
			
		||||
Update records using the `put()` method on the store.
 | 
			
		||||
 | 
			
		||||
### Delete data
 | 
			
		||||
 | 
			
		||||
Delete records using the `delete()` method on the store.
 | 
			
		||||
 | 
			
		||||
### Retrieving (selecting) data
 | 
			
		||||
 | 
			
		||||
Find records with the `get()` method, passing the key value to match against
 | 
			
		||||
and optionally the key to match.
 | 
			
		||||
 | 
			
		||||
### Example
 | 
			
		||||
 | 
			
		||||
    $db = new LiteDb("data.db", 1, function ($db, $oldVersion) {
 | 
			
		||||
        switch ($oldVersion) {
 | 
			
		||||
            case null:
 | 
			
		||||
                $db->createStore('users', 'username')
 | 
			
		||||
                    ->addIndex('username', [ 'unique' => true ])
 | 
			
		||||
                    ->addIndex('id', [ 'autoIncrement' => true ]);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $db->users->add([
 | 
			
		||||
        'username' => 'bob',
 | 
			
		||||
        'password' => 'supersecret'
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    $user = $db->users->get('bob');
 | 
			
		||||
							
								
								
									
										23
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "noccylabs/litedb",
 | 
			
		||||
    "description": "MongoDb/IndexedDb like wrapper around Sqlite",
 | 
			
		||||
    "type": "library",
 | 
			
		||||
    "license": "GPL-3.0-OR-LATER",
 | 
			
		||||
    "authors": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Christopher Vagnetoft",
 | 
			
		||||
            "email": "cvagnetoft@gmail.com"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "autoload": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
            "NoccyLabs\\LiteDb\\": "src/"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "require": {
 | 
			
		||||
        "psr/log": "^1.1"
 | 
			
		||||
    },
 | 
			
		||||
    "require-dev": {
 | 
			
		||||
        "noccylabs/log-hoc": "@dev"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										236
									
								
								src/LiteDb.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/LiteDb.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\LiteDb;
 | 
			
		||||
 | 
			
		||||
use PDO;
 | 
			
		||||
use PDOException;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Psr\Log\NullLogger;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 * 
 | 
			
		||||
 * 
 | 
			
		||||
 */
 | 
			
		||||
class LiteDb
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /** @var int|null The current database schema version or null if not initialized */
 | 
			
		||||
    private $version = null;
 | 
			
		||||
    /** @var string|null The database filename */
 | 
			
		||||
    private $dbfile;
 | 
			
		||||
    /** @var PDO */
 | 
			
		||||
    private $pdo;
 | 
			
		||||
    /** @var array Store metadata */
 | 
			
		||||
    private $storeMeta = [];
 | 
			
		||||
    /** @var ObjectStore[] */
 | 
			
		||||
    private $stores = [];
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    private $metadata = [];
 | 
			
		||||
 | 
			
		||||
    /** @var null|LoggerInterface */
 | 
			
		||||
    public static $logger;
 | 
			
		||||
 | 
			
		||||
    // Metadata constants
 | 
			
		||||
    const META_LDB_VERSION = "ldb.version";
 | 
			
		||||
    const META_SCHEMA_VERSION = "schema.version";
 | 
			
		||||
    const META_STORES = "schema.stores";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string The path to the database file to use
 | 
			
		||||
     * @param int The database version, defaults to 1
 | 
			
		||||
     * @param callable|null The upgrade handler
 | 
			
		||||
     * @throws \RuntimeException
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(string $dbfile, int $version=1, ?callable $upgradeHandler=null)
 | 
			
		||||
    {
 | 
			
		||||
        // Assign a null logger if none has been set
 | 
			
		||||
        if (self::$logger == null) {
 | 
			
		||||
            self::$logger = new NullLogger();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            self::$logger->info("Opening Sqlite3 database file {$dbfile}");
 | 
			
		||||
            /** @var PDO $pdo */
 | 
			
		||||
            $pdo = new PDO("sqlite:" . $dbfile);
 | 
			
		||||
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 | 
			
		||||
            $this->initializeDatabaseFile($pdo);
 | 
			
		||||
            $this->pdo = $pdo;
 | 
			
		||||
        } catch (PDOException $e) {
 | 
			
		||||
            // rethrow
 | 
			
		||||
            throw new \RuntimeException("Database could not be opened/created", 0, $e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $dbVersion = $this->dbGetMeta(self::META_LDB_VERSION, null);
 | 
			
		||||
        if ($dbVersion == null) {
 | 
			
		||||
            $this->dbSetMeta(self::META_LDB_VERSION, 1);
 | 
			
		||||
        } elseif ($dbVersion > 1) {
 | 
			
		||||
            throw new \RuntimeException("Unsupported database version {$dbVersion}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->version = $this->dbGetMeta(self::META_SCHEMA_VERSION, null);
 | 
			
		||||
 | 
			
		||||
        // Call on the upgrade handler if this version is newer
 | 
			
		||||
        if (is_callable($upgradeHandler) && ($version > $this->version)) {
 | 
			
		||||
            self::$logger->notice("The database schema version " . ($this->version??'null') . " need to be upgraded to version {$version}");
 | 
			
		||||
            call_user_func($upgradeHandler, $this, $this->version);
 | 
			
		||||
            // Write the new version number to the database
 | 
			
		||||
            $this->dbSetMeta(self::META_SCHEMA_VERSION, $version);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Refresh the database version, for debug purposes
 | 
			
		||||
        $dbVersion = $this->dbGetMeta(self::META_LDB_VERSION, null);
 | 
			
		||||
        self::$logger->debug("Database version: {$dbVersion}");
 | 
			
		||||
        self::$logger->debug("Current schema version: {$version}");
 | 
			
		||||
        self::$logger->debug("Created stores:");
 | 
			
		||||
        foreach ($this->storeMeta as $k=>$v)
 | 
			
		||||
            self::$logger->debug(sprintf("  %s: %s", $k, json_encode($v)));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open and initialize the database file.
 | 
			
		||||
     * 
 | 
			
		||||
     * Creates all the necessary tables and sets the current database version.
 | 
			
		||||
     * 
 | 
			
		||||
     * @param PDO The opened database handle
 | 
			
		||||
     */
 | 
			
		||||
    protected function initializeDatabaseFile(PDO $pdo)
 | 
			
		||||
    {
 | 
			
		||||
        // check to ensure that if there are more than 0 tables, at least one of them is
 | 
			
		||||
        // the ldb_metadata table. If not, we are trying to open the wrong file.
 | 
			
		||||
        $tables = $pdo->query("SELECT name FROM sqlite_master WHERE type='table';")->fetchAll(PDO::FETCH_ASSOC);
 | 
			
		||||
        $tables = array_map(function ($v) { return $v['name']; }, $tables);
 | 
			
		||||
 | 
			
		||||
        // Check for the magic table
 | 
			
		||||
        if (count($tables) > 0 && !in_array('ldb_metadata', $tables)) {
 | 
			
		||||
            throw new \RuntimeException("Invalid database file, no ldb_metadata table found");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create or update core tables and indexes
 | 
			
		||||
        //
 | 
			
		||||
        //      ldb_metadata        key (string, primary)
 | 
			
		||||
        //                          value
 | 
			
		||||
        //      ldb_store           name (string)
 | 
			
		||||
        //                          options (string) [serialized]
 | 
			
		||||
        //                          indexes (string) [serialized]
 | 
			
		||||
        //                          primkey (int)
 | 
			
		||||
        //
 | 
			
		||||
        $pdo->exec("create table if not exists ldb_metadata (key string primary key, value);");
 | 
			
		||||
        $pdo->exec("create unique index if not exists ldb_metadata_key on ldb_metadata(key);");
 | 
			
		||||
 | 
			
		||||
        // Preload the metadata
 | 
			
		||||
        $meta = $pdo->query("SELECT key,value FROM ldb_metadata;")->fetchAll(PDO::FETCH_ASSOC);
 | 
			
		||||
        foreach ($meta as $row) {
 | 
			
		||||
            $this->metadata[$row['key']] = unserialize($row['value']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Preload the defined stores
 | 
			
		||||
        $this->storeMeta = $this->dbGetMeta(self::META_STORES, []);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create or update an object store
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string The name of the store to create or update
 | 
			
		||||
     * @param null|array An array holding any options for the store
 | 
			
		||||
     */
 | 
			
		||||
    public function createStore(string $storeName, ?array $options=null): ObjectStore
 | 
			
		||||
    {
 | 
			
		||||
        // Make sure our options are an array, and then set a magic flag to indicate that
 | 
			
		||||
        // we want to create the store if it doesn't exist.
 | 
			
		||||
        $options = (array)$options;
 | 
			
		||||
        self::$logger->info("Creating store facade for {$storeName}");
 | 
			
		||||
        $store = new ObjectStore($this, $storeName, $options);
 | 
			
		||||
        $this->stores[$storeName] = $store;
 | 
			
		||||
        return $store;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper magic function to fetch a store
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string The name of the store to access
 | 
			
		||||
     * @return ObjectStore The object store facade
 | 
			
		||||
     */
 | 
			
		||||
    public function __get(string $storeName)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getStore($storeName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch a store
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string The name of the store to access
 | 
			
		||||
     * @return ObjectStore The object store facade
 | 
			
		||||
     */
 | 
			
		||||
    public function getStore(string $storeName)
 | 
			
		||||
    {
 | 
			
		||||
        if (!array_key_exists($storeName, $this->stores) && array_key_exists($storeName, $this->storeMeta)) {
 | 
			
		||||
            // Open the store
 | 
			
		||||
            $this->createStore($storeName);
 | 
			
		||||
        } elseif (!array_key_exists($storeName, $this->storeMeta)) {
 | 
			
		||||
            // Throw exception
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->stores[$storeName];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string
 | 
			
		||||
     * @param mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function dbSetMeta(string $key, $value)
 | 
			
		||||
    {
 | 
			
		||||
        $qKey = $this->pdo->quote($key);
 | 
			
		||||
        $qValue = $this->pdo->quote(serialize($value));
 | 
			
		||||
        $this->pdo->exec("REPLACE INTO ldb_metadata (key,value) VALUES ({$qKey},{$qValue});");
 | 
			
		||||
        self::$logger->info("Setting metadata {$key} = ".json_encode($value));
 | 
			
		||||
 | 
			
		||||
        $this->metadata[$key] = $value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dbSetStoreMeta(string $store, array $meta)
 | 
			
		||||
    {
 | 
			
		||||
        $this->storeMeta[$store] = $meta;
 | 
			
		||||
        $this->dbSetMeta(self::META_STORES, $this->storeMeta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dbGetStoreMeta(string $store): array
 | 
			
		||||
    {
 | 
			
		||||
        if (!array_key_exists($store, $this->storeMeta)) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        return $this->storeMeta[$store];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dbHasStore(string $store): bool
 | 
			
		||||
    {
 | 
			
		||||
        return array_key_exists($store, $this->storeMeta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string
 | 
			
		||||
     * @param mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function dbGetMeta(string $key, $defaultValue=null)
 | 
			
		||||
    {
 | 
			
		||||
        if (array_key_exists($key, $this->metadata)) {
 | 
			
		||||
            return $this->metadata[$key];
 | 
			
		||||
        } else {
 | 
			
		||||
            return $defaultValue;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dbGetHandle(): ?PDO
 | 
			
		||||
    {
 | 
			
		||||
        return $this->pdo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										284
									
								
								src/ObjectStore.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								src/ObjectStore.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
			
		||||
<?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 [];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user