share facebook facebook2 twitter menu hatena pocket slack

2015.06.08 MON

Laravel5でバッチ開発

小谷松 丈樹

WRITTEN BY小谷松 丈樹

PHPフレームワークのベンチマーク一覧の記事を拝見し、
Laravel5が遅いという結果が出てしまいちょっとへこみました。
ま、まぁハロワの早さがフレームワークの優劣じゃないし(震え声
ハロワ程度でもちょっとメモリとか多く使っちゃうだけだし(震え声
などと色々理由を付けて自分の涙を無かったことにしています。

一口に速度といっても2つの指標があって、実行速度と開発速度があって
Laravelは開発速度に重きを置かれているということなので、
こういう差がでちゃうのも仕方ないんじゃないかなーと思っています。

ちなみに、今度日本で初めてLaravelの本が出るらしいですね。ちょっと気になっています。
Laravelエキスパート養成読本[モダンなPHP開発を実現するノウハウ満載!] (Software Design plus)

さて本題に戻って、Laravelでのバッチ開発まわりに関して、
公式ドキュメントを読んでも、どうも的を射なかったのでまとめてみます。

目標

Laravelフレームワークを使用したバッチ処理の開発、cronスケジューリング

主要技術・環境

Laravel5

実装

バッチの実装

まずはバッチクラスの作成をartisanコマンドにて行います。

$ php artisan make:console TestCommand --command='batchtest'
Console command created successfully.

クラス定義が簡単でいつも助かります。
上記コマンドを叩くことで、 TestCommand というクラスファイルが自動で作成され、
batchtest というコマンドが用意され、 $ php artisan batchtest で実行できるようになります。

さて、ファイルができる場所ですが

filedir

project/app/Console/Commands/TestCommand.php にできます。
早速、中身を確認してみましょう。

<?php namespace AppConsoleCommands;
 
use IlluminateConsoleCommand;
use SymfonyComponentConsoleInputInputOption;
use SymfonyComponentConsoleInputInputArgument;
 
class TestCommand extends Command {
 
/**
 * The console command name.
 *
 * @var string
 */
protected $name = 'batchtest';
 
/**
 * The console command description.
 *
 * @var string
 */
protected $description = 'Command description.';
 
/**
 * Create a new command instance.
 *
 * @return void
 */
public function __construct()
{
    parent::__construct();
}
 
/**
 * Execute the console command.
 *
 * @return mixed
 */
public function fire()
{
    //
}
 
/**
 * Get the console command arguments.
 *
 * @return array
 */
protected function getArguments()
{
    return [
        ['example', InputArgument::REQUIRED, 'An example argument.'],
    ];
}
 
/**
 * Get the console command options.
 *
 * @return array
 */
protected function getOptions()
{
    return [
        ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
    ];
}
 
}

見た通りな感じですが、 fire() がメインの動作関数ですね。
fire() を編集してみましょう。とりあえず実行確認のechoしてみます。

    public function fire()
    {
        echo 'コニチワ';
    }

コニチワします。
では実行してみましょう。

$ php artisan batchtest
exception 'InvalidArgumentException' with message 'Command "batchtest" is not defined.' in ~~~
怒られた!

コマンドを登録しないといけないようで。
app/Console/Kernel.php に、下記の通りコマンドクラス定義を追加する必要があります。
ここまでartisanコマンドで追加してくれてもいいと思うのだけどなぁ。

protected $commands = [
    'AppConsoleCommandsInspireCommand',
    'AppConsoleCommandsTestCommand' // 追加
];

TestCommand クラスを呼び出すよう定義して batchtest コマンドを有効化します。
で、再度実行してみます。

$ php artisan batchtest
exception 'RuntimeException' with message 'Not enough arguments.' in ~~~
怒られた!

getArguments() がデフォルトでは引数を一つ受けないとエラーを吐くようになっているようです。
引数を取る必要がなければ関数まるごと削除してしまって良いようです。
とりあえず適当な引数をつけて実行してみましょう。

$ php artisan batchtest "example"
コニチワ
コニチワ!

引数を受けとりたい

引数を受けて処理を分けるというのも多くあるので、そこまで触ってみます。
そもそも初期設定の下記はどういう意味かというと、

    protected function getArguments()
    {
        return [
            ['example', InputArgument::REQUIRED, 'An example argument.'], // 一つの引数を受け取る
            // ['プログラムで受ける変数名', '必須入力か否か', '引数の説明']
        ];
    }

getArguments() の返り値配列の要素数が引数の数。
中の配列が「変数名、必須引数か、引数の説明」となっているので、
初期設定では、「引数を一つ必須入力、受け取ったら変数名を’example’とする」みたいな感じです。

デフォルト引数のような使い方をしたい場合は、 InputArgument::REQUIREDnull とすればいいようです。

        return [
            ['lang', null, 'choose language. default="ja"],
        ];

これで、「引数一つ自由入力、受け取ったら変数名を’lang’とする」という感じでしょうか。
で、どうやって引数を受け取るかですが、 fire() 内で $this->argument('変数名') で使えるようです。
__constract() 内で受け取れないのがちょっと勿体無い感じがしますねぇ。
ということで、処理分けは以下のようにできるようですね。

public function fire()
    {
        $lang = empty($this->argument('lang')) ? 'ja' : $this->argument('lang');
        switch($lang){
            case 'ja':
                echo "コニチワn"; break;
            case 'us':
                echo "ハローn"; break;
            default:
            ~~

動かしてみましょう。

$ php artisan batchtest
コニチワ</p>

<p>$ php artisan batchtest us
ハロー</p>

<p>$ php artisan batchtest hoge
สวัสดี
สวัสดี!!

empty() じゃなくて strlen()==0 で受けるべきかな?まあ用途によって。

cron設定

バッチを作成するたびにサーバにSSHログインしてcronの設定をしないといけないのは面倒ですね。
複数台全てにcrontab設定するのは骨が折れる。
Laravelでは、ソースレベルでコマンド実行のスケジューリングを設定できて素敵です。
crontabに下記を設定します。

* * * * * php /path/to/project/artisan schedule:run 1>> /dev/null 2>&1

すると、毎分バッチ管理スケジューラを起動して、設定にマッチするものを実行するという感じ。
公式ドキュメントには「これ以上簡単にできません!」との記述が。

多分これが一番簡単だと思います。
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('inspire')
                 ->hourly();
        $schedule->command('batchtest')->dailyAt('7:00'); // 追加
    }

これだけで毎朝7時にコニチワできます。
引数をつけたい場合は command('batchtest us') とすればいいだけですね。
バッチが多くなればなるほど、管理の容易さが感じられそう。

結果・考察

毎朝7時にコニチワする定期実行バッチができました。

同じプログラムを複数サーバにデプロイする時は、
サーバのホスト名を見て、バッチサーバでだけ実行するとか、web01だけで実行するなんて事も可能ですね。
PHP処理ならではの細かい設定を入れられて管理のしやすいcron、面白いです。

小谷松 丈樹

小谷松 丈樹

アイレット第一事業部のWebアプリ開発者。PHP、JavaScriptの案件がメイン。モノづくりは常に楽しみながらがモットー。面白いものを生み出して、管理までできるようになるのが目標。