チュートリアル2~データベースの利用とアクションの作成

さて、前の章ではテンプレートやJavaScript・スタイルシートのコンパイルとバンドル機能を使ってTODOアプリのタスク一覧画面を作成しました。

この章ではPine Frameworkのマイグレーション機能を使い、データベースにテーブルを作成してタスクを記録できるようにします。その後で新規タスクを作成するアクションを作成していきます。

bambooコマンドを使ったデータベースのマイグレーション

TODOアプリでは複数のタスクを記録するデータベーステーブルが欠かせません。ここではまず、tasksテーブルを作成してタスクを記録できるようにしてみましょう。

テーブルの作成にはまず、/sites/pine_site/assets/tabledefinitions内にテーブル定義ファイルを作成します。

テーブル定義ファイルの雛形は/documents/TableDefinitionSample.ymlです。これをコピーしてリネームし、テーブル定義ファイルを作成していきましょう。ファイル名はアッパーキャメルケース単数形で命名します。

今回は、Task.ymlとします。

テーブル定義ファイル

ファイルを作成したらこのファイルを開き、テーブル定義を記述していきます。

---
type:               table
table_comment:      タスク管理テーブル
logging:            true
columns:
    task_id:
        type:       BIGINT
        collation:  utf8mb4_general_ci
        allownull:  false
        default:    null
        ai:         true
        extra:      null
        other:      null
        index:      null
        comment:    タスクID
    title:
        type:       VARCHAR(255)
        collation:  utf8mb4_general_ci
        allownull:  false
        default:    null
        ai:         false
        extra:      null
        other:      null
        index:      null
        comment:    タスク名
    status:
        type:       VARCHAR(64)
        collation:  utf8mb4_general_ci
        allownull:  false
        default:    null
        ai:         false
        extra:      null
        other:      null
        index:      null
        comment:    状態
    timelimit:
        type:       DATETIME
        collation:  utf8mb4_general_ci
        allownull:  false
        default:    null
        ai:         false
        extra:      null
        other:      null
        index:      null
        comment:    タスク期限
primary:
    - task_id
unique:     null
index:      null
fulltext:   null
check:      null
foreign:    null

task_idはプライマリキーであり、ai: trueとすることで、AUTO_INCREMENTのカラムにしてあります。

テーブル定義ファイルの記述が終わったら、bambooコマンドを使ってテーブルを生成します。

bamboo pine_site make datamapper Task

上記はbambooコマンドを使ってpine_siteというサイトに定義されている設定で、tasksテーブルとそのエンティティであるTaskデータモデルを生成する、という意味です。

上記コマンドを実行すると、

[root@localhost command]# bamboo pine_site make datamapper Task
Target: Task
* Created files
/var/www/pine_site/sites/pine_site/assets/datamodels/trunk/Task.191212_101606.back
/var/www/pine_site/sites/pine_site/assets/datamodels/Task.php

* Deleted files
/var/www/pine_site/sites/pine_site/assets/datamodels/Task.php

* Created tables
tasks
zzz_tasks

[Success] メソッドは正常に実行されました。

といったメッセージが表示されるはずです。データベースのスキーマを確認すると、

作成されたテーブル情報1

作成されたテーブル情報2

のようにテーブルが作成されているはずです。

また、このテーブルのエンティティであるTask.phpというデータモデルが、/sites/pine_site/assets/datamodels/内に生成されているでしょう。

生成されたDataModelファイル

このファイルの中身は、

declare(strict_types=1);
namespace pine\bamboo;
use pine as pine;

class Task extends BaseDataModel
{

    //*****************************************/
    // const
    //  BOOLEAN = 'boolean',
    //  INTEGER = 'integer',
    //  DOUBLE  = 'double',
    //  FLOAT   = 'double',
    //  STRING  = 'string',
    //  LOB     = 'string',
    //  DATETIME = 'dateTime';
    //  DATE     = 'date';
    //*****************************************/

    protected $table_type   = DataModel::STRICT;
    protected $logging      = true;

    protected $schema  = [
        "task_id"                   => parent::INTEGER,
        "title"                     => parent::STRING,
        "status"                    => parent::STRING,
        "timelimit"                 => parent::DATETIME
    ];

    protected $primary = ["task_id"];

    public function isValid(): bool
    {
        return true;
    }

}

となっています。

これでタスクの登録が行えるようになりました。では、タスクを登録するアクションを作成していきましょう。

新規タスク登録アクションの作成

ここでは、タスク一覧画面の『新規タスクの作成』ボタンをクリックした後に遷移してくる、新規タスク情報入力画面を作成します。

