From 25a9bf7ef0dd522eee12bf0e636e07c7fac24947 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 23 Jun 2021 18:59:11 +0200 Subject: [PATCH 1/4] fix: allow firstOrNew/Create to be called without parameters --- stubs/EloquentBuilder.stubphp | 4 ++-- stubs/HasOneOrMany.stubphp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stubs/EloquentBuilder.stubphp b/stubs/EloquentBuilder.stubphp index c7db1cc9..8ddf5d89 100644 --- a/stubs/EloquentBuilder.stubphp +++ b/stubs/EloquentBuilder.stubphp @@ -141,14 +141,14 @@ class Builder * @param array $values * @return TModel */ - public function firstOrNew(array $attributes, array $values = []) { } + public function firstOrNew(array $attributes = [], array $values = []) { } /** * @param array $attributes * @param array $values * @return TModel */ - public function firstOrCreate(array $attributes, array $values = []) { } + public function firstOrCreate(array $attributes = [], array $values = []) { } /** * @param array $attributes diff --git a/stubs/HasOneOrMany.stubphp b/stubs/HasOneOrMany.stubphp index 2dd65bf7..fcc75080 100644 --- a/stubs/HasOneOrMany.stubphp +++ b/stubs/HasOneOrMany.stubphp @@ -52,7 +52,7 @@ abstract class HasOneOrMany extends Relation * @return \Illuminate\Database\Eloquent\Model * @psalm-return TRelatedModel */ - public function firstOrNew(array $attributes, array $values = []) { } + public function firstOrNew(array $attributes = [], array $values = []) { } /** * @param array $attributes @@ -60,7 +60,7 @@ abstract class HasOneOrMany extends Relation * @return \Illuminate\Database\Eloquent\Model * @psalm-return TRelatedModel */ - public function firstOrCreate(array $attributes, array $values = []) { } + public function firstOrCreate(array $attributes = [], array $values = []) { } /** * @param array $attributes From 98a7bd56b13513cdd7d0273449a2f366bbd0213e Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Wed, 7 Jul 2021 00:42:39 +0200 Subject: [PATCH 2/4] fix: split Builder/HasOneOrMany diffs into 6/8 stubs --- stubs/6/EloquentBuilder.stubphp | 25 +++++++++++++++++++++++++ stubs/6/HasOneOrMany.stubphp | 27 +++++++++++++++++++++++++++ stubs/8/EloquentBuilder.stubphp | 26 ++++++++++++++++++++++++++ stubs/8/HasOneOrMany.stubphp | 30 ++++++++++++++++++++++++++++++ stubs/EloquentBuilder.stubphp | 14 -------------- stubs/HasOneOrMany.stubphp | 17 ----------------- 6 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 stubs/6/EloquentBuilder.stubphp create mode 100644 stubs/6/HasOneOrMany.stubphp create mode 100644 stubs/8/EloquentBuilder.stubphp create mode 100644 stubs/8/HasOneOrMany.stubphp diff --git a/stubs/6/EloquentBuilder.stubphp b/stubs/6/EloquentBuilder.stubphp new file mode 100644 index 00000000..637e07e5 --- /dev/null +++ b/stubs/6/EloquentBuilder.stubphp @@ -0,0 +1,25 @@ + +* @mixin \Illuminate\Database\Eloquent\Builder +*/ +abstract class HasOneOrMany extends Relation +{ + /** + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @psalm-return TRelatedModel + */ + public function firstOrNew(array $attributes, array $values = []) { } + + /** + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @psalm-return TRelatedModel + */ + public function firstOrCreate(array $attributes, array $values = []) { } +} diff --git a/stubs/8/EloquentBuilder.stubphp b/stubs/8/EloquentBuilder.stubphp new file mode 100644 index 00000000..0f4b4ab2 --- /dev/null +++ b/stubs/8/EloquentBuilder.stubphp @@ -0,0 +1,26 @@ + +* @mixin \Illuminate\Database\Eloquent\Builder +*/ +abstract class HasOneOrMany extends Relation +{ + /** + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @psalm-return TRelatedModel + */ + public function firstOrNew(array $attributes = [], array $values = []) { } + + /** + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @psalm-return TRelatedModel + */ + public function firstOrCreate(array $attributes = [], array $values = []) { } +} diff --git a/stubs/EloquentBuilder.stubphp b/stubs/EloquentBuilder.stubphp index 8ddf5d89..5991af50 100644 --- a/stubs/EloquentBuilder.stubphp +++ b/stubs/EloquentBuilder.stubphp @@ -136,20 +136,6 @@ class Builder */ public function findOrNew($id, $columns = ['*']) { } - /** - * @param array $attributes - * @param array $values - * @return TModel - */ - public function firstOrNew(array $attributes = [], array $values = []) { } - - /** - * @param array $attributes - * @param array $values - * @return TModel - */ - public function firstOrCreate(array $attributes = [], array $values = []) { } - /** * @param array $attributes * @param array $values diff --git a/stubs/HasOneOrMany.stubphp b/stubs/HasOneOrMany.stubphp index fcc75080..13463c13 100644 --- a/stubs/HasOneOrMany.stubphp +++ b/stubs/HasOneOrMany.stubphp @@ -4,7 +4,6 @@ namespace Illuminate\Database\Eloquent\Relations; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Collection; /** * @template TRelatedModel of Model @@ -46,22 +45,6 @@ abstract class HasOneOrMany extends Relation */ public function findOrNew($id, $columns = ['*']) { } - /** - * @param array $attributes - * @param array $values - * @return \Illuminate\Database\Eloquent\Model - * @psalm-return TRelatedModel - */ - public function firstOrNew(array $attributes = [], array $values = []) { } - - /** - * @param array $attributes - * @param array $values - * @return \Illuminate\Database\Eloquent\Model - * @psalm-return TRelatedModel - */ - public function firstOrCreate(array $attributes = [], array $values = []) { } - /** * @param array $attributes * @param array $values From d7ebd103d51b3fa11f58f192150d0693da72d733 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 9 Jul 2021 21:38:05 +0200 Subject: [PATCH 3/4] test: ensure firstOrCreate/New work in 6.x/8.x --- tests/acceptance/EloquentBuilderTypes.feature | 59 +++++++++++++++++++ .../acceptance/EloquentRelationTypes.feature | 53 +++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/tests/acceptance/EloquentBuilderTypes.feature b/tests/acceptance/EloquentBuilderTypes.feature index 3eedf72f..05b69b91 100644 --- a/tests/acceptance/EloquentBuilderTypes.feature +++ b/tests/acceptance/EloquentBuilderTypes.feature @@ -165,3 +165,62 @@ Feature: Eloquent Builder types """ When I run Psalm Then I see no errors + + Scenario: cannot call firstOrNew and firstOrCreate without parameters in Laravel 6.x + Given I have the "laravel/framework" package satisfying the "6.*" + And I have the following code + """ + $builder + * @psalm-return User + */ + function test_firstOrCreate(Builder $builder): User { + return $builder->firstOrCreate(); + } + + /** + * @psalm-param Builder $builder + * @psalm-return User + */ + function test_firstOrNew(Builder $builder): User { + return $builder->firstOrNew(); + } + """ + When I run Psalm + Then I see these errors + | Type | Message | + | TooFewArguments | Too few arguments for method Illuminate\Database\Eloquent\Builder::firstorcreate saw 0 | + | TooFewArguments | Too few arguments for method Illuminate\Database\Eloquent\Builder::firstornew saw 0 | + + Scenario: can call firstOrNew and firstOrCreate without parameters in Laravel 8.x + Given I have the "laravel/framework" package satisfying the ">= 8.0" + And I have the following code + """ + $builder + * @psalm-return User + */ + function test_firstOrCreate(Builder $builder): User { + return $builder->firstOrCreate(); + } + + /** + * @psalm-param Builder $builder + * @psalm-return User + */ + function test_firstOrNew(Builder $builder): User { + return $builder->firstOrNew(); + } + """ + When I run Psalm + Then I see no errors diff --git a/tests/acceptance/EloquentRelationTypes.feature b/tests/acceptance/EloquentRelationTypes.feature index 97cf047f..e36764f0 100644 --- a/tests/acceptance/EloquentRelationTypes.feature +++ b/tests/acceptance/EloquentRelationTypes.feature @@ -356,3 +356,56 @@ Feature: Eloquent Relation types """ When I run Psalm Then I see no errors + + Scenario: cannot call firstOrNew and firstOrCreate without parameters in Laravel 6.x + Given I have the "laravel/framework" package satisfying the "6.*" + And I have the following code + """ + function test_hasOne_firstOrCreate(User $user): Phone { + return $user->phone()->firstOrCreate(); + } + + function test_hasOne_firstOrNew(User $user): Phone { + return $user->phone()->firstOrNew(); + } + + function test_hasMany_firstOrCreate(Post $post): Comment { + return $post->comments()->firstOrCreate(); + } + + function test_hasMany_firstOrNew(Post $post): Comment { + return $post->comments()->firstOrNew(); + } + """ + When I run Psalm + Then I see these errors + | Type | Message | + | TooFewArguments | Too few arguments for method Illuminate\Database\Eloquent\Relations\HasOneOrMany::firstorcreate saw 0 | + | TooFewArguments | Too few arguments for method Illuminate\Database\Eloquent\Relations\HasOneOrMany::firstornew saw 0 | + | TooFewArguments | Too few arguments for method Illuminate\Database\Eloquent\Relations\HasOneOrMany::firstorcreate saw 0 | + | TooFewArguments | Too few arguments for method Illuminate\Database\Eloquent\Relations\HasOneOrMany::firstornew saw 0 | + + + Scenario: can call firstOrNew and firstOrCreate without parameters in Laravel 8.x + Given I have the "laravel/framework" package satisfying the ">= 8.0" + And I have the following code + """ + function test_hasOne_firstOrCreate(User $user): Phone { + return $user->phone()->firstOrCreate(); + } + + function test_hasOne_firstOrNew(User $user): Phone { + return $user->phone()->firstOrNew(); + } + + function test_hasMany_firstOrCreate(Post $post): Comment { + return $post->comments()->firstOrCreate(); + } + + function test_hasMany_firstOrNew(Post $post): Comment { + return $post->comments()->firstOrNew(); + } + """ + When I run Psalm + Then I see no errors + From 02f4b50c8aa74e6eaa4d5011f7987a2527d717e7 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Fri, 9 Jul 2021 21:53:36 +0200 Subject: [PATCH 4/4] test: extract common Builder preamble --- tests/acceptance/EloquentBuilderTypes.feature | 91 ++++++++++--------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/tests/acceptance/EloquentBuilderTypes.feature b/tests/acceptance/EloquentBuilderTypes.feature index 05b69b91..12daf5a0 100644 --- a/tests/acceptance/EloquentBuilderTypes.feature +++ b/tests/acceptance/EloquentBuilderTypes.feature @@ -15,73 +15,78 @@ Feature: Eloquent Builder types """ + And I have the following code preamble + """ + + * @return Builder */ - public function getNewQuery(): \Illuminate\Database\Eloquent\Builder + public function getNewQuery(): Builder { return User::query(); } /** - * @return \Illuminate\Database\Eloquent\Builder + * @return Builder */ - public function getNewModelQuery(): \Illuminate\Database\Eloquent\Builder + public function getNewModelQuery(): Builder { return (new User())->newModelQuery(); } /** - * @param \Illuminate\Database\Eloquent\Builder $builder + * @param Builder $builder */ - public function firstOrFailFromBuilderInstance(\Illuminate\Database\Eloquent\Builder $builder): User { + public function firstOrFailFromBuilderInstance(Builder $builder): User { return $builder->firstOrFail(); } /** - * @param \Illuminate\Database\Eloquent\Builder $builder + * @param Builder $builder */ - public function findOrFailFromBuilderInstance(\Illuminate\Database\Eloquent\Builder $builder): User { + public function findOrFailFromBuilderInstance(Builder $builder): User { return $builder->findOrFail(1); } /** - * @param \Illuminate\Database\Eloquent\Builder $builder - * @return \Illuminate\Database\Eloquent\Collection + * @param Builder $builder + * @return Collection */ - public function findMultipleOrFailFromBuilderInstance(\Illuminate\Database\Eloquent\Builder $builder): \Illuminate\Database\Eloquent\Collection { + public function findMultipleOrFailFromBuilderInstance(Builder $builder): Collection { return $builder->findOrFail([1, 2]); } /** - * @param \Illuminate\Database\Eloquent\Builder $builder + * @param Builder $builder */ - public function findOne(\Illuminate\Database\Eloquent\Builder $builder): ?User { + public function findOne(Builder $builder): ?User { return $builder->find(1); } /** - * @param \Illuminate\Database\Eloquent\Builder $builder + * @param Builder $builder */ - public function findViaArray(\Illuminate\Database\Eloquent\Builder $builder): \Illuminate\Database\Eloquent\Collection { + public function findViaArray(Builder $builder): Collection { return $builder->find([1]); } /** - * @return \Illuminate\Database\Eloquent\Builder + * @return Builder */ - public function getWhereBuilderViaInstance(array $attributes): \Illuminate\Database\Eloquent\Builder { + public function getWhereBuilderViaInstance(array $attributes): Builder { return (new User())->where($attributes); } } @@ -90,10 +95,16 @@ Feature: Eloquent Builder types Then I see no errors Scenario: can call static methods on model - Given I have the following code + Given I have the following code preamble """ + * @return Builder */ - public function getWhereBuilderViaStatic(array $attributes): \Illuminate\Database\Eloquent\Builder + public function getWhereBuilderViaStatic(array $attributes): Builder { return User::where($attributes); } /** - * @psalm-return \Illuminate\Database\Eloquent\Collection + * @psalm-return Collection */ - public function getWhereViaStatic(array $attributes): \Illuminate\Database\Eloquent\Collection + public function getWhereViaStatic(array $attributes): Collection { return User::where($attributes)->get(); } @@ -122,20 +133,25 @@ Feature: Eloquent Builder types Then I see no errors Scenario: - Given I have the following code + Given I have the following code preamble """ + * @return Builder */ - public function test_failure(): \Illuminate\Database\Eloquent\Builder + public function test_failure(): Builder { return User::fakeQueryMethodThatDoesntExist(); } @@ -150,11 +166,6 @@ Feature: Eloquent Builder types Scenario: can call methods on underlying query builder Given I have the following code """ - $builder * @psalm-return Builder @@ -170,11 +181,6 @@ Feature: Eloquent Builder types Given I have the "laravel/framework" package satisfying the "6.*" And I have the following code """ - $builder * @psalm-return User @@ -201,11 +207,6 @@ Feature: Eloquent Builder types Given I have the "laravel/framework" package satisfying the ">= 8.0" And I have the following code """ - $builder * @psalm-return User