Ground Sunlight

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

ユーザ用ツール

サイト用ツール


apricot:ext:interceptor

差分

このページの2つのバージョン間の差分を表示します。

この比較画面にリンクする

両方とも前のリビジョン 前のリビジョン
次のリビジョン
前のリビジョン
apricot:ext:interceptor [2020/05/25 17:37]
y2sunlight
apricot:ext:interceptor [2020/06/08 16:25] (現在)
tanaka [Apricot インターセプター]
行 1: 行 1:
-> 編集中 
- 
------ 
- 
 ====== Apricot インターセプター ====== ====== Apricot インターセプター ======
  --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-05-25//  --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-05-25//
行 26: 行 22:
 インターセプター とはアクションの前処理の事です。ミドルウェアと同じでリクエストを中断してレスポンスオブジェクトを生成することもできますが、アクションの後処理はできません。これを図示すると以下のようになります。 インターセプター とはアクションの前処理の事です。ミドルウェアと同じでリクエストを中断してレスポンスオブジェクトを生成することもできますが、アクションの後処理はできません。これを図示すると以下のようになります。
  
-                  Middleware      Action +=== インターセプター構造 === 
-                 ┌──────────┐     ┌───────────────────────────────────────────┐ + 
-                 |          |      Interceptor   Interceptor   Real Action +{{:apricot:ext:ext-fig02.svg?nolink&800}}
-                          |      ┌─────────┐   ┌─────────┐   ┌──────────┐ | +
-  [Request ] --> | -------> | --> |--|-------->|-->|-------->|-->| ────┐    | |  +
-                    ↓          |    ↓    |      ↓    |          | | +
-                    ↓          └─────────┘   └─────────┘          | |  +
-                    ↓               ↓             ↓        |        | | +
-  [Response] <-- | <------  | <-- | <--------------------------- | <───┘    | | +
-                          |                                  └──────────┘ | +
-                 └──────────┘     └───────────────────────────────────────────┘+
  
 上図から分かるようにミドルウェアパイプラインから見ると、インターセプターはアクションに含まれます。ミドルウェアとの一番の違いは、ミドルウェアは基本的に全てのコントローラを対象としているのに対し、インターセプターは、各コントローラで独自に設定ができるという点です。 上図から分かるようにミドルウェアパイプラインから見ると、インターセプターはアクションに含まれます。ミドルウェアとの一番の違いは、ミドルウェアは基本的に全てのコントローラを対象としているのに対し、インターセプターは、各コントローラで独自に設定ができるという点です。
行 73: 行 61:
     protected function intercept($actionName, $interceptors)     protected function intercept($actionName, $interceptors)
     {     {
-        $interceptor_arr is_array($interceptors) ? $interceptors array_slice(func_get_args(),1); +        if ($actionName == \Core\Application::getInstance()->getActionName())
-        if (!array_key_exists($actionName, $this->interceptors))+
         {         {
-            $this->interceptors[$actionName] array();+            $interceptor_arr = is_array($interceptors) ? $interceptors : array_slice(func_get_args(),1); 
 +            $this->interceptors = array_merge($this->interceptors , $interceptor_arr);
         }         }
-        $this->interceptors[$actionName] = array_merge($this->interceptors[$actionName] , $interceptor_arr); 
     }     }
  
行 104: 行 91:
  
         // Invoke Interceptor         // Invoke Interceptor
-        if (array_key_exists($actionName, $this->interceptors))+        $response = null; 
 +        foreach($this->interceptors as $interceptor)
         {         {
-            $response = null; +            if (is_callable($interceptor))
-            foreach($this->interceptors[$actionName] 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))
                 {                 {
-                    // Case of callable +                    $instance = $this;
-                    $response call_user_func_array($interceptor, $iparams);+
                 }                 }
-                elseif(strpos($interceptor,'@')!==false)+                else
                 {                 {
-                    // Case of Controller/Action +                    $class = "\\App\\Controllers\\Interceptors\\{$class}"; 
-                    list($class, $method) = explode('@', $interceptor); +                    $instance = new $class;
-                    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) +                // Call interceptor 
-                +                $response = call_user_func_array(array($instance, $method), $iparams); 
-                    return $response; +            } 
-                }+ 
 +            if ($response instanceof \Core\Foundation\Response) 
 +            
 +                return $response;
             }             }
         }         }
行 148: 行 132:
  
   * ''intercept($actionName, $interceptors)'' メソッド   * ''intercept($actionName, $interceptors)'' メソッド
 +    * リクエストされているアクション(Application::getActionName())が対象となります。
     * アクションにインターセプターを追加します。     * アクションにインターセプターを追加します。
-    * BaseControllerは、インターセプターを配列( <nowiki>$this->$interceptors</nowiki> )で管理しています。+    * BaseControllerは、インターセプターを配列( <nowiki>$this->interceptors</nowiki> )で管理しています。
  
   * ''invokeAction($actionName, $params)'' メソッド   * ''invokeAction($actionName, $params)'' メソッド
行 162: 行 147:
 ===== インターセプターの使用 ===== ===== インターセプターの使用 =====
  
-インターセプターの登録は、コントローラーのコンストラクタで ''intercept()'' メソッドを使って行います。インターセプターにはクロージャーとメソッドの両方が使用できます。簡単なバリデーション処理ならクロジャーで以下のように書きます。+インターセプターの登録は、コントローラーのコンストラクタで ''intercept()'' メソッドを使って行います。インターセプターにはクロージャーとメソッドの両方が使用できます。簡単なバリデーション処理ならクロジャーで以下のように書きます。
  
 <code php> <code php>
行 180: 行 165:
 </code> </code>
  
-インターセプターに渡される引数は、第1引数に、コントローラのインスタンスが、その後にアクションと同じの引数が続きます。+インターセプターに渡される引数は、第1引数に、コントローラのインスタンスが、その後にアクションと同じの引数が続きます。また、インターセプターはレスポンスオブジェクトを返して以降のアクションを中止することができます。
  
-''intercept()'' にメソッドを使用する場合は、以下のように <nowiki>'クラス名@メソッド名'</nowiki> の形式で指定します。+インターセプターにメソッドを使用する場合は、以下のように <nowiki>'クラス名@メソッド名'</nowiki> の形式で指定します。
  
 <code php> <code php>
行 211: 行 196:
 ==== 認証コントローラ ==== ==== 認証コントローラ ====
  
->TODO:+認証コントローラのバリデーションをクロージャ型のインタセプターとして再実装します。[[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>
  
 \\ \\
行 218: 行 308:
 ==== ユーザコントローラ ==== ==== ユーザコントローラ ====
  
->TODO:+ユーザコントローラのバリデーションをメソッド型のインタセプターとして再実装します。[[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.1590395830.txt.gz · 最終更新: 2020/05/25 17:37 by y2sunlight