Skip to content

Commit e985209

Browse files
committed
refactor: Add new hasRationalPrimaryKey()
1 parent 63696a0 commit e985209

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

system/BaseModel.php

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ public function save($row): bool
759759

760760
/**
761761
* This method is called on save to determine if entry have to be updated.
762-
* If this method returns false insert operation will be executed
762+
* If this method returns `false` insert operation will be executed.
763763
*
764764
* @param object|row_array $row
765765
*/
@@ -966,11 +966,7 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch
966966
*/
967967
public function update($id = null, $row = null): bool
968968
{
969-
if (is_bool($id)) {
970-
throw new InvalidArgumentException('update(): argument #1 ($id) should not be boolean.');
971-
}
972-
973-
if (is_numeric($id) || is_string($id)) {
969+
if ($this->hasRationalPrimaryKey($id)) {
974970
$id = [$id];
975971
}
976972

@@ -1097,11 +1093,7 @@ public function updateBatch(?array $set = null, ?string $index = null, int $batc
10971093
*/
10981094
public function delete($id = null, bool $purge = false)
10991095
{
1100-
if (is_bool($id)) {
1101-
throw new InvalidArgumentException('delete(): argument #1 ($id) should not be boolean.');
1102-
}
1103-
1104-
if (! in_array($id, [null, 0, '0'], true) && (is_numeric($id) || is_string($id))) {
1096+
if ($this->hasRationalPrimaryKey($id)) {
11051097
$id = [$id];
11061098
}
11071099

@@ -1896,4 +1888,24 @@ protected function convertToReturnType(array $row, string $returnType): array|ob
18961888

18971889
return $this->converter->reconstruct($returnType, $row);
18981890
}
1891+
1892+
/**
1893+
* Checking that the ID has a rational value.
1894+
*
1895+
* Standard type checks in PHP do not fully verify the validity of ID as a primary key.
1896+
* Especially the values 0, '0', '' are rarely used or are invalid.
1897+
* Example, if you add an entry with ID=0, it will be an error for `$this->insertID`.
1898+
*
1899+
* @param array<int|string, int|string>|float|int|string|null $id
1900+
*
1901+
* @throws InvalidArgumentException
1902+
*/
1903+
protected function hasRationalPrimaryKey($id): bool
1904+
{
1905+
if (is_bool($id)) {
1906+
throw new InvalidArgumentException('The ID value should not be boolean.');
1907+
}
1908+
1909+
return ! in_array($id, [0, '0', '', 0.0], true) && (is_numeric($id) || is_string($id));
1910+
}
18991911
}

system/Model.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ protected function doUpdate($id = null, $row = null): bool
381381

382382
$builder = $this->builder();
383383

384-
if (! in_array($id, [null, '', 0, '0', []], true)) {
384+
if ((is_array($id) && $id !== []) || $this->hasRationalPrimaryKey($id)) {
385385
$builder = $builder->whereIn($this->table . '.' . $this->primaryKey, $id);
386386
}
387387

@@ -409,7 +409,7 @@ protected function doDelete($id = null, bool $purge = false)
409409
$set = [];
410410
$builder = $this->builder();
411411

412-
if (! in_array($id, [null, '', 0, '0', []], true)) {
412+
if ((is_array($id) && $id !== []) || $this->hasRationalPrimaryKey($id)) {
413413
$builder = $builder->whereIn($this->primaryKey, $id);
414414
}
415415

tests/system/Models/MiscellaneousModelTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
namespace CodeIgniter\Models;
1515

1616
use CodeIgniter\Database\Exceptions\DataException;
17+
use CodeIgniter\Exceptions\InvalidArgumentException;
1718
use CodeIgniter\I18n\Time;
19+
use PHPUnit\Framework\Attributes\DataProvider;
1820
use PHPUnit\Framework\Attributes\Group;
1921
use Tests\Support\Models\EntityModel;
2022
use Tests\Support\Models\JobModel;
@@ -122,4 +124,46 @@ public function testEmptyDataInTransformDataToArray(): void
122124
$method = self::getPrivateMethodInvoker($this->model, 'transformDataToArray');
123125
$method([], 'insert');
124126
}
127+
128+
public function testHasRationalPrimaryKeyWithBoolean(): void
129+
{
130+
$this->expectException(InvalidArgumentException::class);
131+
$this->expectExceptionMessage('The ID value should not be boolean.');
132+
133+
$this->createModel(JobModel::class);
134+
$method = self::getPrivateMethodInvoker($this->model, 'hasRationalPrimaryKey');
135+
$method(true); // @phpstan-ignore argument.type
136+
}
137+
138+
/**
139+
* @param float|int|string|null $value
140+
*/
141+
#[DataProvider('provideHasRationalPrimaryKey')]
142+
public function testHasRationalPrimaryKey($value, bool $expected): void
143+
{
144+
$this->createModel(JobModel::class);
145+
$method = self::getPrivateMethodInvoker($this->model, 'hasRationalPrimaryKey');
146+
$this->assertSame($method($value), $expected);
147+
}
148+
149+
/**
150+
* @return list<list<bool|float|int|string|null>>
151+
*/
152+
public static function provideHasRationalPrimaryKey(): iterable
153+
{
154+
return [
155+
[0, false],
156+
['0', false],
157+
[0.0, false],
158+
['', false],
159+
[null, false],
160+
['001', true],
161+
[' ', true],
162+
[' 0 ', true],
163+
[1, true],
164+
['1', true],
165+
['00000000-0000-0000-0000-000000000000', true],
166+
['codeigniter', true],
167+
];
168+
}
125169
}

tests/system/Models/UpdateModelTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ public static function provideUpdateThrowDatabaseExceptionWithoutWhereClause():
581581
[
582582
false,
583583
InvalidArgumentException::class,
584-
'update(): argument #1 ($id) should not be boolean.',
584+
'The ID value should not be boolean.',
585585
],
586586
];
587587
}

0 commit comments

Comments
 (0)