ZendFramework2ガイド

機能編

コントローラープラグイン

コントローラープラグインの役割

コントローラープラグインは、コントローラークラスでよく使われそうな機能を持ったクラスを、コントローラークラスから簡単に利用できるようにした仕組みです。
Zend2のフレームワークのディレクトリの、ZF2\library\Zend\Controller\Plubinの中を見てみるとわかりますが、たくさんのクラスが存在していますが、これが全てプラグインのクラスです。これらのクラスはコントローラークラスから、newなどの手続きを必要とせずに利用が可能になっています。

たとえばParamsというプラグインクラスを見てみましょう。
入門編や、リクエスト情報の取得のところで説明していますが、このクラスのインスタンスはコントローラークラスで$this->params()とするだけで取得できます。
なので例えばこのクラスの持つfromPostというメソッドを実行したい場合、

  $this->params()->fromPost();

の一行で済みます。
他のプラグインも同様です。

Params

Paramsはリクエストパラメーターを取得するためのプラグインです。他でも説明しているため、詳しい説明は省略しますが、以下のようなメソッドを持ちます。

fromPost

POSTパラメーターを取得します。
引数なしで実行すると全POSTパラメーターが配列で取得でき、第一引数でキーを指定するとそのキーの値のみが取得できます。

fromQuery

GETパラメーターを取得します。
引数なしで実行すると全GETパラメーターが配列で取得でき、第一引数でキーを指定するとそのキーの値のみが取得できます。

fromRoute

module.config.phpの設定をもとに、リクエストURLからパラメーターを取得することが出来ます。
引数なしで実行すると全てのパラメーターが配列で取得でき、第一引数でキーを指定するとそのキーの値のみが取得できます。

fromFiles

アップロードされたファイル情報を取得できます。
引数なしで実行すると全てのアップロードファイルが配列で取得でき、第一引数でキーを指定するとそのキーのアップロードファイル情報のみが取得できます。

fromHeader

HTTPリクエストヘッダ情報を取得できます。
引数なしで実行すると全てのヘッダが配列で取得でき、第一引数でキーを指定するとそのキーのヘッダ情報のみが取得できます。

Url

URLルーティング設定情報を参照するためのプラグインです。
コントローラークラスで$this->url()でUrlプラグインのインスタンスが取得出来ます。

fromRoute

リクエストURLから実行中のアクションメソッドまで到達するにはmodule.config.phpなどでのルーティング設定がもとになっています。
fromRouteメソッドは、実行中のアクションメソッドへ導いたルーティング設定のマッチング設定部分の文字列を取得します。
これはLiteralやSegmentのルーティング設定の場合は"route"の設定値、Regexの場合は"spec"の設定値になります。
設定値の中にプレースホルダが存在する場合は、2番目の引数でそのキーに対して値を割り当てる必要があります。これは、$this->params()->fromRoute()で得られた配列をそのまま渡しておけばよいと思います。なぜなら、$this->params()->fromRoute()で得られるパラメーター値は、ルーティング設定の"route"の設定に基づいて得られたものであるため、得られた配列をそのまま$this->url()->fromRoute()の引数として渡せば、必ずプレースホルダ部分を値に置き換わるはずだからです。

module/Hoge/config/module.config.php
            'segment_sample' => array(
                'type'    => 'Segment',
                'options' => array(
                    'route'    => '/hoge/fuga/:piyo',
                    'defaults' => array(
                        '__NAMESPACE__' => 'Hoge\Controller',
                        'controller' => 'Hoge',
                        'action' => 'fuga',
                    ),
                ),
            ),
module/Hoge/Controller/HogeController.php
$route = $this->url()->fromRoute(null, $this->params()->fromRoute());
var_dump($route);
http://example.com/hoge/fuga/fooでアクセスした場合
string(5) "/hoge/fuga/foo" 

気をつけるべきなのは、プラグイン名からしてリクエストURLが取得できるように連想してしまいそうですが、そうではなく、ルーティング設定です。
リクエストURLを取得するのはRequestクラスの役割です。こちらは$this->getRequest()->getUriString()とします。

