4

この状況は、対応するアクションを作成することなく、自分の Web サイトに独自の「ページ」を作成したい場合に発生します。

たとえば、mysite.com/index/books のような URL を持っているとします... mysite.com/index/booksmore または mysite.com/index/pancakes を作成できるようにしたいのですが、インデックス コントローラーでアクションを作成する必要はありません。 . 彼ら (単純な HTML を実行できる非技術者) は、基本的に、アクションを使用せずに単純で静的なページを作成したいと考えています。

存在しないアクションの要求を処理するインデックス コントローラーに一般的なアクションがあるように。どうやってこれを行うのですか、それとも可能ですか?

編集: __call を使用する際の問題の 1 つは、ビュー ファイルがないことです。アクションがないことは意味がありませんが、欠落しているビュー ファイルに対処する必要があります。例外が見つからない場合、フレームワークは例外をスローします (ただし、欠落しているビュー ファイルで 404 にリダイレクトする方法があれば、__call を実行できます)。

4

9 に答える 9

10

魔法の__call方法を使用すると問題なく動作します。ビュー ファイルが存在するかどうかを確認し、存在しない場合は適切な例外をスローする (または他のことを行う) だけです。

public function __call($methodName, $params)
{
    // An action method is called
    if ('Action' == substr($methodName, -6)) {
        $action = substr($methodName, 0, -6);

        // We want to render scripts in the index directory, right?
        $script = 'index/' . $action . '.' . $this->viewSuffix;

        // Script file does not exist, throw exception that will render /error/error.phtml in 404 context
        if (false === $this->view->getScriptPath($script)) {
            require_once 'Zend/Controller/Action/Exception.php';
            throw new Zend_Controller_Action_Exception(
                sprintf('Page "%s" does not exist.', $action), 404);
        }

        $this->renderScript($script);
    }

    // no action is called? Let the parent __call handle things.
    else {
        parent::__call($methodName, $params);
    }
}
于 2012-01-24T22:22:17.513 に答える
3

ルーターで遊ぶ必要があります http://framework.zend.com/manual/en/zend.controller.router.html

ワイルドカードを指定して、特定のモジュールのすべてのアクション(URLを減らすデフォルトのアクション)をキャッチし、URLに従ってビューのレンダリングを処理するアクション(または呼び出されたアクション)を定義できると思います

