📄

Laravel で簡易スクリプトを実行する仕組みを作る

2021/10/15に公開

はじめに

Laravel を使用しているプロジェクトで、1年に一度、データベースに年次データを追加してほしいという依頼が来ていました。
従来は SQL で追加していましたが、何点か問題や改善したい点がありました。

  • 実行した履歴を残したい
  • EloquentPHP の機能を使いたい
  • 一度実行したら再度実行できないようにしたい
  • 特定のサーバの特定のユーザでしか実行できないようにしたい

これを解決するために、Artisan コンソールの機能を拡張してスクリプトを実行できる仕組みを作成しました。
https://readouble.com/laravel/8.x/ja/artisan.html

環境

  • PHP 8.0.x
  • Laravel 8.x

作業工程

実行履歴を残すテーブルを作成

scripts テーブルを作成します。
このテーブルで実行履歴を管理します。
イメージとしては migrations テーブルと似たような感じです。
https://gist.github.com/taisukearase/0489cc2e029cd8ff1ab5a3a826ca40e2#file-2021_10_14_000000_create_scripts-php

Scripts フォルダを作成し、Artisan コマンドの配置場所として登録

class Kernel extends ConsoleKernel
{
    /**
     * Register the commands for the application.
     */
    protected function commands()
    {
        $this->load([
            __DIR__ . '/Commands',
            __DIR__ . '/Scripts', // ここに追加
        ]);

        require base_path('routes/console.php');
    }
}

継承元となる抽象クラスを作成

Artisanコンソールで実行される handle() をオーバーライドし、実行前と実行後の処理をフックできるようにしました。
実行前の処理では以下の機能を実現しています。

  • 一度実行したら再度実行できないようにしたい
  • 特定のサーバの特定のユーザでしか実行できないようにしたい

実行後には scripts テーブルに実行記録を残しています。
https://gist.github.com/taisukearase/0489cc2e029cd8ff1ab5a3a826ca40e2#file-script-php

スクリプトファイルを作成

後は Script 抽象クラスを継承して、スクリプトファイルを作成していきます。

<?php

namespace App\Console\Scripts;

class SampleScript20211014 extends Script
{
    /**
     * The name and signature of the console command.
     */
    protected string $signature = 'scripts:sample-script-20211014';

    /**
     * The console command description.
     */
    protected string $description = 'サンプルスクリプト';

    protected function script()
    {
        \DB::beginTransaction();

	// いろいろな処理

        \DB::commit();

        $this->info('Finish.');
    }
}

テストを作成

抽象クラスである Script.php のテストです。
getMockForAbstractClass() を使ってモックしています。
https://gist.github.com/taisukearase/0489cc2e029cd8ff1ab5a3a826ca40e2#file-scripttest-php

さいごに

tinker の代わりとして使え、実行ファイルも残るので改善ができたと思います。
ただヒューマンエラーは防げないので、乱用しないこと、コードをレビューしてもらうこと、データの追加や変更は必ずバックアップを取ったのち行うことも引き続き大事です。

ご安全に!

Discussion