Skip to content

Commit 921bb4d

Browse files
authored
Add find or fail methods (#421)
1 parent 05c8a56 commit 921bb4d

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

src/NotFoundException.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\ActiveRecord;
6+
7+
use Yiisoft\Db\Exception\Exception;
8+
9+
/**
10+
* Represents an exception thrown when a record is not found.
11+
*/
12+
final class NotFoundException extends Exception
13+
{
14+
}

src/Trait/RepositoryTrait.php

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Yiisoft\ActiveRecord\ActiveQueryInterface;
88
use Yiisoft\ActiveRecord\ActiveRecordInterface;
99
use Yiisoft\Db\Expression\ExpressionInterface;
10+
use Yiisoft\ActiveRecord\NotFoundException;
1011

1112
/**
1213
* Trait to support static methods {@see find()}, {@see findOne()}, {@see findAll()}, {@see findBySql()} to find records.
@@ -43,6 +44,10 @@ trait RepositoryTrait
4344
* Returns an instance of {@see ActiveQueryInterface} instantiated by {@see ActiveRecordInterface::query()} method.
4445
* If the `$condition` parameter is not null, it calls {@see ActiveQueryInterface::andWhere()} method.
4546
* Do not to pass user input to this method, use {@see findByPk()} instead.
47+
*
48+
* @param array|ExpressionInterface|string|null $condition The condition to be applied to the query where clause.
49+
* No condition is applied if `null` (by default).
50+
* @param array $params The parameters to be bound to the SQL statement during execution.
4651
*/
4752
public static function find(array|string|ExpressionInterface|null $condition = null, array $params = []): ActiveQueryInterface
4853
{
@@ -87,13 +92,38 @@ public static function find(array|string|ExpressionInterface|null $condition = n
8792
* $post = Post::findByPk($id);
8893
* ```
8994
*
95+
* @param array|ExpressionInterface|string|null $condition The condition to be applied to the query where clause.
96+
* Returns all records if `null` (by default).
97+
* @param array $params The parameters to be bound to the SQL statement during execution.
98+
*
9099
* @return ActiveRecordInterface[]|array[] An array of ActiveRecord instance, or an empty array if nothing matches.
91100
*/
92101
public static function findAll(array|string|ExpressionInterface|null $condition = null, array $params = []): array
93102
{
94103
return static::find($condition, $params)->all();
95104
}
96105

106+
/**
107+
* Shortcut for {@see findAll()} method with throwing {@see NotFoundException} if no records found.
108+
*
109+
* ```php
110+
* $customers = Customer::findAllOrFail(['is_active' => true]);
111+
* ```
112+
*
113+
* @param array|ExpressionInterface|string|null $condition The condition to be applied to the query where clause.
114+
* Returns all records if `null` (by default).
115+
* @param array $params The parameters to be bound to the SQL statement during execution.
116+
*
117+
* @throws NotFoundException
118+
*
119+
* @return ActiveRecordInterface[]|array[] An array of ActiveRecord instance, or throws {@see NotFoundException}
120+
* if nothing matches.
121+
*/
122+
public static function findAllOrFail(array|string|ExpressionInterface|null $condition = null, array $params = []): array
123+
{
124+
return static::findAll($condition, $params) ?: throw new NotFoundException('No records found.');
125+
}
126+
97127
/**
98128
* Finds an ActiveRecord instance by the given primary key value.
99129
* In the examples below, the `id` column is the primary key of the table.
@@ -123,17 +153,40 @@ public static function findAll(array|string|ExpressionInterface|null $condition
123153
* $customer = Customer::findByPk($id);
124154
* }
125155
* ```
156+
*
157+
* @param array|float|int|string $values The primary key value(s) to find the record.
158+
*
159+
* @return ActiveRecordInterface|array|null Instance matching the primary key value(s), or `null` if nothing matches.
126160
*/
127161
public static function findByPk(array|float|int|string $values): array|ActiveRecordInterface|null
128162
{
129163
return static::instantiate()->query()->findByPk($values);
130164
}
131165

132166
/**
133-
* Creates an {@see ActiveQuery} instance with a given SQL statement.
167+
* Shortcut for {@see findByPk()} method with throwing {@see NotFoundException} if no records found.
168+
*
169+
* ```php
170+
* $customer = Customer::findByPkOrFail(1);
171+
* ```
172+
*
173+
* @param array|float|int|string $values The primary key value(s) to find the record.
174+
*
175+
* @throws NotFoundException
176+
*
177+
* @return ActiveRecordInterface|array|null Instance matching the primary key value(s),
178+
* or throws {@see NotFoundException} if nothing matches.
179+
*/
180+
public static function findByPkOrFail(array|float|int|string $values): array|ActiveRecordInterface|null
181+
{
182+
return static::findByPk($values) ?? throw new NotFoundException('No records found.');
183+
}
184+
185+
/**
186+
* Creates an {@see ActiveQueryInterface} instance with a given SQL statement.
134187
*
135188
* Note: That because the SQL statement is already specified, calling more query modification methods
136-
* (such as {@see where()}, {@see order()) on the created {@see ActiveQuery} instance will have no effect.
189+
* (such as {@see where()}, {@see order()) on the created {@see ActiveQueryInterface} instance will have no effect.
137190
*
138191
* However, calling {@see with()}, {@see asArray()}, {@see indexBy()} or {@see resultCallback()} is still fine.
139192
*
@@ -145,6 +198,8 @@ public static function findByPk(array|float|int|string $values): array|ActiveRec
145198
*
146199
* @param string $sql The SQL statement to be executed.
147200
* @param array $params The parameters to be bound to the SQL statement during execution.
201+
*
202+
* @return ActiveQueryInterface The newly created {@see ActiveQueryInterface} instance.
148203
*/
149204
public static function findBySql(string $sql, array $params = []): ActiveQueryInterface
150205
{
@@ -183,6 +238,10 @@ public static function findBySql(string $sql, array $params = []): ActiveQueryIn
183238
* $post = Post::findByPk($id);
184239
* ```
185240
*
241+
* @param array|ExpressionInterface|string|null $condition The condition to be applied to the query where clause.
242+
* Returns the first record if `null` (by default).
243+
* @param array $params The parameters to be bound to the SQL statement during execution.
244+
*
186245
* @return ActiveRecordInterface|array|null Instance matching the condition, or `null` if nothing matches.
187246
*/
188247
public static function findOne(
@@ -192,6 +251,29 @@ public static function findOne(
192251
return static::find($condition, $params)->one();
193252
}
194253

254+
/**
255+
* Shortcut for {@see findOne()} method with throwing {@see NotFoundException} if no records found.
256+
*
257+
* ```php
258+
* $customer = Customer::findOneOrFail(['id' => 1]);
259+
* ```
260+
*
261+
* @param array|ExpressionInterface|string|null $condition The condition to be applied to the query where clause.
262+
* Returns the first record if `null` (by default).
263+
* @param array $params The parameters to be bound to the SQL statement during execution.
264+
*
265+
* @throws NotFoundException
266+
*
267+
* @return ActiveRecordInterface|array|null Instance matching the condition, or throws {@see NotFoundException}
268+
* if nothing matches.
269+
*/
270+
public static function findOneOrFail(
271+
array|string|ExpressionInterface|null $condition = null,
272+
array $params = [],
273+
): ActiveRecordInterface|array|null {
274+
return static::findOne($condition, $params) ?? throw new NotFoundException('No records found.');
275+
}
276+
195277
protected static function instantiate(): ActiveRecordInterface
196278
{
197279
return new static();

tests/RepositoryTraitTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Yiisoft\ActiveRecord\Tests;
66

77
use Yiisoft\ActiveRecord\ActiveQuery;
8+
use Yiisoft\ActiveRecord\NotFoundException;
89
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer;
910

1011
abstract class RepositoryTraitTest extends TestCase
@@ -45,6 +46,21 @@ public function testFindOne(): void
4546
$this->assertNull($customer);
4647
}
4748

49+
public function testFindOneOrFail(): void
50+
{
51+
$customerQuery = new ActiveQuery(new Customer());
52+
53+
$this->assertEquals(
54+
$customerQuery->where(['id' => 1])->one(),
55+
Customer::findOneOrFail(['id' => 1]),
56+
);
57+
58+
$this->expectException(NotFoundException::class);
59+
$this->expectExceptionMessage('No records found.');
60+
61+
Customer::findOneOrFail(['name' => 'user5']);
62+
}
63+
4864
public function testFindAll(): void
4965
{
5066
$customerQuery = new ActiveQuery(new Customer());
@@ -63,6 +79,21 @@ public function testFindAll(): void
6379
$this->assertCount(3, Customer::findAll(['id' => [1, 2, 3]]));
6480
}
6581

82+
public function testFindAllOrFail(): void
83+
{
84+
$customerQuery = new ActiveQuery(new Customer());
85+
86+
$this->assertEquals(
87+
$customerQuery->where(['id' => [1, 2, 3]])->all(),
88+
Customer::findAllOrFail(['id' => [1, 2, 3]]),
89+
);
90+
91+
$this->expectException(NotFoundException::class);
92+
$this->expectExceptionMessage('No records found.');
93+
94+
Customer::findAllOrFail(['id' => 5]);
95+
}
96+
6697
public function testFindByPk(): void
6798
{
6899
$customerQuery = new ActiveQuery(new Customer());
@@ -76,6 +107,21 @@ public function testFindByPk(): void
76107
$this->assertNull($customer);
77108
}
78109

110+
public function testFindByPkOrFail(): void
111+
{
112+
$customerQuery = new ActiveQuery(new Customer());
113+
114+
$this->assertEquals(
115+
$customerQuery->where(['id' => 1])->one(),
116+
Customer::findByPkOrFail(1),
117+
);
118+
119+
$this->expectException(NotFoundException::class);
120+
$this->expectExceptionMessage('No records found.');
121+
122+
Customer::findByPkOrFail(5);
123+
}
124+
79125
public function testFindBySql(): void
80126
{
81127
$customerQuery = new ActiveQuery(new Customer());

0 commit comments

Comments
 (0)