Skip to content

Commit 2ffd535

Browse files
committed
refactor and return false on failure
1 parent c49da58 commit 2ffd535

File tree

4 files changed

+18
-21
lines changed

4 files changed

+18
-21
lines changed

system/Model.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ protected function doProtectFieldsForInsert(array $row): array
724724
* @param array<string, mixed>|object $attributes
725725
* @param array<string, mixed>|object $values
726726
*/
727-
public function firstOrInsert(array|object $attributes, array|object $values = []): array|object|null
727+
public function firstOrInsert(array|object $attributes, array|object $values = []): array|object|false
728728
{
729729
if (is_object($attributes)) {
730730
$attributes = $this->transformDataToArray($attributes, 'insert');
@@ -748,18 +748,18 @@ public function firstOrInsert(array|object $attributes, array|object $values = [
748748
try {
749749
$id = $this->insert($data);
750750
} catch (UniqueConstraintViolationException) {
751-
return $this->where($attributes)->first();
751+
return $this->where($attributes)->first() ?? false;
752752
}
753753

754754
if ($id === false) {
755755
if ($this->db->getLastException() instanceof UniqueConstraintViolationException) {
756-
return $this->where($attributes)->first();
756+
return $this->where($attributes)->first() ?? false;
757757
}
758758

759-
return null;
759+
return false;
760760
}
761761

762-
return $this->where($this->primaryKey, $id)->first();
762+
return $this->where($this->primaryKey, $id)->first() ?? false;
763763
}
764764

765765
public function update($id = null, $row = null): bool

tests/system/Models/FirstOrInsertModelTest.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function testValuesAreIgnoredWhenRecordExists(): void
6666
['name' => 'Should Not Change', 'country' => 'XX'],
6767
);
6868

69-
$this->assertNotNull($row);
69+
$this->assertNotFalse($row);
7070
$this->assertSame('Derek Jones', $row->name);
7171
$this->assertSame('US', $row->country);
7272
}
@@ -81,7 +81,7 @@ public function testInsertsNewRecordWhenNotFound(): void
8181
'country' => 'US',
8282
]);
8383

84-
$this->assertNotNull($row);
84+
$this->assertNotFalse($row);
8585
$this->assertSame('new@example.com', $row->email);
8686
$this->seeInDatabase('user', ['email' => 'new@example.com', 'deleted_at' => null]);
8787
}
@@ -95,7 +95,7 @@ public function testMergesValuesOnInsert(): void
9595
['name' => 'New User', 'country' => 'CA'],
9696
);
9797

98-
$this->assertNotNull($row);
98+
$this->assertNotFalse($row);
9999
$this->assertSame('New User', $row->name);
100100
$this->assertSame('CA', $row->country);
101101
$this->seeInDatabase('user', [
@@ -119,7 +119,7 @@ public function testAcceptsObjectForValues(): void
119119
$values,
120120
);
121121

122-
$this->assertNotNull($row);
122+
$this->assertNotFalse($row);
123123
$this->assertSame('Object User', $row->name);
124124
$this->assertSame('DE', $row->country);
125125
$this->seeInDatabase('user', ['email' => 'object@example.com', 'deleted_at' => null]);
@@ -134,7 +134,7 @@ public function testAcceptsObjectForAttributes(): void
134134

135135
$row = $this->model->firstOrInsert($attributes);
136136

137-
$this->assertNotNull($row);
137+
$this->assertNotFalse($row);
138138
$this->assertSame('Derek Jones', $row->name);
139139
$this->seeNumRecords(4, 'user', ['deleted_at' => null]);
140140
}
@@ -183,7 +183,7 @@ protected function doInsert(array $row): bool
183183
['name' => 'Race User', 'country' => 'US'],
184184
);
185185

186-
$this->assertNotNull($row);
186+
$this->assertNotFalse($row);
187187
$this->assertSame('race@example.com', $row->email);
188188
// The "other process" inserted exactly one record.
189189
$this->seeNumRecords(1, 'user', ['email' => 'race@example.com', 'deleted_at' => null]);
@@ -218,12 +218,12 @@ protected function doInsert(array $row): bool
218218
['name' => 'Race User', 'country' => 'US'],
219219
);
220220

221-
$this->assertNotNull($row);
221+
$this->assertNotFalse($row);
222222
$this->assertSame('race@example.com', $row->email);
223223
$this->seeNumRecords(1, 'user', ['email' => 'race@example.com', 'deleted_at' => null]);
224224
}
225225

226-
public function testReturnsNullOnNonUniqueErrorWithDebugDisabled(): void
226+
public function testReturnsFalseOnNonUniqueErrorWithDebugDisabled(): void
227227
{
228228
$this->disableDBDebug();
229229

@@ -245,11 +245,11 @@ protected function doInsert(array $row): bool
245245
['name' => 'Error User', 'country' => 'US'],
246246
);
247247

248-
$this->assertNull($result);
248+
$this->assertFalse($result);
249249
$this->dontSeeInDatabase('user', ['email' => 'error@example.com']);
250250
}
251251

252-
public function testReturnsNullOnValidationFailure(): void
252+
public function testReturnsFalseOnValidationFailure(): void
253253
{
254254
// Subclass with strict validation rules that the test data fails.
255255
$model = new class ($this->db) extends UserModel {
@@ -264,7 +264,7 @@ public function testReturnsNullOnValidationFailure(): void
264264
['name' => 'Too Short'],
265265
);
266266

267-
$this->assertNull($result);
267+
$this->assertFalse($result);
268268
$this->dontSeeInDatabase('user', ['email' => 'not-a-valid-email']);
269269
$this->assertNotEmpty($model->errors());
270270
}

user_guide_src/source/changelogs/v4.8.0.rst

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,7 @@ Model
168168
=====
169169

170170
- Added new ``chunkRows()`` method to ``CodeIgniter\Model`` for processing large datasets in smaller chunks.
171-
- Added new ``firstOrInsert()`` method to ``CodeIgniter\Model`` that finds the first row matching the given
172-
attributes or inserts a new one. Uses an optimistic insert strategy to avoid race conditions, relying on
173-
:php:class:`UniqueConstraintViolationException <CodeIgniter\\Database\\Exceptions\\UniqueConstraintViolationException>`
174-
to detect concurrent inserts.
171+
- Added new ``firstOrInsert()`` method to ``CodeIgniter\Model`` that finds the first row matching the given attributes or inserts a new one. See :ref:`model-first-or-insert`.
175172

176173
Libraries
177174
=========

user_guide_src/source/models/model.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ ignored when a matching record already exists.
656656
.. literalinclude:: model/066.php
657657

658658
The method returns the found or newly inserted row in the format defined by
659-
`$returnType`_, or ``null`` on failure (e.g., validation error or database
659+
`$returnType`_, or ``false`` on failure (e.g., validation error or database
660660
error when ``DBDebug`` is ``false``).
661661

662662
.. note:: A database **unique constraint** on the lookup column(s) is required

0 commit comments

Comments
 (0)