Skip to content

Commit a488974

Browse files
authored
[11.x] fix: Model newCollection generics; feat: add HasCollection trait (#52171)
* fix: newCollection generics * feat: add HasCollection trait
1 parent 6c16f47 commit a488974

File tree

4 files changed

+71
-28
lines changed

4 files changed

+71
-28
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent;
4+
5+
/**
6+
* @template TCollection of \Illuminate\Database\Eloquent\Collection
7+
*/
8+
trait HasCollection
9+
{
10+
/**
11+
* Create a new Eloquent Collection instance.
12+
*
13+
* @param array<array-key, \Illuminate\Database\Eloquent\Model> $models
14+
* @return TCollection
15+
*/
16+
public function newCollection(array $models = [])
17+
{
18+
return new static::$collection($models);
19+
}
20+
}

src/Illuminate/Database/Eloquent/Model.php

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToSt
3535
Concerns\HidesAttributes,
3636
Concerns\GuardsAttributes,
3737
ForwardsCalls;
38+
/** @use HasCollection<\Illuminate\Database\Eloquent\Collection<array-key, static>> */
39+
use HasCollection;
3840

3941
/**
4042
* The connection name for the model.
@@ -218,6 +220,13 @@ abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToSt
218220
*/
219221
protected static string $builder = Builder::class;
220222

223+
/**
224+
* The Eloquent collection class to use for the model.
225+
*
226+
* @var class-string<\Illuminate\Database\Eloquent\Collection<*, *>>
227+
*/
228+
protected static string $collection = Collection::class;
229+
221230
/**
222231
* The name of the "created at" column.
223232
*
@@ -1588,20 +1597,6 @@ protected function newBaseQueryBuilder()
15881597
return $this->getConnection()->query();
15891598
}
15901599

1591-
/**
1592-
* Create a new Eloquent Collection instance.
1593-
*
1594-
* @template TKey of array-key
1595-
* @template TModel of \Illuminate\Database\Eloquent\Model
1596-
*
1597-
* @param array<TKey, TModel> $models
1598-
* @return \Illuminate\Database\Eloquent\Collection<TKey, TModel>
1599-
*/
1600-
public function newCollection(array $models = [])
1601-
{
1602-
return new Collection($models);
1603-
}
1604-
16051600
/**
16061601
* Create a new pivot model instance.
16071602
*

src/Illuminate/Notifications/DatabaseNotification.php

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
namespace Illuminate\Notifications;
44

55
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\HasCollection;
67
use Illuminate\Database\Eloquent\Model;
78

89
class DatabaseNotification extends Model
910
{
11+
/** @use HasCollection<DatabaseNotificationCollection> */
12+
use HasCollection;
13+
1014
/**
1115
* The "type" of the primary key ID.
1216
*
@@ -45,6 +49,8 @@ class DatabaseNotification extends Model
4549
'read_at' => 'datetime',
4650
];
4751

52+
protected static string $collection = DatabaseNotificationCollection::class;
53+
4854
/**
4955
* Get the notifiable entity that the notification belongs to.
5056
*
@@ -120,15 +126,4 @@ public function scopeUnread(Builder $query)
120126
{
121127
return $query->whereNull('read_at');
122128
}
123-
124-
/**
125-
* Create a new database notification collection instance.
126-
*
127-
* @param array $models
128-
* @return \Illuminate\Notifications\DatabaseNotificationCollection
129-
*/
130-
public function newCollection(array $models = [])
131-
{
132-
return new DatabaseNotificationCollection($models);
133-
}
134129
}

types/Database/Eloquent/Model.php

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
namespace Illuminate\Types\Model;
44

5+
use Illuminate\Database\Eloquent\Collection;
6+
use Illuminate\Database\Eloquent\HasCollection;
57
use Illuminate\Database\Eloquent\Model;
68
use User;
79

810
use function PHPStan\Testing\assertType;
911

10-
function test(User $user): void
12+
function test(User $user, Post $post, Comment $comment): void
1113
{
1214
assertType('UserFactory', User::factory(function ($attributes, $model) {
1315
assertType('array<string, mixed>', $attributes);
@@ -30,14 +32,45 @@ function test(User $user): void
3032
assertType('Illuminate\Database\Eloquent\Builder<User>', $user->prunable());
3133
assertType('Illuminate\Database\Eloquent\Relations\MorphMany<Illuminate\Notifications\DatabaseNotification, User>', $user->notifications());
3234

33-
assertType('Illuminate\Database\Eloquent\Collection<int, User>', $user->newCollection([new User()]));
34-
assertType('Illuminate\Database\Eloquent\Collection<string, Illuminate\Types\Model\Post>', $user->newCollection(['foo' => new Post()]));
35+
assertType('Illuminate\Database\Eloquent\Collection<(int|string), User>', $user->newCollection([new User()]));
36+
assertType('Illuminate\Types\Model\Posts<(int|string), Illuminate\Types\Model\Post>', $post->newCollection(['foo' => new Post()]));
37+
assertType('Illuminate\Types\Model\Comments', $comment->newCollection([new Comment()]));
3538

3639
assertType('bool', $user->restore());
3740
assertType('User', $user->restoreOrCreate());
3841
assertType('User', $user->createOrRestore());
3942
}
4043

4144
class Post extends Model
45+
{
46+
/** @use HasCollection<Posts<array-key, static>> */
47+
use HasCollection;
48+
49+
protected static string $collection = Posts::class;
50+
}
51+
52+
/**
53+
* @template TKey of array-key
54+
* @template TModel of Post
55+
*
56+
* @extends Collection<TKey, TModel> */
57+
class Posts extends Collection
58+
{
59+
}
60+
61+
final class Comment extends Model
62+
{
63+
/** @use HasCollection<Comments> */
64+
use HasCollection;
65+
66+
/** @param array<array-key, Comment> $models */
67+
public function newCollection(array $models = []): Comments
68+
{
69+
return new Comments($models);
70+
}
71+
}
72+
73+
/** @extends Collection<array-key, Comment> */
74+
final class Comments extends Collection
4275
{
4376
}

0 commit comments

Comments
 (0)