-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add document version feature (#3021)
Co-authored-by: Jérôme Tamarelle <jerome@tamarelle.net>
- Loading branch information
1 parent
65f0a67
commit 179c6a6
Showing
4 changed files
with
169 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace MongoDB\Laravel\Eloquent; | ||
|
||
use Error; | ||
use LogicException; | ||
|
||
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 | ||
{ | ||
/** | ||
* 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 | ||
{ | ||
} | ||
|
||
public static function bootHasSchemaVersion(): void | ||
{ | ||
static::saving(function ($model) { | ||
if ($model->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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace MongoDB\Laravel\Tests\Models; | ||
|
||
use MongoDB\Laravel\Eloquent\HasSchemaVersion; | ||
use MongoDB\Laravel\Eloquent\Model as Eloquent; | ||
|
||
class SchemaVersion extends Eloquent | ||
{ | ||
use HasSchemaVersion; | ||
|
||
public const SCHEMA_VERSION = 2; | ||
|
||
protected $connection = 'mongodb'; | ||
protected $collection = 'documentVersion'; | ||
protected static $unguarded = true; | ||
|
||
public function migrateSchema(int $fromVersion): void | ||
{ | ||
if ($fromVersion < 2) { | ||
$this->age = 35; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
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; | ||
|
||
class SchemaVersionTest extends TestCase | ||
{ | ||
public function tearDown(): void | ||
{ | ||
SchemaVersion::truncate(); | ||
} | ||
|
||
public function testWithBasicDocument() | ||
{ | ||
$document = new SchemaVersion(['name' => '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(); | ||
} | ||
} |