【问题标题】:How do you refactor a Codeigniter controller function that is too long?如何重构太长的 Codeigniter 控制器函数?
【发布时间】:2009-02-10 13:53:37
【问题描述】:

我的控制器中有一个函数比我希望的要长,我想重构它以调用一些离散函数以使其更易于管理。如何在 Codeigniter 控制器中更好地组织长函数?

我的尝试:

我知道您可以通过使用前导下划线 (_myfunc) 命名控制器来在控制器中创建私有函数,但是函数中的变量超出了调用控制器函数的范围。所以你必须从函数中返回所有需要的数据,这很麻烦。

这是管理复杂控制器功能的最佳选择吗?有没有更简单的方法,变量可以像标准类成员变量一样对控制器类都是全局的?

建议?提前致谢!

编辑:有人请求了代码,所以我在下面添加了巨型控制器的代码。改进的一个机会是将 switch 语句中的逻辑移动到单独的函数(删除、预览、排序等)。但我正试图在那之后决定下一步。将大型验证设置代码移到它自己的函数中确实会减轻一些负担,但是我应该将它移到哪里呢?

    function categories() {
    $this->load->library('upload');
    $this->load->model('categories_m');
    $this->load->model('products_m');
    $this->load->model('pages_m');
    $this->load->model('backoffice/backofficecategories_m');
    $data['body'] = $this->load->view('backoffice/categories/navigation_v', '', TRUE);
    $data['cat_tree'] = $this->categories_m->getCategoryTree();
    $data['page_list'] = $this->pages_m->getPageList();
    $data['category_dropdown'] = $this->load->view('backoffice/categories/category_dropdown_v',$data,TRUE);

    switch ($this->uri->segment(3)) { //display views based on parameter in URL.
    case 'delete':          
        $categoryTreeID = $this->sitewide_m->checkURLParam($this->uri->segment(4),'CategoryTree'); //if parameter is in URL, show 404 if invalid parameter is passed. Otherwise, set variable known to be safe.
        if (isset($_POST['delete'])) {
            $this->backofficecategories_m->deleteCategory($categoryTreeID);
            $data['body'] .= '<span class="error">Category Deleted.</span>';
        } else {
            $data['cat_details'] = $this->categories_m->getCategoryDetails('',$categoryTreeID);
            $data['parent_category'] = $this->categories_m->getParentCategory($categoryTreeID);
            $data['products_to_reassign'] = $this->products_m->getProductsInCategory('',$categoryTreeID);   
            $data['body'] .= $this->load->view('backoffice/categories/delete_v',$data,TRUE);  //pull fresh category tree data since tree was just updated.
        }
        break;
    case 'preview':
        if ($this->uri->segment(4)) $data['categoryTreeID'] = $this->sitewide_m->checkURLParam($this->uri->segment(4),'CategoryTree'); //if parameter is in URL, show 404 if invalid parameter is passed. Otherwise, set variable known to be safe.
        $data['cat_details'] = $this->categories_m->getCategoryDetails(NULL,$data['categoryTreeID']); //get category ID being edited from the URL and store it. Returns false if category ID isn't found.
        foreach ($data['cat_details']->result() as $detail) {
            $data['categoryName'] = $detail->Name;
            $data['categoryID'] = $detail->ID;
        }
        $data['body'] .= $this->load->view('backoffice/categories/preview_v', $data, TRUE);
        break;

    ...cases continue...
    default:
        $this->load->library('table');
        $data['body'] .= $this->load->view('backoffice/categories/categories_v', $data, TRUE);
        break;
    }
    $this->load->view('backoffice/template_v',$data);       
}

【问题讨论】:

  • 能不能把控制器的功能贴出来给我们看看?
  • 为什么不让CI做第三段的路由,而不是使用switch语句呢?
  • 这是一个很好的观点 - 我没有的原因是 switch 语句之前的代码适用于 switch 语句正在检查的所有操作。如果我将 switch 语句拆分为单独的函数,如何使 switch 之前的部分可重用?一个单独的函数会超出范围,对吧?

标签: php codeigniter


【解决方案1】:

您在使用模型吗?代码点火器不会强制执行此操作,但除了控制器和视图之外,使用模型是获得更短控制器功能的好方法。或者,您可以将一些函数放在自己的助手中,然后导入它。

而如果你想为整个构造函数设置一些默认值,你可以使用类构造函数。此处对此进行了概述:

