肉とビールとパンケーキ by @sotarok

少し大人になった「肉とご飯と甘いもの」

リダイレクトビューやら


どうなったか,ということですが,
そうですね,私の場合はもはやViewClassそのものをかなり拡張させているので,Ethna的にはできないことが出来るようになっていますが(何w),それは今後色々と本体のほうにもコミットできる部分をしていくつもりです.

その上で,一応現状の私のコードをば.deadlinetimer.com が現在最新のEthnaを使ったコードになってますので,APPIDが Deadline になってますが,その辺は適時読み替えてください.

Controller を拡張して,ActionClass から ViewClass に引数を渡せるようにする

どういうことかというと,まず,コントローラの trriger_WWW に細工をしています.

<?php

//中略

class Deadline_Controller extends Ethna_Controller
{
    // 中略

    /** override
    */
    /**
     *  フレームワークの処理を実行する(WWW)
     *
     *  引数$default_action_nameに配列が指定された場合、その配列で指定された
     *  アクション以外は受け付けない(指定されていないアクションが指定された
     *  場合、配列の先頭で指定されたアクションが実行される)
     *
     *  @access private
     *  @param  mixed   $default_action_name    指定のアクション名
     *  @param  mixed   $fallback_action_name   アクション名が決定できなかった場合に実行されるアクション名
     *  @return mixed   0:正常終了 Ethna_Error:エラー
     */
    function _trigger_WWW($default_action_name = "", $fallback_action_name = "")
    {
        // アクション名の取得
        $action_name = $this->_getActionName($default_action_name, $fallback_action_name);

        // マネージャ実行チェック
        $this->_ethnaManagerEnabledCheck($action_name);

        // アクション定義の取得
        $action_obj =& $this->_getAction($action_name);
        if (is_null($action_obj)) {
            if ($fallback_action_name != "") {
                $this->logger->log(LOG_DEBUG, 'undefined action [%s] -> try fallback action [%s]', $action_name, $fallback_action_name);
                $action_obj =& $this->_getAction($fallback_action_name);
            }
            if (is_null($action_obj)) {
                return Ethna::raiseError("undefined action [%s]", E_APP_UNDEFINED_ACTION, $action_name);
            } else {
                $action_name = $fallback_action_name;
            }
        }

        // アクション実行前フィルタ
        for ($i = 0; $i < count($this->filter_chain); $i++) {
            $r = $this->filter_chain[$i]->preActionFilter($action_name);
            if ($r != null) {
                $this->logger->log(LOG_DEBUG, 'action [%s] -> [%s] by %s', $action_name, $r, get_class($this->filter_chain[$i]));
                $action_name = $r;
            }
        }
        $this->action_name = $action_name;

        // オブジェクト生成
        $backend =& $this->getBackend();

        $form_name = $this->getActionFormName($action_name);
        $this->action_form =& new $form_name($this);
        $this->action_form->setFormVars();
        $backend->setActionForm($this->action_form);

        // アクションに必要とされる全てのオブジェクトが
        // 初期化されたあと、言語切り替えフックを呼ぶ
        $session =& $this->getSession();
        $session->restore();
        $this->_setLanguage($this->locale, $this->system_encoding, $this->client_encoding);

        // バックエンド処理実行
        $forward_name = $backend->perform($action_name);

        // アクション実行後フィルタ
        for ($i = count($this->filter_chain) - 1; $i >= 0; $i--) {
            $r = $this->filter_chain[$i]->postActionFilter($action_name, $forward_name);
            if ($r != null) {
                $this->logger->log(LOG_DEBUG, 'forward [%s] -> [%s] by %s', $forward_name, $r, get_class($this->filter_chain[$i]));
                $forward_name = $r;
            }
        }

        // コントローラで遷移先を決定する(オプション)
        $forward_name_params = $this->_sortForward($action_name, $forward_name);

        // override による 変更箇所はココだよー!
        $preforward_params = array();
        if (is_array($forward_name_params)) {
            $forward_name = array_shift($forward_name_params);
            $preforward_params = $forward_name_params;
        }
        else {
            $forward_name = $forward_name_params;
        }

        if ($forward_name != null) {
            $view_class_name = $this->getViewClassName($forward_name);
            $this->view =& new $view_class_name($backend, $forward_name, $this->_getForwardPath($forward_name));
            //$this->view->preforward();
            // ここだよー
            $preforward_return = call_user_func_array(array($this->view, 'preforward'), $preforward_params);

            $this->view->forward();
        }

        return 0;
    }

}

ViewClass の基底を拡張する

これも色々いじってるんですが,とりあえずもうちょいまとまったらちゃんとコード出すつもりでしたが,とりあえず現状どうなってるかだけ.

