thunderwolf/versionable

为 Laravel Eloquent 提供版本管理功能

安装: 61

依赖: 0

建议者: 0

安全: 0

星星: 0

Forks: 0

类型:软件包

1.0.1 2024-08-25 22:21 UTC

This package is auto-updated.

Last update: 2024-09-25 20:26:14 UTC


README

https://geekflare.com/laravel-eloquent-model-relationship/

versionable 行为为任何 ActiveRecord 对象提供了版本管理功能。使用此行为,您可以

  • 轻松将对象还原到以前的版本
  • 跟踪和浏览对象修改的历史记录
  • 跟踪相关对象的修改

要使用它,需要在您的模型中包含 Versionable 特质 并进行配置,最简单的配置如下

    public static function versionable(): array
    {
        return [
            'version_model' => OurModelVersion::class
        ];
    }

我们还需要一个特殊的模型来存储版本,它将需要 VersionableVersion 特质 并进行配置,最简单的配置如下

    public static function versionableVersion(): array
    {
        return [
            'version_columns' => ['columnA', 'columnB', 'columnN'],
            'versionable_model' => OurModel::class
        ];
    }

基本用法

使用此软件包最基本的方法是使用 Versionable 特质 创建模型,如下所示

<?php

use Illuminate\Database\Eloquent\Model;
use Thunderwolf\EloquentVersionable\Versionable;

class Book extends Model
{
    use Versionable;

    protected $table = 'book';

    protected $fillable = ['title'];

    public $timestamps = false;

    public static function versionable(): array
    {
        return [
            'version_model' => BookVersion::class
        ];
    }
}

并使用 VersionableVersion 特质 创建版本模型,如下所示

<?php

use Illuminate\Database\Eloquent\Model;
use Thunderwolf\EloquentVersionable\VersionableVersion;

class BookVersion extends Model
{
    use VersionableVersion;

    protected $table = 'book_version';

    protected $fillable = ['title'];  // Must be the same as we have in the `version_columns`

    public $timestamps = false;
    public $incrementing = false;

    public static function versionableVersion(): array
    {
        return [
            'version_columns' => ['title'],
            'versionable_model' => Book::class
        ];
    }
}

注册 VersionableServiceProvider 后,您还可以使用蓝图通过 createVersionablecreateVersionableVersion 辅助方法创建表,如下所示

$schema->create('book', function (Blueprint $table1) {
    $table1->increments('id');
    $table1->string('title');
    $table1->createVersionable(['version_model' => BookVersion::class]);
});

$schema->create('book_version', function (Blueprint $table2) {
    $table2->unsignedInteger('title');
    $table2->createVersionableVersion(['versionable_model' => Book::class, 'version_columns' => ['title']]);
});

以类似的方式处理迁移。

模型现在有一个新的列,version_column,用于存储版本号。它还有一个新的表,book_version,用于存储所有 Book 对象的版本,包括过去和现在。您不需要与第二个表交互,因为行为提供了一个易于使用的 API,它会处理所有从主 ActiveRecord 对象中来的版本管理操作。

<?php
$book = new Book();

$book->setAttribute('title', 'War and Peas');
$book->save();
echo $book->getVersion(); // 1

$book->setAttribute('title', 'War and Peace');
$book->save();
echo $book->getVersion(); // 2

$book->toVersion(1);
echo $book->getTitle(); // 'War and Peas'
$book->save();
echo $book->getVersion(); // 3

$diff = $book->compareVersions(1, 2);
// print_r($diff);
// array(
//   'Title' => array(1 => 'War and Peas', 2 => 'War and Pace'),
// );

$id = $book->getKey();

// deleting an object also deletes all its versions
$book->delete();

$obj = Book::query()->find($id);
$this->assertNull($obj);

$collection = BookVersion::query()->where('id',$id)->get();
$collection->isEmpty(); // true

添加每个修订版本的详细信息

为了未来的参考,您可能需要记录谁编辑了对象,以及何时以及为什么。为了启用审计日志功能,将以下三个参数添加到

例如 BookDetails 模型

<?php

use Illuminate\Database\Eloquent\Model;
use Thunderwolf\EloquentVersionable\Versionable;

class BookDetails extends Model
{
    use Versionable;

    protected $table = 'book-details';

    protected $fillable = ['title'];

    public $timestamps = false;

