Skip to content

Instantly share code, notes, and snippets.

@kkiernan
Last active November 27, 2021 03:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkiernan/8d258e74b6fd0a30f1c88420f7068c28 to your computer and use it in GitHub Desktop.
Save kkiernan/8d258e74b6fd0a30f1c88420f7068c28 to your computer and use it in GitHub Desktop.
Versioning trait for Laravel models
<?php
namespace App;
use App\Traits\Versionable;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use Versionable;
/**
* @var array
*/
protected $fillable = ['title', 'body', 'status_id', 'user_id'];
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PostVersion extends Model
{
/**
* @var array
*/
protected $fillable = ['title', 'body', 'status_id', 'user_id', 'post_id'];
}
<?php
namespace App\Traits;
use ReflectionClass;
/**
* Save a copy of the model to the model's corresponding
* "versions" table on create and update.
*
* Idea: Look into handling all model versioning with a
* single db table that stores the attributes as json.
*
* Currently this trait requires manually creating the
* corresponding model and database migration.
*/
trait Versionable
{
/**
* Boot the trait.
*
* @return void
*/
public static function bootVersionable()
{
static::created(function ($model) {
$model->snapshot();
});
static::updated(function ($model) {
$model->snapshot();
});
}
/**
* Save a snapshot of the current model.
*
* @return void
*/
protected function snapshot()
{
$oidFieldName = $this->getOriginalIdFieldName();
$class = $this->getVersionModelClassName();
$class::create($this->toArray() + ['id' => null, $oidFieldName => $this->id]);
}
/**
* Get the name of the foreign key field that references the original record.
*
* @example For a Post model, this returns "post_id" by default
*
* @return string
*/
protected function getOriginalIdFieldName()
{
return strtolower((new ReflectionClass($this))->getShortName()) . '_id';
}
/**
* Get the class name of the "version model".
*
* @example For an App\Post model, this returns "App\PostVersion" by default
*
* @return string
*/
protected function getVersionModelClassName()
{
return get_class($this) . 'Version';
}
/**
* This model can have many versions.
*
* @return Illuminate\Database\Eloquent\Relations\HasMany
*/
public function versions()
{
return $this->hasMany($this->getVersionModelClassName(), $this->getOriginalIdFieldName());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment