CakePHP 2.4是否从插件引发主要应用程序异常?(对请求操作的干扰?)

CakePHP 2.4是否从插件引发主要应用程序异常?(对请求操作的干扰?),cakephp,exception,layout,cakephp-2.4,Cakephp,Exception,Layout,Cakephp 2.4,我正在将CakePHP(v2.44)应用程序中一个运行良好的部分转化为一个插件,当从插件中的控制器中抛出异常时,我得到了最奇怪的行为:异常处理程序/呈现程序开始使用我的主站点在app/View/Layouts/mylayout.ctp中的布局,然后使用app/View/Layouts/error.ctp中的默认布局中断它。以下是摘录: <div><ul><li class="jsdnavpopup blog-menu-categories"> <a hr

我正在将CakePHP(v2.44)应用程序中一个运行良好的部分转化为一个插件,当从插件中的控制器中抛出异常时,我得到了最奇怪的行为:异常处理程序/呈现程序开始使用我的主站点在app/View/Layouts/mylayout.ctp中的布局,然后使用app/View/Layouts/error.ctp中的默认布局中断它。以下是摘录:

<div><ul><li class="jsdnavpopup blog-menu-categories">
<a href='/blog/categories'>All Categories</a><nav>
<!DOCTYPE html PUBLIC "{trimmed for space}">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CakePHP: the rapid development php framework:Error Page</title>
  • CakePHP:快速开发php框架:错误页面

如果你注意到它看起来像一个新的标题,就在蛋糕的构图中间。就在布局中断的地方,我加载了am元素,该元素使用requestAction(在我的开发环境中没有缓存)。如果删除该元素,则在遇到使用requestAction的下一个元素之前,我的布局会呈现得更多。这些requestAction调用没有从引发异常的插件请求操作

我正在使用app/Config/core.php中的所有默认错误处理程序。唯一不同的是在错误视图中指定$this->layout

现在,如果我在插件的视图文件夹中重新创建原始的错误布局和视图,事情就会像我期望的那样工作。但出于实验目的,我把我的主站点布局的重命名副本放在那里,同样的事情。每当遇到使用requestAction的元素时,异常就会中断布局

有什么想法吗

我要重申,当从应用程序中任何非插件的地方抛出异常时,一切都能完美运行。

以下是我如何做到这一点的(根据OP问题评论中的要求)

在我的布局中,我将以下代码放在希望顶部菜单出现的位置:

<?php echo $this->element('Menu.top_navigation'); ?>
请注意,这只是一个包装器——您可以为页脚导航制作更多的包装器,甚至可以制作一个允许您作为参数传递位置的元素

插件/菜单/视图/元素/显示\u ul.ctp:

<?php
    if(empty($depth)){
        $depth = 0;
    }

    switch($depth){
        case 0:
            $classes = "dropdown";
        break;
        default:
            $classes = "";
        break;
    }
?>
<ul class="<?php echo $classes ?> depth_<?php echo $depth ?>">
    <?php foreach ($menus as $menu): ?>
        <li>
            <a href="<?php echo $menu['MenuItem']['url']; ?>">
                <?php echo $menu['MenuItem']['title']; ?>
            </a>
            <?php
                if(count($menu['children']) > 0){
                    echo $this->element(
                        'Menu.display_ul',
                        array(
                            'menus' => $menu['children'],
                            'depth' => $depth + 1
                        )
                    );
                }
            ?>
    </li>
<?php endforeach; ?>
</ul>
菜单函数中可能有比您需要的更多的内容,我正在使用一些积极的缓存例程来减少数据库负载,我建议在担心缓存之前只使用该函数的基本部分

这个解决方案对我来说效果很好,而且我认为它是最不违背MVC概念的解决方案——作为一个设计师,当你处于HTML模式并且认为“嘿,我需要显示顶部菜单”时,它只在你的视图中显示一行,你不必担心修改控制器或模型


我在当前的CakePHP项目中都使用了这种模式,它似乎工作得很好。