Redirect

リダイレクトを実行するためのプラグインです。
コントローラークラスで$this->redirect()でRedirectプラグインのインスタンスが取得できます。

PHPの関数レベルでは、header関数でヘッダ文字列をそのままレスポンスするようなイメージになりますが、そのへんの処理をラッピングしたプラグインということになります。ただ、このメソッド自体は最終的なheader送信を行うわけではなく、イメージ的には送信するheaderを構築するだけのものです。コントローラークラスのアクションメソッドでは基本的にはViewModelのインスタンスを返すことでテンプレートをHTMLとしてレスポンスする仕組みになっていますが、このプラグインのメソッドによって得られる\Zend\Http\ResponseクラスのインスタンスをViewModelの代わりに返すと、リダイレクトが実行されるようになっています。

toUrl

引数で指定したURLへリダイレクトします。
指定するURLはスキーマ、ドメインを含む完全な形のURLか、サイト内のリダイレクトであればドメイン以前を省略したパスだけの形でもOKです。
実際にリダイレクトを実行するには、このメソッドの返り値をアクションメソッドの返り値として返す必要があります。

public function indexAction()
{
    // 同サイト内の/hoge/fugaへリダイレクト
    return $this->redirect()->toUrl('/hoge/fuga');
}
public function indexAction()
{
    // yahooへリダイレクト
    return $this->redirect()->toUrl('http://www.yahoo.co.jp');
}

toRoute

第1引数を未指定の場合は実行中のアクションメソッドへ導いたルーティング設定のマッチング設定部分をリクエストURLとしてリダイレクトします。つまり、同じアクションへリダイレクトすることになります。ただし、ルーティング設定にプレースホルダが含まれる場合は、第2引数で現在のリクエストとは異なる値を設定してリダイレクトすることも可能です。

第1引数を指定する場合はtoUrlメソッドとあまり変わりがないと言えるかもしれません。
記法的にルーティング設定と同じプレースホルダを含め、第2引数でそのプレースホルダに対する値を指定するという方法が取れるというメリットはあります。

public function indexAction()
{
    // 同アクションへリダイレクト
    return $this->redirect()->toRoute();
}
public function indexAction()
{
    // 同サイト内の/hoge/fuga/fooへリダイレクト
    return $this->redirect()->toRoute('/hoge/fuga/:piyo', array('piyo' => 'foo'));
}

Layout

ビューレイアウトに関する処理を行うプラグインです。
コントローラークラスで$this->layout()でLayoutプラグインのインスタンスが取得できます。

setTemplate

setTemplateメソッドによってレイアウトテンプレートを指定することができます。
module.config.phpでデフォルトのレイアウトが設定されている場合は通常はそれが有効になりますが、このメソッドで指定した場合はそれが優先されます。
指定するのは実際のテンプレートのファイル名やパスを指定するわけではないということです。指定するのはmodule.config.phpのtemplate_mapで設定されたキーです。

また、Layoutプラグインのインスタンスを取得するlayoutメソッドの引数にテープレートキーを指定しても同じ動作になります。

module/Hoge/config/module.config.php
    'view_manager' => array(

        'template_map' => array(
            'layout/layout'           => __DIR__ . '/../view/layout/layout.phtml',
            'layout/new_layout'           => __DIR__ . '/../view/layout/new_layout.phtml',  // ←新しいレイアウトテンプレート追加
            'ec/index/index' => __DIR__ . '/../view/ec/index/index.phtml',
            'error/404'               => __DIR__ . '/../view/error/404.phtml',
            'error/index'             => __DIR__ . '/../view/error/index.phtml',
        ),

    ),
module/Hoge/Controller/HogeController.php
public function indexAction()
{
    /* 以下の2パターンは同じ動作 */

    // setTemplateメソッドで指定
    $this->layout()->setTemplate('layout/new_layout');

    // layoutメソッドのパラメータとして指定
    $this->layout('layout/new_layout');
}

getViewModel

レイアウト自体も内部ではViewModelで表現されています。
ViewModelは階層関係があるため、イメージ的にはレイアウトのViewModelがアクションのViewModelを内包しています。
アクションのViewModelはアクションメソッドの中でインスタンスを生成し、返しますが、レイアウトのViewModelはフレームワークによって自動的に生成されており、通常は意識することがありません。しかし、場合によってはそのインスタンスに対して直接何かの処理をする場合も有り得ます。このような場合にLayoutプラグインのgetViewModelメソッドを利用します。

public function indexAction()
{
    $layoutViewModel = $this->layout()->getViewModel();
}

ViewModelインスタンスに対して可能な処理は後述します。

Forward

ルーティングによって呼び出されたコントローラーから、別のコントローラーを呼び出す機能を持つのがForwardです。
コントローラークラスで$this->forward()でForwardプラグインのインスタンスが取得できます。

dispatch

dispatchメソッドでコントローラー名とアクション名を指定することで、別のコントローラーの処理を呼び出すことが出来ます。
戻り値は呼び出したアクションメソッドの戻り値となるため、それをそのまま呼び出し元のアクションメソッドでreturnすれば、呼び出し先のアクションメソッドへルーティングされたのと同じ動きになります。
また、このdispatchメソッドによって別のコントローラーのアクションメソッドを呼び出すと、出力されるビューテンプレートも呼び出し先のアクションのものとなります。

module/Hoge/src/Controller/IndexController.php
class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->forward()->dispatch('Hoge\Controller\Fuga', array('action' => 'foo'));
    }
}
module/Hoge/src/Controller/FugaController.php
class FugaController extends AbstractActionController
{
    public function fooAction()
    {
        $viewModel = new ViewModel(array(
            'message' => 'fooアクションだよ',
        ));
        return $viewModel;
    }
}
module/Hoge/view/hoge/fuga/foo.phtml
メッセージ:<?php echo $message; ?>
ブラウザでIndexController::indexActionへルーティングされるようにアクセス
メッセージ:fooアクションだよ

dipatchメソッドの1番目の引数に指定するのはコントローラー名ですが、ここで言うコントローラー名とは、module.config.phpの"controllers"で設定されたコントローラーリストのキーです。そしてアクション名を指定する2番めの引数は配列です。配列にキー"action"でアクション名を指定する事ができます。ちなみにこの2番目の引数はmodule.config.phpのルーティング設定の"defaults"と同様と考えてください。module.config.phpのルーティング設定ではdefaultsでルーティング先のアクション名の指定をしますが、それと同じです。ただしコントローラー名については1番目の引数で指定します。

自作コントローラープラグイン

コントローラープラグインはあらかじめ用意された物以外にも、自作することが出来ます。

まずはプラグインクラスを作ります。
プラグインクラスとして必要なのは、Zend\Mvc\Controller\Plugin\AbstractPluginクラスを継承することです。

modules/Ec/src/Ec/Controller/Plugin
namespace Ec\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Session\Container;

class Auth extends AbstractPlugin
{
    public function isLogin()
    {
        if ($this->getUid()) {
            return true;
        }
        return false;
    }

    public function getUid()
    {
        $session = new Container();
        if ($session->uid) {
            return $session->uid;
        }
        return null;
    }
}

ログイン前提の通販サイトを想定し、認証に関する処理をプラグインクラスとして作成しました。
これを、コントローラーからプラグインとして呼び出せるようにします。

プラグインをコントローラーに登録

コントローラークラスには$this->pluginsというクラス変数が存在します。
ここにはZend\Controller\Plubin\PluginManagerというクラスのインスタンスが格納されています。
PluginManagerはその名の通り、コントローラープラグインを管理するクラスですが、初期状態では上で紹介しているようなプラグインを持っています。
ここに自作したプラグインを登録します。

public function indexAction()
{
    // プラグインを登録
    $this->plugins->setInvokableClass('auth', 'Ec\Controller\Plugin\Auth');

    // プラグインを利用
    if ($this->auth()->isLogin()) {
        
    }
}

PluginManagerのsetInvokableClassメソッドでプラグインの登録ができます。

$this->plugins->setInvokableClass('{キー}','{クラス名}');

という具合に登録すると、

