Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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, int>|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}";
}
Comment on lines +2944 to +2947
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we no longer have runtime type enforcement for each value, we should add a check manually. Something like:

if (! is_int($val)) {
    throw new TypeError(sprintf(
        'Argument #1 ($columns) must contain only int values, %s given for "%s".',
        get_debug_type($val),
        (string) $col,
    ));
}


$sql = $this->_update($this->QBFrom[0], [$column => "{$column} + {$value}"]);
$sql = $this->_update($this->QBFrom[0], $fields);

if (! $this->testMode) {
$this->resetWrite();
Expand All @@ -2945,13 +2957,25 @@ public function increment(string $column, int $value = 1)
/**
* Decrements a numeric column by the specified value.
*
* @param array<string, int>|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();
Expand Down
30 changes: 24 additions & 6 deletions system/Database/Postgre/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
42 changes: 28 additions & 14 deletions system/Database/SQLSRV/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions tests/_support/Database/Seeds/CITestSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
22 changes: 22 additions & 0 deletions tests/system/Database/Live/IncrementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down Expand Up @@ -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']);
Expand Down
1 change: 1 addition & 0 deletions tests/system/Database/Live/MetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected function setUp(): void
$prefix . 'migrations',
$prefix . 'user',
$prefix . 'job',
$prefix . 'task',
$prefix . 'misc',
$prefix . 'team_members',
$prefix . 'type_test',
Expand Down
3 changes: 3 additions & 0 deletions user_guide_src/source/changelogs/v4.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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(array|string $column, int $value = 1)`` and ``decrement(array|string $column, int $value = 1)``.

Property Scope Changes
======================
Expand Down Expand Up @@ -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.

Forge
-----

Expand Down
12 changes: 8 additions & 4 deletions user_guide_src/source/database/query_builder.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading