KPIの集計処理など、Laravelをコマンドモードで利用したいときの対応方法です。

利用バージョンはこちら。

  • PHP 5.4
  • Laravel 5.0.0

コマンドの作成

コマンドライン用の処理はArtisanのカスタムコマンドとして実装します。
ちなみに、Artisanはアルチザンと読むようです。

公式のドキュメントを参考に実装を進めていきます。

Laravel 5.0.0 Artisan開発
http://readouble.com/laravel/5/0/0/ja/commands.html

スケルトンの作成

次のコマンドを打って、コマンド用のスケルトンを生成します。
ここでは仮にFooというを作ります。

1$ cd /path/to/project
2$ php artisan make:console FooCommand

上記を実行するとapp/Console/Commands以下にファイルが生成されます。

コマンドの実装

次にこのクラスを開いて、処理名、詳細を更新します。

1protected $name        = 'foo';
2protected $description = 'command description';
3

$nameについて、例えばマイグレーションコマンドはmigrate、マイグレーションのロールバックはmigrate:rollbackというコマンド名です。
これに倣って、関連するコマンドは:(コロン)をつけて関連が分かるようにしてやるとGoodですね。

また、コマンドのメイン処理はfire()に実装していきます。

引数

例えばファイル名やテーブルの主キーなど、コマンドクラスで引数を受け取るようにする場合getArguments()にコマンドを定義します。

1protected function getArguments()
2{
3    return [
4        [$name, $mode, $description, $defaultValue],
5    ];
6}
7

$modeはドキュメント通り以下のどちらかを設定します。

引数modeはInputArgument::REQUIREDかInputArgument::OPTIONALのどちらかです。

InputArgument::REQUIREDは必須の引数、InputArgument::OPTIONALは省略可能な引数という意味ですね。

引数の受け取り方は次の通りです。

1// 引数を1つ取得
2$value     = $this->argument('name');
3
4// すべての引数を取得
5$arguments = $this->argument();
6

オプション

引数と同様に、コマンドラインの実行オプションも、クラスに定義することが可能です。

1protected function getOptions()
2{
3    return [
4        [$name, $shortcut, $mode, $description, $defaultValue],
5    ];
6}
7

オプションの受け取り方は次の通りです。

1// オプションを1つ取得
2$value   = $this->option('name');
3
4// すべてのオプションを取得
5$options = $this->option();
6

getArguments()との違いは、ショートカットを指定できる点、モードの設定値が異なる点です。

ドキュメントを見てもよく分からなかったのですが、$nameは--option、$shortcutは-oのように良くあるコマンドの慣例に従って定義してあげる必要があるようです。
ハイフンを省略しようとしたところ、そんな引数は定義されていないというエラーとなりました。

Class Not Foundの対処法

ドキュメントには次の記載がありますが、この通り実装するとClass Not Foundが発生します。

オプションでは、引数modeはInputOption:REQUIRED、InputOption::OPTIONAL、InputOption::VALUE_IS_ARRAY、InputOption::VALUE_NONE

composer updateもできなくなるし、焦りました。
Symfony\Component\Console\Input\InputOption側の定数定義が変わっているためです。

名称が変わっているものは次の2点で、VALUE_IS_ARRAY、VALUE_NONEはOKでした。

NG OK
InputOption::REQUIRED InputOption::VALUE_REQUIRED
InputOption::OPTIONAL InputOption::VALUE_OPTIONAL

コマンドを登録する

最後に/app/Console/Kernel.phpを編集して独自コマンドをartisanに登録します。

1protected $commands = [
2     'App\Console\Commands\Inspire',
3     'App\Console\Commands\Fooommand',
4];
5

サンプルとして、Inspireコマンドが登録されていますので同じように追記するだけです。簡単。

次のコマンドを打って、新規コマンドが利用可能かチェックします。

1$ php artisan list

登録したコマンドを実行してみましょう。

1$ php artisan foo

Artisanコマンドスケジューラー

独自のコマンドを定期的に実行するために、Artisanコマンドスケジューラーを利用します。

Artisanコマンドスケジューラーの特徴は、毎分実行するcronを1つ登録し、何のコマンドを居t実行するかはLaravel側で管理する 点です。
これによって簡単に定期的な処理をバージョン管理下に置くことが可能。
いいですね!

Laravel 5.0.0 Artisanコマンドライン
Artisanコマンドスケジューラー
http://readouble.com/laravel/5/0/0/ja/artisan.html#scheduling-artisan-commands