    public static function versionable(): array
    {
        return [
            'version_model' => BookDetailsVersion::class,
            'log_created_at' => true,
            'log_created_by' => true,
            'log_comment' => true,
        ];
    }
}

BookDetailsVersion 模型

<?php

use Illuminate\Database\Eloquent\Model;
use Thunderwolf\EloquentVersionable\VersionableVersion;

class BookDetailsVersion extends Model
{
    use VersionableVersion;

    protected $table = 'book-details-version';

    protected $fillable = ['title'];  // Must be the same as we have in the `version_columns`

    public $timestamps = false;
    public $incrementing = false;

    public static function versionableVersion(): array
    {
        return [
            'version_columns' => ['title'],
            'versionable_model' => BookDetails::class,
            'log_created_at' => true,
            'log_created_by' => true,
            'log_comment' => true,
        ];
    }
}

以创建这些表

$schema->create('book-details', function (Blueprint $table3) {
    $table3->increments('id');
    $table3->string('title');
    $table3->createVersionable([
        'version_model' => BookDetailsVersion::class,
        'log_created_at' => true,
        'log_created_by' => true,
        'log_comment' => true,
    ]);
});

$schema->create('book-details-version', function (Blueprint $table4) {
    $table4->string('title');
    $table4->createVersionableVersion([
        'versionable_model' => BookDetails::class, 'version_columns' => ['title'],
        'log_created_at' => true,
        'log_created_by' => true,
        'log_comment' => true,
    ]);
});

现在您可以使用 setVersionCreatedBy()setVersionComment() 方法定义每个修订的作者姓名和注释,如下所示

<?php
$book = new BookDetails();
$book->setAttribute('title', 'War and Peas');
$book->setVersionCreatedBy('John Doe');
$book->setVersionComment('Book creation');
$book->save();

$book->setAttribute('title', 'War and Peace');
$book->setVersionCreatedBy('John Doe');
$book->setVersionComment('Corrected typo on book title');
$book->save();

检索修订历史

<?php
// details about each revision are available for all versions of an object
$book->toVersion(1);
echo $book->getVersionCreatedBy(); // 'John Doe'
echo $book->getVersionComment(); // 'Book creation'
// besides, the behavior also logs the creation date for all versions
echo $book->getVersionCreatedAt(); // 'YYYY-MM-DD HH:MM:SS'

// if you need to list the revision details, it is better to use the version object
// than the main object. The following requires only one database query:
foreach ($book->getAllVersions() as $bookVersion) {
    echo sprintf("'%s', Version %d, updated by %s on %s (%s)\n",
        $bookVersion->getTitle(),
        $bookVersion->getVersion(),
        $bookVersion->getVersionCreatedBy(),
        $bookVersion->getVersionCreatedAt(),
        $bookVersion->getVersionComment(),
    );
}
// 'War and Peas', Version 1, updated by John Doe on YYYY-MM-DD HH:MM:SS (Book Creation)
// 'War and Peace', Version 2, updated by John Doe on YYYY-MM-DD HH:MM:SS (Corrected typo on book title)

条件版本

每次创建或修改对象时,您可能不需要新版本。如果您想指定自己的条件,只需在您的存根类中重写 isVersioningNecessary() 方法。每次您调用 save() 主对象时,行为都会在幕后调用它。如果它返回 false,则不会创建版本。

例如 BookVersioningNecessary 模型

<?php

use Illuminate\Database\Eloquent\Model;
use Thunderwolf\EloquentVersionable\Versionable;

class BookVersioningNecessary extends Model
{
    use Versionable {
        isVersioningNecessary as protected traitIsVersioningNecessary;
    }

    protected $table = 'book-versioning-necessary';

    protected $fillable = ['title', 'isbn'];

    public $timestamps = false;

    public function isVersioningNecessary(): bool
    {
        return $this->getAttribute('isbn') !== null && $this->traitIsVersioningNecessary();
    }

    public static function versionable(): array
    {
        return [
            'version_model' => BookVersioningNecessaryVersion::class
        ];
    }
}

BookVersioningNecessaryVersion 模型

<?php

use Illuminate\Database\Eloquent\Model;
use Thunderwolf\EloquentVersionable\VersionableVersion;

class BookVersioningNecessaryVersion extends Model
{
    use VersionableVersion;

    protected $table = 'book-versioning-necessary-version';

    protected $fillable = ['title', 'isbn'];  // Must be the same as we have in the `version_columns`

    public $timestamps = false;
    public $incrementing = false;

    public static function versionableVersion(): array
    {
        return [
            'version_columns' => ['title'],
            'versionable_model' => BookVersioningNecessary::class
        ];
    }
}

以创建这些表

$schema->create('book-versioning-necessary', function (Blueprint $table5) {
    $table5->increments('id');
    $table5->string('title');
    $table5->string('isbn')->nullable();
    $table5->createVersionable(['version_model' => BookVersioningNecessaryVersion::class]);
});

$schema->create('book-versioning-necessary-version', function (Blueprint $table6) {
    $table6->string('title');
    $table6->string('isbn')->nullable();
    $table6->createVersionableVersion(['versionable_model' => BookVersioningNecessary::class, 'version_columns' => ['title', 'isbn']]);
});

并且我们可以像这样使用它

<?php
$book = new BookVersioningNecessary();
$book->setAttribute('title', 'War and Peas');
$book->save(); // book is saved, no new version is created
$book->setAttribute('isbn', '0553213105');
$book->save(); // book is saved, and a new version is created

或者,您可以选择通过在查询类上调用 disableVersioning() 方法来禁用为给定模型的每个对象在每个保存时自动创建新版本。在这种情况下,您仍然可以使用在保存的对象上调用 addVersion() 方法来手动创建对象的版本

<?php
Book::disableVersioning();
$book = new Book();
$book->setAttribute('title', 'Pride and Prejudice');
$book->setVersion(1);
$book->save(); // book is saved, no new version is created
$book->addVersion(); // a new version is created

// you can re-enable versioning using the Query static method enableVersioning()
Book::enableVersioning();

参数

在要版本化的模型的情况下,我们有一些额外的配置参数,这些参数在上文中没有提到

  • version_column 默认设置为 version,您可以通过设置此键来选择此列的名称
  • version_created_at_column 默认设置为 version_created_at,您必须将 log_created_at 设置为 true 才能使用它
  • version_created_by_column 默认设置为 version_created_by,您必须将 log_created_by 设置为 true 才能使用它
  • version_comment_column 默认设置为 version_comment,您必须将 log_comment 设置为 true 才能使用它。

在版本模型的情况下,我们可以设置一些未在上文提到的额外配置参数。

  • versionable_model_foreign_key 默认设置为 id

公共API

// Adds a new version to the object version history and increments the version property
public function save(array $options = []): bool

// Deletes the object and version history
public function delete(): bool

// Checks whether a new version needs to be saved
public function isVersioningNecessary(): bool

// Populates the properties of the current object with values from the requested version. Beware that saving the object afterwards will create a new version (and not update the previous version).
public function toVersion(int $versionNumber): self

// Queries the database for the highest version number recorded for this object
public function getLastVersionNumber(): int

// Returns true if the current object is the last available version
public function isLastVersion(): bool

// Creates a new Version record and saves it. To be used when `isVersioningNecessary()` is false. Beware that it does not take care of incrementing the version number of the main object, and that the main object must be saved prior to calling this method.
public function addVersion(): Model

// Returns all Version objects related to the main object in a collection
public function getAllVersions(): Collection

// Returns a given version object
public function getOneVersion(int $versionNumber): ?Model

// Returns an array of differences showing which parts of a resource changed between two versions
public function compareVersions(int $fromVersionNumber, int $toVersionNumber, string $keys = 'columns', array $ignoredColumns = []): array

// Populates an ActiveRecord object based on a Version object
public function populateFromVersion(Model $version, array &$loadedObjects = []): self

// Defines the author name for the revision
public function setVersionCreatedBy($user): void

// Gets the author name for the revision
public function getVersionCreatedBy()

// Gets the creation date for the revision (the behavior takes care of setting it)
public function getVersionCreatedAt(): Carbon

// Defines the comment for the revision
public function setVersionComment(?string $comment): void

// Gets the comment for the revision
public function getVersionComment(): ?string

静态方法

// Enables versioning for all instances of the related ActiveRecord class
public static function enableVersioning(): void

// Disables versioning for all instances of the related ActiveRecord class
public static function disableVersioning(): void

// Checks whether the versionnig is enabled
public static function isVersioningEnabled(): bool