From 6915a937e79151dfd615984513ed3509e50ea756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 3 Feb 2025 09:34:09 +0000 Subject: [PATCH 01/18] Add NamedScope attribute --- .../Eloquent/Attributes/NamedScope.php | 19 +++++++++++++++ src/Illuminate/Database/Eloquent/Model.php | 24 ++++++++++++++++++- .../Database/EloquentModelScopeTest.php | 14 +++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/Illuminate/Database/Eloquent/Attributes/NamedScope.php diff --git a/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php b/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php new file mode 100644 index 000000000000..66b5d81839ef --- /dev/null +++ b/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php @@ -0,0 +1,19 @@ +getAttributes(NamedScope::class); + + if ($attribute === []) { + return null; + } + + return $method->name; + } + /** * Determine if the model has a given scope. * @@ -1639,7 +1657,7 @@ public function newPivot(self $parent, array $attributes, $table, $exists, $usin */ public function hasNamedScope($scope) { - return method_exists($this, 'scope'.ucfirst($scope)); + return method_exists($this, 'scope'.ucfirst($scope)) || $this->getAttributedNamedScope($scope) !== null; } /** @@ -1651,6 +1669,10 @@ public function hasNamedScope($scope) */ public function callNamedScope($scope, array $parameters = []) { + if (($method = $this->getAttributedNamedScope($scope)) !== null) { + return $this->{$method}(...$parameters); + } + return $this->{'scope'.ucfirst($scope)}(...$parameters); } diff --git a/tests/Integration/Database/EloquentModelScopeTest.php b/tests/Integration/Database/EloquentModelScopeTest.php index 408026738c2e..b1ff7f389e37 100644 --- a/tests/Integration/Database/EloquentModelScopeTest.php +++ b/tests/Integration/Database/EloquentModelScopeTest.php @@ -3,6 +3,7 @@ namespace Illuminate\Tests\Integration\Database; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Attributes\NamedScope; class EloquentModelScopeTest extends DatabaseTestCase { @@ -19,6 +20,13 @@ public function testModelDoesNotHaveScope() $this->assertFalse($model->hasNamedScope('doesNotExist')); } + + public function testModelHasAttributedScope() + { + $model = new TestScopeModel1; + + $this->assertTrue($model->hasNamedScope('existsAsWell')); + } } class TestScopeModel1 extends Model @@ -27,4 +35,10 @@ public function scopeExists() { return true; } + + #[NamedScope] + public function existsAsWell() + { + return true; + } } From 1b11b31508fd194cee317fa940523b336cb9d0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 3 Feb 2025 09:44:15 +0000 Subject: [PATCH 02/18] StyleCI --- src/Illuminate/Database/Eloquent/Model.php | 6 +++--- tests/Integration/Database/EloquentModelScopeTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index bbb01a9aaa69..a4c1feb27e32 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -11,6 +11,7 @@ use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString; use Illuminate\Contracts\Support\Jsonable; use Illuminate\Database\ConnectionResolverInterface as Resolver; +use Illuminate\Database\Eloquent\Attributes\NamedScope; use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\Concerns\AsPivot; @@ -24,9 +25,8 @@ use JsonException; use JsonSerializable; use LogicException; -use Stringable; use ReflectionMethod; -use Illuminate\Database\Eloquent\Attributes\NamedScope; +use Stringable; abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToString, HasBroadcastChannel, Jsonable, JsonSerializable, QueueableEntity, Stringable, UrlRoutable { @@ -1635,7 +1635,7 @@ public function newPivot(self $parent, array $attributes, $table, $exists, $usin public function getAttributedNamedScope(string $scope): ?string { - if (!method_exists($this, $scope)) { + if (! method_exists($this, $scope)) { return null; } diff --git a/tests/Integration/Database/EloquentModelScopeTest.php b/tests/Integration/Database/EloquentModelScopeTest.php index b1ff7f389e37..6c7ad371b854 100644 --- a/tests/Integration/Database/EloquentModelScopeTest.php +++ b/tests/Integration/Database/EloquentModelScopeTest.php @@ -2,8 +2,8 @@ namespace Illuminate\Tests\Integration\Database; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Attributes\NamedScope; +use Illuminate\Database\Eloquent\Model; class EloquentModelScopeTest extends DatabaseTestCase { From df6744313099b8a26e171354e41ed1f65ab835ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:12:37 +0100 Subject: [PATCH 03/18] Make Model::getAttributedNamedScope() protected --- src/Illuminate/Database/Eloquent/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index a4c1feb27e32..7392283d80fa 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -1633,7 +1633,7 @@ public function newPivot(self $parent, array $attributes, $table, $exists, $usin : Pivot::fromAttributes($parent, $attributes, $table, $exists); } - public function getAttributedNamedScope(string $scope): ?string + protected function getAttributedNamedScope(string $scope): ?string { if (! method_exists($this, $scope)) { return null; From 19590d224161bd0e66baa8cee2d40b126960db05 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 6 Feb 2025 18:19:40 +0800 Subject: [PATCH 04/18] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Database/EloquentModelScopeTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Integration/Database/EloquentModelScopeTest.php b/tests/Integration/Database/EloquentModelScopeTest.php index 6c7ad371b854..0122e582ef7f 100644 --- a/tests/Integration/Database/EloquentModelScopeTest.php +++ b/tests/Integration/Database/EloquentModelScopeTest.php @@ -2,6 +2,7 @@ namespace Illuminate\Tests\Integration\Database; +use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Attributes\NamedScope; use Illuminate\Database\Eloquent\Model; @@ -31,14 +32,14 @@ public function testModelHasAttributedScope() class TestScopeModel1 extends Model { - public function scopeExists() + public function scopeExists(Builder $builder) { - return true; + return $builder; } #[NamedScope] - public function existsAsWell() + protected function existsAsWell(Builder $builder) { - return true; + return $builder; } } From d6fc922e38ffea538d34393ba055249f118b566c Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 6 Feb 2025 18:50:59 +0800 Subject: [PATCH 05/18] wip Signed-off-by: Mior Muhammad Zaki --- .../EloquentNamedScopedAttibuteTest.php | 32 ++++++++++++++++ .../Database/Fixtures/NamedScopedUser.php | 38 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/Integration/Database/EloquentNamedScopedAttibuteTest.php create mode 100644 tests/Integration/Database/Fixtures/NamedScopedUser.php diff --git a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php new file mode 100644 index 000000000000..7ad07e1f1bda --- /dev/null +++ b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php @@ -0,0 +1,32 @@ +verified(true); + + $this->assertSame( + 'select * from "named_scoped_users" where "email_verified_at" is not null', + $query->toRawSql(), + ); + } + + public function test_it_can_query_named_scoped_from_static_query() + { + $query = Fixtures\NamedScopedUser::verifiedUser(true); + + $this->assertSame( + 'select * from "named_scoped_users" where "email_verified_at" is not null', + $query->toRawSql(), + ); + } +} diff --git a/tests/Integration/Database/Fixtures/NamedScopedUser.php b/tests/Integration/Database/Fixtures/NamedScopedUser.php new file mode 100644 index 000000000000..596fe38c74af --- /dev/null +++ b/tests/Integration/Database/Fixtures/NamedScopedUser.php @@ -0,0 +1,38 @@ + 'datetime', + 'password' => 'hashed', + ]; + } + + #[NamedScoped] + protected function verified(Builder $builder, bool $email = true) + { + return $builder->when( + $email === true, + fn ($query) => $query->whereNotNull('email_verified_at'), + fn ($query) => $queryu->whereNull('email_verified_at'), + ); + } + + public function scopeVerifiedUser(Builder $builder, bool $email = true) + { + return $builder->when( + $email === true, + fn ($query) => $query->whereNotNull('email_verified_at'), + fn ($query) => $queryu->whereNull('email_verified_at'), + ); + } +} From 6d5aa009d108c78d0eb350b7d777fd37f7aca5b6 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 6 Feb 2025 18:51:24 +0800 Subject: [PATCH 06/18] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Database/EloquentNamedScopedAttibuteTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php index 7ad07e1f1bda..8fb1be380b0b 100644 --- a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php +++ b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php @@ -22,7 +22,7 @@ public function test_it_can_query_named_scoped_from_the_query_builder() public function test_it_can_query_named_scoped_from_static_query() { - $query = Fixtures\NamedScopedUser::verifiedUser(true); + $query = Fixtures\NamedScopedUser::verified(true); $this->assertSame( 'select * from "named_scoped_users" where "email_verified_at" is not null', From 3d3b2cd482c33690b075144dde909e3c15002913 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 6 Feb 2025 19:01:43 +0800 Subject: [PATCH 07/18] wip Signed-off-by: Mior Muhammad Zaki --- .../Database/EloquentNamedScopedAttibuteTest.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php index 8fb1be380b0b..a8b5782de9d9 100644 --- a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php +++ b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php @@ -6,10 +6,19 @@ use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\TestCase; -#[WithConfig('database.default', 'testing')] #[WithMigration] class EloquentNamedScopedAttibuteTest extends TestCase { + protected function setUp(): void + { + parent::setUp(); + + $this->markTestSkippedUnless( + $this->usesSqliteInMemoryDatabaseConnection(), + 'Requires in-memory database connection', + ); + } + public function test_it_can_query_named_scoped_from_the_query_builder() { $query = Fixtures\NamedScopedUser::query()->verified(true); From 9f6db62336e05ad02c75f78f120d2e204b549cf0 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 6 Feb 2025 19:02:54 +0800 Subject: [PATCH 08/18] wip Signed-off-by: Mior Muhammad Zaki --- tests/Integration/Database/EloquentNamedScopedAttibuteTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php index a8b5782de9d9..496a15882383 100644 --- a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php +++ b/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php @@ -2,7 +2,6 @@ namespace Illuminate\Tests\Integration\Database; -use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\TestCase; From 90de5cf0e760b6515eada36ef1e4f8ba771acf12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 17:40:05 +0100 Subject: [PATCH 09/18] =?UTF-8?q?fix(database):=20=E2=9C=8F=20Fix=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Integration/Database/Fixtures/NamedScopedUser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Database/Fixtures/NamedScopedUser.php b/tests/Integration/Database/Fixtures/NamedScopedUser.php index 596fe38c74af..d25bfb6041e3 100644 --- a/tests/Integration/Database/Fixtures/NamedScopedUser.php +++ b/tests/Integration/Database/Fixtures/NamedScopedUser.php @@ -23,7 +23,7 @@ protected function verified(Builder $builder, bool $email = true) return $builder->when( $email === true, fn ($query) => $query->whereNotNull('email_verified_at'), - fn ($query) => $queryu->whereNull('email_verified_at'), + fn ($query) => $query->whereNull('email_verified_at'), ); } @@ -32,7 +32,7 @@ public function scopeVerifiedUser(Builder $builder, bool $email = true) return $builder->when( $email === true, fn ($query) => $query->whereNotNull('email_verified_at'), - fn ($query) => $queryu->whereNull('email_verified_at'), + fn ($query) => $query->whereNull('email_verified_at'), ); } } From 19f48046fa27d56cacf73380dc73e9b9635ccaa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 17:42:45 +0100 Subject: [PATCH 10/18] refactor(database): :truck: Rename attribute class to comply with PSR-4 --- .../Database/Eloquent/Attributes/NamedScope.php | 2 +- ...buteTest.php => EloquentNamedScopeAttibuteTest.php} | 10 +++++----- .../{NamedScopedUser.php => NamedScopeUser.php} | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename tests/Integration/Database/{EloquentNamedScopedAttibuteTest.php => EloquentNamedScopeAttibuteTest.php} (66%) rename tests/Integration/Database/Fixtures/{NamedScopedUser.php => NamedScopeUser.php} (89%) diff --git a/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php b/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php index 66b5d81839ef..c13eb42c1090 100644 --- a/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php +++ b/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php @@ -5,7 +5,7 @@ use Attribute; #[Attribute(Attribute::TARGET_METHOD)] -class NamedScoped +class NamedScope { /** * Create a new attribute instance. diff --git a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php b/tests/Integration/Database/EloquentNamedScopeAttibuteTest.php similarity index 66% rename from tests/Integration/Database/EloquentNamedScopedAttibuteTest.php rename to tests/Integration/Database/EloquentNamedScopeAttibuteTest.php index 496a15882383..858eb9ad6a8e 100644 --- a/tests/Integration/Database/EloquentNamedScopedAttibuteTest.php +++ b/tests/Integration/Database/EloquentNamedScopeAttibuteTest.php @@ -6,7 +6,7 @@ use Orchestra\Testbench\TestCase; #[WithMigration] -class EloquentNamedScopedAttibuteTest extends TestCase +class EloquentNamedScopeAttibuteTest extends TestCase { protected function setUp(): void { @@ -20,20 +20,20 @@ protected function setUp(): void public function test_it_can_query_named_scoped_from_the_query_builder() { - $query = Fixtures\NamedScopedUser::query()->verified(true); + $query = Fixtures\NamedScopeUser::query()->verified(true); $this->assertSame( - 'select * from "named_scoped_users" where "email_verified_at" is not null', + 'select * from "named_scope_users" where "email_verified_at" is not null', $query->toRawSql(), ); } public function test_it_can_query_named_scoped_from_static_query() { - $query = Fixtures\NamedScopedUser::verified(true); + $query = Fixtures\NamedScopeUser::verified(true); $this->assertSame( - 'select * from "named_scoped_users" where "email_verified_at" is not null', + 'select * from "named_scope_users" where "email_verified_at" is not null', $query->toRawSql(), ); } diff --git a/tests/Integration/Database/Fixtures/NamedScopedUser.php b/tests/Integration/Database/Fixtures/NamedScopeUser.php similarity index 89% rename from tests/Integration/Database/Fixtures/NamedScopedUser.php rename to tests/Integration/Database/Fixtures/NamedScopeUser.php index d25bfb6041e3..36052be652f1 100644 --- a/tests/Integration/Database/Fixtures/NamedScopedUser.php +++ b/tests/Integration/Database/Fixtures/NamedScopeUser.php @@ -2,10 +2,10 @@ namespace Illuminate\Tests\Integration\Database\Fixtures; -use Illuminate\Database\Eloquent\Attributes\NamedScoped; +use Illuminate\Database\Eloquent\Attributes\NamedScope; use Illuminate\Database\Eloquent\Builder; -class NamedScopedUser extends User +class NamedScopeUser extends User { /** {@inheritdoc} */ #[\Override] @@ -17,7 +17,7 @@ protected function casts(): array ]; } - #[NamedScoped] + #[NamedScope] protected function verified(Builder $builder, bool $email = true) { return $builder->when( From 799451aa83af56b1b6fee9991a20472e6c88ac6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 18:37:05 +0100 Subject: [PATCH 11/18] refactor(database): :white_check_mark: Simplify test --- .../Database/EloquentNamedScopeAttibuteTest.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/Integration/Database/EloquentNamedScopeAttibuteTest.php b/tests/Integration/Database/EloquentNamedScopeAttibuteTest.php index 858eb9ad6a8e..4eb58a7930bc 100644 --- a/tests/Integration/Database/EloquentNamedScopeAttibuteTest.php +++ b/tests/Integration/Database/EloquentNamedScopeAttibuteTest.php @@ -8,6 +8,8 @@ #[WithMigration] class EloquentNamedScopeAttibuteTest extends TestCase { + protected $query = 'select * from "named_scope_users" where "email_verified_at" is not null'; + protected function setUp(): void { parent::setUp(); @@ -22,19 +24,13 @@ public function test_it_can_query_named_scoped_from_the_query_builder() { $query = Fixtures\NamedScopeUser::query()->verified(true); - $this->assertSame( - 'select * from "named_scope_users" where "email_verified_at" is not null', - $query->toRawSql(), - ); + $this->assertSame($this->query, $query->toRawSql()); } public function test_it_can_query_named_scoped_from_static_query() { $query = Fixtures\NamedScopeUser::verified(true); - $this->assertSame( - 'select * from "named_scope_users" where "email_verified_at" is not null', - $query->toRawSql(), - ); + $this->assertSame($this->query, $query->toRawSql()); } } From ded61f4159ea4ebcae481f51f5724d6f1af9672e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 18:43:17 +0100 Subject: [PATCH 12/18] refactor(database): :recycle: Simplify named local scope attribute check logic --- src/Illuminate/Database/Eloquent/Model.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 7392283d80fa..b77658c0039e 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -1633,20 +1633,20 @@ public function newPivot(self $parent, array $attributes, $table, $exists, $usin : Pivot::fromAttributes($parent, $attributes, $table, $exists); } - protected function getAttributedNamedScope(string $scope): ?string + protected function hasAttributedNamedScope(string $scope): ?string { if (! method_exists($this, $scope)) { - return null; + return false; } $method = new ReflectionMethod($this, $scope); $attribute = $method->getAttributes(NamedScope::class); if ($attribute === []) { - return null; + return false; } - return $method->name; + return true; } /** @@ -1657,7 +1657,7 @@ protected function getAttributedNamedScope(string $scope): ?string */ public function hasNamedScope($scope) { - return method_exists($this, 'scope'.ucfirst($scope)) || $this->getAttributedNamedScope($scope) !== null; + return method_exists($this, 'scope'.ucfirst($scope)) || $this->hasAttributedNamedScope($scope); } /** @@ -1669,8 +1669,8 @@ public function hasNamedScope($scope) */ public function callNamedScope($scope, array $parameters = []) { - if (($method = $this->getAttributedNamedScope($scope)) !== null) { - return $this->{$method}(...$parameters); + if ($this->hasAttributedNamedScope($scope)) { + return $this->{$scope}(...$parameters); } return $this->{'scope'.ucfirst($scope)}(...$parameters); From ab19b5e715baec1e314c61a4b9b258459b9b0490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 18:51:08 +0100 Subject: [PATCH 13/18] fix(database): :bug: Make attributed named scope statically callable --- src/Illuminate/Database/Eloquent/Model.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index b77658c0039e..7ef0d58cc090 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -1633,13 +1633,13 @@ public function newPivot(self $parent, array $attributes, $table, $exists, $usin : Pivot::fromAttributes($parent, $attributes, $table, $exists); } - protected function hasAttributedNamedScope(string $scope): ?string + protected static function hasAttributedNamedScope(string $scope): ?string { - if (! method_exists($this, $scope)) { + if (! method_exists(static::class, $scope)) { return false; } - $method = new ReflectionMethod($this, $scope); + $method = new ReflectionMethod(static::class, $scope); $attribute = $method->getAttributes(NamedScope::class); if ($attribute === []) { @@ -1657,7 +1657,7 @@ protected function hasAttributedNamedScope(string $scope): ?string */ public function hasNamedScope($scope) { - return method_exists($this, 'scope'.ucfirst($scope)) || $this->hasAttributedNamedScope($scope); + return method_exists($this, 'scope'.ucfirst($scope)) || static::hasAttributedNamedScope($scope); } /** @@ -2399,6 +2399,9 @@ public function __call($method, $parameters) */ public static function __callStatic($method, $parameters) { + if (static::hasAttributedNamedScope($method)) { + $parameters = [ static::query(), ...$parameters ]; + } return (new static)->$method(...$parameters); } From 83a21e495f02bc1c18287b1c121fb07fdbeae513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 18:53:22 +0100 Subject: [PATCH 14/18] style(database): :rotating_light: StyleCI --- src/Illuminate/Database/Eloquent/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 7ef0d58cc090..798e18c1b821 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -2400,7 +2400,7 @@ public function __call($method, $parameters) public static function __callStatic($method, $parameters) { if (static::hasAttributedNamedScope($method)) { - $parameters = [ static::query(), ...$parameters ]; + $parameters = [static::query(), ...$parameters]; } return (new static)->$method(...$parameters); } From 50dbb5a3c76093f4ed7d94c5ed3ab56e7efe0196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sat, 8 Feb 2025 18:55:36 +0100 Subject: [PATCH 15/18] style(database): :rotating_light: StyleCI --- src/Illuminate/Database/Eloquent/Model.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 798e18c1b821..41a1d13f2bb3 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -2402,6 +2402,7 @@ public static function __callStatic($method, $parameters) if (static::hasAttributedNamedScope($method)) { $parameters = [static::query(), ...$parameters]; } + return (new static)->$method(...$parameters); } From eb4fc571ddeaa14a78766fd6e7bcfa34464dad3a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 19 Mar 2025 15:26:44 -0500 Subject: [PATCH 16/18] formatting --- .../Eloquent/Attributes/NamedScope.php | 19 ---------- src/Illuminate/Database/Eloquent/Model.php | 38 +++++++++---------- 2 files changed, 18 insertions(+), 39 deletions(-) delete mode 100644 src/Illuminate/Database/Eloquent/Attributes/NamedScope.php diff --git a/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php b/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php deleted file mode 100644 index c13eb42c1090..000000000000 --- a/src/Illuminate/Database/Eloquent/Attributes/NamedScope.php +++ /dev/null @@ -1,19 +0,0 @@ -getAttributes(NamedScope::class); - - if ($attribute === []) { - return false; - } - - return true; - } - /** * Determine if the model has a given scope. * @@ -1657,7 +1641,8 @@ protected static function hasAttributedNamedScope(string $scope): ?string */ public function hasNamedScope($scope) { - return method_exists($this, 'scope'.ucfirst($scope)) || static::hasAttributedNamedScope($scope); + return method_exists($this, 'scope'.ucfirst($scope)) || + static::isScopeMethodWithAttribute($scope); } /** @@ -1669,13 +1654,26 @@ public function hasNamedScope($scope) */ public function callNamedScope($scope, array $parameters = []) { - if ($this->hasAttributedNamedScope($scope)) { + if ($this->isScopeMethodWithAttribute($scope)) { return $this->{$scope}(...$parameters); } return $this->{'scope'.ucfirst($scope)}(...$parameters); } + /** + * Determine if the given method has a scope attribute. + * + * @param string $method + * @return bool + */ + protected static function isScopeMethodWithAttribute(string $method) + { + return method_exists(static::class, $method) && + (new ReflectionMethod(static::class, $method)) + ->getAttributes(LocalScope::class) !== []; + } + /** * Convert the model instance to an array. * @@ -2399,7 +2397,7 @@ public function __call($method, $parameters) */ public static function __callStatic($method, $parameters) { - if (static::hasAttributedNamedScope($method)) { + if (static::isScopeMethodWithAttribute($method)) { $parameters = [static::query(), ...$parameters]; } From 2c44cd5978a2a3bb47549c55aac7de02e9b05892 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 19 Mar 2025 15:26:49 -0500 Subject: [PATCH 17/18] add files --- .../Database/Eloquent/Attributes/Scope.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/Illuminate/Database/Eloquent/Attributes/Scope.php diff --git a/src/Illuminate/Database/Eloquent/Attributes/Scope.php b/src/Illuminate/Database/Eloquent/Attributes/Scope.php new file mode 100644 index 000000000000..ff7d1048cbad --- /dev/null +++ b/src/Illuminate/Database/Eloquent/Attributes/Scope.php @@ -0,0 +1,19 @@ + Date: Wed, 19 Mar 2025 15:27:53 -0500 Subject: [PATCH 18/18] fix tests --- tests/Integration/Database/EloquentModelScopeTest.php | 2 +- tests/Integration/Database/Fixtures/NamedScopeUser.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Database/EloquentModelScopeTest.php b/tests/Integration/Database/EloquentModelScopeTest.php index 0122e582ef7f..7ea822b42810 100644 --- a/tests/Integration/Database/EloquentModelScopeTest.php +++ b/tests/Integration/Database/EloquentModelScopeTest.php @@ -3,7 +3,7 @@ namespace Illuminate\Tests\Integration\Database; use Illuminate\Contracts\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Attributes\NamedScope; +use Illuminate\Database\Eloquent\Attributes\Scope as NamedScope; use Illuminate\Database\Eloquent\Model; class EloquentModelScopeTest extends DatabaseTestCase diff --git a/tests/Integration/Database/Fixtures/NamedScopeUser.php b/tests/Integration/Database/Fixtures/NamedScopeUser.php index 36052be652f1..111258eb3489 100644 --- a/tests/Integration/Database/Fixtures/NamedScopeUser.php +++ b/tests/Integration/Database/Fixtures/NamedScopeUser.php @@ -2,7 +2,7 @@ namespace Illuminate\Tests\Integration\Database\Fixtures; -use Illuminate\Database\Eloquent\Attributes\NamedScope; +use Illuminate\Database\Eloquent\Attributes\Scope as NamedScope; use Illuminate\Database\Eloquent\Builder; class NamedScopeUser extends User