Eloquentで自作traitを作ってis_activeカラムに対する処理を管理する
概要
SoftDelets
のようにis_active
を管理するActiveState
traitを作成して、設定したモデルは自動的にis_active = true
が挿入されるようにしたい
注意
Model Event?がまだ理解できておらず今回は実装していない。もし必要であれば適宜呼び出しを実装する必要がある
実装
SoftDeletesの仕組みを知る
SoftDeletese.php
の仕組みを知る。LaravelのSoftDeletesが論理削除レコードを無視してくれる仕組み #Eloquent - Qiita で大枠を理解してあとはひたすら以下のコードを見るのと実装して動かしてみていた
- framework/src/Illuminate/Database/Eloquent/SoftDeletes.php at 10.x · laravel/framework
- framework/src/Illuminate/Database/Eloquent/SoftDeletingScope.php at 10.x · laravel/framework
要はtraitが読み込まれたら?GlobalScopeにScopeを設定して、withTrashed
のようなメソッドが呼ばれた時は登録していたGlobalScopeを外してやると実現できそう
scope実装
一応SoftDeletes.php
のconst DELETED_AT
のようにconst IS_ACTIVE
でカラムを指定できるように作る
イマイチまだどこから呼ばれているのかというか、どういう原理なのかわかっていないがextend
が呼ばれたらメソッド呼びまくってる実装が面白い
<?php
// app/models/scopes/ActiveSteteScope.php
declare(strict_types=1);
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class ActiveStateScope implements Scope
{
/**
* All of the extensions to be added to the builder.
*
* @var string[]
*/
protected $extensions = ['WithInactive', 'WithoutInactive'];
/**
* Apply the scope to a given Eloquent query builder.
*
* @param Builder $builder
* @param Model $model
* @return void
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where($model->getQualifiedIsActiveColumn(), '=', 1);
}
/**
* Extend the query builder with the needed functions.
*
* @param Builder $builder
* @return void
*/
public function extend(Builder $builder)
{
foreach ($this->extensions as $extension) {
$this->{"add{$extension}"}($builder);
}
}
/**
* Add the with-inactive extension to the builder.
*
* @param Builder $builder
* @return void
*/
public function addWithInactive(Builder $builder): void
{
$builder->macro('withInactive', function (Builder $builder, $withInactive = true) {
if (!$withInactive) {
return $builder->withoutInactive();
}
return $builder->withoutGlobalScope($this);
});
}
/**
* Add the without-inactive extension to the builder.
*
* @param Builder $builder
* @return void
*/
public function addWithoutInactive(Builder $builder): void
{
$builder->macro('withoutInactive', function (Builder $builder) {
$model = $builder->getModel();
$builder->withoutGlobalScope($this)
->where($model->getQualifiedCreatedAtColumn(), '=', 1);
return $builder;
});
}
}
traitの実装
モデルのインスタンスが呼び出すメソッド$model->activate()
とかの処理を書く
唯一static::IS_ACTIVE
がエラーになるのだけが解決できず、いい感じの解決方法がわからなかった。。
<?php
// app/traits/ActiveStete.php
namespace App\Traits;
use App\Models\Scopes\ActiveStateScope;
/**
* @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withInactive(bool $withInactive = true)
* @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withoutInactive()
*/
trait ActiveState
{
public static function bootActiveState(): void
{
static::addGlobalScope(new ActiveStateScope());
}
public function initializeActiveState(): void
{
// initialize何かあれば
// SoftDeletes.phpだとカラムcのcast設定をしている
// $this->casts[$this->getDeletedAtColumn()] = 'datetime';
}
/**
* Determine if the model instance is active.
*
* @return bool
*/
public function activate(): bool
{
$this->{$this->getIsActiveColumn()} = true;
return $this->save();
}
/**
* Determine if the model instance is inactive.
*
* @return bool
*/
public function deactivate(): bool
{
$this->{$this->getIsActiveColumn()} = false;
return $this->save();
}
/**
* Get the name of the "is_active" column.
*
* @return string
*/
public function getIsActiveColumn(): string
{
return defined('static::IS_ACTIVE') ? static::IS_ACTIVE : 'is_active';
}
/**
* Get the fully qualified "is active" column.
*
* @return string
*/
public function getQualifiedIsActiveColumn(): string
{
return $this->qualifyColumn($this->getIsActiveColumn());
}
}
作成中にあった疑問
macro
とは- EloquentではなくLaravelの機能っぽい
- Collections - Laravel 10.x - The PHP Framework For Web Artisansを見るに既存クラスだったりを拡張できるもの?
extend
が呼ばれることはログで確認しているが、誰がどのタイミングで呼んでいるのか- 調べきれなかった…わかったら書く
static:IS_ACTIVE
としている所が警告が出ていてどうやって消すのかわからない- これも調べきれなかった
感想
コードとにらめっこして動作確認してる分には動作しているように見えるが、エッジケースでどういうパターンがあるのかわからず網羅しきれていないが、なんとか形にはできた
今回laravel, eloquentに関して色々調べたが、googleくんが公式ドキュメントよりもブログ記事より優先して表示するようにしてるので、公式を優先して欲しい…
とりあえず動かす事はできたので、composer packageとしてpublishしてみたいな