ヘルパーブローカーとヘルパーの初期化タイミングとビューbasePathの変更について

アクションコントローラーの再利用性を考慮するとViewRendererはなるべく使いたい。でも便利なものほど内部はブラックボックスになっていて微妙にわかりにくいことがあるのでメモ。
検討課題

  • アクションコントローラーを_forwardまたはActionStack等で使いまわす時
  • ViewRendererでレンダリングは自動化しつつ
  • ディレクトリ構成をコントローラー毎に、コントローラー側から変更したい

たとえば、システム全体で、ビュースクリプトの置き場所をviewsからtemplatesに変えたいとすると、Initializer等のブート処理時に

<?php
$viewRenderer->setViewBasePathSpec(':moduleDir/templates');

という具合に指示を出す。
同じ処理をアクションコントローラーから行おうとすると、たとえば、アクションコントローラーのpreDispatchで

<?php
//preDispath内
$this->_helper->get('ViewRenderer')->setViewBasePathSpec(':moduleDir/templates');

とすれば、よさそうだが、これは動作しない。init()でも同様。
理由はViewRendererの初期化タイミングにある。
アクションコントローラー毎の設定なのだけれど

  1. Zend_Controller_Action::__construct(args $args);
    1. $this->_helper = new Zend_Controller_Action_HelperBroker($this); // in ActionController::__construct
      1. each $helper->setActionController($controller); //HelperBroker::__construct()
      2. $helper->init(); //HelperBroker::__construct()
        1. $this->initView(); //ViewRenderer::init
    2. $this->init(); //ActionController::__construct()

という順になっていて、ActionControllerのinit()より前にViewRendererのinitView()がコールされるので、init()でセットしたオプションはその場では有効にならない。単純にオプションをセットしただけではViewRendererの初期化に介入することはできない。
そのため、BasePathSpecを変更したら、そこでinitView()を呼んでやる必要がある。
逆に言えば、次にinitView()がコールされるまで、設定値は活きるので元の値にすぐに戻してもOK(バギーだけどw)。ということで、

ActionControllerで何かの都合でViewRendererの初期化に介入したいときは、

<?php
    public function init()
    {
        if ($helper = $this->_helper->hasHelper('viewRenderer')) {
            $viewRenderer = $this->_helper->getHelper('viewRenderer');
            $oldViewBasePathSpec = $viewRenderer->getViewBasePathSpec();
            $viewRenderer->setViewBasePathSpec(':moduleDir/templates')->initView();
            $viewRenderer->setViewBasePathSpec($oldViewBasePathSpec);
        }
        //$this->initView();
    }

のようにする。