Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 22 additions & 2 deletions system/BaseModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -1099,15 +1099,35 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc
{
if (is_array($set)) {
foreach ($set as &$row) {
// Save the index value from the original row because
// transformDataToArray() may strip it when updateOnlyChanged
// is true.
$updateIndex = null;

if ($this->updateOnlyChanged) {
if (is_array($row)) {
$updateIndex = $row[$index] ?? null;
} elseif ($row instanceof Entity) {
$updateIndex = $row->toRawArray()[$index] ?? null;
} elseif (is_object($row)) {
$updateIndex = $row->{$index} ?? null;
}
}

$row = $this->transformDataToArray($row, 'update');

// Validate data before saving.
if (! $this->skipValidation && ! $this->validate($row)) {
return false;
}

// Save updateIndex for later
$updateIndex = $row[$index] ?? null;
// When updateOnlyChanged is true, restore the pre-extracted
// index into $row. Otherwise read it from the transformed row.
if ($updateIndex !== null) {
$row[$index] = $updateIndex;
} else {
$updateIndex = $row[$index] ?? null;
}

if ($updateIndex === null) {
throw new InvalidArgumentException(
Expand Down
37 changes: 37 additions & 0 deletions tests/system/Models/UpdateModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,43 @@ public function testUpdateBatchWithEntity(): void
$this->assertSame(2, $model->updateBatch([$entity1, $entity2], 'id'));
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/9943
*/
public function testUpdateBatchWithEntityAndUpdateOnlyChanged(): void
{
$entity1 = new User();
$entity1->id = 1;
$entity1->name = 'Derek Jones';
$entity1->email = '[email protected]';
$entity1->country = 'US';
$entity1->syncOriginal();
$entity1->country = 'Greece';

$entity2 = new User();
$entity2->id = 4;
$entity2->name = 'Chris Martin';
$entity2->email = '[email protected]';
$entity2->country = 'UK';
$entity2->syncOriginal();
$entity2->country = 'Finland';

// updateOnlyChanged is true by default. The index field 'id' is
// unchanged but must be preserved for the batch WHERE clause.
$model = $this->createModel(UserModel::class);
$this->assertTrue($this->getPrivateProperty($model, 'updateOnlyChanged'));
$this->assertSame(2, $model->updateBatch([$entity1, $entity2], 'id'));

$this->seeInDatabase('user', [
'name' => 'Derek Jones',
'country' => 'Greece',
]);
$this->seeInDatabase('user', [
'name' => 'Chris Martin',
'country' => 'Finland',
]);
}

public function testUpdateNoPrimaryKey(): void
{
$this->db->table('secondary')->insert([
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/v4.7.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Bugs Fixed
**********

- **ContentSecurityPolicy:** Fixed a bug where ``generateNonces()`` produces corrupted JSON responses by replacing CSP nonce placeholders with unescaped double quotes. The method now automatically JSON-escapes nonce attributes when the response Content-Type is JSON.
- **Model:** Fixed a bug where ``BaseModel::updateBatch()`` threw an exception when ``updateOnlyChanged`` was ``true`` and the index field value did not change.
- **Session:** Fixed a bug in ``MemcachedHandler`` where the constructor incorrectly threw an exception when ``savePath`` was not empty.

See the repo's
Expand Down
16 changes: 8 additions & 8 deletions utils/phpstan-baseline/missingType.property.neon
Original file line number Diff line number Diff line change
Expand Up @@ -468,42 +468,42 @@ parameters:
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$_options has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$_options has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$country has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$country has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$created_at has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$created_at has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$deleted has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$deleted has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$email has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$email has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$id has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$id has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$name has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$name has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

-
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:364\:\:\$updated_at has no type specified\.$#'
message: '#^Property CodeIgniter\\Entity\\Entity@anonymous/tests/system/Models/UpdateModelTest\.php\:401\:\:\$updated_at has no type specified\.$#'
count: 1
path: ../../tests/system/Models/UpdateModelTest.php

Expand Down
Loading