色々やってますが,簡単にいえば,

  • header吐けるメソッドを用意した
  • レイアウトテンプレートにした
  • そんくらいだっけ・・・・?

あれ?なんだこのコード flush まわりが中途半端だぞ!?とりあえず今は気にしない.

ちなみに,以下のViewClassを,Deadline_ViewClassが継承しています.

<?php
/**
 *  Ether_ViewClass.php
 *
 *  @description    ViewClass Ether Expansion: expand methods, utilities.
 *  @author         Sotaro KARASAWA
 *  @package        Ether :  Ethna Expansion
 *  @date           2008/09/23
 *  @version        $Id: app.viewclass.php 600 2008-06-30 23:12:42Z mumumu-org $
 */


//  Ether_ViewClass
/**
 *  View class.
 *
 *  @author     Sotaro KARASAWA
 *  @package    Ether :  Ethna Expansion
 *  @access     public
 */
class Ether_ViewClass extends Ethna_ViewClass
{

    /**
     *  @purotected string  Layout template filename
     */
    protected $_layoutFile = 'layout.tpl';

    /**
     *  @public     boolean  flag of use layout template
     */
    public $hasLayout = true;

    public $hasDefaultHeader = true;

    public $defaultHeader = array(
        "Pragma" => "no-cache",
        "Cache-Control" => "no-cache, no-store, must-revalidate",
    );

    /**
     *  Constructor of ViewClas
     *  to over ride
     *
     *  @access public
     *  @param  object  Ethna_Backend   $backend    backendオブジェクト
     *  @param  string  $forward_name   ビューに関連付けられている遷移名
     *  @param  string  $forward_path   ビューに関連付けられているテンプレートファイル名
     */
    public function Ether_ViewClass(&$backend, $forward_name, $forward_path)
    {
        parent::Ethna_ViewClass($backend, $forward_name, $forward_path);

        $this->db =& Ether_DB::getInstance($backend);

    }

    /**
     *  set common default value.
     *
     *  @access protected
     *  @param  object  Deadline_Renderer  Renderer object.
     */
    function _setDefault(&$renderer)
    {
        // get config setting-
        $isDebug = $this->backend->getConfig()->get('debug');
        $isTplDebug = $this->backend->getConfig()->get('debug_tpl');

        $smarty =& $renderer->engine;

        // あ,ここもデフォルトから変えてるな
        $smarty->left_delimiter = '{{';
        $smarty->right_delimiter = '}}';

        // for debug
        $smarty->force_compile = $isDebug;

        $config =& $this->backend->getConfig();
    }

    /**
     * Override parent method to set Layout Template
     *
     * @access public
     */
    public function forward()
    {
        $this->_setLanguage();
        $renderer =& $this->_getRenderer();
        $this->_setDefault($renderer);

        if ($this->hasDefaultHeader) {
            $this->defaultHeader['Content-Type'] = 'text/html; charset=' . $this->backend->getController()->getClientEncoding();
            $this->header($this->defaultHeader);
        }

        // using layout.tpl flag
        if ($this->hasLayout) {
            // check : layout file existance
            $layout = $this->getLayout();
            if ($this->isTemplateExists($layout)) {
                $content = $renderer->perform($this->forward_path, true);

                if (Ethna::isError($content)) {
                    if ($content->getCode() == E_GENERAL) {
                        $error = 404;
                    }
                    else {
                        $error = 500;
                    }

                    $this->error($error);
                    $content = $renderer->perform($this->forward_path, true);
                }

                $renderer->assign('content', $content);
                $renderer->display($layout, serialize($_SERVER['REQUEST_URI']));
            } else {
                return Ethna::raiseWarning('file "'.$layout.'" not found');
            }
        } else {
            $renderer->perform($this->forward_path);
        }
    }

    /**
     * sending header
     *
     *  @param mixed    array   send header : heade => value
     *                  int     HTTP status code
     *                  string  Header string
     */
    public function header ($status) {
        if (is_array($status)) {
            foreach ($status as $key => $status) {
                header ($key . ": " . $status);
            }
        } else if (is_int($status)) {
            $codes = array(
                100 => "Continue",
                101 => "Switching Protocols",
                200 => "OK",
                201 => "Created",
                202 => "Accepted",
                203 => "Non-Authoritative Information",
                204 => "No Content",
                205 => "Reset Content",
                206 => "Partial Content",
                300 => "Multiple Choices",
                301 => "Moved Permanently",
                302 => "Found",
                303 => "See Other",
                304 => "Not Modified",
                305 => "Use Proxy",
                307 => "Temporary Redirect",
                400 => "Bad Request",
                401 => "Unauthorized",
                402 => "Payment Required",
                403 => "Forbidden",
                404 => "Not Found",
                405 => "Method Not Allowed",
                406 => "Not Acceptable",
                407 => "Proxy Authentication Required",
                408 => "Request Time-out",
                409 => "Conflict",
                410 => "Gone",
                411 => "Length Required",
                412 => "Precondition Failed",
                413 => "Request Entity Too Large",
                414 => "Request-URI Too Large",
                415 => "Unsupported Media Type",
                416 => "Requested range not satisfiable",
                417 => "Expectation Failed",
                500 => "Internal Server Error",
                501 => "Not Implemented",
                502 => "Bad Gateway",
                503 => "Service Unavailable",
                504 => "Gateway Time-out"
            );

            if (array_key_exists($status, $codes)) {
                header("HTTP/1.1: {$status} {$codes[$status]}");
                //vd("HTTP/1.1: {$status} {$codes[$status]}");
            }
        } else {
            // check valid header
            if (preg_match("/^.+\:\s.+$/", $status)) {
                header($status);
            }
        }
    }

