From c3bc5162d5a94a9660240430f43caf277757d458 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Fri, 13 Jun 2025 11:21:19 +0200 Subject: [PATCH 1/8] Fix embed models serialization --- src/Eloquent/EmbedsRelations.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Eloquent/EmbedsRelations.php b/src/Eloquent/EmbedsRelations.php index d5984b08e..c70830ab6 100644 --- a/src/Eloquent/EmbedsRelations.php +++ b/src/Eloquent/EmbedsRelations.php @@ -8,6 +8,7 @@ use MongoDB\Laravel\Relations\EmbedsMany; use MongoDB\Laravel\Relations\EmbedsOne; +use MongoDB\Laravel\Relations\EmbedsOneOrMany; use function class_basename; use function debug_backtrace; @@ -85,4 +86,34 @@ protected function embedsOne($related, $localKey = null, $foreignKey = null, $re return new EmbedsOne($query, $this, $instance, $localKey, $foreignKey, $relation); } + + /** + * Determine if the given key is an embed relationship method on the model. + * + * @param string $key + * @return bool + */ + public function isEmbedRelation($key) + { + return $this->isRelation($key) + && is_a($this->{$key}(), EmbedsOneOrMany::class, true); + } + + /** + * @inheritDoc + */ + public function toArray() + { + $embeds = []; + + foreach (array_keys($this->getAttributes()) as $key) { + if ($this->isEmbedRelation($key)) { + $embeds[] = $key; + } + } + + $this->loadMissing($embeds); + + return parent::toArray(); + } } From 0b2c12c812ce1f13d399d88db7c61307537bb204 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Fri, 13 Jun 2025 11:28:30 +0200 Subject: [PATCH 2/8] refactoring code --- src/Eloquent/EmbedsRelations.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Eloquent/EmbedsRelations.php b/src/Eloquent/EmbedsRelations.php index c70830ab6..257ce7777 100644 --- a/src/Eloquent/EmbedsRelations.php +++ b/src/Eloquent/EmbedsRelations.php @@ -7,7 +7,6 @@ use Illuminate\Support\Str; use MongoDB\Laravel\Relations\EmbedsMany; use MongoDB\Laravel\Relations\EmbedsOne; - use MongoDB\Laravel\Relations\EmbedsOneOrMany; use function class_basename; use function debug_backtrace; @@ -90,7 +89,8 @@ protected function embedsOne($related, $localKey = null, $foreignKey = null, $re /** * Determine if the given key is an embed relationship method on the model. * - * @param string $key + * @param string $key + * * @return bool */ public function isEmbedRelation($key) From 70d0f73b88d3aeced83c3765712f7617979a52fe Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 16 Jun 2025 09:01:46 +0200 Subject: [PATCH 3/8] Move toArray logic --- src/Eloquent/DocumentModel.php | 18 ++++++++++++++++++ src/Eloquent/EmbedsRelations.php | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index 965b1a444..f66e7def3 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -754,4 +754,22 @@ public function refresh() return $this; } + + /** + * @inheritDoc + */ + public function toArray() + { + $embeds = []; + + foreach (array_keys($this->getAttributes()) as $key) { + if ($this->isEmbedRelation($key)) { + $embeds[] = $key; + } + } + + $this->loadMissing($embeds); + + return parent::toArray(); + } } diff --git a/src/Eloquent/EmbedsRelations.php b/src/Eloquent/EmbedsRelations.php index 257ce7777..d1f84770d 100644 --- a/src/Eloquent/EmbedsRelations.php +++ b/src/Eloquent/EmbedsRelations.php @@ -98,22 +98,4 @@ public function isEmbedRelation($key) return $this->isRelation($key) && is_a($this->{$key}(), EmbedsOneOrMany::class, true); } - - /** - * @inheritDoc - */ - public function toArray() - { - $embeds = []; - - foreach (array_keys($this->getAttributes()) as $key) { - if ($this->isEmbedRelation($key)) { - $embeds[] = $key; - } - } - - $this->loadMissing($embeds); - - return parent::toArray(); - } } From 168cf9bed05351f881e7f05df510f2dd7a33897e Mon Sep 17 00:00:00 2001 From: florianJacques Date: Tue, 17 Jun 2025 17:06:17 +0200 Subject: [PATCH 4/8] Run phpcs code fixer --- src/Eloquent/DocumentModel.php | 4 +--- src/Eloquent/EmbedsRelations.php | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index f66e7def3..c4297219d 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -755,9 +755,7 @@ public function refresh() return $this; } - /** - * @inheritDoc - */ + /** @inheritDoc */ public function toArray() { $embeds = []; diff --git a/src/Eloquent/EmbedsRelations.php b/src/Eloquent/EmbedsRelations.php index d1f84770d..20e3ccc71 100644 --- a/src/Eloquent/EmbedsRelations.php +++ b/src/Eloquent/EmbedsRelations.php @@ -8,8 +8,10 @@ use MongoDB\Laravel\Relations\EmbedsMany; use MongoDB\Laravel\Relations\EmbedsOne; use MongoDB\Laravel\Relations\EmbedsOneOrMany; + use function class_basename; use function debug_backtrace; +use function is_a; use const DEBUG_BACKTRACE_IGNORE_ARGS; From 478c25ee310b60c6519b1396bf5c9ebe27119d23 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Fri, 20 Jun 2025 09:27:03 +0200 Subject: [PATCH 5/8] Add tests --- tests/EmbeddedRelationsTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/EmbeddedRelationsTest.php b/tests/EmbeddedRelationsTest.php index 1c68e2d34..c49406c7a 100644 --- a/tests/EmbeddedRelationsTest.php +++ b/tests/EmbeddedRelationsTest.php @@ -972,4 +972,18 @@ public function testUnsetPropertyOnEmbed() $this->assertNull($user->addresses->get(0)->city); $this->assertSame('Kyoto', $user->addresses->get(1)->city); } + + public function testEmbedManyToArrayCast() + { + $user = User::create(['name' => 'John Doe']); + $user->addresses()->saveMany([new Address(['city' => 'London'])]); + + //Reload document + $user = User::where('name', 'John Doe')->first(); + $array = $user->toArray(); + + $this->assertIsString($array['addresses'][0]['id']); + $this->assertIsString($array['addresses'][0]['created_at']); + $this->assertIsString($array['addresses'][0]['updated_at']); + } } From d7d1962f5ec765a93111a19113dfb32077001a19 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Fri, 20 Jun 2025 09:32:04 +0200 Subject: [PATCH 6/8] fix coding standards --- tests/EmbeddedRelationsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/EmbeddedRelationsTest.php b/tests/EmbeddedRelationsTest.php index c49406c7a..d5bef3ea7 100644 --- a/tests/EmbeddedRelationsTest.php +++ b/tests/EmbeddedRelationsTest.php @@ -972,7 +972,7 @@ public function testUnsetPropertyOnEmbed() $this->assertNull($user->addresses->get(0)->city); $this->assertSame('Kyoto', $user->addresses->get(1)->city); } - + public function testEmbedManyToArrayCast() { $user = User::create(['name' => 'John Doe']); From cb3dacc7b12018a502a390ee24c22df5f6f182b3 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Tue, 24 Jun 2025 10:55:18 +0200 Subject: [PATCH 7/8] Load embedded relations automatically only when they are retrieved from the database --- src/Eloquent/DocumentModel.php | 16 ---------------- src/Eloquent/EmbedsRelations.php | 33 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index c4297219d..965b1a444 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -754,20 +754,4 @@ public function refresh() return $this; } - - /** @inheritDoc */ - public function toArray() - { - $embeds = []; - - foreach (array_keys($this->getAttributes()) as $key) { - if ($this->isEmbedRelation($key)) { - $embeds[] = $key; - } - } - - $this->loadMissing($embeds); - - return parent::toArray(); - } } diff --git a/src/Eloquent/EmbedsRelations.php b/src/Eloquent/EmbedsRelations.php index 20e3ccc71..ae6240b1c 100644 --- a/src/Eloquent/EmbedsRelations.php +++ b/src/Eloquent/EmbedsRelations.php @@ -4,11 +4,13 @@ namespace MongoDB\Laravel\Eloquent; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; use MongoDB\Laravel\Relations\EmbedsMany; use MongoDB\Laravel\Relations\EmbedsOne; use MongoDB\Laravel\Relations\EmbedsOneOrMany; +use function array_keys; use function class_basename; use function debug_backtrace; use function is_a; @@ -20,6 +22,13 @@ */ trait EmbedsRelations { + public static function bootEmbedsRelations(): void + { + static::retrieved(function (self $model) { + $model->withEmbedded(); + }); + } + /** * Define an embedded one-to-many relationship. * @@ -88,6 +97,28 @@ protected function embedsOne($related, $localKey = null, $foreignKey = null, $re return new EmbedsOne($query, $this, $instance, $localKey, $foreignKey, $relation); } + /** + * Load embedded relations on the model if they are not already loaded + * + * @param array|string $relations + * + * @return Model + */ + public function withEmbedded($relations = []) + { + if (empty($relations)) { + $relations = []; + + foreach (array_keys($this->getAttributes()) as $key) { + if ($this->isEmbeddedRelation($key)) { + $relations[] = $key; + } + } + } + + return $this->loadMissing($relations); + } + /** * Determine if the given key is an embed relationship method on the model. * @@ -95,7 +126,7 @@ protected function embedsOne($related, $localKey = null, $foreignKey = null, $re * * @return bool */ - public function isEmbedRelation($key) + public function isEmbeddedRelation($key) { return $this->isRelation($key) && is_a($this->{$key}(), EmbedsOneOrMany::class, true); From f8605e78ae5b70b7bd0bceed78f3c00687842a83 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Tue, 24 Jun 2025 11:39:16 +0200 Subject: [PATCH 8/8] Add withEmbeds property to control which relationships to load --- src/Eloquent/EmbedsRelations.php | 33 ++++++++------------------------ tests/EmbeddedRelationsTest.php | 6 ++++-- tests/Models/UserWithEmbeds.php | 10 ++++++++++ 3 files changed, 22 insertions(+), 27 deletions(-) create mode 100644 tests/Models/UserWithEmbeds.php diff --git a/src/Eloquent/EmbedsRelations.php b/src/Eloquent/EmbedsRelations.php index ae6240b1c..f49d67339 100644 --- a/src/Eloquent/EmbedsRelations.php +++ b/src/Eloquent/EmbedsRelations.php @@ -4,13 +4,11 @@ namespace MongoDB\Laravel\Eloquent; -use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; use MongoDB\Laravel\Relations\EmbedsMany; use MongoDB\Laravel\Relations\EmbedsOne; use MongoDB\Laravel\Relations\EmbedsOneOrMany; -use function array_keys; use function class_basename; use function debug_backtrace; use function is_a; @@ -22,10 +20,17 @@ */ trait EmbedsRelations { + /** + * The embeds relations to load on every query. + * + * @var array + */ + protected $withEmbeds = []; + public static function bootEmbedsRelations(): void { static::retrieved(function (self $model) { - $model->withEmbedded(); + $model->load($model->withEmbeds); }); } @@ -97,28 +102,6 @@ protected function embedsOne($related, $localKey = null, $foreignKey = null, $re return new EmbedsOne($query, $this, $instance, $localKey, $foreignKey, $relation); } - /** - * Load embedded relations on the model if they are not already loaded - * - * @param array|string $relations - * - * @return Model - */ - public function withEmbedded($relations = []) - { - if (empty($relations)) { - $relations = []; - - foreach (array_keys($this->getAttributes()) as $key) { - if ($this->isEmbeddedRelation($key)) { - $relations[] = $key; - } - } - } - - return $this->loadMissing($relations); - } - /** * Determine if the given key is an embed relationship method on the model. * diff --git a/tests/EmbeddedRelationsTest.php b/tests/EmbeddedRelationsTest.php index d5bef3ea7..f4065131e 100644 --- a/tests/EmbeddedRelationsTest.php +++ b/tests/EmbeddedRelationsTest.php @@ -11,6 +11,7 @@ use MongoDB\BSON\ObjectId; use MongoDB\Laravel\Tests\Models\Address; use MongoDB\Laravel\Tests\Models\User; +use MongoDB\Laravel\Tests\Models\UserWithEmbeds; use function array_merge; @@ -20,6 +21,7 @@ public function tearDown(): void { Mockery::close(); User::truncate(); + UserWithEmbeds::truncate(); parent::tearDown(); } @@ -975,11 +977,11 @@ public function testUnsetPropertyOnEmbed() public function testEmbedManyToArrayCast() { - $user = User::create(['name' => 'John Doe']); + $user = UserWithEmbeds::create(['name' => 'John Doe']); $user->addresses()->saveMany([new Address(['city' => 'London'])]); //Reload document - $user = User::where('name', 'John Doe')->first(); + $user = UserWithEmbeds::where('name', 'John Doe')->first(); $array = $user->toArray(); $this->assertIsString($array['addresses'][0]['id']); diff --git a/tests/Models/UserWithEmbeds.php b/tests/Models/UserWithEmbeds.php new file mode 100644 index 000000000..746344315 --- /dev/null +++ b/tests/Models/UserWithEmbeds.php @@ -0,0 +1,10 @@ +