Skip to content

Commit

Permalink
[8.x] Adds firstOrFail to Illuminate\Support\Collections and Illumina…
Browse files Browse the repository at this point in the history
…te\Support\LazyCollections (laravel#38420)

* Add firstOrFail method to Collections and LazyCollections

* Restore test that got moved, fixed some grammaticals in test names

* Style fixes

* Make take(2) as take(1) instead

* use unless instead of when

Per Joseph Silber's suggestion

Co-authored-by: Joseph Silber <contact@josephsilber.com>

* Use Unless instead of When

Per Joseph Silber's suggestion

Co-authored-by: Joseph Silber <contact@josephsilber.com>

* Revert L9 changes, add test requested by @JosephSilber

* Remove unneeded try catch

* Remove unneeded include

* Test that when an enumeration does not contain an item, that firstOrFail enumerates the full collection

* Style fix

* Update Collection.php

* Update LazyCollection.php

Co-authored-by: Toby Powell-Blyth <tobypowell-blyth@elasticstage.com>
Co-authored-by: Joseph Silber <contact@josephsilber.com>
Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
4 people authored and victorvilella committed Oct 12, 2021
1 parent 14ae6eb commit c0acf3e
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 2 deletions.
25 changes: 25 additions & 0 deletions src/Illuminate/Collections/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,31 @@ public function sole($key = null, $operator = null, $value = null)
return $items->first();
}

/**
* Get the first item in the collection but throw an exception if no matching items exist.
*
* @param mixed $key
* @param mixed $operator
* @param mixed $value
* @return mixed
*
* @throws \Illuminate\Collections\ItemNotFoundException
*/
public function firstOrFail($key = null, $operator = null, $value = null)
{
$filter = func_num_args() > 1
? $this->operatorForWhere(...func_get_args())
: $key;

$items = $this->when($filter)->filter($filter);

if ($items->isEmpty()) {
throw new ItemNotFoundException;
}

return $items->first();
}

/**
* Chunk the collection into chunks of the given size.
*
Expand Down
24 changes: 24 additions & 0 deletions src/Illuminate/Collections/LazyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,30 @@ public function sole($key = null, $operator = null, $value = null)
->sole();
}

/**
* Get the first item in the collection but throw an exception if no matching items exist.
*
* @param mixed $key
* @param mixed $operator
* @param mixed $value
* @return mixed
*
* @throws \Illuminate\Collections\ItemNotFoundException
*/
public function firstOrFail($key = null, $operator = null, $value = null)
{
$filter = func_num_args() > 1
? $this->operatorForWhere(...func_get_args())
: $key;

return $this
->when($filter)
->filter($filter)
->take(1)
->collect()
->firstOrFail();
}

/**
* Chunk the collection into chunks of the given size.
*
Expand Down
89 changes: 87 additions & 2 deletions tests/Support/SupportCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function testSoleReturnsFirstItemInCollectionIfOnlyOneExists($collection)
/**
* @dataProvider collectionClassProvider
*/
public function testSoleThrowsExceptionIfNoItemsExists($collection)
public function testSoleThrowsExceptionIfNoItemsExist($collection)
{
$this->expectException(ItemNotFoundException::class);

Expand Down Expand Up @@ -129,7 +129,7 @@ public function testSoleReturnsFirstItemInCollectionIfOnlyOneExistsWithCallback(
/**
* @dataProvider collectionClassProvider
*/
public function testSoleThrowsExceptionIfNoItemsExistsWithCallback($collection)
public function testSoleThrowsExceptionIfNoItemsExistWithCallback($collection)
{
$this->expectException(ItemNotFoundException::class);

Expand All @@ -154,6 +154,91 @@ public function testSoleThrowsExceptionIfMoreThanOneItemExistsWithCallback($coll
});
}

/**
* @dataProvider collectionClassProvider
*/
public function testFirstOrFailReturnsFirstItemInCollection($collection)
{
$collection = new $collection([
['name' => 'foo'],
['name' => 'bar'],
]);

$this->assertSame(['name' => 'foo'], $collection->where('name', 'foo')->firstOrFail());
$this->assertSame(['name' => 'foo'], $collection->firstOrFail('name', '=', 'foo'));
$this->assertSame(['name' => 'foo'], $collection->firstOrFail('name', 'foo'));
}

/**
* @dataProvider collectionClassProvider
*/
public function testFirstOrFailThrowsExceptionIfNoItemsExist($collection)
{
$this->expectException(ItemNotFoundException::class);

$collection = new $collection([
['name' => 'foo'],
['name' => 'bar'],
]);

$collection->where('name', 'INVALID')->firstOrFail();
}

/**
* @dataProvider collectionClassProvider
*/
public function testFirstOrFailDoesntThrowExceptionIfMoreThanOneItemExists($collection)
{
$collection = new $collection([
['name' => 'foo'],
['name' => 'foo'],
['name' => 'bar'],
]);

$this->assertSame(['name' => 'foo'], $collection->where('name', 'foo')->firstOrFail());
}

/**
* @dataProvider collectionClassProvider
*/
public function testFirstOrFailReturnsFirstItemInCollectionIfOnlyOneExistsWithCallback($collection)
{
$data = new $collection(['foo', 'bar', 'baz']);
$result = $data->firstOrFail(function ($value) {
return $value === 'bar';
});
$this->assertSame('bar', $result);
}

/**
* @dataProvider collectionClassProvider
*/
public function testFirstOrFailThrowsExceptionIfNoItemsExistWithCallback($collection)
{
$this->expectException(ItemNotFoundException::class);

$data = new $collection(['foo', 'bar', 'baz']);

$data->firstOrFail(function ($value) {
return $value === 'invalid';
});
}

/**
* @dataProvider collectionClassProvider
*/
public function testFirstOrFailDoesntThrowExceptionIfMoreThanOneItemExistsWithCallback($collection)
{
$data = new $collection(['foo', 'bar', 'bar']);

$this->assertSame(
'bar',
$data->firstOrFail(function ($value) {
return $value === 'bar';
})
);
}

/**
* @dataProvider collectionClassProvider
*/
Expand Down
30 changes: 30 additions & 0 deletions tests/Support/SupportLazyCollectionIsLazyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Tests\Support;

use Illuminate\Support\ItemNotFoundException;
use Illuminate\Support\LazyCollection;
use Illuminate\Support\MultipleItemsFoundException;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -986,6 +987,35 @@ public function testSliceIsLazy()
});
}

public function testFindFirstOrFailIsLazy()
{
$this->assertEnumerates(1, function ($collection) {
$collection->firstOrFail();
});

$this->assertEnumerates(1, function ($collection) {
$collection->firstOrFail(function ($item) {
return $item === 1;
});
});

$this->assertEnumerates(100, function ($collection) {
try {
$collection->firstOrFail(function ($item) {
return $item === 101;
});
} catch (ItemNotFoundException $e) {
//
}
});

$this->assertEnumerates(2, function ($collection) {
$collection->firstOrFail(function ($item) {
return $item % 2 === 0;
});
});
}

public function testSomeIsLazy()
{
$this->assertEnumerates(5, function ($collection) {
Expand Down

0 comments on commit c0acf3e

Please sign in to comment.