PHP

SlimフレームワークでLaravelで有名なEloquentのEvent(self::creating,updating,savingイベント)が動かなかったのを解決できた

Posted on

EloquentはLaravelでなくても便利なのでORMとしてCodeigniterやSlimで利用してますが
Eloquentのモデルが設定したイベントが呼ばれないので調べてみました。

エラーにはならず、普通にORMとして機能するんですけど、イベントが如何せんよばれない。。


class User extends Model {
public static function boot(){
	parent::boot();
	self::creating(function ($q) {
log出力とかしたい。
	});
	self::updating(function ($q) {
log出力とかしたいのに、イベントが呼ばれない。。
	});
}

$user = User::find(1);
$user->last_login_at = time();
$user->save();

最初slim eloquent event not working,slim eloquent event not callなどで
ググったけど、SlimでEloquentとなるとModelの基本しか見つからないので
vendorの中をちゃんと読むことにしました。

Illuminate\Database\Eloquent\Model.phpを見ると
トレイトにHasEventsってぽいやつがいる。
そして中でイベントが設定されていれば、Dispatcher読んでる。



Model.php

namespace Illuminate\Database\Eloquent;

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
    use Concerns\HasAttributes,
        Concerns\HasEvents,
        Concerns\HasGlobalScopes,
        Concerns\HasRelationships,
        Concerns\HasTimestamps,
        Concerns\HidesAttributes,
        Concerns\GuardsAttributes,
        ForwardsCalls;


HasEvents.php
namespace Illuminate\Database\Eloquent\Concerns;

use Illuminate\Contracts\Events\Dispatcher;

trait HasEvents
{
    /**
     * The event map for the model.
     *
     * Allows for object-based events for native Eloquent events.
     *
     * @var array
     */
    protected $dispatchesEvents = [];
省略
    /**
     * Get the observable event names.
     *
     * @return array
     */
    public function getObservableEvents()
    {
        return array_merge(
            [
                'retrieved', 'creating', 'created', 'updating', 'updated',
                'saving', 'saved', 'restoring', 'restored',
                'deleting', 'deleted', 'forceDeleted',
            ],
            $this->observables
        );
    }
省略
    protected function registerObserver($class)
    {
        $className = $this->resolveObserverClassName($class);

        // When registering a model observer, we will spin through the possible events
        // and determine if this observer has that method. If it does, we will hook
        // it into the model's event system, making it convenient to watch these.
        foreach ($this->getObservableEvents() as $event) {
            if (method_exists($class, $event)) {
                static::registerModelEvent($event, $className.'@'.$event);
            }
        }
    }

イベントディスパッチャーはIlluminate\Contracts\Events\Dispatcherか。
と思いディレクトリを探すと無い。
そこで気づきました。。イベント設定してもDispatcher無いじゃん。
EventってEloquentが持ってるのかよければ依存で持ってきてると思ったけど。。。違うよね。。

今回はSlimを利用していて、下記のようにDBを読んでいます。


// DIC configuration
$container = $app->getContainer();

$container['db'] = function( $c ) {
	$capsule = new \Illuminate\Database\Capsule\Manager;
	$capsule->addConnection( $c['settings']['db'] );
	$capsule->getConnection()->enableQueryLog();
	$capsule->setAsGlobal();
	$capsule->bootEloquent();
	return $capsule;
};

composer.jsonはこんな感じ


"require": {
	"slim/slim": "^3.12",
	"monolog/monolog": "^1.24",
	"danielstjules/stringy": "^3.1",
	"respect/validation": "^1.1",
	"illuminate/database": "^5.5"
},

さっそくイベントライブラリを追加します。


composer require illuminate/container
composer require illuminate/events

そして、コンテナに入れてるDBにも設定を追加します。
最初間違って、setAsGlobalの後にsetEventDispatcherして動かなくて悩みました。。


use Illuminate\Events\Dispatcher as Dispatcher;
use Illuminate\Container\Container as Container;

// DIC configuration
$container = $app->getContainer();

$container['db'] = function( $c ) {
	$capsule = new \Illuminate\Database\Capsule\Manager;
	$capsule->addConnection( $c['settings']['db'] );
	$capsule->setEventDispatcher( new Dispatcher( new Container ) );
	$capsule->getConnection()->enableQueryLog();
	$capsule->setAsGlobal();
	$capsule->bootEloquent();
	return $capsule;
};

これでSlimフレームワークでもEloquentのModelイベントが動くようになりました。