この場合のリクエストメソッドはGETで、アクション名はnew_taskとします。

pineコマンドを以下のように実行することで、新規アクションとそれに関係するファイルを自動生成できます。

pine pine_site make action home new_task get html

新規アクションの作成

生成されたファイル

これで、GETメソッドによるnew_taskアクションの実行が可能になりました。

ブラウザのURLから/home/new_taskにアクセスしてみましょう。

生成されたファイル

新規アクションに関係するファイルが作成されたら、タスク一覧画面と同様にテンプレートファイルにHTMLを記述し、スタイルを定義します。

/sites/pine_site/module/home/views/templates/get_newtask.twig
<form id="register-task" action="/home/register" method="POST">
    <table>
        <tr>
            <th>タスク名</th>
            <td><input type="text" name="title"></td>
        </tr>
        <tr>
            <th>期限</th>
            <td><input type="text" name="timelimit"></td>
        </tr>
    </table>
    {{ site_hidden_ticket_raw() }}
    <button type="submit">新規タスクを登録する</button>
</form>

上記の{{ site_hidden_ticket_raw() }}という記述は、ワンタイムチケットのhiddenフィールドを出力するためのTwig Functionです。

Pine Frameworkでは二重投稿やCSRF対策のためのワンタイムチケット検査がデフォルトでサポートされており、通常、POSTでのアクションへのアクセスの場合は正しいワンタイムチケットが送信されなかった場合、エラーとなってModelの実行を行いません。

このsite_hidden_ticket_raw()というファンクションは

/sites/pine_site/module/__com/views/SiteCommonView.phpのSiteCommonTwigFunctionsクラス内

に定義されていますのでご確認ください。

新しく作成されたget_newtask.scssはこのままではまだ認識されないので、import.scss内でインポートしてください。

/sites/pine_site/module/home/views/stylesheets/impoert.scss
@import "get_index/get_index.scss";
@import "get_newtask/get_newtask.scss";    // 追記

スタイルシートを記述します。

/sites/pine_site/module/home/views/stylesheets/get_newtask/get_newtask.scss
form#register-task {
    table {
        width:      100%;

        input {
            width:  100%;
        }
    }
    button {
        width:      100%;
    }
}

新規タスク登録フォーム

これで、新規タスク登録フォームが完成しました。

get_newtask.twigテンプレート内の<form/>のactionに/home/registerを設定してあります。methodはPOSTです。

これは、POSTメソッド動作するregisterアクションでフォームからのリクエストを受け取り、新しいタスクを登録する事を想定しています。

では、GETメソッドでのnew_taskアクションを作成した時と同様に、POSTメソッド用のregisterアクションを作成しましょう。

pine pine_site make action home register post html

です。

新規タスク登録アクションの生成

生成されたファイル

フォームからの入力検査用のUMEファイルを設定する

今回はPOSTメソッドでフォームから情報が送られてくるので、これを受け取って検査するバリデーター『UME』を定義します。

UMEファイルは自動生成されており、/sites/pine_site/module/home/logic/umes/PostRegisterUME.phpがそれです。

このクラスのgetValidationDefinitions()関数内に、フォームから送られてくる情報に関する定義を記述しましょう。

/sites/pine_site/module/home/logic/umes/PostRegisterUME.php
    protected function getValidationDefinitions(): array
    {
        return [
            "title" => [
                "name" => "タスク名", "type" => "text", "min" => 1, "max" => 255, 
                "auto_correct" => false, "trim" => pine\UME::TRIM_ALL, "null_byte" => false,
                "method" => pine\UME::POST, "require" => true
            ],
            "timelimit" => [
                "name" => "タスク期限", "type" => "datetime", "min" => 1, "max" => 19, 
                "auto_correct" => true, "trim" => pine\UME::TRIM_ALL, "null_byte" => false,
                "method" => pine\UME::POST, "require" => true
            ],
        ];
    }

以上で、バリデーション定義が完了です。

このように入力値に対するバリデーション定義を記述すると、Actionが呼び出された際、Modelの実行に先立って自動的に入力情報のバリデーション処理が適切に行なわれます。

バリデーションによって入力が不適合であった場合はModelの実行は行なわれず、DTOに不適合に関する情報が記録されて処理がViewに渡されます。

入力が適合した場合のみModelの実行を行うためのAction::logic()関数が実行されます。

タスクをデータベースに登録するModelを作成する

ここでは、入力が適合した場合の新規タスク登録処理モデルを作成してみましょう。

まずpineコマンドで、新規タスクを登録するためのModelであるRegisterNewTaskというモデルを作成します。

