11

我正在使用CodeIgniter设计一个 Web 应用程序(但我认为这个问题通常适用于 Web 应用程序中的MVC模式)。

当我为某些数据库实体设计模型类时(例如,BlogEntry用于说明的 a),我基本上有两种选择:

“经典的OOP”方法是,让类代表实体,即类的一个实例BlogEntry。在 CodeIgniter 中,这将导致类似的代码

class Blogentry extends CI_Model {
    function load($id) {
      // Access database
      $this->id = $dbresult.id;
      $this->title = $dbresult.title;
      $this->author = $dbresult.author;
    }
}

要访问一些博客条目,我会做一个$this->load->model('blogentry');, 调用$this->blogentry->load($id),然后使用模型类实例。

我在野外经常看到的另一种方法是让模型以某种数据结构返回实体。在代码中:

class Blogentry extends CI_Model {
  function get_entry($id) {
    // Access database, yielding $dbresult
    return $dbresult;
}

有了这个我会写

$this->load->model('blogentry');
$the_entry = $this->blogentry->get_entry($id);

在控制器中并使用$the_entry. 在第二种情况下,该类更像是工厂构建器类。

这两种方法中的任何一种都被认为是在 MVC 中执行 M 的“正确方法”吗?我想,我经常看到第二种方法,但我没有看过那么多 MVC-Webapps。关于为什么第一种或第二种方法可能更合适的任何想法?

4

3 回答 3

13

首先,CodeIgniter不是MVC 框架。最适合的范例可能是MVP或类似ORM-Template-Adapter 的东西。因此,让我根据该命名谬误分解我的答案:

在经典/传统 MVC(不是框架声称的 MVC)中,模型不是一个类或单个文件,而是一个包含所有领域业务逻辑的层,通常仅由诸如Domain Objects之类的东西组成,Data Mappers,以及处理调用实体和域逻辑之间的接口的东西,以便将此类代码保持在控制器之外(例如在 MVC 中)。Tereško 将它们命名为“服务”,这对于一个准官方名称来说已经足够了。

所以在传统的 MVC 中,对模型层 com 的请求是通过“服务”进入的,而这些服务又与域对象和数据映射器交互。

显然,所有这些都与 CodeIgniter 和其他框架所称的“MVC”无关。在这种设计模式(我简称为“CIMVC”)中,Model 是一个由 Controller 调用和操作的类,它直接与数据库交互。将数据存储在模型中的成员变量中并没有太多的语义意义,但如果您对类似的东西感兴趣,请查看任何用 CIMVC 编写的 ORM 插件/库。(虽然 ORM 解决方案在符合传统 MVC 方面不会更好......)

至于常见的做法,我见过的大多数 CI 应用程序会将“原始”数据库数据返回给控制器进行处理;这将域业务逻辑保留在模型中。我个人对 CIMVC 的偏好是最小化/消除模型中除域业务逻辑之外的任何内容。

tl;dr - CodeIgniter 中的“传统/正确”MVC 基本上是不可能的,因此试图强制您的 CI 代码符合传统 MVC 范例是愚蠢的(并且可能会让您发疯)

编辑:为了澄清之前对模型层中业务逻辑的一些混淆,是的,您应该在模型层中拥有所有业务逻辑。但是,如果正在进行额外的数据处理(排序、操作等),现在我们进入了违反 DRY 和 SRP 的危险区域;您希望模型是可重用的,因此要小心添加太多或如此专门的逻辑,以至于它在应用程序的其他部分变得无法使用。

于 2012-08-06T17:52:11.033 回答
4

这是与 MVC 范式相关的通用答案,因为我在 CI 方面的经验为 0,但在 Symfony、Zend、Propel 和 Doctrine 方面有丰富的经验。

我发现,在使用原则时,在特定结构中返回对象数据最好添加一个指定所述结构的方法。例如,Doctrine_Record 对象有一个toArray()方法允许我将对象转换为数组,因此我可以使用对象访问器或调用toArray() 并将对象作为数组处理。

// Accessing object data through available methods
foreach ($object->categories() as $category) {
    var_dump((string) $category);
}

// Accessing object data as an array
$objectAsArray = $object->toArray(true);

foreach ($objectAsArray as $element) {
    var_dump($element['Category']);
}

归根结底,您访问数据的方式取决于您的偏好,完全取决于您希望如何处理数据以满足您的需求。在某些情况下,处理数组格式的数据会更快,因为对象水合往往会更多地影响性能。

无论您如何访问数据,我建议您将业务逻辑保留在模型层,这样您就不会最终重复代码(DRY:不要重复自己)。

于 2012-08-06T20:19:07.670 回答
3

首先你必须了解的是,CodeIgniter 与正确的 MVC 或 MVC 启发的设计模式相去甚远。CodeIgniter 实现的模式没有正式名称,但它与 Ruby on Rails 密切相关。

最严重的问题源于模型层是如何实现的。在整个文档中,建议使用活动记录模式,它本身就引入了几个设计问题:

另一个主要问题是缺乏意见。视图应该是包含表示逻辑的实例。这包括选择使用哪些模板来表示从模型层获取的信息。视图不是模板,它们是使用模板的结构。

在 CodeIgniter 之上创建适当模型层的最佳方法是忽略CI_Model,因为恕我直言,扩展此类没有任何好处。而是创建类似Services. 这些结构可以充当与模型层交互的接口(不是 OO 意义上的)。每个服务可以处理多个域对象映射器

基本上服务从控制器接收命令/消息,这些命令/消息改变模型层的状态并提供从模型层提取信息的方法。通常提取会发生在视图实例中,但这在 CodeIgniter 中是不可能的。信息的提取也将发生在“控制器”中,将数据传递给模板。

代码看起来像这样:

class Library 
{

    protected $current_document = null;

    public function acquire_document( $id )
    {
        $document = load_class('Document', 'domain_object');
        $document->set_id( $id );

        $mapper = load_class('Document', 'persistence');
        $mapper->fetch( $document );

        $this->current_document = $document;
    }

    public function get_document_information()
    {
        $document = $this->current_document;        

        if ( $document === null )
        {
            return 'empty';
            // you either did not acquire the document
            // or document was not found
        }
        return $document->get_details();
    }
}

MVC 设计模式实际上是两层的组合:模型层和表示层(应该主要由控制器和视图组成)。

控制器永远不应该直接接触域对象(它们是模型层的一部分),因为这意味着你有一个泄漏的抽象。

因此,总而言之,您所谓的“模型类”应该返回信息。

于 2012-08-07T03:11:33.980 回答