内容へ移動
Ground Sunlight
Windowsで作る - PHPプログラミングの開発環境
ユーザ用ツール
ログイン
サイト用ツール
検索
ツール
文書の表示
以前のリビジョン
バックリンク
最近の変更
メディアマネージャー
サイトマップ
ログイン
>
最近の変更
メディアマネージャー
サイトマップ
トレース:
apricot:ext:interceptor
この文書は読取専用です。文書のソースを閲覧することは可能ですが、変更はできません。もし変更したい場合は管理者に連絡してください。
====== Apricot インターセプター ====== --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-05-25// [[apricot:top|Apricot に戻る]] 関連記事 * [[apricot:configuration|Apricot プロジェクトの作成]] * [[apricot:public|Apricot 公開フォルダ]] * [[apricot:core:top|Apricot コア]] * [[apricot:app:top|Apricot アプリ]] * Apricot 拡張 * [[apricot:ext:middleware|Apricot ミドルウェア]] * [[apricot:ext:access-log|Apricot アクセスログ]] * [[apricot:ext:csrf|Apricot CSRF対策]] * [[apricot:ext:user-auth|Apricot ユーザ認証]] * [[apricot:ext:basic-auth|Apricot 基本認証]] * [[apricot:ext:session-auth|Apricot セッション認証]] * Apricot インターセプター * [[apricot:ext:di-container|Apricot DIコンテナー]] インターセプター とはアクションの前処理の事です。ミドルウェアと同じでリクエストを中断してレスポンスオブジェクトを生成することもできますが、アクションの後処理はできません。これを図示すると以下のようになります。 === インターセプター構造 === {{:apricot:ext:ext-fig02.svg?nolink&800}} 上図から分かるようにミドルウェアパイプラインから見ると、インターセプターはアクションに含まれます。ミドルウェアとの一番の違いは、ミドルウェアは基本的に全てのコントローラを対象としているのに対し、インターセプターは、各コントローラで独自に設定ができるという点です。 インターセプターの主な用途としては入力データの検証(バリデーション)、入力データのフィルタリングや変換です。インターセプターを作ることで、すっきりしたアクションを作ることができます。 \\ ===== ベースコントローラの変更 ===== インターセプターの仕組みを実装するために、コアのベースコントローラ( [[apricot:core:base-controller#BaseControllerクラス|BaseController]] )を以下のように変更します。 {{fa>folder-open-o}} ** /apricot/core/Foundation ** <code php BaseController.php> <?php namespace Core\Foundation; /** * Request Controller Class (Controller Base) */ class BaseController { /** * The interceptors registered on the controller. * @var array */ protected $interceptors = []; /** * Register interceptors on the controller. * * @param string $actionName * @param array|mixed $interceptors array or arguments list */ protected function intercept($actionName, $interceptors) { if ($actionName == \Core\Application::getInstance()->getActionName()) { $interceptor_arr = is_array($interceptors) ? $interceptors : array_slice(func_get_args(),1); $this->interceptors = array_merge($this->interceptors , $interceptor_arr); } } /** * Call real Action * @param string $actionName * @param array $params * @return \Core\Foundation\Response */ protected function callAction($actionName, $params) { return call_user_func_array(array($this, $actionName), $params); } /** * Invoke Action * @param string $actionName * @param array $params * @return \Core\Foundation\Response */ public function invokeAction($actionName, $params) { // Interceptor parameters $iparams = array_merge(array('_controller'=>$this), $params); // Invoke Interceptor $response = null; foreach($this->interceptors as $interceptor) { if (is_callable($interceptor)) { // Case of callable $response = call_user_func_array($interceptor, $iparams); } elseif(strpos($interceptor,'@')!==false) { // Case of Controller/Action list($class, $method) = explode('@', $interceptor); if (empty($class)) { $instance = $this; } else { $class = "\\App\\Controllers\\Interceptors\\{$class}"; $instance = new $class; } // Call interceptor $response = call_user_func_array(array($instance, $method), $iparams); } if ($response instanceof \Core\Foundation\Response) { return $response; } } // Call Action return $this->callAction($actionName, $params); } } </code> ''callAction()'' メソッドに変更はありません。''intercept()'' メソッドを追加し、''invokeAction()'' メソッドを変更します。 * ''intercept($actionName, $interceptors)'' メソッド * リクエストされているアクション(Application::getActionName())が対象となります。 * アクションにインターセプターを追加します。 * BaseControllerは、インターセプターを配列( <nowiki>$this->interceptors</nowiki> )で管理しています。 * ''invokeAction($actionName, $params)'' メソッド * [[apricot:ext:middleware#middlewarepipeline_クラス|ミドルウェアパイプライン]]からはこのメソッドが呼び出されます。 * インターセプターを登録順に実行し最後に実際のアクションを実行します。 * インターセプターがResponseインスタンスを返した場合、処理は中止され、そのインスタンスをアクションのレスポンスとして返します。 インターセプターの使い方は、[[#インターセプターの使用|次項]]をご覧ください。 \\ ===== インターセプターの使用 ===== インターセプターの登録は、コントローラーのコンストラクタで ''intercept()'' メソッドを使って行います。インターセプターにはクロージャー型とメソッド型の両方が使用できます。簡単なバリデーション処理ならクロジャー型で以下のように書きます。 <code php> class FooController extends Controller { public function __construct() { // インターセプターの登録 $this->intercept('action', function(Controller $controller, int $id) { $inputs = Input::all(); ... }); } ... } </code> インターセプターに渡される引数は、第1引数に、コントローラのインスタンスが、その後にアクションと同じの引数が続きます。また、インターセプターはレスポンスオブジェクトを返して以降のアクションを中止することができます。 インターセプターにメソッド型を使用する場合は、以下のように <nowiki>'クラス名@メソッド名'</nowiki> の形式で指定します。 <code php> class FooController extends Controller { public function __construct(User $user) { // インターセプター登録 $this->intercept('action1', 'FooInterceptor@action1'); $this->intercept('action2', 'FooInterceptor@action2'); ... } .... } </code> Apricotでは、インターセプターを配置する場所は以下に決めれられいます。 <code> aprocot/app/Controllers/Interceptors </code> 結果として、インターセプタークラスの名前空間は ''\\App\\Controllers\\Interceptors'' になります。 自分自身( $this )のメソッドを指定する場合は、''<nowiki>'@メソッド名'</nowiki>'' のように指定します。但し、自分自身のメソッドでも public でないとアクセスできません。 \\ ==== 認証コントローラ ==== 認証コントローラのバリデーションをクロージャ型のインタセプターとして再実装します。[[apricot:ext:session-auth|セッション認証]]の章で作った[[apricot:ext:session-auth#authcontroller_クラス|AuthController クラス]]に以下の変更を行います。 - コンストラクタ( <nowiki>__construct()</nowiki> ) を作ります。 - コンストラクタ内で、login()アクションのインタセプターを登録します。<code php> $this->intercept('login', function(Controller $controller) { // バリデーションのロジック }); </code> - login() メソッド内からバリデーション( validate() ) の呼び出し部分を削除します。 - validate() を削除します。 以下に修正後の最終的な AuthController.php を示します。 {{fa>folder-open-o}} ** /apricot/app/Controllers ** <code php AuthController.php> <?php namespace App\Controllers; use Core\Input; use Core\Foundation\ErrorBag; use App\Foundation\Security\AuthUser; use App\Foundation\Controller; use App\Foundation\ValidatorErrorBag; /** * Authコントローラ */ class AuthController extends Controller { public function __construct() { // インターセプターの登録 $this->intercept('login', function(Controller $controller) { $inputs = Input::all(); // Validation $v =(new \Valitron\Validator($inputs)) ->rule('required', 'account') ->rule('alphaNum','account') ->rule('ascii','password') ->labels(inputLabels('auth.login')); if(!$v->validate()) { $errorBag = new ValidatorErrorBag($v->errors()); return redirect(back())->withInputs()->withErrors($errorBag); } }); } /** * ログインフォーム表示 * @return \Core\Foundation\Response */ public function showForm() { if (AuthUser::check()) { // 認証済ならトップ画面表示 return redirect(route('')); } if (AuthUser::remember()) { // 自動認証できたらトップ画面表示 return redirect(route('')); } return render('login'); } /** * ログイン(ユーザ認証) * @return \Core\Foundation\Response */ public function login() { $inputs = Input::all(); if (!AuthUser::authenticate($inputs['account'], $inputs['password'], !empty($inputs['remember']))) { // ユーザが見つからない $errorBag = new ErrorBag([__('auth.login.error.no_account')]); return redirect(back())->withInputs()->withErrors($errorBag); } // ログイン成功 return redirect(AuthUser::getPathAfterLogin()); } /** * ログアウト * @return \Core\Foundation\Response */ public function logout() { // セッションの破棄 AuthUser::forget(); // ログイン画面表示 return redirect(route("login")); } } </code> \\ ==== ユーザコントローラ ==== ユーザコントローラのバリデーションをメソッド型のインタセプターとして再実装します。[[apricot:app:user-edit|ユーザ登録画面]]の章で作った[[apricot:app:user-edit#ユーザコントローラ|ユーザコントローラ( UserController )]]に以下の変更を行います。 >ユーザコントローラには、[[apricot:app:validation#ユーザコントローラ|バリデーション]]の章と[[apricot:app:transaction#ユーザコントローラ|トランザクション]]の章で変更を加えています。 - コンストラクタ内で、insert()アクションのインタセプターを登録します。<code php> $this->intercept('insert', 'UserInterceptor@insert'); </code> - 同様に、update()アクションのインタセプターを登録します。<code php> $this->intercept('update', 'UserInterceptor@update'); </code> - insert() と update()メソッド内からバリデーションの呼び出し部分を削除します。 以下に修正後の最終的な UserController.php を示します。 {{fa>folder-open-o}} ** /apricot/app/Controllers ** <code php 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 { /** * User * @var \App\Models\User */ private $user; /** * ユーザコントローラの生成 */ public function __construct() { // モデル $this->user = new User(); // インターセプター登録 $this->intercept('insert', 'UserInterceptor@insert'); $this->intercept('update', 'UserInterceptor@update'); // トランザクションアクション登録 $this->transactional('insert','update','delete'); } /** * ユーザ一覧 * @return \Core\Foundation\Response */ public function index() { // 全件検索 $users = $this->user->findAll(); return render("user.index", ["users"=>$users]); } /** * ユーザ新規登録 * @return \Core\Foundation\Response */ public function create() { // 新規作成 $user = $this->user->create(); return render("user.create", ["user"=>$user]); } /** * ユーザレコード挿入 * @return \Core\Foundation\Response */ public function insert() { $inputs = Input::all(); try { // ユーザレコード挿入 $user = $this->user->insert($inputs); } catch(\Exception $e) { throw new ApplicationException(__('messages.error.db.insert'),$e->getMessage(),0,$e); } // ユーザ一編集画面にリダイレクト return redirect(route("user/{$user->id}/edit"))->with('msg',__('messages.success.db.insert')); } /** * ユーザ編集 * @return \Core\Foundation\Response */ public function edit(int $id) { // 主キー検索 $user = $this->user->findOne($id); if ($user!==false) { return render("user.edit", ["user"=>$user]); } else { return redirect(route("users"))->withOldErrors(); } } /** * ユーザレコード更新 * @param int $id * @return \Core\Foundation\Response */ public function update(int $id) { $inputs = Input::all(); try { // レコード更新 $this->user->update($id, $inputs); } catch(ApplicationException $e) { throw $e; } catch(\Exception $e) { throw new ApplicationException(__('messages.error.db.update'),$e->getMessage(),0,$e); } // ユーザ一編集画面にリダイレクト return redirect(route("user/{$id}/edit"))->with('msg',__('messages.success.db.update')); } /** * ユーザレコード削除 * @param int $id * @return \Core\Foundation\Response */ public function delete(int $id) { try { // レコード削除 $this->user->delete($id); } catch(ApplicationException $e) { throw $e; } catch(\Exception $e) { throw new ApplicationException(__('messages.error.db.delete'),$e->getMessage(),0,$e); } // ユーザ一覧画面にリダイレクト return redirect(route("users"))->with('msg',__('messages.success.db.delete')); } } </code> \\
apricot/ext/interceptor.txt
· 最終更新: 2020/06/08 16:25 by
tanaka
ページ用ツール
文書の表示
以前のリビジョン
バックリンク
文書の先頭へ