http://codeigniter.com/user_guide/general/controllers.html#constructors

【讨论】:

  • 谢谢斯图尔特。是的,使用模型,虽然我不关心 Codeigniter 对模型的限制性视图。 Codeigniter 的文档基本上说您只将 db 查询放在模型中。我想将我的很多逻辑移到我的模型中,但这似乎违背了 codeigniter 的 model=db 的想法。同意吗?
  • 是的,模型只是用于处理数据的额外层,就像在 MVC 方法中一样。如果您想遵守代码点火器“规则”,最好是使用私有函数。或者,如果它们将成为可重用的函数,请将它们写在帮助程序中。
【解决方案2】:

查看您的代码,您正在使用一种方法来执行多个操作。我会让每个动作都有自己的方法。公共资源可以是类成员并在构造函数中加载。

因此,您可以将您的 url 更改为“category_controller/add”,而不是像“your_controller/categories/add”这样的 url,并为每个操作设置一个方法。如果您不想更改网址,请使用路由:

$route['your_controller/categories/(.*)'] = 'your_controller/$1';

【讨论】:

    【解决方案3】:

    服务层会有所帮助。

    【讨论】:

    • 我相信我理解服务层的要点,但你能用一个例子澄清一下吗?
    • 如果我没记错的话,tudu.sourceforge.net 有一个不错的服务层实现。看看源头。这是另一个非常简单的解释:is.gd/jgl6。另外,请查看这段代码is.gd/jgnQ。祝你好运!
    【解决方案4】:

    如果你想将你的逻辑保持在同一个控制器中,你可以通过在函数名前加下划线来模拟私有方法,例如:_myMethod()。就像link 所说的那样,函数名前的下划线会阻止 CI 从 URL 调用它。 例如,您可以在 Categories 控制器中创建 _delete()、_preview()、_order() 等方法。但是,如果您使用相同的逻辑来删除、预览、订购等其他内容,也许您应该将这些方法移动到模型或助手中。

    【讨论】:

      【解决方案5】:

      就个人而言,我认为您使用单个控制器方法做的太多了。我要做的第一件事就是将您的 CRUD(创建读取更新删除)函数分离为单独的方法。例如,您的示例是使用“类别”,为什么不使用单独的“类别”控制器?

      class Categories extends Controller
      {
        function __construct()
        {
          parent::Controller();
        }
      
        function index() 
        {
          //display logic/code here
        }
      
        function edit()
        {
          //get the category to update from the post or url for editing
          //do the editing, etc
        }
      
        function delete()
        {
          //delete the category
        }
      
        function add()
        {
          //create the new category
        }
      }
      

      您的 URL 将引用类别控制器:

      http://www.example.com/categories/edit http://www.example.com/categories/delete 等等

      我建议的第二件事是升级到 CodeIgniter 1.7.1 - 更新后的 form_validation 库可以轻松地将所有验证规则移动到单独的配置文件中。

      【讨论】:

      • 感谢 JayTee 的帖子。我同意这两个想法都是朝着正确方向迈出的一步。这是我在 codeigniter 中构建的第一个控制器,我仍在学习其约束范围内的最佳实践。它慢慢变得太大了。
      【解决方案6】:

      尝试在 codeigniter 中查看控制器的 _remap() 函数。

      使用它,您可以将公共代码保留在 _remap 函数中,然后从 _remap 中调用任何其他函数进行删除、更新等操作(基于 uri_segment(3))。

      【讨论】:

        【解决方案7】:

        您使用的是什么版本的 PHP? PHP 5 支持真正的 OO,因此您可以声明将被解释器视为私有函数:

        private function foo(){
        ...
        }
        

        如果您希望扩展您的类(子类)的类能够访问该函数,请将 private 替换为 protected.

        我从未使用过 CodeIgnniter,因此恐怕无法帮助您解决特定的问题域。但是,重构一个增长为 long 的函数是一个非常常见的问题,有通用的解决方案。 Martin Fowler 是一个聪明的人,他写了一些关于这个主题的书,很受好评,所以你可以看看你是否能找到his books 中的一本。还有在线教程可以帮助您开始重构。

        【讨论】:

        • 感谢杰里米的回答。我熟悉一般的重构原则,只是想在 Codeigniter 的约束下工作时找出最佳方法。
        【解决方案8】:

        您可以将常用函数放入库中并调用它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-28
          • 2018-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多