$this->{キー}

で、登録したプラグインクラスのインスタンスが得られます。

事前に登録しておく

上のような登録方法では、呼び出す前に登録がいちいちセットになり、普通にnewするのと変わらず、あまり便利な感じがしません。そこで、無意識に登録が行なわれるような仕組みにしていきます。
そのためにやらなくてはいけない事としては、アクションメソッドが呼び出されるより前のタイミングで、コントローラークラスのインスタンスにプラグインを登録することです。そして、当然これはコントローラーインスタンスが生成可能な状態になった後でないとできません。
呼び出されるコントローラーは、リクエストURLをもとにしてルーティング設定により決定されます。これが行われるのはrouteイベントです。
イベントの順序としては、

  bootstrap → route → dispatch

となっています。
なのでプラグインの登録はrouteイベントでコントローラーが決定し、インスタンスが生成出来る状態になった後で、しかもアクションメソッドが呼び出されるdispatchイベントよりも前で行なわなければならないということになります。この間に新たなイベントを作ってもいいですが、それなりの手続きが必要になるので、今回はもっと簡単にやりたいところです。

各イベントでは復数の処理が実行されます。例えばdispatchイベントの中でも、アクションメソッドが呼び出される処理はその一つに過ぎません。
なので、同じdispatchイベントの中でも、アクションメソッドを呼び出している処理よりも前のタイミングでプラグイン登録をすればいいことになります。
今回はこの方法で行きます。

dispatchイベントに処理を追加するには当然dispatchイベントよりも前のタイミングで行う必要があります。
モジュールディレクトリの直下にはModule.phpが存在しますが、ModuleクラスにあるonBootstrapメソッドはbootstrapイベント時に呼び出されるメソッドです。
bootstrapイベントはアプリケーションが起動した直後に実行される初期処理的なイベントで、タイミング的にも問題ありません。
なのでModuleクラスのonBootstrapで、dispatchイベントへの処理の追加を行うようにします。

module/Ec/Module.php
namespace Ec;

use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Ec\Controller\Plugin\PluginRegistListener;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager        = $e->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);
        
        // ここを追加
        $eventManager->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'), 100);
    }

    // ここを追加
    public function onDispatch(MvcEvent $e)
    {		
        $routeMatch       = $e->getRouteMatch();
        $controllerName   = $routeMatch->getParam('controller', 'not-found');
        $controllerLoader = $e->getApplication()->getServiceManager()->get('ControllerLoader');
        $controller = $controllerLoader->get($controllerName);
        $pluginManager = $controller->getPluginManager();
        $pluginManager->setInvokableClass('auth', 'Ec\Controller\Plugin\Auth');
    }


}

ModuleクラスにonDispatchというメソッドを追加し、ここでコントローラーへプラグインの登録を行う処理を書きました。
そしてこのメソッドをdispatchイベントに登録しているのがonBootstrapイベント内に追加した1行です。

イベントを管理しているEventManagerというクラスのインスタンスに、onDispatchメソッドを登録しているのがわかると思います。
これでModuleクラスのonDispatchメソッドがdispatchイベント発火時に実行される処理として登録されます。
登録を行っているattachメソッドの3番目の引数に100という数値を指定していますが、これは登録するメソッドの実行優先度を表しています。優先度とは、同じイベントに対して登録された復数のメソッドの処理順序を決める数値です。大きいほど先に処理されます。
今回はアクションメソッドが呼ばれるより前にプラグインを登録しておきたいので、優先度100で登録します。
コントローラーのアクションメソッドは優先度1で登録されているため、それより大きい値で登録すれば先に実行されるわけです。なぜ100かというと特に意味はありません。適当に大きめの数値を設定しただけで、1より大きければ2でもかまいません。

これで準備は完了です。
アクションメソッド内でプラグインを呼び出してみましょう。

module/Ec/src/Ec/Controller/IndexController.php
public function indexAction()
{
    // プラグインを利用
    if ($this->auth()->isLogin()) {
        
    }
}

最初から用意されているプラグイン同様、いきなり$this->auth()で呼び出すことが出来ます。