【初心者用】CakePHPを学んでいこう⑨(バッチ)

前回はCakePHPでFormsテーブルの情報を取得する簡単なAPIを作成しました。

今回はバッチ処理とは何か、そしてCakePHPのCommandクラスを使ってバッチを作成・実行する方法をご紹介します。

1. 環境

OS:Windows11

XAMPP:8.0.25 (PHP8.0.25)

Composer:2.5.1

CakePHP:4.4

2. 「バッチ処理」とは?

情報システム関連の仕事にかかわっていると、一度は聞いたことがあるワードだと思います。

「バッチ」は英語の「Batch」が語源となっており、「束」「一回分」という意味になります。

つまり、一連の処理をあらかじめ登録しておき一括処理する方法、または一定の期間・データを一括処理する方法ということです。

Webへのアクセスはリアルタイムで行われますが(リアルタイム処理)、バッチは〇時~など時間を決め、まとめて処理を行います。


バッチ処理という言葉が使われ始めた歴史は、コンピュータ開発初期にまでさかのぼります。

プログラマがプログラムをパンチカードなどを使い、その紙をオペレーターに渡してオペレータがまとめてコンピュータに投入して処理していたことが由来だと言われています。


また、コンピュータの世界以外でも「バッチ」という言葉は使われています。

そこでも一定の量を、または一回ずつなどの意味で用いられています。

3. バッチ処理で何ができる?

上記で説明したように、バッチ処理は一定のデータをまとめて処理することができる方法です。

バッチ処理というのは、私たちの身近な場所で活躍しています。

例えば、企業であれば社員の給与計算のシステムや、売上管理のためその日の売上をまとめて処理するなどの例が挙げられます。

こういったバッチは、システム利用者の少ない夜間に処理される場合が多く、これを夜間バッチといいます。


4. バッチ処理のメリット


4-1. 1.時間や場所を問わずに処理できる

あらかじめ設定した時間に実行することができるため、実行する人などは必要なく好きな時に処理を実行することができます。

そのため、業務時間外といった時間も有効活用することができます。

4-2. 2.効率的に処理できる

一人が一つずつデータを処理していくことは現実的ではないので、バッチ処理で効率的にたくさんのデータを処理することができます。

また人が手作業で処理することでミスが発生する場合がありますが、バッチ処理であればプログラムに沿って処理されるため、ミスを発生させずに処理することができます。

4-3. 3.コンピュータリソースを最大限に活用できる

サーバといったコンピュータはその性能を20パーセントも引き出せていないと言われています。

このようなコンピュータリソースを最大限に活用することで、大量のデータの一括処理を行うことができます。

5. バッチ処理のデメリット


5-1. 1.データ処理の遅延

膨大なデータのバッチ処理によって、システムトラブルが発生したというニュースを聞いたことがあるのではないでしょうか。

ビッグデータの分析が盛んになったことで、一回での処理データ量が日々膨大になってきます。

データ量が多くなることで、処理に時間がかかり夜間に実行したバッチが朝までかかり、システムの動作に影響を及ぼす場合もあります。

5-2. 2.リアルタイムで処理状況を確認できない

前述の通り、バッチ処理は一括処理のためリアルタイムで処理や集計状況を確認することは出来ません。

処理のたびに確認する必要があるのなら、リアルタイム処理で実行します。

5-3. 3.ブラックボックス化

バッチ処理はそのほとんどが担当者が独自で作成して実行しているため、プログラムの意図が担当者以外には伝わりにくいというケースがあります。

もし、担当者が退職してしまうと問題が発生したとき、処理の意図を読み解きながら解析するなど余計な時間がかかってしまいます。

6. 実際にバッチ処理を作成してみよう

では、実際にCakePHPでバッチ処理を作成してみましょう。

CakePHPでは、「Command」クラスを使用します。

ちなみに前バージョンまで使用されていた、コマンドを実行するクラスとして「Shell」がありますが、こちらは今後のバージョンアップで削除されるため、「Command」を使用しましょう。


6-1. 1.コントローラーを作成する

Commandコントローラーはsrc/Commandのディレクトリ内に作成します。

../cake_pjt/src/Command/HogeCommand.php


out("テストです。");
    }
}
?>


まずは簡単に、コマンドを実行すると「テストです。」という文字列が表示される処理を作成します。

コマンドヘルパーの出力メソッドを使用して文字を出力しています。

参考:https://book.cakephp.org/4/ja/console-commands/input-output.html


これをコマンドプロンプトで実行してみましょう。

以下のディレクトリまで移動します。

C:\xampp\htdocs\cake_pjt\bin


「cake hoge」が実行するコマンドになります。

「hoge」の部分は、先ほど作成したCommandコントローラー名と一致するようにしてください。


無事表示されました。

6-2. 2.DBからデータを取得して出力する

では、このコマンドを実行したら「Forms」テーブルから指定したIDの人の名前を取得するようにしてみましょう。

コマンドにIDの引数を持たして、その値をもとにテーブルを取得できるようにします。

「HogeCommand.php」を修正します。


addArgument('id', ['help' => 'Forms id']); // ・・・②
        return $parser;
    }

    public function execute(Arguments $args, ConsoleIo $io)
    {
        // Fromsモデルを読み込む
        $this->loadModel('Forms'); // ・・・③
        $id = $args->getArgument('id');
        $data = $this->Forms->get($id); // ・・・④
        $io->out("こんにちは、{$data->name} さん。"); // ・・・⑤
    }
}
?>


