読者です 読者をやめる 読者になる 読者になる

ytake Hatena

Web Application Developer

Laravel-Aspect update MessageDriven, QueryLog (1.7)

リリースノート作ればいいんですが、便利機能をいくつか追加しましたので、 使い所などを踏まえて追加機能の紹介です。

今回からLaravel5.2, 5.1は1.6、5.3以上は1.7となりました。
5.5リリース後、以前のバージョン対応は停止する予定です。

What

そもそもこのライブラリは、ビジネス要件をサポートするシステム要件を分離し、
AOPを用いてアプリケーションをサポートするものです。
Cache削除追加更新, Transactionをはじめ、Log出力やリトライ処理など
どれもビジネス要件を解決する実装コードに含めてしまうとどうしても複雑になりがちです。

それらはビジネス要件以外のシステム要件なども多く入ってしまうと、
大規模アプリケーションでは理解が難しくなっていくため、そんな要件をサポートするためのものです。

ユースケースによるアスペクト指向ソフトウェア開発 (Object Oriented Selectionシリーズ)

ユースケースによるアスペクト指向ソフトウェア開発 (Object Oriented Selectionシリーズ)

そんなサポートの機能によく使うものを追加しました

Message Driven

これは特定のメソッドの処理をQueueにpushする機能です。
illuminate/busを利用してQueueに挿入しますが、dispatchヘルパーと用途的には同じです。

Queueを利用するため、serializeできないClosureが含まれている場合はエラーになります。

Usage

<?php

declare(strict_types=1);

namespace App\Foundation;

use Ytake\LaravelAspect\Annotation\Loggable;
use Ytake\LaravelAspect\Annotation\LazyQueue;
use Ytake\LaravelAspect\Annotation\MessageDriven;

class MessageDispatcher
{
    /**
     * @Loggable
     * @MessageDriven(
     *     @LazyQueue(10)
     * )
     *
     * @param string $message
     */
    public function queued(string $message)
    {
        $this->hello($message);
    }

    /**
     * @Loggable
     * @param string $message
     *
     * @return string
     */
    public function hello(string $message): string
    {   
        return $message;
    }
}

MessageDrivenアノテーションの第一引数でQueueの振る舞いが変更されます。
LazyQueueをアノテーションで指定すると、databaseやredisなどのドライバーを利用するQueueとして作用し、
EagerQueueを指定すると、syncドライバを利用して遅延なしで実行されます。

上記の例の様に他のアノテーションと組み合わせることもできます。
この場合はQueueに queued メソッドの処理が登録されます。
helloメソッドが実行されるときに引数と実行時間をlogに出力します。

Query Log

Laravelを使っている方はdatabaseのクエリログ取得にEventを利用したり、 Debugbarといったものを使って表示させているかと思います。

Eventを毎回ServiceProviderに記述して必要な処理のところだけLogを出力して・・、
となるとなかなか手間がかかります。

Debugbarは便利ではありますが、商用環境にこのライブラリをインストールするわけにもいかず。。

*大幅にパフォーマンス劣化、実行しているものが見えるため場合によっては脆弱性に繋がるかもしれません

ですが商用環境でSlowQueryを解決するためにコードに記述することも多いと思いますが、
どうしてもアプリケーション本来の要件とは違うコードが記述されるため、処理が複雑になっていくこともあると思います。

そんな問題をAOPで解決する機能です。

Usage

EloquentでもDBファサードやConstruct Injectionでも構いませんが下記の様に、
特定の処理を実行するメソッドに対して @QueryLogアノテーションを記述します。

<?php

use Ytake\LaravelAspect\Annotation\QueryLog;
use Illuminate\Database\ConnectionResolverInterface;

/**
 * Class AspectQueryLog
 */
class AspectQueryLog
{
    /** @var ConnectionResolverInterface */
    protected $db;

    /**
     * @param ConnectionResolverInterface $db
     */
    public function __construct(ConnectionResolverInterface $db)
    {
        $this->db = $db;
    }

    /**
     * @QueryLog
     */
    public function multipleDatabaseAppendRecord()
    {
        $this->db->connection()->statement('CREATE TABLE tests (test varchar(255) NOT NULL)');
        $this->db->connection('testing_second')->statement('CREATE TABLE tests (test varchar(255) NOT NULL)');
        $this->db->connection()->table("tests")->insert(['test' => 'testing']);
        $this->db->connection('testing_second')->table("tests")->insert(['test' => 'testing second']);
    }
}

上記の例では multipleDatabaseAppendRecord メソッドが実行されると、
このメソッド内でコールされたSQLがログファイルに出力されます

Transactional拡張

今までTransactionalアノテーションは一つのメソッドに対して一つしか指定できなかったため、
複数のデータベースに対してTransactionを指示したい場合は、下記の様にする必要がありました。

<?php

namespace App\Domain\Service;

use Ytake\LaravelAspect\Annotation\Transactional;

class ProductActivation
{
    /**
     * @Transactional("db1")
     */
    public function activate()
    {
        $this->activateCampaign();
        // 処理
    }

    /**
     * @Transactional("db2")
     */
    public function activateCampaign()
    {

    }
}

これを1メソッドに対して同時に複数指定することができる様になりました。

<?php

namespace App\Domain\Service;

use Ytake\LaravelAspect\Annotation\Transactional;

class ProductActivation
{
    /**
     * @Transactional({"db1", "db2"})
     */
    public function activate()
    {
        // 処理
    }
}