From afc7f0cc5ae301304048a96aff51829f5e91cd3e Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 12:20:05 +0530 Subject: [PATCH 01/11] feat: update increment and decrement methods to accept multiple columns as input Co-authored-by: Copilot --- system/Database/BaseBuilder.php | 36 ++++++++++++++++++++----- system/Database/Postgre/Builder.php | 30 ++++++++++++++++----- system/Database/SQLSRV/Builder.php | 42 +++++++++++++++++++---------- 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 883c1aa7e5ec..b10cdeab0f70 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -2925,13 +2925,25 @@ protected function _deleteBatch(string $table, array $keys, array $values): stri /** * Increments a numeric column by the specified value. * + * @param array|string $column The column(s) to increment. Can be a string or an associative array of column => value pairs. + * @param int $value The value to increment by (default is 1). Ignored if $column is an associative array. + * * @return bool */ - public function increment(string $column, int $value = 1) + public function increment(array|string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + if (is_string($column)) { + $column = [$column => $value]; + } + + $fields = []; + + foreach ($column 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(); @@ -2945,13 +2957,25 @@ public function increment(string $column, int $value = 1) /** * Decrements a numeric column by the specified value. * + * @param array|string $column The column(s) to decrement. Can be a string or an associative array of column => value pairs. + * @param int $value The value to decrement by (default is 1). Ignored if $column is an associative array. + * * @return bool */ - public function decrement(string $column, int $value = 1) + public function decrement(array|string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + if (is_string($column)) { + $column = [$column => $value]; + } + + $fields = []; + + foreach ($column 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..7919baa299c0 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -93,11 +93,20 @@ public function orderBy(string $orderBy, string $direction = '', ?bool $escape = * * @throws DatabaseException */ - public function increment(string $column, int $value = 1) + public function increment(array|string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + if (is_string($column)) { + $column = [$column => $value]; + } + + $fields = []; + + foreach ($column as $col => $val) { + $col = $this->db->protectIdentifiers($col); + $fields[$col] = "to_number({$col}, '9999999') + {$val}"; + } - $sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); @@ -115,11 +124,20 @@ public function increment(string $column, int $value = 1) * * @throws DatabaseException */ - public function decrement(string $column, int $value = 1) + public function decrement(array|string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + if (is_string($column)) { + $column = [$column => $value]; + } + + $fields = []; + + foreach ($column as $col => $val) { + $col = $this->db->protectIdentifiers($col); + $fields[$col] = "to_number({$col}, '9999999') - {$val}"; + } - $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..6ca07fd921e9 100644 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -236,17 +236,24 @@ protected function _update(string $table, array $values): string * * @return bool */ - public function increment(string $column, int $value = 1) + public function increment(array|string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + if (is_string($column)) { + $column = [$column => $value]; + } - if ($this->castTextToInt) { - $values = [$column => "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) + {$value})"]; - } else { - $values = [$column => "{$column} + {$value}"]; + $fields = []; + + foreach ($column as $col => $val) { + $col = $this->db->protectIdentifiers($col); + if ($this->castTextToInt) { + $fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) + {$val})"; + } else { + $fields[$col] = "{$col} + {$val}"; + } } - $sql = $this->_update($this->QBFrom[0], $values); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); @@ -262,17 +269,24 @@ public function increment(string $column, int $value = 1) * * @return bool */ - public function decrement(string $column, int $value = 1) + public function decrement(array|string $column, int $value = 1) { - $column = $this->db->protectIdentifiers($column); + if (is_string($column)) { + $column = [$column => $value]; + } - if ($this->castTextToInt) { - $values = [$column => "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) - {$value})"]; - } else { - $values = [$column => "{$column} + {$value}"]; + $fields = []; + + foreach ($column as $col => $val) { + $col = $this->db->protectIdentifiers($col); + if ($this->castTextToInt) { + $fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) - {$val})"; + } else { + $fields[$col] = "{$col} - {$val}"; + } } - $sql = $this->_update($this->QBFrom[0], $values); + $sql = $this->_update($this->QBFrom[0], $fields); if (! $this->testMode) { $this->resetWrite(); From bff9dc8e6d2013771ea0717b880e5bf84dcf0a1f Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 12:20:26 +0530 Subject: [PATCH 02/11] feat: add task table and related seeder --- .../20160428212500_Create_test_tables.php | 12 ++++++++++ .../_support/Database/Seeds/CITestSeeder.php | 17 ++++++++++++++ tests/system/Database/Live/IncrementTest.php | 22 +++++++++++++++++++ tests/system/Database/Live/MetadataTest.php | 1 + 4 files changed, 52 insertions(+) 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..5811eb236381 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 testIncrementWithMultipleColumns(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->db->table('task') + ->where('name', 'task1') + ->increment(['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 testDecrementWithMultipleColumns(): void + { + $this->hasInDatabase('task', ['name' => 'task2', 'description' => '6', 'priority' => '5']); + + $this->db->table('task') + ->where('name', 'task2') + ->decrement(['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', From 9d6990585113504055b36d757d46b06a5e8057e9 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 12:20:38 +0530 Subject: [PATCH 03/11] docs: Update documentation --- user_guide_src/source/changelogs/v4.8.0.rst | 3 +++ user_guide_src/source/database/query_builder.rst | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index a9a33ee50165..8cc229657828 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -60,6 +60,7 @@ Method Signature Changes - **Config:** ``CodeIgniter\Config\Services::request()`` no longer accepts any parameter. - **Database:** The following methods have had their signatures updated to remove deprecated parameters: - ``CodeIgniter\Database\Forge::_createTable()`` no longer accepts the deprecated ``$ifNotExists`` parameter. The method signature is now ``_createTable(string $table, array $attributes)``. + - ``CodeIgniter\Database\BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders now accept a string or array for the first parameter, allowing multiple columns to be incremented/decremented in a single call. The method signatures are now ``increment(string|array $column, int $value = 1)`` and ``decrement(string|array $column, int $value = 1)``. Property Scope Changes ====================== @@ -200,6 +201,8 @@ Database Query Builder ------------- +- Added support for incrementing/decrementing multiple columns in a single call to ``BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders. See :ref:`query-builder-increment-decrement-multiple-columns` for details. + Forge ----- diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index 480b8c18d92e..471f3d71e006 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -2033,22 +2033,26 @@ 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 array|string $column: The name of the column(s) to increment. If multiple, then should be an array of column names with their values. + :param int $value: The amount to increment in the column (will be ignored if $column is an array) 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``. + .. note:: Prior to v4.8.0, only single columns could be incremented. + .. php:method:: decrement($column[, $value = 1]) - :param string $column: The name of the column to decrement - :param int $value: The amount to decrement in the column + :param array|string $column: The name of the column(s) to decrement. If multiple, then should be an array of column names with their values. + :param int $value: The amount to decrement in the column (will be ignored if $column is an array) Decrements 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``. + .. note:: Prior to v4.8.0, only single columns could be decremented. + .. php:method:: truncate() :returns: ``true`` on success, ``false`` on failure, string on test mode From 77f431ad832eab1f1231bd9f016ab5f76473486f Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 12:32:44 +0530 Subject: [PATCH 04/11] docs: Make the signature consistent with actual one --- user_guide_src/source/changelogs/v4.8.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 8cc229657828..355d9d90daca 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -60,7 +60,7 @@ Method Signature Changes - **Config:** ``CodeIgniter\Config\Services::request()`` no longer accepts any parameter. - **Database:** The following methods have had their signatures updated to remove deprecated parameters: - ``CodeIgniter\Database\Forge::_createTable()`` no longer accepts the deprecated ``$ifNotExists`` parameter. The method signature is now ``_createTable(string $table, array $attributes)``. - - ``CodeIgniter\Database\BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders now accept a string or array for the first parameter, allowing multiple columns to be incremented/decremented in a single call. The method signatures are now ``increment(string|array $column, int $value = 1)`` and ``decrement(string|array $column, int $value = 1)``. + - ``CodeIgniter\Database\BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders now accept a string or array for the first parameter, allowing multiple columns to be incremented/decremented in a single call. The method signatures are now ``increment(array|string $column, int $value = 1)`` and ``decrement(array|string $column, int $value = 1)``. Property Scope Changes ====================== From a845cf429cd7ac500ab815dbe5d0f3534c3917a9 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 12:34:10 +0530 Subject: [PATCH 05/11] docs: Remove unnecessary link --- user_guide_src/source/changelogs/v4.8.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 355d9d90daca..a5437aab2abb 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -201,7 +201,7 @@ Database Query Builder ------------- -- Added support for incrementing/decrementing multiple columns in a single call to ``BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders. See :ref:`query-builder-increment-decrement-multiple-columns` for details. +- Added support for incrementing/decrementing multiple columns in a single call to ``BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders. Forge ----- From ca4fdf7fcfbfbdf931c190a8f065ed81154b91c1 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 15:11:47 +0530 Subject: [PATCH 06/11] feat: Added new methods instead of BC Co-authored-by: Copilot --- system/Database/BaseBuilder.php | 38 +++++++++++-------- system/Database/Postgre/Builder.php | 36 ++++++------------ system/Database/SQLSRV/Builder.php | 36 +++++++----------- tests/system/Database/Live/IncrementTest.php | 8 ++-- user_guide_src/source/changelogs/v4.8.0.rst | 3 +- .../source/database/query_builder.rst | 28 +++++++++++--- 6 files changed, 75 insertions(+), 74 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index b10cdeab0f70..8a2a0d4f5def 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -2925,20 +2925,23 @@ protected function _deleteBatch(string $table, array $keys, array $values): stri /** * Increments a numeric column by the specified value. * - * @param array|string $column The column(s) to increment. Can be a string or an associative array of column => value pairs. - * @param int $value The value to increment by (default is 1). Ignored if $column is an associative array. - * * @return bool */ - public function increment(array|string $column, int $value = 1) + public function increment(string $column, int $value = 1) { - if (is_string($column)) { - $column = [$column => $value]; - } + 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 ($column as $col => $val) { + foreach ($columns as $col => $val) { $col = $this->db->protectIdentifiers($col); $fields[$col] = "{$col} + {$val}"; } @@ -2957,20 +2960,23 @@ public function increment(array|string $column, int $value = 1) /** * Decrements a numeric column by the specified value. * - * @param array|string $column The column(s) to decrement. Can be a string or an associative array of column => value pairs. - * @param int $value The value to decrement by (default is 1). Ignored if $column is an associative array. - * * @return bool */ - public function decrement(array|string $column, int $value = 1) + public function decrement(string $column, int $value = 1) { - if (is_string($column)) { - $column = [$column => $value]; - } + 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 ($column as $col => $val) { + foreach ($columns as $col => $val) { $col = $this->db->protectIdentifiers($col); $fields[$col] = "{$col} - {$val}"; } diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 7919baa299c0..aef5c3dbd8e6 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -87,23 +87,17 @@ 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(array|string $column, int $value = 1) + public function incrementAll(array $columns): bool { - if (is_string($column)) { - $column = [$column => $value]; - } - $fields = []; - foreach ($column as $col => $val) { - $col = $this->db->protectIdentifiers($col); - $fields[$col] = "to_number({$col}, '9999999') + {$val}"; + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); + $fields[$column] = "to_number({$column}, '9999999') + {$value}"; } $sql = $this->_update($this->QBFrom[0], $fields); @@ -118,23 +112,17 @@ public function increment(array|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(array|string $column, int $value = 1) + public function decrementAll(array $columns): bool { - if (is_string($column)) { - $column = [$column => $value]; - } - $fields = []; - foreach ($column as $col => $val) { - $col = $this->db->protectIdentifiers($col); - $fields[$col] = "to_number({$col}, '9999999') - {$val}"; + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); + $fields[$column] = "to_number({$column}, '9999999') - {$value}"; } $sql = $this->_update($this->QBFrom[0], $fields); diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php index 6ca07fd921e9..e214f5592315 100644 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -232,24 +232,20 @@ 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(array|string $column, int $value = 1) + public function incrementAll(array $columns): bool { - if (is_string($column)) { - $column = [$column => $value]; - } - $fields = []; - foreach ($column as $col => $val) { - $col = $this->db->protectIdentifiers($col); + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); if ($this->castTextToInt) { - $fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) + {$val})"; + $fields[$column] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) + {$value})"; } else { - $fields[$col] = "{$col} + {$val}"; + $fields[$column] = "{$column} + {$value}"; } } @@ -265,24 +261,20 @@ public function increment(array|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(array|string $column, int $value = 1) + public function decrementAll(array $columns): bool { - if (is_string($column)) { - $column = [$column => $value]; - } - $fields = []; - foreach ($column as $col => $val) { - $col = $this->db->protectIdentifiers($col); + foreach ($columns as $column => $value) { + $column = $this->db->protectIdentifiers($column); if ($this->castTextToInt) { - $fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) - {$val})"; + $fields[$column] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) - {$value})"; } else { - $fields[$col] = "{$col} - {$val}"; + $fields[$column] = "{$column} - {$value}"; } } diff --git a/tests/system/Database/Live/IncrementTest.php b/tests/system/Database/Live/IncrementTest.php index 5811eb236381..98f294b03251 100644 --- a/tests/system/Database/Live/IncrementTest.php +++ b/tests/system/Database/Live/IncrementTest.php @@ -51,13 +51,13 @@ public function testIncrementWithValue(): void $this->seeInDatabase('job', ['name' => 'incremental', 'description' => '8']); } - public function testIncrementWithMultipleColumns(): void + public function testIncrementAll(): void { $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); $this->db->table('task') ->where('name', 'task1') - ->increment(['description' => 2, 'priority' => 3]); + ->incrementAll(['description' => 2, 'priority' => 3]); $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '4']); } @@ -98,13 +98,13 @@ public function testDecrementWithValue(): void $this->seeInDatabase('job', ['name' => 'incremental', 'description' => '4']); } - public function testDecrementWithMultipleColumns(): void + public function testDecrementAll(): void { $this->hasInDatabase('task', ['name' => 'task2', 'description' => '6', 'priority' => '5']); $this->db->table('task') ->where('name', 'task2') - ->decrement(['description' => 2, 'priority' => 3]); + ->decrementAll(['description' => 2, 'priority' => 3]); $this->seeInDatabase('task', ['name' => 'task2', 'description' => '4', 'priority' => '2']); } diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index a5437aab2abb..0e6c8353cee5 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -60,7 +60,6 @@ Method Signature Changes - **Config:** ``CodeIgniter\Config\Services::request()`` no longer accepts any parameter. - **Database:** The following methods have had their signatures updated to remove deprecated parameters: - ``CodeIgniter\Database\Forge::_createTable()`` no longer accepts the deprecated ``$ifNotExists`` parameter. The method signature is now ``_createTable(string $table, array $attributes)``. - - ``CodeIgniter\Database\BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders now accept a string or array for the first parameter, allowing multiple columns to be incremented/decremented in a single call. The method signatures are now ``increment(array|string $column, int $value = 1)`` and ``decrement(array|string $column, int $value = 1)``. Property Scope Changes ====================== @@ -201,7 +200,7 @@ Database Query Builder ------------- -- Added support for incrementing/decrementing multiple columns in a single call to ``BaseBuilder::increment()`` and ``decrement()`` as well as driver-specific builders. +- 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 471f3d71e006..bc9a26433b6c 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -2033,25 +2033,41 @@ Class Reference .. php:method:: increment($column[, $value = 1]) - :param array|string $column: The name of the column(s) to increment. If multiple, then should be an array of column names with their values. - :param int $value: The amount to increment in the column (will be ignored if $column is an array) + :param string $column: The name of the column to increment + :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``. - .. note:: Prior to v4.8.0, only single columns could be incremented. + .. 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 array|string $column: The name of the column(s) to decrement. If multiple, then should be an array of column names with their values. - :param int $value: The amount to decrement in the column (will be ignored if $column is an array) + :param string $column: The name of the column to decrement + :param int $value: The amount to decrement in the column Decrements 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``. - .. note:: Prior to v4.8.0, only single columns could be decremented. + .. 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() From acdc9743e998aead11e1320130fe9ee69cddf608 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 25 Apr 2026 16:57:30 +0530 Subject: [PATCH 07/11] fix: PHPStan errors --- utils/phpstan-baseline/loader.neon | 2 +- utils/phpstan-baseline/method.childReturnType.neon | 12 +----------- .../phpstan-baseline/missingType.iterableValue.neon | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) 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: From 3f8d8870106da3613b605f3ff0482c71b1d74691 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sun, 3 May 2026 15:48:16 +0530 Subject: [PATCH 08/11] Applying suggested changes. --- system/Database/BaseBuilder.php | 51 +++++- system/Database/Postgre/Builder.php | 59 +++++-- system/Database/SQLSRV/Builder.php | 64 +++++-- tests/system/Database/Live/IncrementTest.php | 164 +++++++++++++++--- user_guide_src/source/changelogs/v4.8.0.rst | 2 +- .../source/database/query_builder.rst | 14 +- 6 files changed, 292 insertions(+), 62 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 8a2a0d4f5def..b081f7c9a06a 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -19,6 +19,7 @@ use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Traits\ConditionalTrait; use Config\Feature; +use TypeError; /** * Class BaseBuilder @@ -2929,19 +2930,36 @@ protected function _deleteBatch(string $table, array $keys, array $values): stri */ public function increment(string $column, int $value = 1) { - return $this->incrementAll([$column => $value]); + return $this->incrementMany([$column], $value); } /** - * Increments multiple numeric columns by the specified values. + * Increments multiple numeric columns by the specified value(s). * - * @param array $columns An array of column => value pairs to increment. + * @param array|list $columns A list of columns or array of column => value pairs to increment. + * @param int $value The value to increment by if $columns is a list of column names. */ - public function incrementAll(array $columns): bool + public function incrementMany(array $columns, int $value = 1): bool { + if ($columns === []) { + throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.'); + } + + if (array_is_list($columns)) { + $columns = array_fill_keys($columns, $value); + } + $fields = []; foreach ($columns as $col => $val) { + if (! is_int($val)) { + throw new TypeError(sprintf( + 'Argument #1 ($columns) must contain only int values, %s given for "%s".', + get_debug_type($val), + $col, + )); + } + $col = $this->db->protectIdentifiers($col); $fields[$col] = "{$col} + {$val}"; } @@ -2964,19 +2982,36 @@ public function incrementAll(array $columns): bool */ public function decrement(string $column, int $value = 1) { - return $this->decrementAll([$column => $value]); + return $this->decrementMany([$column], $value); } /** - * Decrements multiple numeric columns by the specified values. + * Decrements multiple numeric columns by the specified value(s). * - * @param array $columns An array of column => value pairs to decrement. + * @param array|list $columns A list of columns or array of column => value pairs to decrement. + * @param int $value The value to decrement by if $columns is a list of column names. */ - public function decrementAll(array $columns): bool + public function decrementMany(array $columns, int $value = 1): bool { + if ($columns === []) { + throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.'); + } + + if (array_is_list($columns)) { + $columns = array_fill_keys($columns, $value); + } + $fields = []; foreach ($columns as $col => $val) { + if (! is_int($val)) { + throw new TypeError(sprintf( + 'Argument #1 ($columns) must contain only int values, %s given for "%s".', + get_debug_type($val), + $col, + )); + } + $col = $this->db->protectIdentifiers($col); $fields[$col] = "{$col} - {$val}"; } diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index aef5c3dbd8e6..5f5a6735ac96 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -17,6 +17,7 @@ use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\RawSql; use CodeIgniter\Exceptions\InvalidArgumentException; +use TypeError; /** * Builder for Postgre @@ -87,17 +88,34 @@ public function orderBy(string $orderBy, string $direction = '', ?bool $escape = } /** - * Increments multiple numeric columns by the specified values. + * Increments multiple numeric columns by the specified value(s). * - * @param array $columns An array of column => value pairs to increment. + * @param array|list $columns A list of columns or array of column => value pairs to increment. + * @param int $value The value to increment by if $columns is a list of column names. */ - public function incrementAll(array $columns): bool + public function incrementMany(array $columns, int $value = 1): bool { + if ($columns === []) { + throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.'); + } + + if (array_is_list($columns)) { + $columns = array_fill_keys($columns, $value); + } + $fields = []; - foreach ($columns as $column => $value) { - $column = $this->db->protectIdentifiers($column); - $fields[$column] = "to_number({$column}, '9999999') + {$value}"; + foreach ($columns as $col => $val) { + if (! is_int($val)) { + throw new TypeError(sprintf( + 'Argument #1 ($columns) must contain only int values, %s given for "%s".', + get_debug_type($val), + $col, + )); + } + + $col = $this->db->protectIdentifiers($col); + $fields[$col] = "to_number({$col}, '9999999') + {$val}"; } $sql = $this->_update($this->QBFrom[0], $fields); @@ -112,17 +130,34 @@ public function incrementAll(array $columns): bool } /** - * Decrements multiple numeric columns by the specified values. + * Decrements multiple numeric columns by the specified value(s). * - * @param array $columns An array of column => value pairs to decrement. + * @param array|list $columns A list of columns or array of column => value pairs to decrement. + * @param int $value The value to decrement by if $columns is a list of column names. */ - public function decrementAll(array $columns): bool + public function decrementMany(array $columns, int $value = 1): bool { + if ($columns === []) { + throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.'); + } + + if (array_is_list($columns)) { + $columns = array_fill_keys($columns, $value); + } + $fields = []; - foreach ($columns as $column => $value) { - $column = $this->db->protectIdentifiers($column); - $fields[$column] = "to_number({$column}, '9999999') - {$value}"; + foreach ($columns as $col => $val) { + if (! is_int($val)) { + throw new TypeError(sprintf( + 'Argument #1 ($columns) must contain only int values, %s given for "%s".', + get_debug_type($val), + $col, + )); + } + + $col = $this->db->protectIdentifiers($col); + $fields[$col] = "to_number({$col}, '9999999') - {$val}"; } $sql = $this->_update($this->QBFrom[0], $fields); diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php index e214f5592315..ffda2ba6cb08 100644 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -18,7 +18,9 @@ use CodeIgniter\Database\Exceptions\DataException; use CodeIgniter\Database\RawSql; use CodeIgniter\Database\ResultInterface; +use CodeIgniter\Exceptions\InvalidArgumentException; use Config\Feature; +use TypeError; /** * Builder for SQLSRV @@ -232,20 +234,37 @@ protected function _update(string $table, array $values): string } /** - * Increments multiple numeric columns by the specified values. + * Increments multiple numeric columns by the specified value(s). * - * @param array $columns An array of column => value pairs to increment. + * @param array|list $columns A list of columns or array of column => value pairs to increment. + * @param int $value The value to increment by if $columns is a list of column names. */ - public function incrementAll(array $columns): bool + public function incrementMany(array $columns, int $value = 1): bool { + if ($columns === []) { + throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.'); + } + + if (array_is_list($columns)) { + $columns = array_fill_keys($columns, $value); + } + $fields = []; - foreach ($columns as $column => $value) { - $column = $this->db->protectIdentifiers($column); + foreach ($columns as $col => $val) { + if (! is_int($val)) { + throw new TypeError(sprintf( + 'Argument #1 ($columns) must contain only int values, %s given for "%s".', + get_debug_type($val), + $col, + )); + } + + $col = $this->db->protectIdentifiers($col); if ($this->castTextToInt) { - $fields[$column] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) + {$value})"; + $fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) + {$val})"; } else { - $fields[$column] = "{$column} + {$value}"; + $fields[$col] = "{$col} + {$val}"; } } @@ -261,20 +280,37 @@ public function incrementAll(array $columns): bool } /** - * Decrements multiple numeric columns by the specified values. + * Decrements multiple numeric columns by the specified value(s). * - * @param array $columns An array of column => value pairs to decrement. + * @param array|list $columns A list of columns or array of column => value pairs to decrement. + * @param int $value The value to decrement by if $columns is a list of column names. */ - public function decrementAll(array $columns): bool + public function decrementMany(array $columns, int $value = 1): bool { + if ($columns === []) { + throw new InvalidArgumentException('Argument #1 ($columns) cannot be empty.'); + } + + if (array_is_list($columns)) { + $columns = array_fill_keys($columns, $value); + } + $fields = []; - foreach ($columns as $column => $value) { - $column = $this->db->protectIdentifiers($column); + foreach ($columns as $col => $val) { + if (! is_int($val)) { + throw new TypeError(sprintf( + 'Argument #1 ($columns) must contain only int values, %s given for "%s".', + get_debug_type($val), + $col, + )); + } + + $col = $this->db->protectIdentifiers($col); if ($this->castTextToInt) { - $fields[$column] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$column})) - {$value})"; + $fields[$col] = "CONVERT(VARCHAR(MAX),CONVERT(INT,CONVERT(VARCHAR(MAX), {$col})) - {$val})"; } else { - $fields[$column] = "{$column} - {$value}"; + $fields[$col] = "{$col} - {$val}"; } } diff --git a/tests/system/Database/Live/IncrementTest.php b/tests/system/Database/Live/IncrementTest.php index 98f294b03251..d3dc347614dc 100644 --- a/tests/system/Database/Live/IncrementTest.php +++ b/tests/system/Database/Live/IncrementTest.php @@ -13,10 +13,12 @@ namespace CodeIgniter\Database\Live; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use PHPUnit\Framework\Attributes\Group; use Tests\Support\Database\Seeds\CITestSeeder; +use TypeError; /** * @internal @@ -51,29 +53,89 @@ public function testIncrementWithValue(): void $this->seeInDatabase('job', ['name' => 'incremental', 'description' => '8']); } - public function testIncrementAll(): void + public function testResetStateAfterIncrement(): void + { + $this->hasInDatabase('job', ['name' => 'account1', 'description' => '10']); + $this->hasInDatabase('job', ['name' => 'account2', 'description' => '10']); + + $builder = $this->db->table('job'); + + $builder->where('name', 'account1')->increment('description'); + $builder->where('name', 'account2')->increment('description'); + + $this->seeInDatabase('job', ['name' => 'account1', 'description' => '11']); + $this->seeInDatabase('job', ['name' => 'account2', 'description' => '11']); + } + + public function testIncrementMany(): void { $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); $this->db->table('task') ->where('name', 'task1') - ->incrementAll(['description' => 2, 'priority' => 3]); + ->incrementMany(['description' => 2, 'priority' => 3]); $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '4']); } - public function testResetStateAfterIncrement(): void + public function testIncrementManyWithValue(): void { - $this->hasInDatabase('job', ['name' => 'account1', 'description' => '10']); - $this->hasInDatabase('job', ['name' => 'account2', 'description' => '10']); + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); - $builder = $this->db->table('job'); + $this->db->table('task') + ->where('name', 'task1') + ->incrementMany(['description', 'priority'], 2); - $builder->where('name', 'account1')->increment('description'); - $builder->where('name', 'account2')->increment('description'); + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '3']); + } - $this->seeInDatabase('job', ['name' => 'account1', 'description' => '11']); - $this->seeInDatabase('job', ['name' => 'account2', 'description' => '11']); + public function testIncrementManyWithNegativeValue(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->db->table('task') + ->where('name', 'task1') + ->incrementMany(['description' => 2, 'priority' => -1]); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '0']); + } + + public function testIncrementManyWithEmptyColumns(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #1 ($columns) cannot be empty.'); + + $this->db->table('task') + ->where('name', 'task1') + ->incrementMany([]); + } + + public function testIncrementManyWithNonIntegerValues(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->expectException(TypeError::class); + $this->expectExceptionMessage('Argument #1 ($columns) must contain only int values, string given for "priority".'); + + $this->db->table('task') + ->where('name', 'task1') + ->incrementMany(['description' => 2, 'priority' => 'wrongValue']); + } + + public function testResetStateAfterIncrementMany(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('task', ['name' => 'task2', 'description' => '2', 'priority' => '4']); + + $builder = $this->db->table('task'); + + $builder->where('name', 'task1')->incrementMany(['description', 'priority']); + $builder->where('name', 'task2')->incrementMany(['description', 'priority']); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '7', 'priority' => '2']); + $this->seeInDatabase('task', ['name' => 'task2', 'description' => '3', 'priority' => '5']); } public function testDecrement(): void @@ -98,17 +160,6 @@ 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']); @@ -122,4 +173,75 @@ public function testResetStateAfterDecrement(): void $this->seeInDatabase('job', ['name' => 'account1', 'description' => '9']); $this->seeInDatabase('job', ['name' => 'account2', 'description' => '9']); } + + public function testDecrementMany(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->db->table('task') + ->where('name', 'task1') + ->decrementMany(['description' => 2, 'priority' => 3]); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '4', 'priority' => '-2']); + } + + public function testDecrementManyWithValue(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->db->table('task') + ->where('name', 'task1') + ->decrementMany(['description', 'priority'], 2); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '4', 'priority' => '-1']); + } + + public function testDecrementManyWithNegativeValues(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->db->table('task') + ->where('name', 'task1') + ->decrementMany(['description' => 2, 'priority' => -1]); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '4', 'priority' => '2']); + } + + public function testDecrementManyWithEmptyColumns(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #1 ($columns) cannot be empty.'); + + $this->db->table('task') + ->where('name', 'task1') + ->decrementMany([]); + } + + public function testDecrementManyWithNonIntegerValues(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + + $this->expectException(TypeError::class); + $this->expectExceptionMessage('Argument #1 ($columns) must contain only int values, string given for "priority".'); + + $this->db->table('task') + ->where('name', 'task1') + ->decrementMany(['description' => 2, 'priority' => 'wrongValue']); + } + + public function testResetStateAfterDecrementMany(): void + { + $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('task', ['name' => 'task2', 'description' => '2', 'priority' => '4']); + + $builder = $this->db->table('task'); + + $builder->where('name', 'task1')->decrementMany(['description', 'priority']); + $builder->where('name', 'task2')->decrementMany(['description', 'priority']); + + $this->seeInDatabase('task', ['name' => 'task1', 'description' => '5', 'priority' => '0']); + $this->seeInDatabase('task', ['name' => 'task2', 'description' => '1', 'priority' => '3']); + } } diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 0e6c8353cee5..f6ea71e9d702 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -200,7 +200,7 @@ Database Query Builder ------------- -- Added new ``incrementAll()`` and ``decrementAll()`` methods to ``CodeIgniter\Database\BaseBuilder`` for performing bulk increment/decrement operations. +- Added new ``incrementMany()`` and ``decrementMany()`` 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 bc9a26433b6c..c67bba75f36a 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -2034,17 +2034,18 @@ 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) + .. php:method:: incrementMany($columns[, $value = 1]) .. versionadded:: 4.8.0 - :param array $columns: An array of column names with their amounts to increment by + :param array $columns: A list of columns or array of column => value pairs to decrement. + :param int $value: The amount to increment in the columns, if $columns is a list of columns. 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 @@ -2053,17 +2054,18 @@ Class Reference .. php:method:: decrement($column[, $value = 1]) :param string $column: The name of the column to decrement - :param int $value: The amount to decrement in the column + :param int $value: The amount to decrement in the column Decrements 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:: decrementAll(array $columns) + .. php:method:: decrementMany($columns[, $value = 1]) .. versionadded:: 4.8.0 - :param array $columns: An array of column names with their amounts to decrement by + :param array $columns: A list of columns or array of column => value pairs to decrement. + :param int $value: The amount to decrement in the columns, if $columns is a list of columns. 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 From 789b35dc9ea36b8375b34f5ecc927cb0d1f50ad0 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Tue, 5 May 2026 15:36:59 +0530 Subject: [PATCH 09/11] Fix error in docs and add return types --- user_guide_src/source/database/query_builder.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index c67bba75f36a..5e0ae528e2a2 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -2044,8 +2044,10 @@ Class Reference .. versionadded:: 4.8.0 - :param array $columns: A list of columns or array of column => value pairs to decrement. + :param array $columns: A list of columns or array of column => value pairs to increment. :param int $value: The amount to increment in the columns, if $columns is a list of columns. + :returns: ``true`` on success, ``false`` on failure + :rtype: bool 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 @@ -2066,6 +2068,8 @@ Class Reference :param array $columns: A list of columns or array of column => value pairs to decrement. :param int $value: The amount to decrement in the columns, if $columns is a list of columns. + :returns: ``true`` on success, ``false`` on failure + :rtype: bool 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 From ea68b61ac704299aa909c2bae96faf21db6ff73b Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Tue, 5 May 2026 15:51:29 +0530 Subject: [PATCH 10/11] Switch to job table instead of new task table --- .../20160428212500_Create_test_tables.php | 12 -- tests/system/Database/Live/IncrementTest.php | 116 +++++++++--------- tests/system/Database/Live/MetadataTest.php | 1 - 3 files changed, 58 insertions(+), 71 deletions(-) diff --git a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php index 45aba855ba16..74fb2aa072f3 100644 --- a/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php +++ b/tests/_support/Database/Migrations/20160428212500_Create_test_tables.php @@ -40,17 +40,6 @@ 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], @@ -193,7 +182,6 @@ 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/system/Database/Live/IncrementTest.php b/tests/system/Database/Live/IncrementTest.php index d3dc347614dc..2c013002439d 100644 --- a/tests/system/Database/Live/IncrementTest.php +++ b/tests/system/Database/Live/IncrementTest.php @@ -69,73 +69,73 @@ public function testResetStateAfterIncrement(): void public function testIncrementMany(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); - $this->db->table('task') - ->where('name', 'task1') - ->incrementMany(['description' => 2, 'priority' => 3]); + $this->db->table('job') + ->where('name', 'job1') + ->incrementMany(['description' => 2, 'created_at' => 3]); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '4']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '8', 'created_at' => 4]); } public function testIncrementManyWithValue(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); - $this->db->table('task') - ->where('name', 'task1') - ->incrementMany(['description', 'priority'], 2); + $this->db->table('job') + ->where('name', 'job1') + ->incrementMany(['description', 'created_at'], 2); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '3']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '8', 'created_at' => 3]); } public function testIncrementManyWithNegativeValue(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); - $this->db->table('task') - ->where('name', 'task1') - ->incrementMany(['description' => 2, 'priority' => -1]); + $this->db->table('job') + ->where('name', 'job1') + ->incrementMany(['description' => 2, 'created_at' => -1]); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '8', 'priority' => '0']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '8', 'created_at' => 0]); } public function testIncrementManyWithEmptyColumns(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #1 ($columns) cannot be empty.'); - $this->db->table('task') + $this->db->table('job') ->where('name', 'task1') ->incrementMany([]); } public function testIncrementManyWithNonIntegerValues(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); $this->expectException(TypeError::class); - $this->expectExceptionMessage('Argument #1 ($columns) must contain only int values, string given for "priority".'); + $this->expectExceptionMessage('Argument #1 ($columns) must contain only int values, string given for "created_at".'); - $this->db->table('task') - ->where('name', 'task1') - ->incrementMany(['description' => 2, 'priority' => 'wrongValue']); + $this->db->table('job') + ->where('name', 'job1') + ->incrementMany(['description' => 2, 'created_at' => 'wrongValue']); } public function testResetStateAfterIncrementMany(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); - $this->hasInDatabase('task', ['name' => 'task2', 'description' => '2', 'priority' => '4']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); + $this->hasInDatabase('job', ['name' => 'job2', 'description' => '2', 'created_at' => 4]); - $builder = $this->db->table('task'); + $builder = $this->db->table('job'); - $builder->where('name', 'task1')->incrementMany(['description', 'priority']); - $builder->where('name', 'task2')->incrementMany(['description', 'priority']); + $builder->where('name', 'job1')->incrementMany(['description', 'created_at']); + $builder->where('name', 'job2')->incrementMany(['description', 'created_at']); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '7', 'priority' => '2']); - $this->seeInDatabase('task', ['name' => 'task2', 'description' => '3', 'priority' => '5']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '7', 'created_at' => 2]); + $this->seeInDatabase('job', ['name' => 'job2', 'description' => '3', 'created_at' => 5]); } public function testDecrement(): void @@ -176,72 +176,72 @@ public function testResetStateAfterDecrement(): void public function testDecrementMany(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); - $this->db->table('task') - ->where('name', 'task1') - ->decrementMany(['description' => 2, 'priority' => 3]); + $this->db->table('job') + ->where('name', 'job1') + ->decrementMany(['description' => 2, 'created_at' => 3]); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '4', 'priority' => '-2']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '4', 'created_at' => -2]); } public function testDecrementManyWithValue(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); - $this->db->table('task') - ->where('name', 'task1') - ->decrementMany(['description', 'priority'], 2); + $this->db->table('job') + ->where('name', 'job1') + ->decrementMany(['description', 'created_at'], 2); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '4', 'priority' => '-1']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '4', 'created_at' => -1]); } public function testDecrementManyWithNegativeValues(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); - $this->db->table('task') - ->where('name', 'task1') - ->decrementMany(['description' => 2, 'priority' => -1]); + $this->db->table('job') + ->where('name', 'job1') + ->decrementMany(['description' => 2, 'created_at' => -1]); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '4', 'priority' => '2']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '4', 'created_at' => 2]); } public function testDecrementManyWithEmptyColumns(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #1 ($columns) cannot be empty.'); - $this->db->table('task') + $this->db->table('job') ->where('name', 'task1') ->decrementMany([]); } public function testDecrementManyWithNonIntegerValues(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); $this->expectException(TypeError::class); - $this->expectExceptionMessage('Argument #1 ($columns) must contain only int values, string given for "priority".'); + $this->expectExceptionMessage('Argument #1 ($columns) must contain only int values, string given for "created_at".'); - $this->db->table('task') - ->where('name', 'task1') - ->decrementMany(['description' => 2, 'priority' => 'wrongValue']); + $this->db->table('job') + ->where('name', 'job1') + ->decrementMany(['description' => 2, 'created_at' => 'wrongValue']); } public function testResetStateAfterDecrementMany(): void { - $this->hasInDatabase('task', ['name' => 'task1', 'description' => '6', 'priority' => '1']); - $this->hasInDatabase('task', ['name' => 'task2', 'description' => '2', 'priority' => '4']); + $this->hasInDatabase('job', ['name' => 'job1', 'description' => '6', 'created_at' => 1]); + $this->hasInDatabase('job', ['name' => 'job2', 'description' => '2', 'created_at' => 4]); - $builder = $this->db->table('task'); + $builder = $this->db->table('job'); - $builder->where('name', 'task1')->decrementMany(['description', 'priority']); - $builder->where('name', 'task2')->decrementMany(['description', 'priority']); + $builder->where('name', 'job1')->decrementMany(['description', 'created_at']); + $builder->where('name', 'job2')->decrementMany(['description', 'created_at']); - $this->seeInDatabase('task', ['name' => 'task1', 'description' => '5', 'priority' => '0']); - $this->seeInDatabase('task', ['name' => 'task2', 'description' => '1', 'priority' => '3']); + $this->seeInDatabase('job', ['name' => 'job1', 'description' => '5', 'created_at' => 0]); + $this->seeInDatabase('job', ['name' => 'job2', 'description' => '1', 'created_at' => 3]); } } diff --git a/tests/system/Database/Live/MetadataTest.php b/tests/system/Database/Live/MetadataTest.php index d9f54bf98a85..ad70d281af0a 100644 --- a/tests/system/Database/Live/MetadataTest.php +++ b/tests/system/Database/Live/MetadataTest.php @@ -40,7 +40,6 @@ protected function setUp(): void $prefix . 'migrations', $prefix . 'user', $prefix . 'job', - $prefix . 'task', $prefix . 'misc', $prefix . 'team_members', $prefix . 'type_test', From d1c57474086de14953cb348d1bb989795db9a624 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Tue, 5 May 2026 15:55:13 +0530 Subject: [PATCH 11/11] Removed task table from seeder --- tests/_support/Database/Seeds/CITestSeeder.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/_support/Database/Seeds/CITestSeeder.php b/tests/_support/Database/Seeds/CITestSeeder.php index c418a70c3c20..a34a5b304f03 100644 --- a/tests/_support/Database/Seeds/CITestSeeder.php +++ b/tests/_support/Database/Seeds/CITestSeeder.php @@ -60,23 +60,6 @@ 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',