diff --git a/src/DbConfig.php b/src/DbConfig.php index cbc218f..fb28469 100644 --- a/src/DbConfig.php +++ b/src/DbConfig.php @@ -24,13 +24,29 @@ class DbConfig implements IConfig { /** @var array */ private array $values = []; + /** @var string[] */ + private array $extraFieldNames; + + /** @var mixed[] */ + private array $extraFieldValues; + + /** + * @param IDbConnection $dbConn + * @param string $tableName + * @param string $nameField + * @param string $valueField + * @param array $extraFields + */ public function __construct( IDbConnection $dbConn, private string $tableName, private string $nameField = 'config_name', - private string $valueField = 'config_value' + private string $valueField = 'config_value', + array $extraFields = [], ) { $this->cache = new DbStatementCache($dbConn); + $this->extraFieldNames = array_keys($extraFields); + $this->extraFieldValues = array_values($extraFields); } public static function validateName(string $name): bool { @@ -90,15 +106,23 @@ class DbConfig implements IConfig { $names = array_values($names); $nameCount = count($names); - $stmt = $this->cache->get(sprintf( + $query = sprintf( 'SELECT COUNT(*) FROM %s WHERE %s IN (%s)', $this->tableName, $this->nameField, DbTools::prepareListString($nameCount) - )); - for($i = 0; $i < $nameCount; ++$i) - $stmt->addParameter($i + 1, $names[$i]); - $stmt->execute(); + ); + foreach($this->extraFieldNames as $extraFieldName) + $query .= sprintf(' AND %s = ?', $extraFieldName); + $stmt = $this->cache->get($query); + + $args = 0; + foreach($names as $name) + $stmt->addParameter(++$args, $name); + foreach($this->extraFieldValues as $extraFieldValue) + $stmt->addParameter(++$args, $extraFieldValue); + + $stmt->execute(); $result = $stmt->getResult(); if($result->next()) return $result->getInteger(0) >= $nameCount; @@ -117,14 +141,21 @@ class DbConfig implements IConfig { unset($this->values[$name]); $nameCount = count($names); - $stmt = $this->cache->get(sprintf( + $query = sprintf( 'DELETE FROM %s WHERE %s IN (%s)', $this->tableName, $this->nameField, DbTools::prepareListString($nameCount) - )); + ); + foreach($this->extraFieldNames as $extraFieldName) + $query .= sprintf(' AND %s = ?', $extraFieldName); - for($i = 0; $i < $nameCount; ++$i) - $stmt->addParameter($i + 1, $names[$i]); + $stmt = $this->cache->get($query); + + $args = 0; + foreach($names as $name) + $stmt->addParameter(++$args, $name); + foreach($this->extraFieldValues as $extraFieldValue) + $stmt->addParameter(++$args, $extraFieldValue); $stmt->execute(); } @@ -136,6 +167,8 @@ class DbConfig implements IConfig { $hasRange = $range !== 0; $query = sprintf('SELECT %s, %s FROM %s', $this->nameField, $this->valueField, $this->tableName); + foreach($this->extraFieldNames as $i => $extraFieldName) + $query .= sprintf(' %s %s = ?', $i < 1 ? 'WHERE' : 'AND', $extraFieldName); if($hasRange) { if($range < 0) throw new InvalidArgumentException('$range must be a positive integer.'); @@ -146,10 +179,15 @@ class DbConfig implements IConfig { } $stmt = $this->cache->get($query); + + $args = 0; + foreach($this->extraFieldValues as $extraFieldValue) + $stmt->addParameter(++$args, $extraFieldValue); if($hasRange) { - $stmt->addParameter(1, $range); - $stmt->addParameter(2, $offset); + $stmt->addParameter(++$args, $range); + $stmt->addParameter(++$args, $offset); } + $stmt->execute(); $result = $stmt->getResult(); @@ -184,13 +222,22 @@ class DbConfig implements IConfig { $names = array_values($names); $nameCount = count($names); - $stmt = $this->cache->get(sprintf( - 'SELECT %s, %s FROM %s WHERE config_name IN (%s)', - $this->nameField, $this->valueField, $this->tableName, + $query = sprintf( + 'SELECT %s, %s FROM %s WHERE %s IN (%s)', + $this->nameField, $this->valueField, $this->tableName, $this->nameField, DbTools::prepareListString($nameCount) - )); - for($i = 0; $i < $nameCount; ++$i) - $stmt->addParameter($i + 1, $names[$i]); + ); + foreach($this->extraFieldNames as $extraFieldName) + $query .= sprintf(' AND %s = ?', $extraFieldName); + + $stmt = $this->cache->get($query); + + $args = 0; + foreach($names as $name) + $stmt->addParameter(++$args, $name); + foreach($this->extraFieldValues as $extraFieldValue) + $stmt->addParameter(++$args, $extraFieldValue); + $stmt->execute(); $result = $stmt->getResult(); @@ -207,9 +254,16 @@ class DbConfig implements IConfig { if(empty($values)) return; + $fields = [$this->nameField, $this->valueField]; + if(count($this->extraFieldNames) > 0) + $fields = array_merge($fields, $this->extraFieldNames); + $fieldCount = count($fields); + $fields = implode(', ', $fields); + $stmt = $this->cache->get(sprintf( - 'INSERT INTO %s (%s, %s) VALUES (?, ?)', - $this->tableName, $this->nameField, $this->valueField + 'INSERT INTO %s (%s) VALUES (%s)', + $this->tableName, $fields, + DbTools::prepareListString($fieldCount) )); foreach($values as $name => $value) { @@ -225,8 +279,12 @@ class DbConfig implements IConfig { $this->removeValues($name); - $stmt->addParameter(1, $name); - $stmt->addParameter(2, serialize($value)); + $args = 0; + $stmt->addParameter(++$args, $name); + $stmt->addParameter(++$args, serialize($value)); + foreach($this->extraFieldValues as $extraFieldValue) + $stmt->addParameter(++$args, $extraFieldValue); + $stmt->execute(); } } diff --git a/tests/DbConfigTest.php b/tests/DbConfigTest.php index 8406126..a6338c8 100644 --- a/tests/DbConfigTest.php +++ b/tests/DbConfigTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; * @covers \Syokuhou\GetValuesTrait */ final class DbConfigTest extends TestCase { + private \Index\Data\IDbConnection $dbConn; private \Syokuhou\DbConfig $config; private const VALUES = [ @@ -34,18 +35,33 @@ final class DbConfigTest extends TestCase { 'test.int' => 'i:243230;', ]; - protected function setUp(): void { - $dbConn = \Index\Data\DbTools::create('sqlite::memory:'); - $dbConn->execute('CREATE TABLE skh_config (config_name TEXT NOT NULL COLLATE NOCASE, config_value BLOB NOT NULL, PRIMARY KEY (config_name))'); + private const USER_VALUES = [ + 'mobile.left_handed' => ['b:0;', 'b:1;'], + 'profile.allow_indexing' => ['b:1;', 'b:0;'], + ]; - $stmt = $dbConn->prepare('INSERT INTO skh_config (config_name, config_value) VALUES (?, ?)'); + protected function setUp(): void { + $this->dbConn = \Index\Data\DbTools::create('sqlite::memory:'); + $this->dbConn->execute('CREATE TABLE skh_config (config_name TEXT NOT NULL COLLATE NOCASE, config_value BLOB NOT NULL, PRIMARY KEY (config_name))'); + $this->dbConn->execute('CREATE TABLE skh_user_settings (user_id INTEGER NOT NULL, setting_name TEXT NOT NULL COLLATE NOCASE, setting_value BLOB NOT NULL, PRIMARY KEY (user_id, setting_name))'); + + $stmt = $this->dbConn->prepare('INSERT INTO skh_config (config_name, config_value) VALUES (?, ?)'); foreach(self::VALUES as $name => $value) { $stmt->addParameter(1, $name); $stmt->addParameter(2, $value); $stmt->execute(); } - $this->config = new \Syokuhou\DbConfig($dbConn, 'skh_config'); + $stmt = $this->dbConn->prepare('INSERT INTO skh_user_settings (user_id, setting_name, setting_value) VALUES (?, ?, ?)'); + for($i = 1; $i <= 10; ++$i) + foreach(self::USER_VALUES as $name => $value) { + $stmt->addParameter(1, $i); + $stmt->addParameter(2, $name); + $stmt->addParameter(3, $value[$i % 2]); + $stmt->execute(); + } + + $this->config = new \Syokuhou\DbConfig($this->dbConn, 'skh_config'); } public function testScoping(): void { @@ -152,4 +168,13 @@ final class DbConfigTest extends TestCase { $this->assertFalse(\Syokuhou\DbConfig::validateName('this..is.not.valid')); $this->assertFalse(\Syokuhou\DbConfig::validateName('First.may.Not.be.uppercase')); } + + public function testUserSettings(): void { + for($i = 1; $i <= 10; ++$i) { + $config = new \Syokuhou\DbConfig($this->dbConn, 'skh_user_settings', 'setting_name', 'setting_value', ['user_id' => $i]); + + foreach(self::USER_VALUES as $name => $value) + $this->assertEquals(unserialize($value[$i % 2]), $config->getBoolean($name)); + } + } }