4

这种情况是由于有人想在他们的网站中创建自己的“页面”而不必创建相应的操作。

所以说他们有一个像 mysite.com/index/books 这样的 URL... 他们希望能够创建 mysite.com/index/booksmore 或 mysite.com/index/pancakes 但不必在索引控制器中创建任何操作. 他们(可以做简单 html 的非技术人员)基本上想要创建一个简单的静态页面,而无需使用操作。

就像索引控制器中会有一些通用操作来处理对不存在操作的请求。你如何做到这一点,甚至有可能吗?

编辑:使用 __call 的一个问题是缺少视图文件。缺少动作变得没有意义,但现在您必须处理丢失的视图文件。如果找不到异常,框架将抛出​​异常(尽管如果有办法让它重定向到丢失的视图文件上的 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

如果您想使用 gabriel1836 的 _call() 方法,您应该能够禁用布局和视图,然后呈现您想要的任何内容。

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

我需要让现有的模块/控制器/操作在 Zend Framework 应用程序中正常工作,然后有一个包罗万象的路由,将任何未知的内容发送到 PageController,它可以从数据库表中挑选用户指定的 url 并显示页面。我不想在用户指定的 url 前面有一个控制器名称。我希望 /my/custom/url 而不是 /page/my/custom/url 通过 PageController。所以上述解决方案都不适合我。

我最终扩展了 Zend_Controller_Router_Route_Module: 使用几乎所有的默认行为,并且只是稍微调整了控制器名称,所以如果控制器文件存在,我们照常路由到它。如果它不存在,那么 url 一定是一个奇怪的自定义 URL,所以它被发送到 PageController,整个 url 作为参数保持不变。

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 回答
2

我通过重写 dispatch 方法并处理找不到操作时抛出的异常来实现一个包罗万象的方法:

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 回答
1

你可以使用神奇的__call()函数。例如:

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

stunti 的建议是我这样做的方式。我的特定解决方案如下(这使用您指定的任何控制器的 indexAction()。在我的情况下,每个操作都使用 indexAction 并根据 url 从数据库中提取内容):

  1. 获取路由器的实例(一切都在您的引导文件中,顺便说一句):

    $router = $frontController->getRouter();

  2. 创建自定义路由:

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

  3. 将新路由传递给前端控制器:

    $frontController->setRouter($router);

我没有使用 gabriel 的 __call 方法(只要您不需要视图文件,它确实适用于缺少的方法),因为这仍然会引发有关缺少相应视图文件的错误。

于 2009-01-29T02:18:53.350 回答
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 回答
0

@Steve 如上所述-您的解决方案对我来说听起来很理想,但我不确定您是如何在引导程序中实现它的?

于 2010-11-26T14:44:33.857 回答