pine pine_site make model home RegisterNewTask

新規タスク登録用モデルの生成

生成されたモデル

このモデルに、新規タスクを登録する処理を記述しましょう。

/sites/pine_site/module/home/logic/models/RegisterNewTaskModel.php
declare(strict_types=1);
namespace pine\app;
use pine as pine;
use pine\bamboo as bamboo;

class RegisterNewTaskModel extends SiteCommonModel
{
    public function exec(\pine\Dto $dto): bool
    {
        $t  = new bamboo\Task();
        $t->title       = $dto->R["title"];
        $t->status      = "not-started";
        $t->timelimit   = $dto->R["timelimit"];
        $this->bamboo->insert($t)->execute();

        return true;
    }
}

たったこれだけです。

DTOのメンバ変数Rには、ブラウザから送られたヴァリデーション・整形済みのリクエスト情報が格納されています。

ですから、先ほど作成したテーブルのエンティティであるTaskというDataModelのインスタンスを生成し、そのプロパティに値をセットしてから、Bambooクラスのインスタンス参照である$this->bambooにTaskオブジェクトをINSERTします。

Actoin::logic()内で、上記モデルを実行する

あとはこのRegisterNewTaskModelをregisterアクションで実行するように、PostRegister::logic()内で呼び出しを行います。

/sites/pine_site/module/home/logic/actions/PostRegister.php
    protected function logic(\pine\Dto $dto): bool
    {
        // 新規タスクの登録
        (new RegisterNewTaskModel())->exec($dto);

        return true;
    }

さて、では/home画面から、新規タスクを登録してみましょう。

無事タスクが登録されると下記のような画面が表示され、データベースにレコードが追加されています。

新規タスク登録完了

テーブルに作成されたレコード

正常に登録されなかったりエラーが出た時は…?

まだエラー処理について記述を行っていないため、コードの記述間違い等があるとエラー画面やエラーメッセージが表示されると思います。

テーブルに作成されたレコード

こうした場合は、/sites/pine_site/__logs/ディレクトリ内に日時エラーログが出力されています。こちらを確認して、問題点を修正してください。

また、ヴァリデーションエラーなどが発生している場合、Viewにはエラー情報がDTOに付加されて渡ってきています。

View::draw()メソッド
    /**
     * 画面表示
     *   SiteCommonView::site_common()->CommandCommonView::cmd_common()->View::draw()
     * 
     * @param   \pine\Dto       $dto
     * @param   bool            $action_result
     * @return  bool
     */
    protected function draw(\pine\Dto $dto, bool $action_result): bool
    {
        if($dto->on_error === true) { return $this->site_apology($dto); }       // システムエラー発生時

        return ($dto->response->status === true)
                            ? $this->normal($dto)
                            : $this->normal($dto)
                            ;
    }

上記の$dto->response->statusがfalseの場合、何らかのエラーが発生しています。

この場合は、$dto->response->message、及び$dto->response->verrorプロパティを検査することで、何が起きているのか確認できます。

例えば、

echo $dto->response->message . "<br>\n";
print_r($dto->response->verror);
echo "<br>\n";

といったコードでエラー情報を確認できます。

if($dto->on_error === true) { return $this->site_apology($dto); }       // システムエラー発生時

にあるsite_apology()メソッド実装されていません

これは通常は、サイトで共通の致命的エラー画面が表示されることを想定しています。

/sites/[site_id]/module/__com/views/SiteCommonView.phpの中にsite_apology()関数を作成して、エラー画面を表示する処理を記述してください。

また、エラーログに出力されている

[unkown] 2019/12/12 14:39:14 #EAU345PBP14

といったタイムスタンプの横の#で始まる文字列は、エラーが発生した時間を元に自動生成されるエラートラッキング番号です。

この情報は$dto->response->tracking_numberとしてViewに引き渡されており、__logsに出力されるログ情報と対応しています。

トラッキング番号のフォーマットルールは、

アルファベットは A~Zまでのうちゼロと間違いやすいO(オー)を除いた25文字の文字列を利用

フォーマット: ログ種別@アルファベット1文字
                  + 年@アルファベット2文字
                  + 年間通算日@数字3文字 
                  + 時間@アルファベット1文字 
                  + 分@アルファベット2文字 
                  + 秒@数字2文字

となっています。

エラーの解決などにご利用ください。

この章はここまでです。次の章では今回登録したタスクについて/home画面の一覧に反映するのと、タスク情報の変更を行えるように修正します。

>> チュートリアル3~登録されたタスクの一覧表示反映とタスクの編集機能