为什么要使用requestAction?我很确定问题是由于在不需要的地方使用RequestAction造成的,几乎总是有更好的方法来做事情,而不是旋转一个完整的新调度周期。如果需要在元素中执行某些逻辑,我发现最好的方法是将该逻辑放在模型中,然后使用ClassRegistry::init实例化模型,或者在模型中使用静态方法。您可以从元素内部调用它们,而不会出现任何问题。这样做的好处是性能更好,并且避免了类似的问题。元素使用
requestAction()
从适用的控制器加载菜单。我正在考虑从
beforeFilter()
中简单地设置菜单,并完全消除分派周期。这也会有一些其他的好处,直到发现这个问题,我才意识到这一点。但是现在我很好奇,为什么插件和我的应用程序会出现这种机制。我曾经使用requestAction做一些事情,但发现它导致了大量问题。我真的不建议将菜单放入beforeFilter。我这样做的方式是在菜单插件的菜单模型中创建一个函数,称为“display\u Menu\u widget”,它返回请求的菜单。在您的元素中,实例化模型,调用函数,然后使用此数据在菜单中循环并显示它们。如果你想要代码,请告诉我,我会给出一个答案,告诉你我是如何做到的。是的,看起来beforeFilter也不能做到这一点,因为我必须实例化我想要菜单的每个控制器。但是从视图访问模型似乎违反了MVC。我想知道我是否应该为我的模型编写一个助手,让助手违反MVC。这正在成为一个蠕虫的罐头,但我意识到重构不相关的比特从长远来看会更好。仍然想知道CakePHP是如何在渲染中途重新发送头的,但现在我确信放弃requestAction。如果您使用模型或助手发布答案,我将接受它。我将测试它。它避免了
requestData
破坏插件抛出的异常。看起来可能是我问题的解决方案。我采取了稍微不同的方法,只在我的助手中破坏MVC,但它避免了requestAction,所有这些都是受您启发的。谢谢
<?php
    if(empty($depth)){
        $depth = 0;
    }

    switch($depth){
        case 0:
            $classes = "dropdown";
        break;
        default:
            $classes = "";
        break;
    }
?>
<ul class="<?php echo $classes ?> depth_<?php echo $depth ?>">
    <?php foreach ($menus as $menu): ?>
        <li>
            <a href="<?php echo $menu['MenuItem']['url']; ?>">
                <?php echo $menu['MenuItem']['title']; ?>
            </a>
            <?php
                if(count($menu['children']) > 0){
                    echo $this->element(
                        'Menu.display_ul',
                        array(
                            'menus' => $menu['children'],
                            'depth' => $depth + 1
                        )
                    );
                }
            ?>
    </li>
<?php endforeach; ?>
</ul>
/**
 * display_menu_widget method
 *
 * @param array $options
 * @return void
 */
public function display_menu_widget($options = array()) {

    $defaults = array(
        'position' => 'top'
    );

    $settings = array_merge($defaults, $options);

    $this->recursive = 0;

    $menuItems = array();

    $conditions = array(
        'Menu.position' => $settings['position']
    );

    $menuDetails = $this->find('first', array('recursive' => -1, 'conditions' => $conditions));


    $menuPosition = $menuDetails[$this->alias]['position'];
    $parentId = $menuDetails[$this->alias][$this->primaryKey];

    $conditions = array(
        $this->MenuItem->alias . '.menu_id' => $parentId
    );

    $this->MenuItem->recursive = 0;

    $cacheName = 'default' . 'ModelMenuItem';
    $cacheKey = $this->generateCacheName(array('type' => 'threaded', 'conditions' => $conditions));

    $menuItems = Cache::read($cacheKey, $cacheName);

    if(empty($menuItems) || PWFunctions::forceCacheFlush()){
        $menuItems = $this->MenuItem->find('threaded', array('conditions' => $conditions));

        Cache::write($cacheKey, $menuItems, $cacheName);
    }

    return $menuItems;
}