Ground Sunlight

Windowsで作る - Webプログラミングの開発環境(PHP)

ユーザ用ツール

サイト用ツール


サイドバー

メインメニュー

道具箱

リポジトリ編

フレームワーク編

Webサービス編

自然言語処理環境編

メタ
リンク


このページへのアクセス
今日: 1 / 昨日: 1
総計: 19

apricot:usage:ja:controller
編集中

Apricot コントローラ

ルーティングとコントローラ(Auto Wiring)

ルーティング

以下のように、config/routes.php にスタブコントローラのルートを追加して下さい。

/apricot/config

routes.php
<?php
/**
 * This file contains callback for route definitions.
 */
return function (FastRoute\RouteCollector $r)
{
    /** @var string $base route base path */
    $base = Apricot\Application::getInstance()->getRouteBase();
 
    // Creates a route group with a common prefix.
    $r->addGroup($base, function (FastRoute\RouteCollector $r) use($base)
    {
        // Authentication
        $r->get ('/login', 'AuthController@showForm');
        $r->post('/login', 'AuthController@login');
        $r->get ('/logout', 'AuthController@logout');
 
        // User
        $r->get ('/users', 'UserController@index');
        $r->get ('/user/create', 'UserController@create');
        $r->post('/user/insert', 'UserController@insert');
        $r->get ('/user/{id:\d+}/edit', 'UserController@edit');
        $r->post('/user/{id:\d+}/update', 'UserController@update');
        $r->post('/user/{id:\d+}/delete', 'UserController@delete');
 
        // Home
        $r->get ('/home', 'HomeController@index');
        $r->get('[/]', function() use($base){
            header("Location: " . $base.'/home');
        });
 
        // Stub
        $r->get('/stub[/{no:\d+}]', 'StubController@index');
    });
};

ルーティング設定に関してはFastRouteを参照して下さい。


コントローラ

上述のアプリ用のコントローラベースを継承してスタブコントローラを作ります。

/apricot/app/Controllers

StubController.php
<?php
namespace App\Controllers;
 
use App\Foundation\Container;
use App\Foundation\Controller;
 
/**
 * Stub Controller
 */
class StubController extends Controller
{
    /**
     * Index Page for this controller.
     *
     * @return \Apricot\Foundation\Response
     */
    public function index(int $no=null)
    {
        $title = "Stub {$no}";
 
        /*
         * Example for Container
         * @var \App\Models\User $user
         */
        $user = Container::get('user');
        $userCount = count($user->findAll());
        $messages[] = "Number of registered users : {$userCount}";
 
        return render('stub',['title'=>$title,'messages'=>$messages]);
    }
}
  • スタブコントローラにindexアクションを実装してる例です
  • ボイラープレートのrender()関数を呼び出してレスポンスをレンダリングしています
  • render(string $view=null, array $variables=[])
    1. $view : テンプレート名
      上例では assets/views/stub.blade.php がテンプレートファイルになります
    2. $variables : テンプレート変数の連想配列
      上例では [$title,$message] をテンプレートに渡しています。


Auto Wiring

以下は、現状のユーザコントローラのコンストラクタです。

    public function __construct()
    {
        // モデル
        $this->user = new User();
        ...
    }

このコードを Auto Wiring によって、次のようにすることがここでの目的です。

    public function __construct(User $user)
    {
        // モデル
        $this->user = $user;
        ...
    }

Auto Wiring は、コンストラクタの型ヒントによって生成するオブジェクトを判断します。(オブジェクト型でない引数には対応していません)



インターセプター

インターセプター とはアクションの前処理の事です。ミドルウェアと同じでリクエストを中断してレスポンスオブジェクトを生成することもできますが、アクションの後処理はできません。これを図示すると以下のようになります。

インターセプター構造

上図から分かるようにミドルウェアパイプラインから見ると、インターセプターはアクションに含まれます。ミドルウェアとの一番の違いは、ミドルウェアは基本的に全てのコントローラを対象としているのに対し、インターセプターは、各コントローラで独自に設定ができるという点です。

インターセプターの主な用途としては入力データの検証(バリデーション)、入力データのフィルタリングや変換です。インターセプターを作ることで、すっきりしたアクションを作ることができます。

インターセプターの使用

インターセプターの登録は、コントローラーのコンストラクタで intercept() メソッドを使って行います。インターセプターにはクロージャー型とメソッド型の両方が使用できます。簡単なバリデーション処理ならクロジャー型で以下のように書きます。

class FooController extends Controller
{
    public function __construct()
    {
        // インターセプターの登録
        $this->intercept('action', function(Controller $controller, int $id)
        {
            $inputs = Input::all();
            ...
        });
    }
    ...
}

