Skip to content

Commit 179c6a6

Browse files
Add document version feature (#3021)
Co-authored-by: Jérôme Tamarelle <[email protected]>
1 parent 65f0a67 commit 179c6a6

File tree

4 files changed

+169
-2
lines changed

4 files changed

+169
-2
lines changed

Diff for: CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4-
## [4.6.0] - upcoming
4+
## [4.6.0] - 2024-07-09
55

6-
* Add `DocumentTrait` to use any 3rd party model with MongoDB @GromNaN in [#2580](https://github.com/mongodb/laravel-mongodb/pull/2580)
6+
* Add `DocumentModel` trait to use any 3rd party model with MongoDB @GromNaN in [#2580](https://github.com/mongodb/laravel-mongodb/pull/2580)
7+
* 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)
78
* Add support for Closure for Embed pagination @GromNaN in [#3027](https://github.com/mongodb/laravel-mongodb/pull/3027)
89

910
## [4.5.0] - 2024-06-20

Diff for: src/Eloquent/HasSchemaVersion.php

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Laravel\Eloquent;
6+
7+
use Error;
8+
use LogicException;
9+
10+
use function sprintf;
11+
12+
/**
13+
* Use this trait to implement schema versioning in your models. The document
14+
* is updated automatically when its schema version retrieved from the database
15+
* is lower than the current schema version of the model.
16+
*
17+
* class MyVersionedModel extends Model
18+
* {
19+
* use HasSchemaVersion;
20+
*
21+
* public const int SCHEMA_VERSION = 1;
22+
*
23+
* public function migrateSchema(int $fromVersion): void
24+
* {
25+
* // Your logic to update the document to the current schema version
26+
* }
27+
* }
28+
*
29+
* @see https://www.mongodb.com/docs/manual/tutorial/model-data-for-schema-versioning/
30+
*
31+
* Requires PHP 8.2+
32+
*/
33+
trait HasSchemaVersion
34+
{
35+
/**
36+
* This method should be implemented in the model to migrate a document from
37+
* an older schema version to the current schema version.
38+
*/
39+
public function migrateSchema(int $fromVersion): void
40+
{
41+
}
42+
43+
public static function bootHasSchemaVersion(): void
44+
{
45+
static::saving(function ($model) {
46+
if ($model->getAttribute($model::getSchemaVersionKey()) === null) {
47+
$model->setAttribute($model::getSchemaVersionKey(), $model->getModelSchemaVersion());
48+
}
49+
});
50+
51+
static::retrieved(function (self $model) {
52+
$version = $model->getSchemaVersion();
53+
54+
if ($version < $model->getModelSchemaVersion()) {
55+
$model->migrateSchema($version);
56+
$model->setAttribute($model::getSchemaVersionKey(), $model->getModelSchemaVersion());
57+
}
58+
});
59+
}
60+
61+
/**
62+
* Get Current document version, fallback to 0 if not set
63+
*/
64+
public function getSchemaVersion(): int
65+
{
66+
return $this->{static::getSchemaVersionKey()} ?? 0;
67+
}
68+
69+
protected static function getSchemaVersionKey(): string
70+
{
71+
return 'schema_version';
72+
}
73+
74+
protected function getModelSchemaVersion(): int
75+
{
76+
try {
77+
return $this::SCHEMA_VERSION;
78+
} catch (Error) {
79+
throw new LogicException(sprintf('Constant %s::SCHEMA_VERSION is required when using HasSchemaVersion', $this::class));
80+
}
81+
}
82+
}

Diff for: tests/Models/SchemaVersion.php

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Laravel\Tests\Models;
6+
7+
use MongoDB\Laravel\Eloquent\HasSchemaVersion;
8+
use MongoDB\Laravel\Eloquent\Model as Eloquent;
9+
10+
class SchemaVersion extends Eloquent
11+
{
12+
use HasSchemaVersion;
13+
14+
public const SCHEMA_VERSION = 2;
15+
16+
protected $connection = 'mongodb';
17+
protected $collection = 'documentVersion';
18+
protected static $unguarded = true;
19+
20+
public function migrateSchema(int $fromVersion): void
21+
{
22+
if ($fromVersion < 2) {
23+
$this->age = 35;
24+
}
25+
}
26+
}

Diff for: tests/SchemaVersionTest.php

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Laravel\Tests;
6+
7+
use Illuminate\Support\Facades\DB;
8+
use LogicException;
9+
use MongoDB\Laravel\Eloquent\HasSchemaVersion;
10+
use MongoDB\Laravel\Eloquent\Model;
11+
use MongoDB\Laravel\Tests\Models\SchemaVersion;
12+
13+
class SchemaVersionTest extends TestCase
14+
{
15+
public function tearDown(): void
16+
{
17+
SchemaVersion::truncate();
18+
}
19+
20+
public function testWithBasicDocument()
21+
{
22+
$document = new SchemaVersion(['name' => 'Luc']);
23+
$this->assertEmpty($document->getSchemaVersion());
24+
$document->save();
25+
26+
// The current schema version of the model is stored by default
27+
$this->assertEquals(2, $document->getSchemaVersion());
28+
29+
// Test automatic migration
30+
SchemaVersion::insert([
31+
['name' => 'Vador', 'schema_version' => 1],
32+
]);
33+
$document = SchemaVersion::where('name', 'Vador')->first();
34+
$this->assertEquals(2, $document->getSchemaVersion());
35+
$this->assertEquals(35, $document->age);
36+
37+
$document->save();
38+
39+
// The migrated version is saved
40+
$data = DB::connection('mongodb')
41+
->collection('documentVersion')
42+
->where('name', 'Vador')
43+
->get();
44+
45+
$this->assertEquals(2, $data[0]['schema_version']);
46+
}
47+
48+
public function testIncompleteImplementation(): void
49+
{
50+
$this->expectException(LogicException::class);
51+
$this->expectExceptionMessage('::SCHEMA_VERSION is required when using HasSchemaVersion');
52+
$document = new class extends Model {
53+
use HasSchemaVersion;
54+
};
55+
56+
$document->save();
57+
}
58+
}

0 commit comments

Comments
 (0)