Zend Framework 中的通用、包罗万象的操作...可以完成吗?

发布于 2024-07-13 00:26:05 字数 433 浏览 15 评论 0原文

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

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

就像索引控制器中会有一些通用操作来处理不存在操作的请求一样。 你如何做到这一点或者这是否可能?

编辑: 使用 __call 的一个问题是缺少视图文件。 缺少操作变得毫无意义,但现在您必须处理丢失的视图文件。 如果找不到,框架将抛出​​异常(尽管如果有一种方法可以让它在缺少视图文件时重定向到 404 __call 是可行的。)

This situation arises from someone wanting to create their own "pages" in their web site without having to get into creating the corresponding actions.

So say they have a URL like mysite.com/index/books... they want to be able to create mysite.com/index/booksmore or mysite.com/index/pancakes but not have to create any actions in the index controller. They (a non-technical person who can do simple html) basically want to create a simple, static page without having to use an action.

Like there would be some generic action in the index controller that handles requests for a non-existent action. How do you do this or is it even possible?

edit: One problem with using __call is the lack of a view file. The lack of an action becomes moot but now you have to deal with the missing view file. The framework will throw an exception if it cannot find one (though if there were a way to get it to redirect to a 404 on a missing view file __call would be doable.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

土豪我们做朋友吧 2024-07-20 00:26:05

使用神奇的 __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);
    }
}

Using the magic __call method works fine, all you have to do is check if the view file exists and throw the right exception (or do enything else) if not.

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);
    }
}
捶死心动 2024-07-20 00:26:05

你得玩路由器
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 函数中只需检索参数并显示正确的块。
我还没有尝试过,所以你可能需要稍微修改一下代码

You have to play with the router
http://framework.zend.com/manual/en/zend.controller.router.html

I think you can specify a wildcard to catch every action on a specific module (the default one to reduce the url) and define an action that will take care of render the view according to the url (or even action called)

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

in you customAction function just retrieve the params and display the right block.
I haven't tried so you might have to hack the code a little bit

惜醉颜 2024-07-20 00:26:05

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

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);

If you want to use gabriel1836's _call() method you should be able to disable the layout and view and then render whatever you want.

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
情绪失控 2024-07-20 00:26:05

我需要让现有的模块/控制器/操作在 Zend Framework 应用程序中正常工作,但随后有一个包罗万象的路由,将任何未知内容发送到 PageController,该 PageController 可以从数据库表中选取用户指定的 url 并显示页面。 我不想在用户指定的网址前面有一个控制器名称。 我希望 /my/custom/url 而不是 /page/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:它捕获现有控制器上的无效操作。

I needed to have existing module/controller/actions working as normal in a Zend Framework app, but then have a catchall route that sent anything unknown to a PageController that could pick user specified urls out of a database table and display the page. I didn't want to have a controller name in front of the user specified urls. I wanted /my/custom/url not /page/my/custom/url to go via the PageController. So none of the above solutions worked for me.

I ended up extending Zend_Controller_Router_Route_Module: using almost all the default behaviour, and just tweaking the controller name a little so if the controller file exists, we route to it as normal. If it does not exist then the url must be a weird custom one, so it gets sent to the PageController with the whole url intact as a parameter.

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;
    }
}

So my MemberController will work fine as /member/login, /member/preferences etc, and other controllers can be added at will. The ErrorController is still needed: it catches invalid actions on existing controllers.

隔岸观火 2024-07-20 00:26:05

我通过重写调度方法并处理未找到操作时引发的异常来实现包罗万象:

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');
    }
}

I implemented a catch-all by overriding the dispatch method and handling the exception that is thrown when the action is not found:

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');
    }
}
一绘本一梦想 2024-07-20 00:26:05

你可以使用魔法 __call() 函数。 例如:

public function __call($name, $arguments) 
{
     // Render Simple HTML View
}

You could use the magic __call() function. For example:

public function __call($name, $arguments) 
{
     // Render Simple HTML View
}
迷迭香的记忆 2024-07-20 00:26:05

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 方法(只要您不需要视图文件,它就适用于缺少的方法),因为这仍然会引发有关缺少相应视图文件的错误。

stunti's suggestion was the way I went with this. My particular solution is as follows (this uses indexAction() of whichever controller you specify. In my case every action was using indexAction and pulling content from a database based on the url):

  1. Get an instance of the router (everything is in your bootstrap file, btw):

    $router = $frontController->getRouter();

  2. Create the custom route:

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

  3. Pass the new route to the front controller:

    $frontController->setRouter($router);

I did not go with gabriel's __call method (which does work for missing methods as long as you don't need a view file) because that still throws an error about the missing corresponding view file.

南七夏 2024-07-20 00:26:05

以供将来参考,以 gabriel1836 和 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();
}

For future reference, building on gabriel1836 & ejunker's thoughts, I dug up an option that gets more to the point (and upholds the MVC paradigm). Besides, it makes more sense to read "use specialized view" than "don't use any view".


// 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();
}
青春有你 2024-07-20 00:26:05

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

@Steve as above - your solution sounds ideal for me but I am unsure how you implmeented it in the bootstrap?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文