From d04aa5394c36d66ae3c7b51e089a58c1b4c6e5cf Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 12:32:55 +0200 Subject: [PATCH 01/14] Add document version feature --- src/Eloquent/HasDocumentVersion.php | 37 +++++++++++++++++++++++++++++ tests/ModelTest.php | 8 +++++++ tests/Models/DocumentVersion.php | 18 ++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 src/Eloquent/HasDocumentVersion.php create mode 100644 tests/Models/DocumentVersion.php diff --git a/src/Eloquent/HasDocumentVersion.php b/src/Eloquent/HasDocumentVersion.php new file mode 100644 index 000000000..3386887cc --- /dev/null +++ b/src/Eloquent/HasDocumentVersion.php @@ -0,0 +1,37 @@ +{$versionKey} = defined(static::class.'::DOCUMENT_VERSION') + ? (int) static::DOCUMENT_VERSION + : 1; + } + }); + } + + /** + * Get document version key + * + * @return string + */ + protected static function getDocumentVersionKey(): string + { + return defined(static::class.'::DOCUMENT_VERSION_KEY') + ? (string) static::DOCUMENT_VERSION_KEY + : '__v'; + } +} diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 3c4cbd8df..13adf70a0 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -19,6 +19,7 @@ use MongoDB\Laravel\Connection; use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Tests\Models\Book; +use MongoDB\Laravel\Tests\Models\DocumentVersion; use MongoDB\Laravel\Tests\Models\Guarded; use MongoDB\Laravel\Tests\Models\IdIsBinaryUuid; use MongoDB\Laravel\Tests\Models\IdIsInt; @@ -54,6 +55,7 @@ public function tearDown(): void Book::truncate(); Item::truncate(); Guarded::truncate(); + DocumentVersion::truncate(); } public function testNewModel(): void @@ -1188,6 +1190,12 @@ public function testCreateWithNullId() $this->assertSame(1, User::count()); } + public function testDocumentVersion() + { + $document = DocumentVersion::create(['name' => 'versionTest']); + $this->assertEquals(1, $document->__v); + } + /** @param class-string $modelClass */ private static function registerModelEvents(string $modelClass, array &$events): void { diff --git a/tests/Models/DocumentVersion.php b/tests/Models/DocumentVersion.php new file mode 100644 index 000000000..3e9ee0a08 --- /dev/null +++ b/tests/Models/DocumentVersion.php @@ -0,0 +1,18 @@ + Date: Mon, 1 Jul 2024 15:25:52 +0200 Subject: [PATCH 02/14] Add retrieved model migration and version helpers --- src/Eloquent/HasDocumentVersion.php | 30 +++++++++++++++++--- tests/DocumentVersionTest.php | 44 +++++++++++++++++++++++++++++ tests/ModelTest.php | 8 ------ tests/Models/DocumentVersion.php | 10 +++++++ 4 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 tests/DocumentVersionTest.php diff --git a/src/Eloquent/HasDocumentVersion.php b/src/Eloquent/HasDocumentVersion.php index 3386887cc..bb8123b3f 100644 --- a/src/Eloquent/HasDocumentVersion.php +++ b/src/Eloquent/HasDocumentVersion.php @@ -6,6 +6,8 @@ trait HasDocumentVersion { + public $documentVersion = 1; + /** * Auto call on model instance as booting * @@ -16,11 +18,31 @@ public static function bootHasDocumentVersion(): void static::saving(function ($model) { $versionKey = static::getDocumentVersionKey(); if (!empty($versionKey)) { - $model->{$versionKey} = defined(static::class.'::DOCUMENT_VERSION') - ? (int) static::DOCUMENT_VERSION - : 1; + $model->{$versionKey} = $model->documentVersion ?? 1; } }); + + static::retrieved(function ($model) { + $model->migrateDocumentVersion($model->getDocumentVersion()); + }); + } + + /** + * migrate model document version schema + * + * @param int $fromVersion + * @return void + */ + public function migrateDocumentVersion(int $fromVersion): void {} + + /** + * Get Current document version + * + * @return int + */ + public function getDocumentVersion(): int + { + return (int) $this->{static::getDocumentVersionKey()} ?? 0; } /** @@ -32,6 +54,6 @@ protected static function getDocumentVersionKey(): string { return defined(static::class.'::DOCUMENT_VERSION_KEY') ? (string) static::DOCUMENT_VERSION_KEY - : '__v'; + : 'schema_version'; } } diff --git a/tests/DocumentVersionTest.php b/tests/DocumentVersionTest.php new file mode 100644 index 000000000..d855d9437 --- /dev/null +++ b/tests/DocumentVersionTest.php @@ -0,0 +1,44 @@ + 'Luc']); + $this->assertEmpty($document->getDocumentVersion()); + $document->save(); + + $this->assertEquals(1, $document->getDocumentVersion()); + $this->assertNull($document->age); + + $document = DocumentVersion::query()->where('name', 'Luc')->first(); + $this->assertEquals(35, $document->age); + + dump($document->toArray()); + + // Test without migration + $newDocument = new DocumentVersion(['name' => 'Vador']); + $newDocument->documentVersion = 2; + $newDocument->save(); + + $this->assertEquals(2, $newDocument->getDocumentVersion()); + $this->assertNull($newDocument->age); + + $document = DocumentVersion::query()->where('name', 'Vador')->first(); + $this->assertNull($newDocument->age); + + dump($newDocument->toArray()); + } +} diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 13adf70a0..3c4cbd8df 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -19,7 +19,6 @@ use MongoDB\Laravel\Connection; use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Tests\Models\Book; -use MongoDB\Laravel\Tests\Models\DocumentVersion; use MongoDB\Laravel\Tests\Models\Guarded; use MongoDB\Laravel\Tests\Models\IdIsBinaryUuid; use MongoDB\Laravel\Tests\Models\IdIsInt; @@ -55,7 +54,6 @@ public function tearDown(): void Book::truncate(); Item::truncate(); Guarded::truncate(); - DocumentVersion::truncate(); } public function testNewModel(): void @@ -1190,12 +1188,6 @@ public function testCreateWithNullId() $this->assertSame(1, User::count()); } - public function testDocumentVersion() - { - $document = DocumentVersion::create(['name' => 'versionTest']); - $this->assertEquals(1, $document->__v); - } - /** @param class-string $modelClass */ private static function registerModelEvents(string $modelClass, array &$events): void { diff --git a/tests/Models/DocumentVersion.php b/tests/Models/DocumentVersion.php index 3e9ee0a08..b5b6ee2a9 100644 --- a/tests/Models/DocumentVersion.php +++ b/tests/Models/DocumentVersion.php @@ -12,7 +12,17 @@ class DocumentVersion extends Eloquent { use HasDocumentVersion; + public $documentVersion = 1; + protected $connection = 'mongodb'; protected $collection = 'documentVersion'; protected static $unguarded = true; + public function migrateDocumentVersion(int $fromVersion): void + { + if ($fromVersion) { + if ($fromVersion < 2) { + $this->age = 35; + } + } + } } From 32b7cfa37f632c6484da1dd5b78aa9c3cdd532ae Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 15:31:18 +0200 Subject: [PATCH 03/14] Add tests --- tests/DocumentVersionTest.php | 6 +----- tests/Models/DocumentVersion.php | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/DocumentVersionTest.php b/tests/DocumentVersionTest.php index d855d9437..dc24ce787 100644 --- a/tests/DocumentVersionTest.php +++ b/tests/DocumentVersionTest.php @@ -26,8 +26,6 @@ public function testCreateWithNullId() $document = DocumentVersion::query()->where('name', 'Luc')->first(); $this->assertEquals(35, $document->age); - dump($document->toArray()); - // Test without migration $newDocument = new DocumentVersion(['name' => 'Vador']); $newDocument->documentVersion = 2; @@ -36,9 +34,7 @@ public function testCreateWithNullId() $this->assertEquals(2, $newDocument->getDocumentVersion()); $this->assertNull($newDocument->age); - $document = DocumentVersion::query()->where('name', 'Vador')->first(); + $newDocument = DocumentVersion::query()->where('name', 'Vador')->first(); $this->assertNull($newDocument->age); - - dump($newDocument->toArray()); } } diff --git a/tests/Models/DocumentVersion.php b/tests/Models/DocumentVersion.php index b5b6ee2a9..7735e2124 100644 --- a/tests/Models/DocumentVersion.php +++ b/tests/Models/DocumentVersion.php @@ -12,8 +12,6 @@ class DocumentVersion extends Eloquent { use HasDocumentVersion; - public $documentVersion = 1; - protected $connection = 'mongodb'; protected $collection = 'documentVersion'; protected static $unguarded = true; From 8fb72e7a0c76f534b3f9bd752d0669d40db230b3 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 15:33:00 +0200 Subject: [PATCH 04/14] remove php doc --- tests/Models/DocumentVersion.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Models/DocumentVersion.php b/tests/Models/DocumentVersion.php index 7735e2124..50aea8229 100644 --- a/tests/Models/DocumentVersion.php +++ b/tests/Models/DocumentVersion.php @@ -7,7 +7,6 @@ use MongoDB\Laravel\Eloquent\HasDocumentVersion; use MongoDB\Laravel\Eloquent\Model as Eloquent; -/** @property int __v */ class DocumentVersion extends Eloquent { use HasDocumentVersion; From cdbdb4211d12b8449144aada57359a174018e75a Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 15:52:17 +0200 Subject: [PATCH 05/14] Fix version manually upgrade --- src/Eloquent/HasDocumentVersion.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Eloquent/HasDocumentVersion.php b/src/Eloquent/HasDocumentVersion.php index bb8123b3f..ddc07f7b6 100644 --- a/src/Eloquent/HasDocumentVersion.php +++ b/src/Eloquent/HasDocumentVersion.php @@ -16,14 +16,14 @@ trait HasDocumentVersion public static function bootHasDocumentVersion(): void { static::saving(function ($model) { - $versionKey = static::getDocumentVersionKey(); - if (!empty($versionKey)) { - $model->{$versionKey} = $model->documentVersion ?? 1; - } + $version = $model->getDocumentVersion(); + if (empty($version)) { + $model->{static::getDocumentVersionKey()} = $model->documentVersion ?? 1; + } }); static::retrieved(function ($model) { - $model->migrateDocumentVersion($model->getDocumentVersion()); + $model->migrateDocumentVersion($model->getDocumentVersion() ?? 0); }); } @@ -38,11 +38,11 @@ public function migrateDocumentVersion(int $fromVersion): void {} /** * Get Current document version * - * @return int + * @return int|null */ - public function getDocumentVersion(): int + public function getDocumentVersion(): ?int { - return (int) $this->{static::getDocumentVersionKey()} ?? 0; + return $this->{static::getDocumentVersionKey()} ?? null; } /** From a224baa6db7d2b30c2ed8a3efa72215d74466976 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 16:31:03 +0200 Subject: [PATCH 06/14] Add document version setter, remove public property --- src/Eloquent/HasDocumentVersion.php | 34 +++++++++++++++++++++++------ tests/DocumentVersionTest.php | 3 ++- tests/Models/DocumentVersion.php | 5 ++++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Eloquent/HasDocumentVersion.php b/src/Eloquent/HasDocumentVersion.php index ddc07f7b6..6aa12becd 100644 --- a/src/Eloquent/HasDocumentVersion.php +++ b/src/Eloquent/HasDocumentVersion.php @@ -6,8 +6,6 @@ trait HasDocumentVersion { - public $documentVersion = 1; - /** * Auto call on model instance as booting * @@ -17,23 +15,26 @@ public static function bootHasDocumentVersion(): void { static::saving(function ($model) { $version = $model->getDocumentVersion(); - if (empty($version)) { - $model->{static::getDocumentVersionKey()} = $model->documentVersion ?? 1; + + if (!$version) { + $model->setDocumentVersion(static::getCurrentVersion()); } }); static::retrieved(function ($model) { - $model->migrateDocumentVersion($model->getDocumentVersion() ?? 0); + $model->migrateDocumentVersion($model->getDocumentVersion()); }); } /** * migrate model document version schema * - * @param int $fromVersion + * @param int|null $fromVersion * @return void */ - public function migrateDocumentVersion(int $fromVersion): void {} + public function migrateDocumentVersion($fromVersion): void + { + } /** * Get Current document version @@ -45,6 +46,15 @@ public function getDocumentVersion(): ?int return $this->{static::getDocumentVersionKey()} ?? null; } + /** + * @param int $version + * @return void + */ + public function setDocumentVersion(int $version): void + { + $this->{static::getDocumentVersionKey()} = $version; + } + /** * Get document version key * @@ -56,4 +66,14 @@ protected static function getDocumentVersionKey(): string ? (string) static::DOCUMENT_VERSION_KEY : 'schema_version'; } + + /** + * @return int + */ + protected static function getCurrentVersion(): int + { + return defined(static::class.'::DOCUMENT_VERSION') + ? (int) static::DOCUMENT_VERSION + : 1; + } } diff --git a/tests/DocumentVersionTest.php b/tests/DocumentVersionTest.php index dc24ce787..84f904d3c 100644 --- a/tests/DocumentVersionTest.php +++ b/tests/DocumentVersionTest.php @@ -25,10 +25,11 @@ public function testCreateWithNullId() $document = DocumentVersion::query()->where('name', 'Luc')->first(); $this->assertEquals(35, $document->age); + $this->assertEquals(2, $document->getDocumentVersion()); // Test without migration $newDocument = new DocumentVersion(['name' => 'Vador']); - $newDocument->documentVersion = 2; + $newDocument->setDocumentVersion(2); $newDocument->save(); $this->assertEquals(2, $newDocument->getDocumentVersion()); diff --git a/tests/Models/DocumentVersion.php b/tests/Models/DocumentVersion.php index 50aea8229..4f7021cf2 100644 --- a/tests/Models/DocumentVersion.php +++ b/tests/Models/DocumentVersion.php @@ -14,11 +14,14 @@ class DocumentVersion extends Eloquent protected $connection = 'mongodb'; protected $collection = 'documentVersion'; protected static $unguarded = true; - public function migrateDocumentVersion(int $fromVersion): void + + public function migrateDocumentVersion($fromVersion): void { if ($fromVersion) { if ($fromVersion < 2) { $this->age = 35; + + $this->setDocumentVersion(2); } } } From 733b6d5de72a3162ea1b0523e18b9d55b7b0cac5 Mon Sep 17 00:00:00 2001 From: Jacques Florian Date: Mon, 1 Jul 2024 17:23:47 +0200 Subject: [PATCH 07/14] Update src/Eloquent/HasDocumentVersion.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Tamarelle --- src/Eloquent/HasDocumentVersion.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Eloquent/HasDocumentVersion.php b/src/Eloquent/HasDocumentVersion.php index 6aa12becd..7319fc8f0 100644 --- a/src/Eloquent/HasDocumentVersion.php +++ b/src/Eloquent/HasDocumentVersion.php @@ -27,12 +27,9 @@ public static function bootHasDocumentVersion(): void } /** - * migrate model document version schema - * - * @param int|null $fromVersion - * @return void + * Migrate document to the current schema version. */ - public function migrateDocumentVersion($fromVersion): void + public function migrateDocumentVersion(int $fromVersion): void { } From 3eeb9911704985fe5182da2ff511f8cc5f28fbf9 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 19:12:29 +0200 Subject: [PATCH 08/14] Rename trait and refacto --- src/Eloquent/HasDocumentVersion.php | 76 ----------------------------- src/Eloquent/HasSchemaVersion.php | 66 +++++++++++++++++++++++++ tests/DocumentVersionTest.php | 18 ++++--- tests/Models/DocumentVersion.php | 8 +-- 4 files changed, 81 insertions(+), 87 deletions(-) delete mode 100644 src/Eloquent/HasDocumentVersion.php create mode 100644 src/Eloquent/HasSchemaVersion.php diff --git a/src/Eloquent/HasDocumentVersion.php b/src/Eloquent/HasDocumentVersion.php deleted file mode 100644 index 7319fc8f0..000000000 --- a/src/Eloquent/HasDocumentVersion.php +++ /dev/null @@ -1,76 +0,0 @@ -getDocumentVersion(); - - if (!$version) { - $model->setDocumentVersion(static::getCurrentVersion()); - } - }); - - static::retrieved(function ($model) { - $model->migrateDocumentVersion($model->getDocumentVersion()); - }); - } - - /** - * Migrate document to the current schema version. - */ - public function migrateDocumentVersion(int $fromVersion): void - { - } - - /** - * Get Current document version - * - * @return int|null - */ - public function getDocumentVersion(): ?int - { - return $this->{static::getDocumentVersionKey()} ?? null; - } - - /** - * @param int $version - * @return void - */ - public function setDocumentVersion(int $version): void - { - $this->{static::getDocumentVersionKey()} = $version; - } - - /** - * Get document version key - * - * @return string - */ - protected static function getDocumentVersionKey(): string - { - return defined(static::class.'::DOCUMENT_VERSION_KEY') - ? (string) static::DOCUMENT_VERSION_KEY - : 'schema_version'; - } - - /** - * @return int - */ - protected static function getCurrentVersion(): int - { - return defined(static::class.'::DOCUMENT_VERSION') - ? (int) static::DOCUMENT_VERSION - : 1; - } -} diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php new file mode 100644 index 000000000..bdb96f02b --- /dev/null +++ b/src/Eloquent/HasSchemaVersion.php @@ -0,0 +1,66 @@ +getSchemaVersion()) { + $model->setSchemaVersion($model->currentSchemaVersion); + } + }); + + static::retrieved(function ($model) { + if ($model->getSchemaVersion() < $model->currentSchemaVersion) { + $model->migrateSchemaVersion($model->getSchemaVersion()); + } + }); + } + + /** + * Migrate document to the current schema version. + */ + public function migrateSchema(int $fromVersion): void + { + } + + /** + * Get Current document version + * + * @return int + */ + public function getSchemaVersion(): int + { + return $this->{static::getSchemaVersionKey()} ?? 0; + } + + /** + * @param int $version + * @return void + */ + public function setSchemaVersion(int $version): void + { + $this->{static::getSchemaVersionKey()} = $version; + } + + /** + * Get document version key + * + * @return string + */ + public static function getSchemaVersionKey(): string + { + return 'schema_version'; + } +} diff --git a/tests/DocumentVersionTest.php b/tests/DocumentVersionTest.php index 84f904d3c..f17c91ad3 100644 --- a/tests/DocumentVersionTest.php +++ b/tests/DocumentVersionTest.php @@ -14,25 +14,29 @@ public function tearDown(): void DocumentVersion::truncate(); } - public function testCreateWithNullId() + public function testWithBasicDocument() { $document = new DocumentVersion(['name' => 'Luc']); - $this->assertEmpty($document->getDocumentVersion()); + $this->assertEmpty($document->getSchemaVersion()); $document->save(); - $this->assertEquals(1, $document->getDocumentVersion()); + $this->assertEquals(1, $document->getSchemaVersion()); $this->assertNull($document->age); - $document = DocumentVersion::query()->where('name', 'Luc')->first(); + $document->currentSchemaVersion = 2; + $document->migrateSchemaVersion($document->getSchemaVersion()); + $this->assertEquals(35, $document->age); - $this->assertEquals(2, $document->getDocumentVersion()); + $this->assertEquals(2, $document->getSchemaVersion()); // Test without migration $newDocument = new DocumentVersion(['name' => 'Vador']); - $newDocument->setDocumentVersion(2); + $newDocument->setSchemaVersion(2); $newDocument->save(); + $newDocument->currentSchemaVersion = 2; + $newDocument->migrateSchema($newDocument->getSchemaVersion()); - $this->assertEquals(2, $newDocument->getDocumentVersion()); + $this->assertEquals(2, $newDocument->getSchemaVersion()); $this->assertNull($newDocument->age); $newDocument = DocumentVersion::query()->where('name', 'Vador')->first(); diff --git a/tests/Models/DocumentVersion.php b/tests/Models/DocumentVersion.php index 4f7021cf2..802e9c76f 100644 --- a/tests/Models/DocumentVersion.php +++ b/tests/Models/DocumentVersion.php @@ -4,24 +4,24 @@ namespace MongoDB\Laravel\Tests\Models; -use MongoDB\Laravel\Eloquent\HasDocumentVersion; +use MongoDB\Laravel\Eloquent\HasSchemaVersion; use MongoDB\Laravel\Eloquent\Model as Eloquent; class DocumentVersion extends Eloquent { - use HasDocumentVersion; + use HasSchemaVersion; protected $connection = 'mongodb'; protected $collection = 'documentVersion'; protected static $unguarded = true; - public function migrateDocumentVersion($fromVersion): void + public function migrateSchemaVersion($fromVersion): void { if ($fromVersion) { if ($fromVersion < 2) { $this->age = 35; - $this->setDocumentVersion(2); + $this->setSchemaVersion(2); } } } From 9f2c49fcc7fe74a1cf0388fbb668a09c5dda4e50 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 19:46:00 +0200 Subject: [PATCH 09/14] Typo fix, and rename classes --- src/Eloquent/HasSchemaVersion.php | 2 +- .../{DocumentVersion.php => SchemaVersion.php} | 4 ++-- ...cumentVersionTest.php => SchemaVersionTest.php} | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) rename tests/Models/{DocumentVersion.php => SchemaVersion.php} (83%) rename tests/{DocumentVersionTest.php => SchemaVersionTest.php} (70%) diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php index bdb96f02b..7fa383712 100644 --- a/src/Eloquent/HasSchemaVersion.php +++ b/src/Eloquent/HasSchemaVersion.php @@ -23,7 +23,7 @@ public static function bootHasSchemaVersion(): void static::retrieved(function ($model) { if ($model->getSchemaVersion() < $model->currentSchemaVersion) { - $model->migrateSchemaVersion($model->getSchemaVersion()); + $model->migrateSchema($model->getSchemaVersion()); } }); } diff --git a/tests/Models/DocumentVersion.php b/tests/Models/SchemaVersion.php similarity index 83% rename from tests/Models/DocumentVersion.php rename to tests/Models/SchemaVersion.php index 802e9c76f..862a90546 100644 --- a/tests/Models/DocumentVersion.php +++ b/tests/Models/SchemaVersion.php @@ -7,7 +7,7 @@ use MongoDB\Laravel\Eloquent\HasSchemaVersion; use MongoDB\Laravel\Eloquent\Model as Eloquent; -class DocumentVersion extends Eloquent +class SchemaVersion extends Eloquent { use HasSchemaVersion; @@ -15,7 +15,7 @@ class DocumentVersion extends Eloquent protected $collection = 'documentVersion'; protected static $unguarded = true; - public function migrateSchemaVersion($fromVersion): void + public function migrateSchema($fromVersion): void { if ($fromVersion) { if ($fromVersion < 2) { diff --git a/tests/DocumentVersionTest.php b/tests/SchemaVersionTest.php similarity index 70% rename from tests/DocumentVersionTest.php rename to tests/SchemaVersionTest.php index f17c91ad3..25d47753a 100644 --- a/tests/DocumentVersionTest.php +++ b/tests/SchemaVersionTest.php @@ -4,19 +4,19 @@ namespace MongoDB\Laravel\Tests; -use MongoDB\Laravel\Tests\Models\DocumentVersion; +use MongoDB\Laravel\Tests\Models\SchemaVersion; use MongoDB\Laravel\Tests\Models\User; -class DocumentVersionTest extends TestCase +class SchemaVersionTest extends TestCase { public function tearDown(): void { - DocumentVersion::truncate(); + SchemaVersion::truncate(); } public function testWithBasicDocument() { - $document = new DocumentVersion(['name' => 'Luc']); + $document = new SchemaVersion(['name' => 'Luc']); $this->assertEmpty($document->getSchemaVersion()); $document->save(); @@ -24,13 +24,13 @@ public function testWithBasicDocument() $this->assertNull($document->age); $document->currentSchemaVersion = 2; - $document->migrateSchemaVersion($document->getSchemaVersion()); + $document->migrateSchema($document->getSchemaVersion()); $this->assertEquals(35, $document->age); $this->assertEquals(2, $document->getSchemaVersion()); // Test without migration - $newDocument = new DocumentVersion(['name' => 'Vador']); + $newDocument = new SchemaVersion(['name' => 'Vador']); $newDocument->setSchemaVersion(2); $newDocument->save(); $newDocument->currentSchemaVersion = 2; @@ -39,7 +39,7 @@ public function testWithBasicDocument() $this->assertEquals(2, $newDocument->getSchemaVersion()); $this->assertNull($newDocument->age); - $newDocument = DocumentVersion::query()->where('name', 'Vador')->first(); + $newDocument = SchemaVersion::query()->where('name', 'Vador')->first(); $this->assertNull($newDocument->age); } } From ba0b900fb977a18e92f32936f123804b706b5f85 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 20:55:30 +0200 Subject: [PATCH 10/14] Add upgrade schema function --- src/Eloquent/HasSchemaVersion.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php index 7fa383712..d526d3ba0 100644 --- a/src/Eloquent/HasSchemaVersion.php +++ b/src/Eloquent/HasSchemaVersion.php @@ -22,9 +22,7 @@ public static function bootHasSchemaVersion(): void }); static::retrieved(function ($model) { - if ($model->getSchemaVersion() < $model->currentSchemaVersion) { - $model->migrateSchema($model->getSchemaVersion()); - } + $model->migrateSchema($model->upgradeSchemaVersion()); }); } @@ -35,6 +33,20 @@ public function migrateSchema(int $fromVersion): void { } + /** + * migrate schema and set current model version + * @return void + */ + public function upgradeSchemaVersion(): void + { + $version = $this->getSchemaVersion(); + + if ($version && $version < $this->currentSchemaVersion) { + $this->migrateSchema($version); + $this->setSchemaVersion($this->currentSchemaVersion); + } + } + /** * Get Current document version * From f9d785d025602425f19e5c59bca07feb54480ac8 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 20:58:27 +0200 Subject: [PATCH 11/14] Remove spaces --- src/Eloquent/HasSchemaVersion.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php index d526d3ba0..c3659eb78 100644 --- a/src/Eloquent/HasSchemaVersion.php +++ b/src/Eloquent/HasSchemaVersion.php @@ -63,7 +63,7 @@ public function getSchemaVersion(): int */ public function setSchemaVersion(int $version): void { - $this->{static::getSchemaVersionKey()} = $version; + $this->{static::getSchemaVersionKey()} = $version; } /** From f406aeaf696347ad0a0dfe5d7e49f54c268cd772 Mon Sep 17 00:00:00 2001 From: florianJacques Date: Mon, 1 Jul 2024 21:16:38 +0200 Subject: [PATCH 12/14] Fix events --- src/Eloquent/HasSchemaVersion.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php index c3659eb78..7ba5aa045 100644 --- a/src/Eloquent/HasSchemaVersion.php +++ b/src/Eloquent/HasSchemaVersion.php @@ -22,7 +22,7 @@ public static function bootHasSchemaVersion(): void }); static::retrieved(function ($model) { - $model->migrateSchema($model->upgradeSchemaVersion()); + $model->upgradeSchemaVersion(); }); } From 9e302b8df6f5f549481160234157ca30be9dd33e Mon Sep 17 00:00:00 2001 From: Jacques Florian Date: Tue, 2 Jul 2024 23:17:16 +0200 Subject: [PATCH 13/14] Update src/Eloquent/HasSchemaVersion.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Tamarelle --- CHANGELOG.md | 5 +++-- src/Eloquent/HasSchemaVersion.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 777a22304..5f2a2f9e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. -## [4.6.0] - upcoming +## [4.6.0] - 2024-07-09 -* Add `DocumentTrait` to use any 3rd party model with MongoDB @GromNaN in [#2580](https://github.com/mongodb/laravel-mongodb/pull/2580) +* Add `DocumentModel` trait to use any 3rd party model with MongoDB @GromNaN in [#2580](https://github.com/mongodb/laravel-mongodb/pull/2580) +* Add `HasSchemaVersion` trait to help implementing the [schema versioning pattern](https://www.mongodb.com/docs/manual/tutorial/model-data-for-schema-versioning/) @florianJacques in [#3021](https://github.com/mongodb/laravel-mongodb/pull/3021) * Add support for Closure for Embed pagination @GromNaN in [#3027](https://github.com/mongodb/laravel-mongodb/pull/3027) ## [4.5.0] - 2024-06-20 diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php index 7ba5aa045..57f344a26 100644 --- a/src/Eloquent/HasSchemaVersion.php +++ b/src/Eloquent/HasSchemaVersion.php @@ -6,7 +6,7 @@ trait HasSchemaVersion { - public $currentSchemaVersion = 1; + public int $currentSchemaVersion = 1; /** * Auto call on model instance as booting From a8ca8aa6e5194331917890c2977f8ac8ccf99e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 9 Jul 2024 14:18:02 +0200 Subject: [PATCH 14/14] Use a constant to store SCHEMA_VERSION --- src/Eloquent/HasSchemaVersion.php | 98 ++++++++++++++++--------------- tests/Models/SchemaVersion.php | 12 ++-- tests/SchemaVersionTest.php | 47 +++++++++------ 3 files changed, 86 insertions(+), 71 deletions(-) diff --git a/src/Eloquent/HasSchemaVersion.php b/src/Eloquent/HasSchemaVersion.php index 57f344a26..8849f655a 100644 --- a/src/Eloquent/HasSchemaVersion.php +++ b/src/Eloquent/HasSchemaVersion.php @@ -4,75 +4,79 @@ namespace MongoDB\Laravel\Eloquent; -trait HasSchemaVersion -{ - public int $currentSchemaVersion = 1; +use Error; +use LogicException; - /** - * Auto call on model instance as booting - * - * @return void - */ - public static function bootHasSchemaVersion(): void - { - static::saving(function ($model) { - if (!$model->getSchemaVersion()) { - $model->setSchemaVersion($model->currentSchemaVersion); - } - }); - - static::retrieved(function ($model) { - $model->upgradeSchemaVersion(); - }); - } +use function sprintf; +/** + * Use this trait to implement schema versioning in your models. The document + * is updated automatically when its schema version retrieved from the database + * is lower than the current schema version of the model. + * + * class MyVersionedModel extends Model + * { + * use HasSchemaVersion; + * + * public const int SCHEMA_VERSION = 1; + * + * public function migrateSchema(int $fromVersion): void + * { + * // Your logic to update the document to the current schema version + * } + * } + * + * @see https://www.mongodb.com/docs/manual/tutorial/model-data-for-schema-versioning/ + * + * Requires PHP 8.2+ + */ +trait HasSchemaVersion +{ /** - * Migrate document to the current schema version. + * This method should be implemented in the model to migrate a document from + * an older schema version to the current schema version. */ public function migrateSchema(int $fromVersion): void { } - /** - * migrate schema and set current model version - * @return void - */ - public function upgradeSchemaVersion(): void + public static function bootHasSchemaVersion(): void { - $version = $this->getSchemaVersion(); + static::saving(function ($model) { + if ($model->getAttribute($model::getSchemaVersionKey()) === null) { + $model->setAttribute($model::getSchemaVersionKey(), $model->getModelSchemaVersion()); + } + }); - if ($version && $version < $this->currentSchemaVersion) { - $this->migrateSchema($version); - $this->setSchemaVersion($this->currentSchemaVersion); - } + static::retrieved(function (self $model) { + $version = $model->getSchemaVersion(); + + if ($version < $model->getModelSchemaVersion()) { + $model->migrateSchema($version); + $model->setAttribute($model::getSchemaVersionKey(), $model->getModelSchemaVersion()); + } + }); } /** - * Get Current document version - * - * @return int + * Get Current document version, fallback to 0 if not set */ public function getSchemaVersion(): int { return $this->{static::getSchemaVersionKey()} ?? 0; } - /** - * @param int $version - * @return void - */ - public function setSchemaVersion(int $version): void + protected static function getSchemaVersionKey(): string { - $this->{static::getSchemaVersionKey()} = $version; + return 'schema_version'; } - /** - * Get document version key - * - * @return string - */ - public static function getSchemaVersionKey(): string + protected function getModelSchemaVersion(): int { - return 'schema_version'; + try { + return $this::SCHEMA_VERSION; + } catch (Error) { + throw new LogicException(sprintf('Constant %s::SCHEMA_VERSION is required when using HasSchemaVersion', $this::class)); + } } } diff --git a/tests/Models/SchemaVersion.php b/tests/Models/SchemaVersion.php index 862a90546..cacfc3f65 100644 --- a/tests/Models/SchemaVersion.php +++ b/tests/Models/SchemaVersion.php @@ -11,18 +11,16 @@ class SchemaVersion extends Eloquent { use HasSchemaVersion; + public const SCHEMA_VERSION = 2; + protected $connection = 'mongodb'; protected $collection = 'documentVersion'; protected static $unguarded = true; - public function migrateSchema($fromVersion): void + public function migrateSchema(int $fromVersion): void { - if ($fromVersion) { - if ($fromVersion < 2) { - $this->age = 35; - - $this->setSchemaVersion(2); - } + if ($fromVersion < 2) { + $this->age = 35; } } } diff --git a/tests/SchemaVersionTest.php b/tests/SchemaVersionTest.php index 25d47753a..dfe2f5122 100644 --- a/tests/SchemaVersionTest.php +++ b/tests/SchemaVersionTest.php @@ -4,8 +4,11 @@ namespace MongoDB\Laravel\Tests; +use Illuminate\Support\Facades\DB; +use LogicException; +use MongoDB\Laravel\Eloquent\HasSchemaVersion; +use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Tests\Models\SchemaVersion; -use MongoDB\Laravel\Tests\Models\User; class SchemaVersionTest extends TestCase { @@ -20,26 +23,36 @@ public function testWithBasicDocument() $this->assertEmpty($document->getSchemaVersion()); $document->save(); - $this->assertEquals(1, $document->getSchemaVersion()); - $this->assertNull($document->age); - - $document->currentSchemaVersion = 2; - $document->migrateSchema($document->getSchemaVersion()); + // The current schema version of the model is stored by default + $this->assertEquals(2, $document->getSchemaVersion()); - $this->assertEquals(35, $document->age); + // Test automatic migration + SchemaVersion::insert([ + ['name' => 'Vador', 'schema_version' => 1], + ]); + $document = SchemaVersion::where('name', 'Vador')->first(); $this->assertEquals(2, $document->getSchemaVersion()); + $this->assertEquals(35, $document->age); + + $document->save(); - // Test without migration - $newDocument = new SchemaVersion(['name' => 'Vador']); - $newDocument->setSchemaVersion(2); - $newDocument->save(); - $newDocument->currentSchemaVersion = 2; - $newDocument->migrateSchema($newDocument->getSchemaVersion()); + // The migrated version is saved + $data = DB::connection('mongodb') + ->collection('documentVersion') + ->where('name', 'Vador') + ->get(); - $this->assertEquals(2, $newDocument->getSchemaVersion()); - $this->assertNull($newDocument->age); + $this->assertEquals(2, $data[0]['schema_version']); + } - $newDocument = SchemaVersion::query()->where('name', 'Vador')->first(); - $this->assertNull($newDocument->age); + public function testIncompleteImplementation(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('::SCHEMA_VERSION is required when using HasSchemaVersion'); + $document = new class extends Model { + use HasSchemaVersion; + }; + + $document->save(); } }