From d149d36e8d5ca3be89572023e5ced506a4046d93 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sat, 7 Sep 2024 12:15:40 -0400 Subject: [PATCH 1/2] Add findOrFail to Eloquent Collection --- .../Database/Eloquent/Collection.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index 9640e41c540b..7fe826d165b1 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -50,6 +50,37 @@ public function find($key, $default = null) return Arr::first($this->items, fn ($model) => $model->getKey() == $key, $default); } + /** + * Find a model in the collection by key or throw an exception. + * + * @param mixed $key + * @return TModel + * + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + */ + public function findOrFail($key) + { + $result = $this->find($key); + + if (is_array($key) && count($result) === count(array_unique($key))) { + return $result; + } elseif (! is_array($key) && ! is_null($result)) { + return $result; + } + + $exception = new ModelNotFoundException; + + if (! $model = head($this->items)) { + throw $exception; + } + + $ids = is_array($key) ? array_diff($key, $result->modelKeys()) : $key; + + $exception->setModel(get_class($model), $ids); + + throw $exception; + } + /** * Load a set of relationships onto the collection. * From d6ca9b1ba81ff30ed69ac9840746457c81b3bb44 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sat, 7 Sep 2024 12:17:04 -0400 Subject: [PATCH 2/2] Add tests --- .../DatabaseEloquentCollectionTest.php | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/Database/DatabaseEloquentCollectionTest.php b/tests/Database/DatabaseEloquentCollectionTest.php index d5e9d64652d3..7b9b6655980f 100755 --- a/tests/Database/DatabaseEloquentCollectionTest.php +++ b/tests/Database/DatabaseEloquentCollectionTest.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model as Eloquent; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Collection as BaseCollection; use LogicException; use Mockery as m; @@ -202,6 +203,59 @@ public function testFindMethodFindsManyModelsById() $this->assertEquals([2, 3], $c->find([2, 3, 4])->pluck('id')->all()); } + public function testFindOrFailFindsModelById() + { + $mockModel = m::mock(Model::class); + $mockModel->shouldReceive('getKey')->andReturn(1); + $c = new Collection([$mockModel]); + + $this->assertSame($mockModel, $c->findOrFail(1)); + } + + public function testFindOrFailFindsManyModelsById() + { + $model1 = (new TestEloquentCollectionModel)->forceFill(['id' => 1]); + $model2 = (new TestEloquentCollectionModel)->forceFill(['id' => 2]); + + $c = new Collection; + $this->assertInstanceOf(Collection::class, $c->findOrFail([])); + $this->assertCount(0, $c->findOrFail([])); + + $c->push($model1); + $this->assertCount(1, $c->findOrFail([1])); + $this->assertEquals(1, $c->findOrFail([1])->first()->id); + + $c->push($model2); + $this->assertCount(2, $c->findOrFail([1, 2])); + + $this->expectException(ModelNotFoundException::class); + $this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\TestEloquentCollectionModel] 3'); + + $c->findOrFail([1, 2, 3]); + } + + public function testFindOrFailThrowsExceptionWithMessageWhenOtherModelsArePresent() + { + $model = (new TestEloquentCollectionModel)->forceFill(['id' => 1]); + + $c = new Collection([$model]); + + $this->expectException(ModelNotFoundException::class); + $this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\TestEloquentCollectionModel] 2'); + + $c->findOrFail(2); + } + + public function testFindOrFailThrowsExceptionWithoutMessageWhenOtherModelsAreNotPresent() + { + $c = new Collection(); + + $this->expectException(ModelNotFoundException::class); + $this->expectExceptionMessage(''); + + $c->findOrFail(1); + } + public function testLoadMethodEagerLoadsGivenRelationships() { $c = $this->getMockBuilder(Collection::class)->onlyMethods(['first'])->setConstructorArgs([['foo']])->getMock();