diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 883c1aa7e5ec..8a2a0d4f5def 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -2929,9 +2929,24 @@ protected function _deleteBatch(string $table, array $keys, array $values): stri */ public function increment(string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + return $this->incrementAll([$column => $value]); + } + + /** + * Increments multiple numeric columns by the specified values. + * + * @param array $columns An array of column => value pairs to increment. + */ + public function incrementAll(array $columns): bool + { + $fields = []; + + foreach ($columns as $col => $val) { + $col = $this->db->protectIdentifiers($col); + $fields[$col] = "{$col} + {$val}"; + } - $sql = $this->_update($this->QBFrom[0], [$column => "{$column} + {$value}"]); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); @@ -2949,9 +2964,24 @@ public function increment(string $column, int $value = 1) */ public function decrement(string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + return $this->decrementAll([$column => $value]); + } + + /** + * Decrements multiple numeric columns by the specified values. + * + * @param array $columns An array of column => value pairs to decrement. + */ + public function decrementAll(array $columns): bool + { + $fields = []; + + foreach ($columns as $col => $val) { + $col = $this->db->protectIdentifiers($col); + $fields[$col] = "{$col} - {$val}"; + } - $sql = $this->_update($this->QBFrom[0], [$column => "{$column}-{$value}"]); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 126ab5741892..aef5c3dbd8e6 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -87,17 +87,20 @@ public function orderBy(string $orderBy, string $direction = '', ?bool $escape = } /** - * Increments a numeric column by the specified value. + * Increments multiple numeric columns by the specified values. * - * @return mixed - * - * @throws DatabaseException + * @param array $columns An array of column => value pairs to increment. */ - public function increment(string $column, int $value = 1) + public function incrementAll(array $columns): bool { - $column = $this->db->protectIdentifiers($column); + $fields = []; + + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); + $fields[$column] = "to_number({$column}, '9999999') + {$value}"; + } - $sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); @@ -109,17 +112,20 @@ public function increment(string $column, int $value = 1) } /** - * Decrements a numeric column by the specified value. + * Decrements multiple numeric columns by the specified values. * - * @return mixed - * - * @throws DatabaseException + * @param array $columns An array of column => value pairs to decrement. */ - public function decrement(string $column, int $value = 1) + public function decrementAll(array $columns): bool { - $column = $this->db->protectIdentifiers($column); + $fields = []; + + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); + $fields[$column] = "to_number({$column}, '9999999') - {$value}"; + } - $sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') - {$value}"]); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php index 96890bf45cb2..e214f5592315 100644 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -232,21 +232,24 @@ protected function _update(string $table, array $values): string } /** - * Increments a numeric column by the specified value. + * Increments multiple numeric columns by the specified values. * - * @return bool + * @param array $columns An array of column => value pairs to increment. */ - public function increment(string $column, int $value = 1) + public function incrementAll(array $columns): bool { - $column = $this->db->protectIdentifiers($column); + $fields = []; - if ($this->castTextToInt) { - $values = [$column => "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) + {$value})"]; - } else { - $values = [$column => "{$column} + {$value}"]; + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); + if ($this->castTextToInt) { + $fields[$column] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) + {$value})"; + } else { + $fields[$column] = "{$column} + {$value}"; + } } - $sql = $this->_update($this->QBFrom[0], $values); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); @@ -258,21 +261,24 @@ public function increment(string $column, int $value = 1) } /** - * Decrements a numeric column by the specified value. + * Decrements multiple numeric columns by the specified values. * - * @return bool + * @param array $columns An array of column => value pairs to decrement. */ - public function decrement(string $column, int $value = 1) + public function decrementAll(array $columns): bool { - $column = $this->db->protectIdentifiers($column); + $fields = []; - if ($this->castTextToInt) { - $values = [$column => "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) - {$value})"]; - } else { - $values = [$column => "{$column} + {$value}"]; + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); + if ($this->castTextToInt) { + $fields[$column] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) - {$value})"; + } else { + $fields[$column] = "{$column} - {$value}"; + } } - $sql = $this->_update($this->QBFrom[0], $values); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); diff --git a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php index 74fb2aa072f3..45aba855ba16 100644 --- a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php +++ b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php @@ -40,6 +40,17 @@ public function up(): void 'deleted_at' => ['type' => 'INTEGER', 'constraint' => 11, 'null' => true], ])->addKey('id', true)->createTable('job', true); + // Task Table + $this->forge->addField([ + 'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true], + 'name' => ['type' => 'VARCHAR', 'constraint' => 40], + 'description' => ['type' => 'VARCHAR', 'constraint' => 400, 'null' => true], + 'priority' => ['type' => 'VARCHAR', 'constraint' => 40, 'null' => true], + 'created_at' => ['type' => 'INTEGER', 'constraint' => 11, 'null' => true], + 'updated_at' => ['type' => 'INTEGER', 'constraint' => 11, 'null' => true], + 'deleted_at' => ['type' => 'INTEGER', 'constraint' => 11, 'null' => true], + ])->addKey('id', true)->createTable('task', true); + // Misc Table $this->forge->addField([ 'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true], @@ -182,6 +193,7 @@ public function down(): void { $this->forge->dropTable('user', true); $this->forge->dropTable('job', true); + $this->forge->dropTable('task', true); $this->forge->dropTable('misc', true); $this->forge->dropTable('type_test', true); $this->forge->dropTable('empty', true); diff --git a/tests/_support/Database/Seeds/CITestSeeder.php b/tests/_support/Database/Seeds/CITestSeeder.php index a34a5b304f03..c418a70c3c20 100644 --- a/tests/_support/Database/Seeds/CITestSeeder.php +++ b/tests/_support/Database/Seeds/CITestSeeder.php @@ -60,6 +60,23 @@ public function run(): void 'description' => 'Only Coldplay can actually called Musician', ], ], + 'task' => [ + [ + 'name' => 'Grocery', + 'description' => 'Go to the grocery store and buy some food', + 'priority' => '1', + ], + [ + 'name' => 'Write Tests', + 'description' => 'Write tests for the application', + 'priority' => '2', + ], + [ + 'name' => 'Fix Bug', + 'description' => 'Fix the bug and report to the manager', + 'priority' => '3', + ], + ], 'misc' => [ [ 'key' => '\\xxxfoo456', diff --git a/tests/system/Database/Live/IncrementTest.php b/tests/system/Database/Live/IncrementTest.php index 0051e1a9039d..98f294b03251 100644 --- a/tests/system/Database/Live/IncrementTest.php +++ b/tests/system/Database/Live/IncrementTest.php @@ -51,6 +51,17 @@ public function testIncrementWithValue(): void $this->seeInDatabase('job', ['name' => 'incremental', 'description' => '8']); } + public function testIncrementAll(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->db->table('task') + ->where('name', 'task1') + ->incrementAll(['description' => 2, 'priority' => 3]); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '4']); + } + public function testResetStateAfterIncrement(): void { $this->hasInDatabase('job', ['name' => 'account1', 'description' => '10']); @@ -87,6 +98,17 @@ public function testDecrementWithValue(): void $this->seeInDatabase('job', ['name' => 'incremental', 'description' => '4']); } + public function testDecrementAll(): void + { + $this->hasInDatabase('task', ['name' => 'task2', 'description' => '6', 'priority' => '5']); + + $this->db->table('task') + ->where('name', 'task2') + ->decrementAll(['description' => 2, 'priority' => 3]); + + $this->seeInDatabase('task', ['name' => 'task2', 'description' => '4', 'priority' => '2']); + } + public function testResetStateAfterDecrement(): void { $this->hasInDatabase('job', ['name' => 'account1', 'description' => '10']); diff --git a/tests/system/Database/Live/MetadataTest.php b/tests/system/Database/Live/MetadataTest.php index ad70d281af0a..d9f54bf98a85 100644 --- a/tests/system/Database/Live/MetadataTest.php +++ b/tests/system/Database/Live/MetadataTest.php @@ -40,6 +40,7 @@ protected function setUp(): void $prefix . 'migrations', $prefix . 'user', $prefix . 'job', + $prefix . 'task', $prefix . 'misc', $prefix . 'team_members', $prefix . 'type_test', diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index a9a33ee50165..0e6c8353cee5 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -200,6 +200,8 @@ Database Query Builder ------------- +- Added new ``incrementAll()`` and ``decrementAll()`` methods to ``CodeIgniter\Database\BaseBuilder`` for performing bulk increment/decrement operations. + Forge ----- diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index 480b8c18d92e..bc9a26433b6c 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -2034,12 +2034,22 @@ Class Reference .. php:method:: increment($column[, $value = 1]) :param string $column: The name of the column to increment - :param int $value: The amount to increment in the column + :param int $value: The amount to increment in the column Increments the value of a field by the specified amount. If the field is not a numeric field, like a ``VARCHAR``, it will likely be replaced with ``$value``. + .. php:method:: incrementAll(array $columns) + + .. versionadded:: 4.8.0 + + :param array $columns: An array of column names with their amounts to increment by + + Increments the value of multiple fields by the specified amounts. If a field + is not a numeric field, like a ``VARCHAR``, it will likely be replaced + with the amount specified for that field. + .. php:method:: decrement($column[, $value = 1]) :param string $column: The name of the column to decrement @@ -2049,6 +2059,16 @@ Class Reference is not a numeric field, like a ``VARCHAR``, it will likely be replaced with ``$value``. + .. php:method:: decrementAll(array $columns) + + .. versionadded:: 4.8.0 + + :param array $columns: An array of column names with their amounts to decrement by + + Decrements the value of multiple fields by the specified amounts. If a field + is not a numeric field, like a ``VARCHAR``, it will likely be replaced + with the amount specified for that field. + .. php:method:: truncate() :returns: ``true`` on success, ``false`` on failure, string on test mode diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index 60c71993ab4b..bf64e82e8883 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 2036 errors +# total 2032 errors includes: - argument.type.neon diff --git a/utils/phpstan-baseline/method.childReturnType.neon b/utils/phpstan-baseline/method.childReturnType.neon index cb796e2ba89f..fc3b3272ee8c 100644 --- a/utils/phpstan-baseline/method.childReturnType.neon +++ b/utils/phpstan-baseline/method.childReturnType.neon @@ -1,4 +1,4 @@ -# total 28 errors +# total 26 errors parameters: ignoreErrors: @@ -37,21 +37,11 @@ parameters: count: 1 path: ../../system/Database/Postgre/Builder.php - - - message: '#^Return type \(mixed\) of method CodeIgniter\\Database\\Postgre\\Builder\:\:decrement\(\) should be covariant with return type \(bool\) of method CodeIgniter\\Database\\BaseBuilder\:\:decrement\(\)$#' - count: 1 - path: ../../system/Database/Postgre/Builder.php - - message: '#^Return type \(mixed\) of method CodeIgniter\\Database\\Postgre\\Builder\:\:delete\(\) should be covariant with return type \(bool\|string\) of method CodeIgniter\\Database\\BaseBuilder\:\:delete\(\)$#' count: 1 path: ../../system/Database/Postgre/Builder.php - - - message: '#^Return type \(mixed\) of method CodeIgniter\\Database\\Postgre\\Builder\:\:increment\(\) should be covariant with return type \(bool\) of method CodeIgniter\\Database\\BaseBuilder\:\:increment\(\)$#' - count: 1 - path: ../../system/Database/Postgre/Builder.php - - message: '#^Return type \(mixed\) of method CodeIgniter\\Database\\Postgre\\Builder\:\:replace\(\) should be covariant with return type \(CodeIgniter\\Database\\BaseResult\|CodeIgniter\\Database\\Query\|string\|false\) of method CodeIgniter\\Database\\BaseBuilder\:\:replace\(\)$#' count: 1 diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index a8f562bbcc12..65cc1700d441 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 1228 errors +# total 1226 errors parameters: ignoreErrors: