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 new file mode 100644 index 000000000..8849f655a --- /dev/null +++ b/src/Eloquent/HasSchemaVersion.php @@ -0,0 +1,82 @@ +getAttribute($model::getSchemaVersionKey()) === null) { + $model->setAttribute($model::getSchemaVersionKey(), $model->getModelSchemaVersion()); + } + }); + + 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, fallback to 0 if not set + */ + public function getSchemaVersion(): int + { + return $this->{static::getSchemaVersionKey()} ?? 0; + } + + protected static function getSchemaVersionKey(): string + { + return 'schema_version'; + } + + protected function getModelSchemaVersion(): int + { + 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 new file mode 100644 index 000000000..cacfc3f65 --- /dev/null +++ b/tests/Models/SchemaVersion.php @@ -0,0 +1,26 @@ +age = 35; + } + } +} diff --git a/tests/SchemaVersionTest.php b/tests/SchemaVersionTest.php new file mode 100644 index 000000000..dfe2f5122 --- /dev/null +++ b/tests/SchemaVersionTest.php @@ -0,0 +1,58 @@ + 'Luc']); + $this->assertEmpty($document->getSchemaVersion()); + $document->save(); + + // The current schema version of the model is stored by default + $this->assertEquals(2, $document->getSchemaVersion()); + + // 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(); + + // The migrated version is saved + $data = DB::connection('mongodb') + ->collection('documentVersion') + ->where('name', 'Vador') + ->get(); + + $this->assertEquals(2, $data[0]['schema_version']); + } + + 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(); + } +}