①引数を受け取るため、「ConsoleOptionParser」クラスを読み込みます。

②「addArgument」で受け取る引数名を設定します。

③「Forms」モデルを読み込みます。

④IDをもとにデータを取得します。

⑤名前を出力します。


実行してみましょう。

引数を渡すときは、以下のようにします。

cake hoge 14


id = 14 の田中太郎さんが無事取得できました。

6-3. 3.定期バッチを作成する

「Forms」テーブルに「send_mail_flg:メール送信結果」というカラムを追加します。

お問い合わせした際メールを送信しますが、その際メール送信に成功したら「1」、失敗だと「0」となるとします。

このバッチでは毎日特定の時間にメール再送処理を実行するようにします。


それではメール再送処理を新たなCommandコントローラーに記載します。

../cake_pjt/src/Command/MailCommand.php


<?php

namespace App\Command;

use Cake\Console\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Mailer\Mailer;

class MailCommand extends Command
{
    /**
     * execute
     *
     */
    public function execute(Arguments $args, ConsoleIo $io)
    {
        $this->log('Batch Start', 'debug');
        // Fromsモデルを読み込む
        $this->loadModel('Forms');
        // メイン処理
        $this->_sendMail();
        $this->log('Batch End', 'debug');
    }

    /**
     * メイン処理
     *
     */
    private function _sendMail()
    {
        try {
            $cond = [
                'conditions' => [
                    'send_mail_flg' => 0
                ]
            ];
            $list = $this->Forms->find('all', $cond)->all();
            if (count($list) == 0) {
                $this->log('対象のデータがありませんでした', 'debug');
                return true;
            }

            foreach ($list as $data) {
                // メール送信
                $mailer = new Mailer();
                $mailer->setTransport('gmail');
                $mail = $data->mail;
                $name = $data->name;
                $message = $data->contents . "\n";
                $mailer
                    ->setEmailFormat('text')
                    ->setFrom(['送信メールアドレス@xxx.com' => 'テストサイト'])
                    ->setTo($mail)
                    ->setSubject('お問い合わせありがとうございます')
                    ->setViewVars(['name' => $name, 'message' => $message])
                    ->viewBuilder()
                    ->setTemplate('send');
                $mailer->deliver();

                // メールが送信出来たら「send_mail_flg」を更新
                $form = $this->Forms->newEmptyEntity();
                $form->id = $data->id;
                $form->send_mail_flg = 1;
                if (!$this->Forms->save($form)) {
                    $this->log(sprintf('send_mail_flgの更新に失敗しました[id : %s]', $data->id), 'error');
                    continue;
                }
            }
        } catch (\Throwable $th) {
            return false;
        }
        return true;
    }
}
?>


処理の流れは

 send_mail_flg が0のものを取得
    ↓
 メールを送信
    ↓
 send_mail_flg を1へ更新

となっています。


また、エラー等あればログファイルに出力するようにしています。

ログファイルは、XAMPP環境であれば以下に用意されています。

C:\xampp\htdocs\cake_pjt\logs


正しく動作するか確認したいため、まずは手動で実行しますが、その前にDBのデータで「send_mail_flg」の値を0にしておいてください。




実行すると、メール送信とDBが更新されることを確認してください。




これで処理は完成です。

それでは、毎日特定の時間に実行されるよう準備していきます。

本来はLinuxのCronを使用したかったのですが、私の環境ではXAMPP環境というのもあり、用意していなかったので、今回はWindowsの標準機能を利用していこうと思います。

必要なもの

 ①この処理を実行するコマンドが書いてある「.bat」ファイル
 ②バックグラウンドで実行するための「.vbs」ファイル
 ③タスクスケジューラ

①まず、以下の新しく作成したディレクトリにbatファイルを作成します。

C:\batch\sendMail.bat


C:\xampp\htdocs\cake_pjt\bin\cake.bat mail



②次に、vbsファイルを作成します。

C:\batch\sendMail_batch.vbs


Set ws = CreateObject("Wscript.Shell") 
ws.run "cmd /c ""C:\batch\sendMail.bat""", 0



③タスクスケジューラに実行ファイルを設定します。

タスクスケジューラを開き、「タスクの作成」をクリックします。


全般タブでは、実行する処理の名前を入力します。


トリガータブをクリックし、トリガーを新規追加します。

時間はお好きな時間をセットしておいてください。


操作タブをクリックし、操作を新規追加します。

「プログラム/スクリプト」に「wscript.exe」、引数の追加には先ほど作成したvbsのパス「C:\batch\sendMail_batch.vbs」を入力してください。


あとはOKボタンを押して準備完了です。

設定した時間に実行されれば、メール再送信バッチの完成です!

7. まとめ

今回はバッチ処理についての説明と、CakePHPでのバッチ処理の実装について紹介しました。

これまでバッチ処理を作成してきた経験はありますが、今回調べることでバッチ処理についてより理解が深まりました。

CakePHPでのバッチ処理は、用意されているCommandクラスを使用すれば簡単に実装できますので、ぜひ挑戦してみてください。