new Zend_Controller_Router_Route('index/*', 
array('controller' => 'index', 'action' => 'custom', 'module'=>'index') 

customAction関数では、パラメータを取得して適切なブロックを表示するだけです。私は試したことがないので、コードを少しハックする必要があるかもしれません

于 2009-01-28T02:13:15.937 に答える
2

ディスパッチメソッドをオーバーライドし、アクションが見つからない場合にスローされる例外を処理することで、キャッチオールを実装しました。

public function dispatch($action)
{
    try {
        parent::dispatch($action);
    }
    catch (Zend_Controller_Action_Exception $e) {
        $uristub = $this->getRequest()->getActionName();
        $this->getRequest()->setActionName('index');
        $this->getRequest()->setParam('uristub', $uristub);
        parent::dispatch('indexAction');
    }
}
于 2012-02-24T20:36:50.323 に答える
2

gabriel1836 の _call() メソッドを使用する場合は、レイアウトとビューを無効にしてから、必要なものをレンダリングできるはずです。

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
于 2009-01-30T19:08:52.260 に答える
2

既存のモジュール/コントローラー/アクションを Zend Framework アプリで通常どおり動作させる必要がありましたが、データベース テーブルからユーザー指定の URL を選択してページを表示できる PageController に未知のものを送信するキャッチオール ルートが必要でした。ユーザーが指定した URL の前にコントローラー名を付けたくありませんでした。/page/my/custom/url ではなく /my/custom/url が PageController を経由するようにしたかったのです。したがって、上記の解決策はどれもうまくいきませんでした。

Zend_Controller_Router_Route_Module を拡張することになりました。ほぼすべてのデフォルトの動作を使用し、コントローラー名を少し調整して、コントローラー ファイルが存在する場合は、通常どおりルーティングします。存在しない場合、URL は奇妙なカスタム URL である必要があるため、URL 全体がそのままパラメーターとして PageController に送信されます。

class UDC_Controller_Router_Route_Catchall extends Zend_Controller_Router_Route_Module
{
    private $_catchallController = 'page';
    private $_catchallAction     = 'index';
    private $_paramName          = 'name';

    //-------------------------------------------------------------------------
    /*! \brief takes most of the default behaviour from Zend_Controller_Router_Route_Module
        with the following changes:
        - if the path includes a valid module, then use it
        - if the path includes a valid controller (file_exists) then use that
            - otherwise use the catchall
    */
    public function match($path, $partial = false)
    {
        $this->_setRequestKeys();

        $values = array();
        $params = array();

        if (!$partial) {
            $path = trim($path, self::URI_DELIMITER);
        } else {
            $matchedPath = $path;
        }

        if ($path != '') {
            $path = explode(self::URI_DELIMITER, $path);

            if ($this->_dispatcher && $this->_dispatcher->isValidModule($path[0])) {
                $values[$this->_moduleKey] = array_shift($path);
                $this->_moduleValid = true;
            }

            if (count($path) && !empty($path[0])) {
                $module = $this->_moduleValid ? $values[$this->_moduleKey] : $this->_defaults[$this->_moduleKey];
                $file = $this->_dispatcher->getControllerDirectory( $module ) . '/' . $this->_dispatcher->formatControllerName( $path[0] ) . '.php'; 
                if (file_exists( $file ))
                {
                    $values[$this->_controllerKey] = array_shift($path);
                }
                else
                {
                    $values[$this->_controllerKey] = $this->_catchallController;
                    $values[$this->_actionKey] = $this->_catchallAction;
                    $params[$this->_paramName] = join( self::URI_DELIMITER, $path );
                    $path = array();
                }
            }

            if (count($path) && !empty($path[0])) {
                $values[$this->_actionKey] = array_shift($path);
            }

            if ($numSegs = count($path)) {
                for ($i = 0; $i < $numSegs; $i = $i + 2) {
                    $key = urldecode($path[$i]);
                    $val = isset($path[$i + 1]) ? urldecode($path[$i + 1]) : null;
                    $params[$key] = (isset($params[$key]) ? (array_merge((array) $params[$key], array($val))): $val);
                }
            }
        }

        if ($partial) {
            $this->setMatchedPath($matchedPath);
        }

        $this->_values = $values + $params;

        return $this->_values + $this->_defaults;
    }
}

したがって、私の MemberController は /member/login、/member/preferences などとして正常に機能し、他のコントローラーを自由に追加できます。ErrorController は引き続き必要です。既存のコントローラーで無効なアクションをキャッチします。

于 2009-08-31T04:49:34.047 に答える
1

魔法の__call()関数を使用できます。例えば:

public function __call($name, $arguments) 
{
     // Render Simple HTML View
}
于 2009-01-28T02:01:49.623 に答える
1

将来の参考のために、gabriel1836 と ejunker の考えに基づいて、私はより的確な (そして MVC パラダイムを支持する) オプションを掘り下げました。その上、「ビューを使用しない」よりも「特殊なビューを使用する」と読む方が理にかなっています。


// 1. Catch & process overloaded actions.
public function __call($name, $arguments)
{
    // 2. Provide an appropriate renderer.
    $this->_helper->viewRenderer->setRender('overload');
    // 3. Bonus: give your view script a clue about what "action" was requested.
    $this->view->action = $this->getFrontController()->getRequest()->getActionName();
}
于 2009-06-08T20:57:20.860 に答える
1

スタンティの提案は、私がこれを行った方法でした。私の特定の解決策は次のとおりです(これは、指定したコントローラーのindexAction()を使用します。私の場合、すべてのアクションはindexActionを使用し、URLに基​​づいてデータベースからコンテンツをプルしていました):

  1. ルーターのインスタンスを取得します (すべてブートストラップ ファイルにあります):

    $router = $frontController->getRouter();

  2. カスタム ルートを作成します。

    $router->addRoute('controllername', new Zend_Controller_Router_Route('controllername/*', array('controller'=>'controllername')));

  3. 新しいルートをフロント コントローラーに渡します。

    $frontController->setRouter($router);

私はガブリエルの __call メソッドを使用しませんでした (ビュー ファイルが必要ない限り、欠落しているメソッドに対して機能します)。対応するビュー ファイルが欠落しているというエラーが引き続きスローされるためです。

于 2009-01-29T02:18:53.350 に答える
0

@Steve上記のように-あなたのソリューションは私にとって理想的に聞こえますが、ブートストラップでどのようにそれを実装したのかわかりませんか?

于 2010-11-26T14:44:33.857 に答える