スケジュールの登録

app/Console/Kernel.phpschedule()にバッチ処理を登録していきます。

スケジュールもいろいろな書き方をサポート

 1protected function schedule(Schedule $schedule)
 2{
 3    $schedule->command('inspire')->hourly();
 4
 5    // cron風
 6    $schedule->command('foo')->cron('* * * * *');
 7
 8    // ○分毎の処理
 9    $schedule->command('foo')->everyMinute();
10    $schedule->command('foo')->everyFiveMinutes();
11    $schedule->command('foo')->everyTenMinutes();
12    $schedule->command('foo')->everyThirtyMinutes();
13
14    // 毎日行うジョブ
15    $schedule->command('foo')->daily();
16    $schedule->command('foo')->dailyAt('15:00');
17    $schedule->command('foo')->twiceDaily();
18
19    // ウィークデーに行うジョブ
20    $schedule->command('foo')->weekdays();
21    $schedule->command('foo')->weekly();
22}
23

launchd

コマンドを作成して実行スケジュールを設定しましたので、次にcronを登録しましょう。

ところが、Macではcronを使うには少々手間がかかるということで、Mac版cron(?)であるlaunchdを設定します。

plistの作成

Usersディレクトリの下にplistを作成します。
ファイル名は任意で問題ありません。

私は以下のファイルを作成しました。

1$ vi ~/Library/LaunchAgents/Laravel-task-scheduler.plist
 1<?xml version=”1.0″ encoding=”UTF-8″?>
 2<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
 3<plist version=”1.0″>
 4<dict>
 5
 6<key>Label</key>
 7<string>Laravel-task-scheduler.plist</string>
 8
 9<key>ProgramArguments</key>
10<array>
11<string>/usr/local/php5/bin/php</string>
12<string>/path/to/project/artisan</string>
13<string>schedule:run</string>
14</array>
15
16<key>RunAtLoad</key>
17<true/>
18
19<key>WorkingDirectory</key>
20<string>/path/to/project/</string>
21
22<key>StartInterval</key>
23<integer>60</integer>
24
25<key>StandardErrorPath</key>
26<string>/Users/yourName/launchd/err.log</string>
27<key>StandardOutPath</key>
28<string>/Users/yourName/launchd/log.log</string>
29
30</dict>
31</plist>

RunAtLoadをtrueで、plistロード時に即ジョブが実行されますので挙動チェックに重宝しました。

plistのロード

plistをロードすることでジョブの実行が開始されます。

1launchctl load ~/Library/LaunchAgents/pl.sample.perl.plist

ジョブを停止する時はunloadします。

1launchctl unload ~/Library/LaunchAgents/pl.sample.perl.plist

plistを更新した場合はunloadしてからloadしてください。

ハマった点

WorkingDirectoryには、Laravelプロジェクトのプロジェクトルートを指定します。

ジョブ実行時はLaravelで側で次のコードが呼ばれます。

1// Illuminate/Console/Scheduling/Schedule.php
2return $this->exec(PHP_BINARY.' artisan '.$command, $parameters);
3

つまり、スケジュール実行時に「execで新しくPHPのプロセスを実行」しています。

launchdのデフォルトは ルート(/)で処理が実行されるため、artisanにコマンドが届かない のです。
そのため、WorkingDirectoryをするか、artisanにパスを通してやらないとならないのですね。

これはわかりにくかったです。

ターミナルから直接artisanを叩く場合と異なり、実行権限も変わってしまうため、chmod 777 artisanで実行権限を付与して処理が通るようになりました。


launchdは以下のブログを参考にいたしました。
ありがとうございました。

tweeeetyのぶろぐ的めも
Mac OS Xでlaunchdでcronのように定期実行するメモ - launchd.plistの作成とか http://tweeeety.hateblo.jp/entry/2015/01/06/215425


PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

竹澤 有貴,栗生 和明,新原 雅司,大村 創太郎
出版社:ソシム  発売日:2018-09-26

Amazonで詳細を見る

PHPフレームワーク Laravel入門

掌田津耶乃
出版社:秀和システム  発売日:2017-09-16

Amazonで詳細を見る

初めてのPHP

David Sklar
出版社:オライリージャパン  発売日:2017-03-18

Amazonで詳細を見る

この記事の著者 Webrow (うぇぶろう)
Web アプリ開発、 Web 顧問 エンジニア、WordPress サポートいたします。