Skip to content

Commit 4d7ca1c

Browse files
Merge branch '9.x'
2 parents 6df2351 + fbe7ed8 commit 4d7ca1c

File tree

11 files changed

+176
-23
lines changed

11 files changed

+176
-23
lines changed

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -435,16 +435,22 @@ public function findOrFail($id, $columns = ['*'])
435435
$id = $id instanceof Arrayable ? $id->toArray() : $id;
436436

437437
if (is_array($id)) {
438-
if (count($result) === count(array_unique($id))) {
439-
return $result;
438+
if (count($result) !== count(array_unique($id))) {
439+
throw (new ModelNotFoundException)->setModel(
440+
get_class($this->model), array_diff($id, $result->modelKeys())
441+
);
440442
}
441-
} elseif (! is_null($result)) {
443+
442444
return $result;
443445
}
444446

445-
throw (new ModelNotFoundException)->setModel(
446-
get_class($this->model), $id
447-
);
447+
if (is_null($result)) {
448+
throw (new ModelNotFoundException)->setModel(
449+
get_class($this->model), $id
450+
);
451+
}
452+
453+
return $result;
448454
}
449455

450456
/**
@@ -866,9 +872,7 @@ public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName =
866872
*/
867873
protected function ensureOrderForCursorPagination($shouldReverse = false)
868874
{
869-
$orders = collect($this->query->orders);
870-
871-
if ($orders->count() === 0) {
875+
if (empty($this->query->orders) && empty($this->query->unionOrders)) {
872876
$this->enforceOrderBy();
873877
}
874878

@@ -880,6 +884,10 @@ protected function ensureOrderForCursorPagination($shouldReverse = false)
880884
})->toArray();
881885
}
882886

887+
if ($this->query->unionOrders) {
888+
return collect($this->query->unionOrders);
889+
}
890+
883891
return collect($this->query->orders);
884892
}
885893

src/Illuminate/Http/Resources/CollectsResources.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace Illuminate\Http\Resources;
44

5+
use Illuminate\Http\Resources\Json\JsonResource;
56
use Illuminate\Pagination\AbstractCursorPaginator;
67
use Illuminate\Pagination\AbstractPaginator;
78
use Illuminate\Support\Collection;
89
use Illuminate\Support\Str;
10+
use LogicException;
911
use ReflectionClass;
1012
use Traversable;
1113

