From 954c2a1f7437795921b0b29baeb70cb1f1178a56 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 14:06:13 +1000 Subject: [PATCH 01/19] Add WithRelations attribute --- .../Queue/Attributes/WithRelations.php | 14 +++++ src/Illuminate/Queue/SerializesModels.php | 15 ++++-- .../Queue/ModelSerializationTest.php | 52 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 src/Illuminate/Queue/Attributes/WithRelations.php diff --git a/src/Illuminate/Queue/Attributes/WithRelations.php b/src/Illuminate/Queue/Attributes/WithRelations.php new file mode 100644 index 000000000000..039fb10a9fec --- /dev/null +++ b/src/Illuminate/Queue/Attributes/WithRelations.php @@ -0,0 +1,14 @@ +setValue( - $this, $this->getRestoredPropertyValue($values[$name]) - ); + $value = $this->getRestoredPropertyValue($values[$name]); + + $property->setValue($this, $value); + + if (($value instanceof Model || $value instanceof Collection) && ($attributes = $property->getAttributes(WithRelations::class))) { + $relations = $attributes[0]->getArguments()[0]; + + $value->load($relations); + } } } diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index bd3b401c6576..d09af683589f 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Schema\Blueprint; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Queue\Attributes\WithRelations; use Illuminate\Queue\Attributes\WithoutRelations; use Illuminate\Queue\SerializesModels; use LogicException; @@ -374,6 +375,45 @@ public function test_serialization_types_empty_custom_eloquent_collection() $this->assertTrue(true); } + + public function test_it_respects_with_relations_attribute_for_models() + { + $user = User::create([ + 'email' => 'taylor@laravel.com', + ]); + + $serialized = serialize(new ModelSerializationWithRelations($user)); + $this->assertSame( + 'O:66:"Illuminate\Tests\Integration\Queue\ModelSerializationWithRelations":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";i:1;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', + $serialized + ); + + /** @var ModelSerializationWithRelations $unserialized */ + $unserialized = unserialize($serialized); + + $this->assertTrue($unserialized->property->relationLoaded('roles')); + } + + public function test_it_respects_with_relations_attribute_for_collections() + { + $taylor = User::create([ + 'email' => 'taylor@laravel.com', + ]); + $tim = User::create([ + 'email' => 'tim@laravel.com', + ]); + + $serialized = serialize(new ModelSerializationWithRelations(new Collection([$taylor, $tim]))); + $this->assertSame( + 'O:66:"Illuminate\Tests\Integration\Queue\ModelSerializationWithRelations":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";a:2:{i:0;i:1;i:1;i:2;}s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', + $serialized, + ); + + /** @var ModelSerializationWithRelations $unserialized */ + $unserialized = unserialize($serialized); + + $this->assertTrue($unserialized->property->every->relationLoaded('roles')); + } } trait TraitBootsAndInitializersTest @@ -567,6 +607,18 @@ public function __construct(public User $user, public DataValueObject $value) } } +class ModelSerializationWithRelations +{ + use SerializesModels; + + public function __construct( + #[WithRelations(['roles'])] + public $property + ) { + // + } +} + class ModelRelationSerializationTestClass { use SerializesModels; From 2d14b924edeefb3d03f7283d493a47559523b06d Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:10:19 +1000 Subject: [PATCH 02/19] rename --- .../Attributes/{WithRelations.php => LoadRelationships.php} | 0 src/Illuminate/Queue/SerializesModels.php | 4 ++-- tests/Integration/Queue/ModelSerializationTest.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/Illuminate/Queue/Attributes/{WithRelations.php => LoadRelationships.php} (100%) diff --git a/src/Illuminate/Queue/Attributes/WithRelations.php b/src/Illuminate/Queue/Attributes/LoadRelationships.php similarity index 100% rename from src/Illuminate/Queue/Attributes/WithRelations.php rename to src/Illuminate/Queue/Attributes/LoadRelationships.php diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index 97365ebc3ca2..8b010b835491 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; -use Illuminate\Queue\Attributes\WithRelations; +use Illuminate\Queue\Attributes\LoadRelationships; use Illuminate\Queue\Attributes\WithoutRelations; use ReflectionClass; use ReflectionProperty; @@ -96,7 +96,7 @@ public function __unserialize(array $values) $property->setValue($this, $value); - if (($value instanceof Model || $value instanceof Collection) && ($attributes = $property->getAttributes(WithRelations::class))) { + if (($value instanceof Model || $value instanceof Collection) && ($attributes = $property->getAttributes(LoadRelationships::class))) { $relations = $attributes[0]->getArguments()[0]; $value->load($relations); diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index d09af683589f..7604103743ff 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Schema\Blueprint; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Queue\Attributes\WithRelations; +use Illuminate\Queue\Attributes\LoadRelationships; use Illuminate\Queue\Attributes\WithoutRelations; use Illuminate\Queue\SerializesModels; use LogicException; @@ -612,7 +612,7 @@ class ModelSerializationWithRelations use SerializesModels; public function __construct( - #[WithRelations(['roles'])] + #[LoadRelationships(['roles'])] public $property ) { // From 211989d30b8801710c6a9f20b0f126234946ac8b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:11:53 +1000 Subject: [PATCH 03/19] Use loadMissing --- src/Illuminate/Queue/SerializesModels.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index 8b010b835491..31f342040798 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -99,7 +99,7 @@ public function __unserialize(array $values) if (($value instanceof Model || $value instanceof Collection) && ($attributes = $property->getAttributes(LoadRelationships::class))) { $relations = $attributes[0]->getArguments()[0]; - $value->load($relations); + $value->loadMissing($relations); } } } From 1150ce2615a7813aa121f6b30273f0239e6b3aec Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:12:46 +1000 Subject: [PATCH 04/19] Fix name --- src/Illuminate/Queue/Attributes/LoadRelationships.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Queue/Attributes/LoadRelationships.php b/src/Illuminate/Queue/Attributes/LoadRelationships.php index 039fb10a9fec..8edca5764198 100644 --- a/src/Illuminate/Queue/Attributes/LoadRelationships.php +++ b/src/Illuminate/Queue/Attributes/LoadRelationships.php @@ -5,7 +5,7 @@ use Attribute; #[Attribute(Attribute::TARGET_PROPERTY)] -class WithoutRelations +class LoadRelationships { public function __construct(public array $relations) { From cd191602abe779565e16e1e85645ca80393fff55 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:22:21 +1000 Subject: [PATCH 05/19] Rename test class --- .../Queue/ModelSerializationTest.php | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index 7604103743ff..dab1e316974e 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -5,11 +5,13 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Pivot; +use Illuminate\Database\Events\QueryExecuted; use Illuminate\Database\Schema\Blueprint; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Queue\Attributes\LoadRelationships; use Illuminate\Queue\Attributes\WithoutRelations; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\DB; use LogicException; use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\TestCase; @@ -382,13 +384,13 @@ public function test_it_respects_with_relations_attribute_for_models() 'email' => 'taylor@laravel.com', ]); - $serialized = serialize(new ModelSerializationWithRelations($user)); + $serialized = serialize(new ModelSerializationLoadRelationships($user)); $this->assertSame( - 'O:66:"Illuminate\Tests\Integration\Queue\ModelSerializationWithRelations":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";i:1;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', + 'O:70:"Illuminate\Tests\Integration\Queue\ModelSerializationLoadRelationships":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";i:1;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', $serialized ); - /** @var ModelSerializationWithRelations $unserialized */ + /** @var ModelSerializationLoadRelationships $unserialized */ $unserialized = unserialize($serialized); $this->assertTrue($unserialized->property->relationLoaded('roles')); @@ -403,13 +405,13 @@ public function test_it_respects_with_relations_attribute_for_collections() 'email' => 'tim@laravel.com', ]); - $serialized = serialize(new ModelSerializationWithRelations(new Collection([$taylor, $tim]))); + $serialized = serialize(new ModelSerializationLoadRelationships(new Collection([$taylor, $tim]))); $this->assertSame( - 'O:66:"Illuminate\Tests\Integration\Queue\ModelSerializationWithRelations":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";a:2:{i:0;i:1;i:1;i:2;}s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', + 'O:70:"Illuminate\Tests\Integration\Queue\ModelSerializationLoadRelationships":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";a:2:{i:0;i:1;i:1;i:2;}s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', $serialized, ); - /** @var ModelSerializationWithRelations $unserialized */ + /** @var ModelSerializationLoadRelationships $unserialized */ $unserialized = unserialize($serialized); $this->assertTrue($unserialized->property->every->relationLoaded('roles')); @@ -607,7 +609,20 @@ public function __construct(public User $user, public DataValueObject $value) } } -class ModelSerializationWithRelations +class ModelSerializationLoadRelationships +{ + use SerializesModels; + + public function __construct( + #[LoadRelationships(['roles'])] + public $property + ) { + // + } +} + +#[WithoutRelations] +class ModelSerializationWithoutRelationsLoadRelationships { use SerializesModels; From b524a51b859cd323e5b88d6cf9c97ac5ec496940 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:22:27 +1000 Subject: [PATCH 06/19] ensure load missing --- .../Queue/ModelSerializationTest.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index dab1e316974e..71b0092b9619 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -416,6 +416,26 @@ public function test_it_respects_with_relations_attribute_for_collections() $this->assertTrue($unserialized->property->every->relationLoaded('roles')); } + + public function test_it_only_loads_missing_relationships() + { + $user = User::create([ + 'email' => 'taylor@laravel.com', + ])->load('roles'); + + $serialized = serialize(new ModelSerializationLoadRelationships($user)); + + $queries = []; + DB::listen(function (QueryExecuted $query) use (&$queries) { + $queries[] = $query; + }); + + /** @var ModelSerializationLoadRelationships $unserialized */ + $unserialized = unserialize($serialized); + + $this->assertCount(2, $queries); + $this->assertTrue($unserialized->property->relationLoaded('roles')); + } } trait TraitBootsAndInitializersTest From de26570f9de732125574a4e54afafa6371018842 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:29:15 +1000 Subject: [PATCH 07/19] wip --- src/Illuminate/Queue/SerializesModels.php | 4 ++++ tests/Integration/Queue/ModelSerializationTest.php | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index 31f342040798..6e4dfc005508 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -102,6 +102,10 @@ public function __unserialize(array $values) $value->loadMissing($relations); } } + + if (method_exists($this, 'bootOnQueue')) { + $this->bootOnQueue(); + } } /** diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index 71b0092b9619..1cad3aa74b9b 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -436,6 +436,16 @@ public function test_it_only_loads_missing_relationships() $this->assertCount(2, $queries); $this->assertTrue($unserialized->property->relationLoaded('roles')); } + + public function test_it_can_mix_both_without_relations_and_load_relationships() + { + $this->markTestIncomplete('TODO'); + } + + public function test_it_can_use_generic_boot_method() + { + $this->markTestIncomplete('TODO'); + } } trait TraitBootsAndInitializersTest From d579cf774c373c6109a6e73f52224b1623e1e832 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:34:37 +1000 Subject: [PATCH 08/19] Fill out test --- .../Queue/ModelSerializationTest.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index 1cad3aa74b9b..e469a576b64f 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -439,7 +439,21 @@ public function test_it_only_loads_missing_relationships() public function test_it_can_mix_both_without_relations_and_load_relationships() { - $this->markTestIncomplete('TODO'); + $order = Order::create()->load('products'); + + $serialized = serialize(new ModelSerializationWithoutRelationsLoadRelationships($order)); + + $queries = []; + DB::listen(function (QueryExecuted $query) use (&$queries) { + $queries[] = $query; + }); + + /** @var ModelSerializationWithoutRelationsLoadRelationships $unserialized */ + $unserialized = unserialize($serialized); + + $this->assertCount(2, $queries); + $this->assertTrue($unserialized->property->relationLoaded('lines')); + $this->assertTrue(! $unserialized->property->relationLoaded('products')); } public function test_it_can_use_generic_boot_method() @@ -657,7 +671,7 @@ class ModelSerializationWithoutRelationsLoadRelationships use SerializesModels; public function __construct( - #[LoadRelationships(['roles'])] + #[LoadRelationships(['lines'])] public $property ) { // From a89a86169c1b8d91ac1e53f4c9ce42f4b5e96abe Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Apr 2025 15:37:09 +1000 Subject: [PATCH 09/19] Add boot on queue tests --- .../Queue/ModelSerializationTest.php | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index e469a576b64f..f08ab025822b 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -458,7 +458,21 @@ public function test_it_can_mix_both_without_relations_and_load_relationships() public function test_it_can_use_generic_boot_method() { - $this->markTestIncomplete('TODO'); + $order = Order::create(); + + $serialized = serialize(new ModelSerializationWithBootOnQueueHook($order)); + + $queries = []; + DB::listen(function (QueryExecuted $query) use (&$queries) { + $queries[] = $query; + }); + + /** @var ModelSerializationWithoutRelationsLoadRelationships $unserialized */ + $unserialized = unserialize($serialized); + + $this->assertCount(2, $queries); + $this->assertTrue($unserialized->property->relationLoaded('lines')); + $this->assertTrue(! $unserialized->property->relationLoaded('products')); } } @@ -678,6 +692,22 @@ public function __construct( } } +class ModelSerializationWithBootOnQueueHook +{ + use SerializesModels; + + public function __construct( + public $property, + ) { + // + } + + protected function bootOnQueue() + { + $this->property->load('lines'); + } +} + class ModelRelationSerializationTestClass { use SerializesModels; From 434a20416fc2ce27467177ac2e190f9303042a63 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:29:04 +1000 Subject: [PATCH 10/19] Rename to initializeOnQueue --- src/Illuminate/Queue/SerializesModels.php | 4 ++-- tests/Integration/Queue/ModelSerializationTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index 6e4dfc005508..3583f4c5a665 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -103,8 +103,8 @@ public function __unserialize(array $values) } } - if (method_exists($this, 'bootOnQueue')) { - $this->bootOnQueue(); + if (method_exists($this, 'initializeOnQueue')) { + $this->initializeOnQueue(); } } diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index f08ab025822b..c08c64afec09 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -702,7 +702,7 @@ public function __construct( // } - protected function bootOnQueue() + protected function initializeOnQueue() { $this->property->load('lines'); } From 4a24d3f60b163498513ed449debf225494fb4923 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:31:07 +1000 Subject: [PATCH 11/19] Use load --- src/Illuminate/Queue/SerializesModels.php | 2 +- tests/Integration/Queue/ModelSerializationTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index 3583f4c5a665..f6040fea3bff 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -99,7 +99,7 @@ public function __unserialize(array $values) if (($value instanceof Model || $value instanceof Collection) && ($attributes = $property->getAttributes(LoadRelationships::class))) { $relations = $attributes[0]->getArguments()[0]; - $value->loadMissing($relations); + $value->load($relations); } } diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index c08c64afec09..789f421ffc1a 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -417,7 +417,7 @@ public function test_it_respects_with_relations_attribute_for_collections() $this->assertTrue($unserialized->property->every->relationLoaded('roles')); } - public function test_it_only_loads_missing_relationships() + public function test_it_reloads_already_loaded_relationships() { $user = User::create([ 'email' => 'taylor@laravel.com', @@ -433,7 +433,7 @@ public function test_it_only_loads_missing_relationships() /** @var ModelSerializationLoadRelationships $unserialized */ $unserialized = unserialize($serialized); - $this->assertCount(2, $queries); + $this->assertCount(3, $queries); $this->assertTrue($unserialized->property->relationLoaded('roles')); } From 7c7804d7ab76b000c5511f8f2206ea82460342cc Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:34:46 +1000 Subject: [PATCH 12/19] Rename to eager load --- .../Attributes/{LoadRelationships.php => EagerLoad.php} | 2 +- src/Illuminate/Queue/SerializesModels.php | 9 ++++++--- tests/Integration/Queue/ModelSerializationTest.php | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) rename src/Illuminate/Queue/Attributes/{LoadRelationships.php => EagerLoad.php} (88%) diff --git a/src/Illuminate/Queue/Attributes/LoadRelationships.php b/src/Illuminate/Queue/Attributes/EagerLoad.php similarity index 88% rename from src/Illuminate/Queue/Attributes/LoadRelationships.php rename to src/Illuminate/Queue/Attributes/EagerLoad.php index 8edca5764198..d82ad62f8270 100644 --- a/src/Illuminate/Queue/Attributes/LoadRelationships.php +++ b/src/Illuminate/Queue/Attributes/EagerLoad.php @@ -5,7 +5,7 @@ use Attribute; #[Attribute(Attribute::TARGET_PROPERTY)] -class LoadRelationships +class EagerLoad { public function __construct(public array $relations) { diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index f6040fea3bff..dbdef18cc568 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; -use Illuminate\Queue\Attributes\LoadRelationships; +use Illuminate\Queue\Attributes\EagerLoad; use Illuminate\Queue\Attributes\WithoutRelations; use ReflectionClass; use ReflectionProperty; @@ -96,8 +96,11 @@ public function __unserialize(array $values) $property->setValue($this, $value); - if (($value instanceof Model || $value instanceof Collection) && ($attributes = $property->getAttributes(LoadRelationships::class))) { - $relations = $attributes[0]->getArguments()[0]; + if ( + ($value instanceof Model || $value instanceof Collection) && + ($attribute = $property->getAttributes(EagerLoad::class)[0] ?? null) + ) { + $relations = $attribute->getArguments()[0]; $value->load($relations); } diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index 789f421ffc1a..a5a3654c9da4 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -8,7 +8,7 @@ use Illuminate\Database\Events\QueryExecuted; use Illuminate\Database\Schema\Blueprint; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Queue\Attributes\LoadRelationships; +use Illuminate\Queue\Attributes\EagerLoad; use Illuminate\Queue\Attributes\WithoutRelations; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\DB; @@ -672,7 +672,7 @@ class ModelSerializationLoadRelationships use SerializesModels; public function __construct( - #[LoadRelationships(['roles'])] + #[EagerLoad(['roles'])] public $property ) { // @@ -685,7 +685,7 @@ class ModelSerializationWithoutRelationsLoadRelationships use SerializesModels; public function __construct( - #[LoadRelationships(['lines'])] + #[EagerLoad(['lines'])] public $property ) { // From 6550c65078539f96f2da37d58cdecd95dfe69f9c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:36:34 +1000 Subject: [PATCH 13/19] Improve typing --- src/Illuminate/Queue/Attributes/EagerLoad.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Illuminate/Queue/Attributes/EagerLoad.php b/src/Illuminate/Queue/Attributes/EagerLoad.php index d82ad62f8270..4abd9ea53519 100644 --- a/src/Illuminate/Queue/Attributes/EagerLoad.php +++ b/src/Illuminate/Queue/Attributes/EagerLoad.php @@ -7,6 +7,9 @@ #[Attribute(Attribute::TARGET_PROPERTY)] class EagerLoad { + /** + * @param list $relations + */ public function __construct(public array $relations) { // From b2048948e525a3fdeb84d22f3a080423b5c8de95 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:45:37 +1000 Subject: [PATCH 14/19] Improve tests --- .../Queue/ModelSerializationTest.php | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index a5a3654c9da4..dd9ad4101f22 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -378,66 +378,59 @@ public function test_serialization_types_empty_custom_eloquent_collection() $this->assertTrue(true); } - public function test_it_respects_with_relations_attribute_for_models() + public function test_it_can_eagerly_load_relationships_on_models() { $user = User::create([ 'email' => 'taylor@laravel.com', ]); + $queries = []; + DB::listen(function (QueryExecuted $query) use (&$queries) { + $queries[] = $query; + }); - $serialized = serialize(new ModelSerializationLoadRelationships($user)); - $this->assertSame( - 'O:70:"Illuminate\Tests\Integration\Queue\ModelSerializationLoadRelationships":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";i:1;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', - $serialized - ); - - /** @var ModelSerializationLoadRelationships $unserialized */ - $unserialized = unserialize($serialized); + $unserialized = unserialize(serialize(new ModelSerializationEagerLoadRoles($user))); - $this->assertTrue($unserialized->property->relationLoaded('roles')); + $this->assertFalse($user->relationLoaded('roles')); + $this->assertTrue($unserialized->value->relationLoaded('roles')); + $this->assertCount(2, $queries); } - public function test_it_respects_with_relations_attribute_for_collections() + public function test_it_can_eagerly_load_relationships_on_collections() { - $taylor = User::create([ - 'email' => 'taylor@laravel.com', - ]); - $tim = User::create([ - 'email' => 'tim@laravel.com', + $users = new Collection([ + User::create(['email' => 'taylor@laravel.com']), + User::create(['email' => 'tim@laravel.com']), ]); + $queries = []; + DB::listen(function (QueryExecuted $query) use (&$queries) { + $queries[] = $query; + }); - $serialized = serialize(new ModelSerializationLoadRelationships(new Collection([$taylor, $tim]))); - $this->assertSame( - 'O:70:"Illuminate\Tests\Integration\Queue\ModelSerializationLoadRelationships":1:{s:8:"property";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:39:"Illuminate\Tests\Integration\Queue\User";s:2:"id";a:2:{i:0;i:1;i:1;i:2;}s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}}', - $serialized, - ); - - /** @var ModelSerializationLoadRelationships $unserialized */ - $unserialized = unserialize($serialized); + $unserialized = unserialize(serialize(new ModelSerializationEagerLoadRoles($users))); - $this->assertTrue($unserialized->property->every->relationLoaded('roles')); + $this->assertTrue($unserialized->value->every->relationLoaded('roles')); + $this->assertFalse($users->some->relationLoaded('roles')); + $this->assertCount(2, $queries); } - public function test_it_reloads_already_loaded_relationships() + public function test_it_eager_loads_already_loaded_relationships() { $user = User::create([ 'email' => 'taylor@laravel.com', ])->load('roles'); - - $serialized = serialize(new ModelSerializationLoadRelationships($user)); - $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - /** @var ModelSerializationLoadRelationships $unserialized */ - $unserialized = unserialize($serialized); + $unserialized = unserialize(serialize(new ModelSerializationEagerLoadRoles($user))); + $this->assertTrue($unserialized->value->relationLoaded('roles')); + $this->assertTrue($user->relationLoaded('roles')); $this->assertCount(3, $queries); - $this->assertTrue($unserialized->property->relationLoaded('roles')); } - public function test_it_can_mix_both_without_relations_and_load_relationships() + public function test_it_can_mix_without_relations_and_eager_load() { $order = Order::create()->load('products'); @@ -667,13 +660,13 @@ public function __construct(public User $user, public DataValueObject $value) } } -class ModelSerializationLoadRelationships +class ModelSerializationEagerLoadRoles { use SerializesModels; public function __construct( #[EagerLoad(['roles'])] - public $property + public $value, ) { // } From 41eebb5412eaca41c16a9f204d179f759556e5f8 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:48:18 +1000 Subject: [PATCH 15/19] Improve tests --- .../Integration/Queue/ModelSerializationTest.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index dd9ad4101f22..f925fe3bc0f1 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -433,20 +433,16 @@ public function test_it_eager_loads_already_loaded_relationships() public function test_it_can_mix_without_relations_and_eager_load() { $order = Order::create()->load('products'); - - $serialized = serialize(new ModelSerializationWithoutRelationsLoadRelationships($order)); - $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - /** @var ModelSerializationWithoutRelationsLoadRelationships $unserialized */ - $unserialized = unserialize($serialized); + $unserialized = unserialize(serialize(new ModelSerializationWithoutRelationsAndEagerLoadLines($order))); + $this->assertTrue($unserialized->value->relationLoaded('lines')); + $this->assertTrue(! $unserialized->value->relationLoaded('products')); $this->assertCount(2, $queries); - $this->assertTrue($unserialized->property->relationLoaded('lines')); - $this->assertTrue(! $unserialized->property->relationLoaded('products')); } public function test_it_can_use_generic_boot_method() @@ -460,7 +456,7 @@ public function test_it_can_use_generic_boot_method() $queries[] = $query; }); - /** @var ModelSerializationWithoutRelationsLoadRelationships $unserialized */ + /** @var ModelSerializationWithoutRelationsAndEagerLoadLines $unserialized */ $unserialized = unserialize($serialized); $this->assertCount(2, $queries); @@ -673,13 +669,13 @@ public function __construct( } #[WithoutRelations] -class ModelSerializationWithoutRelationsLoadRelationships +class ModelSerializationWithoutRelationsAndEagerLoadLines { use SerializesModels; public function __construct( #[EagerLoad(['lines'])] - public $property + public $value ) { // } From ca646ee8482574fe0f71dd4b2868ac6d72719753 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 08:54:11 +1000 Subject: [PATCH 16/19] Improve tests --- .../Queue/ModelSerializationTest.php | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index f925fe3bc0f1..4b0e5488c749 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -383,12 +383,15 @@ public function test_it_can_eagerly_load_relationships_on_models() $user = User::create([ 'email' => 'taylor@laravel.com', ]); + + $serialize = serialize(new ModelSerializationEagerLoadRoles($user)); + $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - $unserialized = unserialize(serialize(new ModelSerializationEagerLoadRoles($user))); + $unserialized = unserialize($serialize); $this->assertFalse($user->relationLoaded('roles')); $this->assertTrue($unserialized->value->relationLoaded('roles')); @@ -401,12 +404,15 @@ public function test_it_can_eagerly_load_relationships_on_collections() User::create(['email' => 'taylor@laravel.com']), User::create(['email' => 'tim@laravel.com']), ]); + + $serialize = serialize(new ModelSerializationEagerLoadRoles($users)); + $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - $unserialized = unserialize(serialize(new ModelSerializationEagerLoadRoles($users))); + $unserialized = unserialize($serialize); $this->assertTrue($unserialized->value->every->relationLoaded('roles')); $this->assertFalse($users->some->relationLoaded('roles')); @@ -417,28 +423,40 @@ public function test_it_eager_loads_already_loaded_relationships() { $user = User::create([ 'email' => 'taylor@laravel.com', - ])->load('roles'); + ]); + $user->roles()->create(); + $user->load('roles'); + + $serialized = serialize(new ModelSerializationEagerLoadRoles($user)); + + // create a role while the value is serialized... + $user->roles()->create(); + $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - - $unserialized = unserialize(serialize(new ModelSerializationEagerLoadRoles($user))); + $unserialized = unserialize($serialized); $this->assertTrue($unserialized->value->relationLoaded('roles')); $this->assertTrue($user->relationLoaded('roles')); + $this->assertCount(2, $unserialized->value->roles); + $this->assertCount(1, $user->roles); $this->assertCount(3, $queries); } public function test_it_can_mix_without_relations_and_eager_load() { $order = Order::create()->load('products'); + + $serialize = serialize(new ModelSerializationWithoutRelationsAndEagerLoadLines($order)); + $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - $unserialized = unserialize(serialize(new ModelSerializationWithoutRelationsAndEagerLoadLines($order))); + $unserialized = unserialize($serialize); $this->assertTrue($unserialized->value->relationLoaded('lines')); $this->assertTrue(! $unserialized->value->relationLoaded('products')); From f0a2ff7c33552f9040b17146bee56b64b73d03f0 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 09:05:41 +1000 Subject: [PATCH 17/19] Limit initialize to Queueable trait --- src/Illuminate/Queue/SerializesModels.php | 6 +++++- .../Queue/ModelSerializationTest.php | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index dbdef18cc568..f64ab2eeb737 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Queue\Queueable; use Illuminate\Queue\Attributes\EagerLoad; use Illuminate\Queue\Attributes\WithoutRelations; use ReflectionClass; @@ -106,7 +107,10 @@ public function __unserialize(array $values) } } - if (method_exists($this, 'initializeOnQueue')) { + if ( + in_array(Queueable::class, class_uses_recursive($this)) && + method_exists($this, 'initializeOnQueue') + ) { $this->initializeOnQueue(); } } diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index 4b0e5488c749..622d1ed95a99 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Events\QueryExecuted; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Foundation\Queue\Queueable; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Queue\Attributes\EagerLoad; use Illuminate\Queue\Attributes\WithoutRelations; @@ -463,23 +464,22 @@ public function test_it_can_mix_without_relations_and_eager_load() $this->assertCount(2, $queries); } - public function test_it_can_use_generic_boot_method() + public function test_it_initilize_on_the_queue() { $order = Order::create(); - $serialized = serialize(new ModelSerializationWithBootOnQueueHook($order)); + $serialized = serialize(new ModelSerializationWithInitilizeOnQueueHook($order)); $queries = []; DB::listen(function (QueryExecuted $query) use (&$queries) { $queries[] = $query; }); - /** @var ModelSerializationWithoutRelationsAndEagerLoadLines $unserialized */ $unserialized = unserialize($serialized); + $this->assertTrue($unserialized->value->relationLoaded('lines')); + $this->assertTrue(! $unserialized->value->relationLoaded('products')); $this->assertCount(2, $queries); - $this->assertTrue($unserialized->property->relationLoaded('lines')); - $this->assertTrue(! $unserialized->property->relationLoaded('products')); } } @@ -699,19 +699,21 @@ public function __construct( } } -class ModelSerializationWithBootOnQueueHook +class ModelSerializationWithInitilizeOnQueueHook { - use SerializesModels; + use Queueable; public function __construct( - public $property, + public $value, ) { // } protected function initializeOnQueue() { - $this->property->load('lines'); + $this->value->load([ + 'lines' => fn ($q) => $q->where('product_id', 5), + ]); } } From faa146a549e6b2b3209af9564c4c38a71064c5cb Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 09:15:59 +1000 Subject: [PATCH 18/19] Use lower-level trait --- src/Illuminate/Queue/SerializesModels.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index f64ab2eeb737..c9eb04f55752 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -2,9 +2,9 @@ namespace Illuminate\Queue; +use Illuminate\Bus\Queueable; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; -use Illuminate\Foundation\Queue\Queueable; use Illuminate\Queue\Attributes\EagerLoad; use Illuminate\Queue\Attributes\WithoutRelations; use ReflectionClass; @@ -107,10 +107,8 @@ public function __unserialize(array $values) } } - if ( - in_array(Queueable::class, class_uses_recursive($this)) && - method_exists($this, 'initializeOnQueue') - ) { + + if (in_array(Queueable::class, class_uses_recursive($this)) && method_exists($this, 'initializeOnQueue')) { $this->initializeOnQueue(); } } From 4596d26b18f625b33988ea5934591ca8ac0fd587 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 1 May 2025 09:18:09 +1000 Subject: [PATCH 19/19] Lint --- src/Illuminate/Queue/SerializesModels.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Queue/SerializesModels.php b/src/Illuminate/Queue/SerializesModels.php index c9eb04f55752..ec390e37c6b7 100644 --- a/src/Illuminate/Queue/SerializesModels.php +++ b/src/Illuminate/Queue/SerializesModels.php @@ -107,7 +107,6 @@ public function __unserialize(array $values) } } - if (in_array(Queueable::class, class_uses_recursive($this)) && method_exists($this, 'initializeOnQueue')) { $this->initializeOnQueue(); }