インターセプターに渡される引数は、第1引数に、コントローラのインスタンスが、その後にアクションと同じの引数が続きます。また、インターセプターはレスポンスオブジェクトを返して以降のアクションを中止することができます。

インターセプターにメソッド型を使用する場合は、以下のように 'クラス名@メソッド名' の形式で指定します。

class FooController extends Controller
{
    public function __construct(User $user)
    {
        // インターセプター登録
        $this->intercept('action1', 'FooInterceptor@action1');
        $this->intercept('action2', 'FooInterceptor@action2');
        ...
    }
    ....
}

Apricotでは、インターセプターを配置する場所は以下に決めれられいます。

aprocot/app/Controllers/Interceptors

結果として、インターセプタークラスの名前空間は \\App\\Controllers\\Interceptors になります。

自分自身( $this )のメソッドを指定する場合は、'@メソッド名' のように指定します。但し、自分自身のメソッドでも public でないとアクセスできません。


バリデーション

Apricotでは Valitron を使いサーバ側のバリデーションで使用します。以下では、Valitron の設定方法について述べます。詳細は Valitron の README をご覧下さい。

言語ファイル

Valitronの言語ファイルを以下の手順でカスタマイズします。

  1. vendor/vlucas/valitron/lang/Valitron/ja.php を以下にコピーします
    • assets/lang/ja/vlucas.valitron.php
  2. コピーした vlucas.valitron.php を以下のように変更します。

/apricot/assets/lang/ja

vlucas.valitron.php
<?php
 
return array(
    'required'      => "を入力してください",
    'equals'        => "は「%s」と同じ内容を入力してください",
    'different'     => "は「%s」と異なる内容を入力してください",
    'accepted'      => "に同意してください",
    'numeric'       => "は数値を入力してください",
    'integer'       => "は半角数字で入力してください",
    'length'        => "は%d文字で入力してください",
    'min'           => "には%sより大きな値を入力してください",
    'max'           => "には%sより小さな値を入力してください",
    'listContains'  => "には選択できない値が含まれています",
    'in'            => "には選択できない値が含まれています",
    'notIn'         => "には選択できない値が含まれています",
    'ip'            => "はIPアドレスの書式として正しくありません",
    'email'         => "の書式が正しくありません", /* 修正 */
    'url'           => "はURLの書式として正しくありません",
    'urlActive'     => "はアクティブなドメインではありません",
    'alpha'         => "は半角英字で入力してください",
    'alphaNum'      => "は半角英数字で入力してください",
    'slug'          => "は半角英数字、もしくは「-」「_」の文字で入力してください",
    'regex'         => "の書式が正しくありません",
    'date'          => "の書式が正しくありません", /* 修正 */
    'dateFormat'    => "は「%s」の書式で日付を入力してください",
    'dateBefore'    => "は「%s」以前の日付を入力してください",
    'dateAfter'     => "は「%s」以後の日付を入力してください",
    'contains'      => "は「%s」を含んでいなければいけません",
    'boolean'       => "は真偽値である必要があります",
    'lengthBetween' => "は%d%d文字で入力してください",
    'creditCard'    => "はクレジットカード番号の書式として正しくありません",
    'lengthMin'     => "は%d文字以上入力してください",
    'lengthMax'     => "は%d文字以内で入力してください",
    'instanceOf'    => "は「%s」のインスタンスではありません",
    'ascii'         => "は半角文字で入力して下さい", /* 追加 */
    'unique'        => "は既に使用されています", /* 追加 */
);
  • 'email' と 'date' のテキストを変更します
  • 'ascii' と 'unique' のテキストを追加します


uniqueルール

uniqueルールはデーターベースのユニーク制約の為のルールです。

使用法: rule('unique',$unique_column_name, $table_name, $id_column_name)

  • $unique_column_name — ユニークカラム名
  • $table_name — テーブル名
  • $id_column_name — IDカラム名
// 使用例
$validator = new \Valitron\Validator($inputs);
$validator->rule('unique','account', 'user', 'id');


ValidatorErrorBagクラス


使用例:ユーザインターセプター

インターセプターメソッドのシグネチャには次の規則があります:

  • interceptorMethod( Controller $controller, [, mixed $… ] ):mixed
    1. $controller — インターセプターを呼び出したコントローラ
    2. $… — インターセプターを呼び出したアクションメソッドと同じ引数
    • 戻り値
      1. 成功の場合 — void を返す
      2. 失敗の場合 — Responseオブジェクトを返す

以下にユーザインターセプター( UserInterceptor )を示します。

/apricot/app/Controllers/Interceptors

UserInterceptor.php
<?php
namespace App\Controllers\Interceptors;
 
use Apricot\Input;
use App\Foundation\Controller;
use App\Foundation\ValidatorErrorBag;
 
