php - 这个 MVC Controller 代码是否需要重构?

标签 php model-view-controller controller helpers

我正在为 MVC 应用程序 (Kohana/PHP) 编写一个 CSV/Excel-->MySQL 导入管理器

我有一个名为“ImportManager”的 Controller ,它有一个名为“index”(默认)的 Action ,它在网格中显示所有有效的 .csv .xls 位于特定目录中并准备好导入的文件。然后用户可以选择他想要导入的文件。

但是,由于 .csv 文件导入到一个 数据库表和 .xls 文件导入到多个 数据库表,我需要处理这个抽象。因此,我创建了一个名为 SmartImportFilehelper 类,我将每个文件发送到该类,.csv.xls 并且然后我得到然后要求这个“智能”对象将该文件中的工作表(是一个或多个)添加到我的集合中。这是我在 PHP 代码中的操作方法:

public function action_index()
{
    $view = new View('backend/application/importmanager');

    $smart_worksheets = array();
    $raw_files = glob('/data/import/*.*');
    if (count($raw_files) > 0)
    {
        foreach ($raw_files as $raw_file)
        {
            $smart_import_file = new Backend_Application_Smartimportfile($raw_file);
            $smart_worksheets = $smart_import_file->add_smart_worksheets_to($smart_worksheets); 
        }
    }
    $view->set('smart_worksheets', $smart_worksheets);

    $this->request->response = $view;
}

SmartImportFile 类如下所示:

class Backend_Application_Smartimportfile
{
    protected $file_name;
    protected $file_extension;
    protected $file_size;
    protected $when_file_copied;
    protected $file_name_without_extension;
    protected $path_info;
    protected $current_smart_worksheet = array();

    protected $smart_worksheets = array();

    public function __construct($file_name)
    {
        $this->file_name = $file_name;
        $this->file_name_without_extension = current(explode('.', basename($this->file_name)));

        $this->path_info = pathinfo($this->file_name);
        $this->when_file_copied = date('Y-m-d H:i:s', filectime($this->file_name));
        $this->file_extension = strtolower($this->path_info['extension']);
        $this->file_extension = strtolower(pathinfo($this->file_name, PATHINFO_EXTENSION));
        if(in_array($this->file_extension, array('csv','xls','xlsx')))
        {
            $this->current_smart_worksheet = array();
            $this->process_file();
        }
    }

    private function process_file()
    {
        $this->file_size = filesize($this->file_name);
        if(in_array($this->file_extension, array('xls','xlsx')))
        {
            if($this->file_size < 4000000)
            {
                $this->process_all_worksheets_of_excel_file();
            }
        }
        else if($this->file_extension == 'csv')
        {
            $this->process_csv_file();
        }

    }

    private function process_all_worksheets_of_excel_file()
    {
        $worksheet_names = Import_Driver_Excel::get_worksheet_names_as_array($this->file_name);
        if (count($worksheet_names) > 0)
        {
            foreach ($worksheet_names as $worksheet_name)
            {
                $this->current_smart_worksheet['name'] = basename($this->file_name).' ('.$worksheet_name.')';
                $this->current_smart_worksheet['kind'] = strtoupper($this->file_extension);
                $this->current_smart_worksheet['file_size'] = $this->file_size;
                $this->current_smart_worksheet['when_file_copied'] = $this->when_file_copied;
                $this->current_smart_worksheet['table_name'] = $this->file_name_without_extension.'__'.$worksheet_name;
                $this->assign_database_table_fields();
                $this->smart_worksheets[] = $this->current_smart_worksheet;
            }
        }
    }

    private function process_csv_file()
    {
        $this->current_smart_worksheet['name'] = basename($this->file_name);
        $this->current_smart_worksheet['kind'] = strtoupper($this->file_extension);
        $this->current_smart_worksheet['file_size'] = $this->file_size;
        $this->current_smart_worksheet['when_file_copied'] = $this->when_file_copied;

        $this->current_smart_worksheet['table_name'] = $this->file_name_without_extension;
        $this->assign_database_table_fields();


        $this->smart_worksheets[] = $this->current_smart_worksheet;
    }