    public function redirect ($url)
    {
        $this->hasDefaultHeader = false;
        $this->hasLayout = false;

        $this->header(302);
        $this->header(array('Location' => $url));
    }

    public function flush ($url, $message = "", $wait = 3)
    {
        $flush = compact($url, $message, $wait);
        $this->af->setApp('flush', $flush);
    }

    /**
     *  set layout
     */
    public function setLayout ($filename)
    {
        // check layout file existance
        if ($this->isTemplateExists($filename)) {
            $this->_layoutFile = $filename;
            return true;
        }
        else {
            return Ethna::raiseWarning('file "'.$filename.'" not found');
        }
    }

    /**
     * get layout
     */
    public function getLayout ()
    {
        return $this->_layoutFile;
    }

    /**
     * check template existance
     *
     * @param   string  $filename   template file name
     * @access  public
     * @return  boolean
     */
    public function isTemplateExists($filename)
    {
        $renderer = $this->_getRenderer();
        if ($renderer->templateExists($filename)) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Error
     */
    function error($code)
    {
        $this->hasDefaultHeader = false;

        $this->header($code);

        // template 以下に error404.tpl とかがあれば, preforward で $this->error(404); とかすればいい
        $this->forward_path = "error{$code}.tpl";
    }
}

ActionClass で引数を渡して redirect viewに return

Redirect ViewClass は以下の通り.

<?php

class Deadline_View_Redirect extends Deadline_ViewClass
{
    /**
     *  preprocess before forwarding.
     *
     *  @access public
     */
    function preforward($url = null)
    {

        if (is_null($url)) {
            $this->redirect($this->config->get('url'));
        }
        else {
            if ($this->isAbsoluteUrl($url)) {
                $this->redirect($url);
            }
            else {
                if (substr($this->config->get('url'), -1) === '/') {
                    $base = $this->config->get('url');
                }
                else {
                    $base = $this->config->get('url') . '/';
                }

                if (substr($url, 0) === '/') {
                    $suff = substr($url, 1);
                }
                else {
                    $suff = $url;
                }

                $this->redirect($base . $suff);
            }
        }

        // TODO: ここでexitしてるとテストがうごかないから forward 側でうんちゃらする
        exit;
    }

    function isAbsoluteUrl($url)
    {
        if (preg_match("@^(https?|ftp)://.+@", $url)) {
            return true;
        }
        else {
            return false;
        }
    }
}

?>


例えば,以下のがDeadlinetimerでタイマーを登録するActionClass.DBも結構手をいれてますのでその辺は適当にw

<?php
/**
 *  register action implementation.
 *
 *  @author     Sotaro KARASAWA <sotaro.k@gmail.com>
 *  @access     public
 *  @package    Deadline
 */
class Deadline_Action_Register extends Deadline_ActionClass
{
    /**
     *  preprocess of register Action.
     *
     *  @access public
     *  @return string    forward name(null: success.
     *                                false: in case you want to exit.)
     */
    function prepare()
    {
        if ($this->af->validate() > 0) {
            return 'index';
        }
        return null;
    }

    /**
     *  register action implementation.
     *
     *  @access public
     *  @return string  forward name.
     */
    function perform()
    {
        $data = $this->af->getArray();

        $this->db->Timer->setArray($data);
        $id = $this->db->Timer->save();

        if (Ethna::isError($id)) {
            return $this->error($id);
        }
        else {
            return array('redirect', "timer/$id",);
        }
    }
}


そんなかんじで,

return array('redirect', "/hoge");

とか

return array('redirect', "http://deadlinetimer.com");

とか,当然

return 'redirect';

とかだとトップに飛んだりなど.

まとまってなくてすみませんw

超中途半端なまま放置した

にもブクマくださっているみたいなので,現状私がどうしているかだけ書いておこうと思ってw

参考になれば幸いです.