@@ -45,15 +47,21 @@ protected function collectResource($resource)
4547
*/
4648
protected function collects()
4749
{
48-
if ($this->collects) {
49-
return $this->collects;
50-
}
50+
$collects = null;
5151

52-
if (str_ends_with(class_basename($this), 'Collection') &&
52+
if ($this->collects) {
53+
$collects = $this->collects;
54+
} elseif (str_ends_with(class_basename($this), 'Collection') &&
5355
(class_exists($class = Str::replaceLast('Collection', '', get_class($this))) ||
5456
class_exists($class = Str::replaceLast('Collection', 'Resource', get_class($this))))) {
55-
return $class;
57+
$collects = $class;
5658
}
59+
60+
if (! $collects || is_subclass_of($collects, JsonResource::class)) {
61+
return $collects;
62+
}
63+
64+
throw new LogicException('Resource collections must collect instances of '.JsonResource::class.'.');
5765
}
5866

5967
/**

src/Illuminate/Support/Str.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,18 @@ public static function substrReplace($string, $replace, $offset = 0, $length = n
922922
return substr_replace($string, $replace, $offset, $length);
923923
}
924924

925+
/**
926+
* Swap multiple keywords in a string with other keywords.
927+
*
928+
* @param array $map
929+
* @param string $subject
930+
* @return string
931+
*/
932+
public static function swap(array $map, $subject)
933+
{
934+
return strtr($subject, $map);
935+
}
936+
925937
/**
926938
* Make a string's first character uppercase.
927939
*

src/Illuminate/Support/Stringable.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,13 +726,24 @@ public function substrCount($needle, $offset = null, $length = null)
726726
* @param string|array $replace
727727
* @param array|int $offset
728728
* @param array|int|null $length
729-
* @return string|array
729+
* @return static
730730
*/
731731
public function substrReplace($replace, $offset = 0, $length = null)
732732
{
733733
return new static(Str::substrReplace($this->value, $replace, $offset, $length));
734734
}
735735

736+
/**
737+
* Swap multiple keywords in a string with other keywords.
738+
*
739+
* @param array $map
740+
* @return static
741+
*/
742+
public function swap(array $map)
743+
{
744+
return new static(strtr($this->value, $map));
745+
}
746+
736747
/**
737748
* Trim the string of the given characters.
738749
*
@@ -981,6 +992,26 @@ public function dd()
981992
exit(1);
982993
}
983994

995+
/**
996+
* Get the underlying string value.
997+
*
998+
* @return string
999+
*/
1000+
public function value()
1001+
{
1002+
return $this->toString();
1003+
}
1004+
1005+
/**
1006+
* Get the underlying string value.
1007+
*
1008+
* @return string
1009+
*/
1010+
public function toString()
1011+
{
1012+
return $this->value;
1013+
}
1014+
9841015
/**
9851016
* Convert the object to a string when JSON encoded.
9861017
*

tests/Database/DatabaseEloquentBuilderTest.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,27 @@ public function testFindOrFailMethodWithManyThrowsModelNotFoundException()
130130
{
131131
$this->expectException(ModelNotFoundException::class);
132132

133+
$model = $this->getMockModel();
134+
$model->shouldReceive('getKey')->andReturn(1);
135+
133136
$builder = m::mock(Builder::class.'[get]', [$this->getMockQueryBuilder()]);
134-
$builder->setModel($this->getMockModel());
137+
$builder->setModel($model);
135138
$builder->getQuery()->shouldReceive('whereIn')->once()->with('foo_table.foo', [1, 2]);
136-
$builder->shouldReceive('get')->with(['column'])->andReturn(new Collection([1]));
139+
$builder->shouldReceive('get')->with(['column'])->andReturn(new Collection([$model]));
137140
$builder->findOrFail([1, 2], ['column']);
138141
}
139142

140143
public function testFindOrFailMethodWithManyUsingCollectionThrowsModelNotFoundException()
141144
{
142145
$this->expectException(ModelNotFoundException::class);
143146

147+
$model = $this->getMockModel();
148+
$model->shouldReceive('getKey')->andReturn(1);
149+
144150
$builder = m::mock(Builder::class.'[get]', [$this->getMockQueryBuilder()]);
145-
$builder->setModel($this->getMockModel());
151+
$builder->setModel($model);
146152
$builder->getQuery()->shouldReceive('whereIn')->once()->with('foo_table.foo', [1, 2]);
147-
$builder->shouldReceive('get')->with(['column'])->andReturn(new Collection([1]));
153+
$builder->shouldReceive('get')->with(['column'])->andReturn(new Collection([$model]));
148154
$builder->findOrFail(new Collection([1, 2]), ['column']);
149155
}
150156

tests/Database/DatabaseEloquentIntegrationTest.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -616,26 +616,35 @@ public function testFindOrFailWithSingleIdThrowsModelNotFoundException()
616616
{
617617
$this->expectException(ModelNotFoundException::class);
618618
$this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\EloquentTestUser] 1');
619+
$this->expectExceptionObject(
620+
(new ModelNotFoundException())->setModel(EloquentTestUser::class, [1]),
621+
);
619622

620623
EloquentTestUser::findOrFail(1);
621624
}
622625

623626
public function testFindOrFailWithMultipleIdsThrowsModelNotFoundException()
624627
{
625628
$this->expectException(ModelNotFoundException::class);
626-
$this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\EloquentTestUser] 1, 2');
629+
$this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\EloquentTestUser] 2, 3');
630+
$this->expectExceptionObject(
631+
(new ModelNotFoundException())->setModel(EloquentTestUser::class, [2, 3]),
632+
);
627633

628634
EloquentTestUser::create(['id' => 1, 'email' => '[email protected]']);
629-
EloquentTestUser::findOrFail([1, 2]);
635+
EloquentTestUser::findOrFail([1, 2, 3]);
630636
}
631637

632638
public function testFindOrFailWithMultipleIdsUsingCollectionThrowsModelNotFoundException()
633639
{
634640
$this->expectException(ModelNotFoundException::class);
635-
$this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\EloquentTestUser] 1, 2');
641+
$this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\EloquentTestUser] 2, 3');
642+
$this->expectExceptionObject(
643+
(new ModelNotFoundException())->setModel(EloquentTestUser::class, [2, 3]),
644+
);
636645

637646
EloquentTestUser::create(['id' => 1, 'email' => '[email protected]']);
638-
EloquentTestUser::findOrFail(new Collection([1, 2]));
647+
EloquentTestUser::findOrFail(new Collection([1, 1, 2, 3]));
639648
}
640649

641650
public function testOneToOneRelationship()

tests/Integration/Database/EloquentCursorPaginateTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ public function testCursorPaginationOnTopOfColumns()
3636
$this->assertCount(15, TestPost::cursorPaginate(15, ['id', 'title']));
3737
}
3838

39+
public function testPaginationWithUnion()
40+
{
41+
TestPost::create(['title' => 'Hello world', 'user_id' => 1]);
42+
TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
43+
TestPost::create(['title' => 'Howdy', 'user_id' => 3]);
44+
TestPost::create(['title' => '4th', 'user_id' => 4]);
45+
46+
$table1 = TestPost::query()->whereIn('user_id', [1, 2]);
47+
$table2 = TestPost::query()->whereIn('user_id', [3, 4]);
48+
49+
$result = $table1->unionAll($table2)
50+
->orderBy('user_id', 'desc')
51+
->cursorPaginate(1);
52+
53+
self::assertSame(['user_id'], $result->getOptions()['parameters']);
54+
}
55+
3956
public function testPaginationWithDistinct()
4057
{
4158
for ($i = 1; $i <= 3; $i++) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Http\Fixtures;
4+
5+
use Illuminate\Http\Resources\Json\ResourceCollection;
6+
7+
class PostModelCollectionResource extends ResourceCollection
8+
{
9+
public $collects = Post::class;
10+
11+
public function toArray($request)
12+
{
13+
return ['data' => $this->collection];
14+
}
15+
}

tests/Integration/Http/ResourceTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Illuminate\Tests\Integration\Http\Fixtures\Post;
2222
use Illuminate\Tests\Integration\Http\Fixtures\PostCollectionResource;
2323
use Illuminate\Tests\Integration\Http\Fixtures\PostCollectionResourceWithPaginationInformation;
24+
use Illuminate\Tests\Integration\Http\Fixtures\PostModelCollectionResource;
2425
use Illuminate\Tests\Integration\Http\Fixtures\PostResource;
2526
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithAnonymousResourceCollectionWithPaginationInformation;
2627
use Illuminate\Tests\Integration\Http\Fixtures\PostResourceWithExtraData;
@@ -36,6 +37,7 @@
3637
use Illuminate\Tests\Integration\Http\Fixtures\ResourceWithPreservedKeys;
3738
use Illuminate\Tests\Integration\Http\Fixtures\SerializablePostResource;
3839
use Illuminate\Tests\Integration\Http\Fixtures\Subscription;
40+
use LogicException;
3941
use Mockery;
4042
use Orchestra\Testbench\TestCase;
4143

@@ -1043,6 +1045,19 @@ public function testCollectionResourcesAreCountable()
10431045
$this->assertCount(2, $collection);
10441046
}
10451047

1048+
public function testCollectionResourcesMustCollectResources()
1049+
{
1050+
$posts = collect([
1051+
new Post(['id' => 1, 'title' => 'Test title']),
1052+
new Post(['id' => 2, 'title' => 'Test title 2']),
1053+
]);
1054+
1055+
$this->expectException(LogicException::class);
1056+
$this->expectExceptionMessage('must collect');
1057+
1058+
new PostModelCollectionResource($posts);
1059+
}
1060+
10461061
public function testKeysArePreservedIfTheResourceIsFlaggedToPreserveKeys()
10471062
{
10481063
$data = [

tests/Support/SupportStrTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,24 @@ public function testPadRight()
621621
$this->assertSame('Alien ', Str::padRight('Alien', 10));
622622
}
623623

624+
public function testSwapKeywords(): void
625+
{
626+
$this->assertSame(
627+
'PHP 8 is fantastic',
628+
Str::swap([
629+
'PHP' => 'PHP 8',
630+
'awesome' => 'fantastic',
631+
], 'PHP is awesome')
632+
);
633+
634+
$this->assertSame(
635+
'foo bar baz',
636+
Str::swap([
637+
'ⓐⓑ' => 'baz',
638+
], 'foo bar ⓐⓑ')
639+
);
640+
}
641+
624642
public function testWordCount()
625643
{
626644
$this->assertEquals(2, Str::wordCount('Hello, world!'));

0 commit comments

Comments
 (0)