Skip to content

Commit a8ca8aa

Browse files
committed
Use a constant to store SCHEMA_VERSION
1 parent 9e302b8 commit a8ca8aa

File tree

3 files changed

+86
-71
lines changed

3 files changed

+86
-71
lines changed

src/Eloquent/HasSchemaVersion.php

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,75 +4,79 @@
44

55
namespace MongoDB\Laravel\Eloquent;
66

7-
trait HasSchemaVersion
8-
{
9-
public int $currentSchemaVersion = 1;
7+
use Error;
8+
use LogicException;
109

11-
/**
12-
* Auto call on model instance as booting
13-
*
14-
* @return void
15-
*/
16-
public static function bootHasSchemaVersion(): void
17-
{
18-
static::saving(function ($model) {
19-
if (!$model->getSchemaVersion()) {
20-
$model->setSchemaVersion($model->currentSchemaVersion);
21-
}
22-
});
23-
24-
static::retrieved(function ($model) {
25-
$model->upgradeSchemaVersion();
26-
});
27-
}
10+
use function sprintf;
2811

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+
{
2935
/**
30-
* Migrate document to the current schema version.
36+
* This method should be implemented in the model to migrate a document from
37+
* an older schema version to the current schema version.
3138
*/
3239
public function migrateSchema(int $fromVersion): void
3340
{
3441
}
3542

36-
/**
37-
* migrate schema and set current model version
38-
* @return void
39-
*/
40-
public function upgradeSchemaVersion(): void
43+
public static function bootHasSchemaVersion(): void
4144
{
42-
$version = $this->getSchemaVersion();
45+
static::saving(function ($model) {
46+
if ($model->getAttribute($model::getSchemaVersionKey()) === null) {
47+
$model->setAttribute($model::getSchemaVersionKey(), $model->getModelSchemaVersion());
48+
}
49+
});
4350

44-
if ($version && $version < $this->currentSchemaVersion) {
45-
$this->migrateSchema($version);
46-
$this->setSchemaVersion($this->currentSchemaVersion);
47-
}
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+
});
4859
}
4960

5061
/**
51-
* Get Current document version
52-
*
53-
* @return int
62+
* Get Current document version, fallback to 0 if not set
5463
*/
5564
public function getSchemaVersion(): int
5665
{
5766
return $this->{static::getSchemaVersionKey()} ?? 0;
5867
}
5968

60-
/**
61-
* @param int $version
62-
* @return void
63-
*/
64-
public function setSchemaVersion(int $version): void
69+
protected static function getSchemaVersionKey(): string
6570
{
66-
$this->{static::getSchemaVersionKey()} = $version;
71+
return 'schema_version';
6772
}
6873

69-
/**
70-
* Get document version key
71-
*
72-
* @return string
73-
*/
74-
public static function getSchemaVersionKey(): string
74+
protected function getModelSchemaVersion(): int
7575
{
76-
return 'schema_version';
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+
}
7781
}
7882
}

tests/Models/SchemaVersion.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,16 @@ class SchemaVersion extends Eloquent
1111
{
1212
use HasSchemaVersion;
1313

14+
public const SCHEMA_VERSION = 2;
15+
1416
protected $connection = 'mongodb';
1517
protected $collection = 'documentVersion';
1618
protected static $unguarded = true;
1719

18-
public function migrateSchema($fromVersion): void
20+
public function migrateSchema(int $fromVersion): void
1921
{
20-
if ($fromVersion) {
21-
if ($fromVersion < 2) {
22-
$this->age = 35;
23-
24-
$this->setSchemaVersion(2);
25-
}
22+
if ($fromVersion < 2) {
23+
$this->age = 35;
2624
}
2725
}
2826
}

tests/SchemaVersionTest.php

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
namespace MongoDB\Laravel\Tests;
66

7+
use Illuminate\Support\Facades\DB;
8+
use LogicException;
9+
use MongoDB\Laravel\Eloquent\HasSchemaVersion;
10+
use MongoDB\Laravel\Eloquent\Model;
711
use MongoDB\Laravel\Tests\Models\SchemaVersion;
8-
use MongoDB\Laravel\Tests\Models\User;
912

1013
class SchemaVersionTest extends TestCase
1114
{
@@ -20,26 +23,36 @@ public function testWithBasicDocument()
2023
$this->assertEmpty($document->getSchemaVersion());
2124
$document->save();
2225

23-
$this->assertEquals(1, $document->getSchemaVersion());
24-
$this->assertNull($document->age);
25-
26-
$document->currentSchemaVersion = 2;
27-
$document->migrateSchema($document->getSchemaVersion());
26+
// The current schema version of the model is stored by default
27+
$this->assertEquals(2, $document->getSchemaVersion());
2828

29-
$this->assertEquals(35, $document->age);
29+
// Test automatic migration
30+
SchemaVersion::insert([
31+
['name' => 'Vador', 'schema_version' => 1],
32+
]);
33+
$document = SchemaVersion::where('name', 'Vador')->first();
3034
$this->assertEquals(2, $document->getSchemaVersion());
35+
$this->assertEquals(35, $document->age);
36+
37+
$document->save();
3138

32-
// Test without migration
33-
$newDocument = new SchemaVersion(['name' => 'Vador']);
34-
$newDocument->setSchemaVersion(2);
35-
$newDocument->save();
36-
$newDocument->currentSchemaVersion = 2;
37-
$newDocument->migrateSchema($newDocument->getSchemaVersion());
39+
// The migrated version is saved
40+
$data = DB::connection('mongodb')
41+
->collection('documentVersion')
42+
->where('name', 'Vador')
43+
->get();
3844

39-
$this->assertEquals(2, $newDocument->getSchemaVersion());
40-
$this->assertNull($newDocument->age);
45+
$this->assertEquals(2, $data[0]['schema_version']);
46+
}
4147

42-
$newDocument = SchemaVersion::query()->where('name', 'Vador')->first();
43-
$this->assertNull($newDocument->age);
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();
4457
}
4558
}

0 commit comments

Comments
 (0)