/**
 * User Interceptor
 */
class UserInterceptor
{
    /**
     * Interceptor for insert method.
     *
     * @return void|\Apricot\Foundation\Response return Response if failed
     */
    public function insert(Controller $controller)
    {
        $inputs = Input::all();
 
        // Validation
        $v =(new \Valitron\Validator($inputs))
        ->rule('required', ['account','password'])
        ->rule('alphaNum','account')
        ->rule('unique','account','user','id')
        ->rule('ascii','password')
        ->rule('equals','password','password_confirmation')
        ->rule('email', 'email')
        ->labels(inputLabels('messages.user.create'));
 
        if(!$v->validate())
        {
            $errorBag = new ValidatorErrorBag($v->errors());
            return redirect(back())->withInputs()->withErrors($errorBag);
        }
 
        // Removes unnecessary inputs
        Input::remove('password_confirmation');
    }
 
    /**
     * Interceptor for update method.
     *
     * @param int $id
     * @return void|\Apricot\Foundation\Response return Response if failed
     */
    public function update(Controller $controller, int $id)
    {
        $inputs = Input::all();
 
        // Validation
        $v =(new \Valitron\Validator($inputs))
        ->rule('ascii','password')
        ->rule('equals','password','password_confirmation')
        ->rule('email', 'email')
        ->labels(inputLabels('messages.user.create'));
 
        if(!$v->validate())
        {
            $errorBag = new ValidatorErrorBag($v->errors());
            return redirect(back())->withInputs()->withErrors($errorBag);
        }
 
        // Removes unnecessary inputs
        Input::remove('password_confirmation');
    }
}

バリデーションの手順は以下の通りです。

  1. Input::all()でフォームデータを取得します
  2. Validatorの生成
    • rule()メソッドで検証ルールを適用します
    • labels()メソッドでエラーメッセージで使う項目名を設定します
  3. Validatorのvalidate()メソッドで検証します
    • 検証エラーの時
      • withInputs()で入力変数をフラッシュ変数に保存します
      • withErrors()でバリデーションのエラーバッグをフラッシュ変数に保存します
      • redirect()で前画面にリダイレクトするResponseオブジェクトをします
    • 検証成功の時
      • 不要になったバリデーション用のフォームデータを削除します

検証ルール

  • insert() : レコード挿入時
    • required — 必須入力(account, password)
    • alphaNum — 半角英数入力(account)
    • unique — ユニーク制約(account)
    • ascii — 半角入力(password)
    • equals — 確認入力(password = password_confirmation)
    • email — メールアドレス(email)
  • update() : レコード更新時
    • ascii — 半角入力(password)
    • equals — 確認入力(password = password_confirmation)
    • email — メールアドレス(email)


トランザクション

ユーザ登録画面にトランザクションの機能を追加します。トランザクションを作るか否かはアクション毎に設定できるようにします。また、トランザクション機能を追加することによりアクションでスローされる ApplicationException をキャッチして(エラー画面に遷移することなく)入力画面でエラーメッセージを表示できるようになります。アクションでスローされるApplicationException には以下のものがあります。

  • 楽観的ロック例外(OptimissticLockException)
  • 対象レコードが存在しない(レコード更新時および削除時)
  • その他のアクションで発生するException
    ( パースエラーなどの Error は対象外です )

通常、トランザクションは更新系の処理で設定するするので、参照系で発生した全ての例外は集約エラーハンドラで処理されエラー画面が表示されます。この状況を避けたい場合は、次の何れかの選択になるでしょう。

  • 参照系アクションもトランザクションを設定する
  • アクション内のcatchブロックでエラーメッセージ付きの入力画面をレンダリングする


使用例:ユーザコントローラ

アクションのトランザクション処理を有効にしたい場合は、コントローラのコンストラクタの中でControllerクラスのtransactional()メソッドを使って、以下のようにします。

  • transactional( 'action1', … );

/apricot/app/Controllers

UserController.php
<?php
namespace App\Controllers;
 
use App\Exceptions\ApplicationException;
use App\Foundation\Controller;
use App\Models\User;
use Core\Input;
 
/**
 * ユーザコントローラ
 */
class UserController extends Controller
{
    ...
 
    /**
     * ユーザコントローラの生成
     */
    public function __construct()
    {
        // モデル
        $this->user = new User();
 
        // トランザクションアクション登録
        $this->transactional('insert','update','delete');
    }
 
    ...
}
  • transactional()を使って3つのアクション( insert(), update(), delete() )のトランザクション処理が有効になるように設定しています。


コメント

コメントを入力. Wiki文法が有効です:
 
apricot/usage/ja/controller.txt · 最終更新: 2020/08/12 10:08 by y2sunlight