    private function assign_database_table_fields()
    {
        $db = Database::instance('import_excel');
        $sql = "SHOW TABLE STATUS WHERE name = '".$this->current_smart_worksheet['table_name']."'";
        $result = $db->query(Database::SELECT, $sql, FALSE)->as_array();
        if(count($result))
        {
            $when_table_created = $result[0]['Create_time'];
            $when_file_copied_as_date = strtotime($this->when_file_copied);
            $when_table_created_as_date = strtotime($when_table_created);
            if($when_file_copied_as_date > $when_table_created_as_date)
            {
                $this->current_smart_worksheet['status'] = 'backend.application.import.status.needtoreimport';
            }
            else
            {
                $this->current_smart_worksheet['status'] = 'backend.application.import.status.isuptodate';
            }
            $this->current_smart_worksheet['when_table_created'] = $when_table_created;
        }
        else
        {
            $this->current_smart_worksheet['when_table_created'] = 'backend.application.import.status.tabledoesnotexist';
            $this->current_smart_worksheet['status'] = 'backend.application.import.status.needtoimport';
        }
    }

    public function add_smart_worksheets_to(Array $smart_worksheets = array())
    {
        return array_merge($smart_worksheets, $this->get_smart_worksheets());
    }

    public function get_smart_worksheets()
    {
        if ( ! is_array($this->smart_worksheets))
        {
            return array();
        }

        return $this->smart_worksheets;
    }

}

在一次代码审查中,我被告知最好不要有这样的辅助类,而是将大部分代码保留在 Controller 操作方法中本身。论点是您应该能够查看 Controller 操作中的代码并了解它的作用,而不是让它调用自身之外的外部帮助程序类。 我不同意。我的论点是:

  • 您应该在任何时候创建帮助程序类,它可以使代码更清晰,因为在这种情况下,它抽象出一些文件具有一个工作表或多个工作表的事实 中的工作表,并允许将来轻松扩展,例如,如果我们还想从 sqlite 文件或什至从其中包含文件的 目录 导入,这类抽象将能够很好地处理这个问题。
  • 将大部分代码从这个辅助类移回到 Controller 中会迫使我在 Controller 中创建内部变量,这对这个特定的操作有意义,但可能有意义也可能没有意义 Controller 中的其他操作方法。
  • 如果我在 C# 中对此进行编程,我会将这个辅助类设为一个嵌套类,它实际上是一个内部数据结构,它是在 Controller 类内部且仅对 Controller 类可用,但由于 PHP 不允许嵌套类,因此我需要在 Controller “外部”调用一个类,以帮助以一种使代码清晰的方式管理这种抽象和可读性

根据您在MVC模式下编程的经验,是否应该将上述帮助类重构回 Controller ?

最佳答案

Controller 有两种方法:使其变薄或变厚。当我开始使用 MVC 冒险时,我犯了一个创建厚 Controller 的错误——现在我更喜欢让它尽可能薄。我认为您的解决方案很好。

以下是我将如何进一步重新设计您的代码:

class Backend_Application_SmartImport {

    public function __construct( $raw_files ) {
    }

    public function process() {     
        foreach ($raw_files as $raw_file) {
            // (...)
            $oSmartImportFileInstance = $this->getSmartImportFileInstance( $smart_import_file_extension );
        }
    }   

    protected function getSmartImportFileInstance( $smart_import_file_extension ) {
        switch ( $smart_import_file_extension ) {
            case 'xml':
                return new Backend_Application_SmartImportFileXml();
            // (...)
        }
    }
}

abstract class Backend_Application_SmartImportFile {
    // common methods for importing from xml or cvs
    abstract function process();
}

class Backend_Application_SmartImportFileCVS extends Backend_Application_SmartImportFile {
    // methods specified for cvs importing
}

class Backend_Application_SmartImportFileXls extends Backend_Application_SmartImportFile {
    // methods specified for xls importing
}

想法是有两个类负责处理xml和cvs继承自一个基类。主类使用一种特殊方法来检测应如何处理数据 (Strategy Pattern)。 Controller 只是将文件列表传递给 Backend_Application_SmartImport 类的实例,并将处理方法的结果传递给 View 。

我的解决方案的优点是代码更加解耦,您可以轻松、干净地添加新的处理类型,如 xml、pdf 等。

关于php - 这个 MVC Controller 代码是否需要重构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4733037/

相关文章:

php - 有没有办法通过他们的 API 搜索特定搜索词的所有 Disqus 评论

php - mysql案例然后无法识别的关键字和无法识别的 token

php - 我应该合并我的创建和更新 Controller 吗?

c# - 如何在 ASP.net Core 2 中创建动态 API

events - Extjs 4.2 监听器函数中的参数

php - 使用 $_GET 和干净的 URL

java - 在您自己的代码库中,类别名是一种不好的做法吗?

ruby-on-rails - Ruby Post Controller中的语法错误

java - 在运行时创建 JLabel : View in Swing does not update as intended

java - 如何在 Spring MVC 中没有 ModelAttribute 的情况下处理/显示错误?