刚刚得到理解 MVC 框架上,经常不知道多少代码应放在模型中。我倾向于有的数据访问类,具有与此类似的方法︰

public function CheckUsername($connection, $username)
{
    try
    {
        $data = array();
        $data['Username'] = $username;

        //// SQL
        $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE Username = :Username";

        //// Execute statement
        return $this->ExecuteObject($connection, $sql, $data);
    }
    catch(Exception $e)
    {
        throw $e;
    }
}

我的模型往往能够映射到数据库表的实体类。

模型对象应有所有数据库映射属性以及上面的代码,或者是不是可以分开的代码实际上执行数据库工作?

我将最终有四个层呢?

2011-05-03 00:28:42
问题评论:

为什么是您捕获异常只是为了再次引发这些?

@Elias van Ootegem︰ 错过了点。正是在这种情况下捕获它们毫无意义。

@Elias van Ootegem︰ 是吧?如果它适用于重新抛出,则意味着上层捕捉该异常。但如果没有一个,然后它会 catched 它不该毫无意义重新抛出...(如果仍未收到,请小样小测试代码)

@Elias van Ootegem︰ 也不知道您所说的内容,不处理异常,特定图层,并不意味着它会终止应用程序请构造 (或更确切地说︰ 不能构造) 其中该重新抛出是有必要的代码示例。让我们停止此 offtopic 对话,

@drrcknlsn︰ 这就是一个有效的参数,但在该情况下至少捕获预期将引发异常,一般Exception没有文档的价值。个人如果停在该道路上会选择 PHPDoc 的@exception或一些类似的机制,使其显示在生成的文档。

回答:

的免责声明︰下面是我如何理解基于 PHP 的 web 应用程序的上下文中的 MVC 类似模式的说明。所有外部链接的内容中使用的目的是为了解释术语和概念,并意味着我自己令人信服的主题。

首先必须澄清的是︰该模型是一个图层.

第二︰ 没有区别传统的 MVC和我们在 web 开发中的使用。下面是一些较旧的答案我写道,它简要介绍了它们有什么不同。

不是哪一种模式︰

该模型不是类或任何单个对象。这是一个非常常见的错误,以使(我所做过,当我开始学习否则编写原始的答案也是如此),因为大多数框架非但此误解。

两者都不是它的对象关系映射技术 (ORM) 和数据库表的抽象。告诉您的任何人否则很有可能试图出售给另一个全新的 ORM 或整个框架。

哪一种模式是︰

在适当的 MVC 适配,M 包含所有域的业务逻辑和模型层主要由三种类型的结构是︰

  • 域对象

    域对象是纯粹域信息; 一个逻辑容器它通常表示的问题域空间中的逻辑实体。通常称为业务逻辑.

    这是您在其中定义如何发送发票之前, 验证数据或计算订单的总成本。在同一时间,域对象是存储-既不在其中(SQL 数据库,REST API,文本文件等) 从完全不知道也甚至如果他们获取保存或检索。

  • 数据 Mappers

    这些对象只是负责存储的。如果将信息存储在数据库中,这是 SQL 生存之地。或也许您使用 XML 文件来存储数据,并且数据 Mappers您正在分析从和到 XML 文件。

  • 服务

    您可以将其视为"更高级别的域对象",但而不是业务逻辑服务负责域对象Mappers之间的交互。这些结构最终创建"公用"与域业务逻辑进行交互界面。可以避免,但在对泄漏到控制器的某些域逻辑的影响.

    本主题中的ACL 实现问题相关的答案-它可能非常有用。

如何与模型进行交互?

系统必备组件︰观看讲座"全局状态和单一实例"和从干净的代码讨论"看不看的事情 !"

模型层和 MVC 三联式其他部分之间的通信仅通过服务时发生。明确的界限具有一些其他优点︰

  • 它有助于强制实施单一责任原则(SRP)
  • 提供附加余地的情况下逻辑更改
  • 将控制器保留尽可能简单,
  • 如果您以往任何时候都需要一个外部 API 提供了一个清晰的蓝图,

请确保视图控制器实例 (为该传入的请求) 都具有访问相同版本的模型层的最简单方法是,让他们都具有相同的ServiceFactory实例。我所做的那样像这样︰

/*
 * Closure for providing lazy initialization of DB connection
 */
$dbhProvider = function() {
    $instance = new PDO('mysql:host=localhost;dbname=******;charset=UTF-8', 
    '**username**', '**password**');
    $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    return $instance;
};

/* 
 * Creates basic structures, which will be used for 
 * interaction with model layer
 */
$serviceFactory = new ServiceFactory(
    new DataMapperFactory($dbhProvider),
    new DomainObjectFactory
    );
$serviceFactory->setDefaultNamespace('ApplicationService');

/*
 * Initializes the routing mechanism
 */
$configuration = json_decode(
    file_get_contents(__DIR__ . '/config/routes.json'), true);
$router = new Router(new RouteBuilder);
$router->import($configuration);

/*
 * Gets the part of URI after the "?" symbol
 */
$uri = isset($_SERVER['REQUEST_URI']) 
           ? $_SERVER['REQUEST_URI'] 
           : '/';

/*
 * Initializes the request abstraction and 
 * apply routing pattens to that instance
 */
$request = new Request($uri);
$router->route($request);

/* 
 * Initialization of View 
 */
$class = 'ApplicationView' . $request->getResourceName();
$view = new $class($serviceFactory);
$view->setDefaultTemplateLocation(__DIR__ . '/templates');

/*
 * Initialization of Controller
 */
$class = 'ApplicationController' . $request->getResourceName();
$controller = new $class($serviceFactory, $view);

/*
 * Execute the necessary command on the controller
 */
$command = $request->getCommand();
$controller->{$command}($request);

/*
 * Produces the response
 */
echo $view->render();

这将允许您可以初始化不太复杂的 MVC 应用程序 (请注意是不缓存,也不包括身份验证/授权)。正如您所看到的$serviceFactory对象的视图对象和控制器对象之间共享和跟踪的初始化的服务。

此外,您可能会注意到匿名$dbhProvider函数只交给DataMapperFactory实例中,将创建所有数据 Mappers在任何给定的服务。

与给定的代码中,控制器实例将更改状态的模型层,并 (根据 Model2 MVC) 的视图实例会从模型层中请求数据.

如何构建模型?

由于没有一个单一的"模型"类 (如上所述),真的不这样做"构建模型"。而是可以从开始进行服务,哪些是能够执行某些方法。然后实现域对象Mappers.

服务方法的示例︰

这可能是识别服务 (这查明用户标识) 中的简化的身份验证方法。

但应该考虑此示例与上面一样直接相关,因为作为身份验证过程的一部分,它应该发生后的立即$serviceFactory已创建 (检查-如果-已登录的一部分),而authenticate()方法将从内调用控制器。身份验证将密切与交互 (但是分开) 和授权服务。

namespace Service;

class Recognitions
{
    // -- snip --

    /* This is an EXAMPLE, not a production-level code.
       Do not copy-paste! */
    public function authenticate( $username, $password )
    {
        $account = $this->domainObjectFactory->build('User');
        $mapper  = $this->dataMapperFactory->build('User');

        $account->setUsername( $username );
        $mapper->fetch( $account );

        if ( $account->matchPassword($password) )
        {
            $state = $this->dataMapperFactory->build('Cookie');
        }
        else
        {
            $state = $this->dataMapperFactory->build('Session');
        }

        $state->store($account);

    }
    // -- snip --
}

如您所见,在这一级别的抽象,没有迹象的数据曾从。它可能是一个数据库,但它也可能是只是用于测试目的的 mock 对象。

P.S.这也是部分引入缓存了。例如,作为额外的映射器.

一些附加的注释︰

  1. 数据库表和模型

    尽管有时没有直接的 1:1:1 的关系之间的数据库表,域对象映射程序,在大型项目中可能会比预期不太常见︰

    • 使用单个域对象的信息可能从不同的表映射,对象本身有没有持久性数据库中。

      示例︰如果您生成的每月报告。这将从不同的表,在收集信息的但在数据库中没有神奇的MonthlyReport表。

    • 一个映射器会影响多个表。

      示例︰当您存储User对象中的数据,此域对象可以包含其他域对象的Group实例的集合。如果改变它们,存储User数据映射程序将不得不更新和/或多个表中插入项。

    • 单个域对象中的数据存储在多个表中。

      示例︰在大型系统中 (认为︰ 中等大小的社交网络),则可能是务实来存储用户的身份验证数据,分别从较大的数据块的内容,很少需要经常访问的数据。在这种情况下可能仍有一个User类,但它所包含的信息将取决于是否提取的完整详细信息。

  2. 不是模板视图。

    (如果您没有使用该模式的 MVP 变体) 的 MVC 中的视图实例负责的表示逻辑。这意味着每个视图将通常使用的一些模板。它获取模型层中的数据,根据收到的信息、 选择模板和设置值。

    从这中获得的好处之一就是 re-usability。如果您创建的ListView类,然后,与编写良好的代码,您可以具有相同的类处理演示文稿的用户列表和文章下面的评论。因为它们都有相同的表示逻辑。您只需切换模板。

    可以使用任一本机 PHP 模板,也可以使用某些第三方模板化引擎。此外可能有一些第三方库,它们是能够完全替换视图实例。

  3. 旧版本的答案呢?

    仅主要的变化是,什么在旧版本中,调用模型是实际的服务很好,"库类比"的其余部分保持。

    我看到的唯一缺陷是,这将是一个非常奇怪的库,因为它会返回您的信息从书籍,但不是允许触摸书籍本身,因为否则抽象将开始"泄漏"。我可能需要考虑更多的管接头类比。

  4. 视图控制器的实例之间的关系是什么?

    MVC 结构组成两个图层︰ 表示和模型。表示层中的主要结构是视图和控制器。

    当您处理使用 MVC 设计模式的网站时,最好是具有视图与控制器之间的 1:1 关系。每个视图表示整个页面,您的网站中,并且要处理所有传入的请求对该特定视图的专用的控制器。

    例如,来表示一个打开的项目,应该有ApplicationControllerDocumentApplicationViewDocument文章(当然可能有一些不直接与文章的XHR组件)处理时,这将包含所有表示层的主要功能.

@Rinzler,您将注意到,,无在该链接,任何称有关模型 (一个注释中除外)。它是只"面向对象的接口与数据库表"如果您尝试此模具模型的类似事情中,则会违反SRPLSP .

@hafichuk 只的情况下,合理采用ActiveRecord模式时为原型设计。当您开始编写代码,以便用于生产平均时,它将成为一种反模式,因为它混合了存储和业务逻辑。而且,由于模型层是 MVC 中的其他部分完全不知道。这不会更改这取决于原始模式的变体即使是使用 MVVM。没有"多个模型",它们未映射到任何东西。模型是一个图层。

@EddieB 的祝贺。以某种方式设法错过整个此文章中的有无"模型"点。此外,虽然不错,簿是有关应用程序模型 (例如 UML 和 whatnot)。它涉及 MVC 仅在一章中。

也看到,他发明了 MVC 文章可能具有一些优势。

The model is not a class or any single object.虽然我是与此确认在我的项目中,我将不归纳这。在最简单的模型中可能只是一个类。

一切都是业务逻辑属于在模型中,无论是数据库查询、 计算、 REST 调用,等等。

可以用模型本身进行数据访问,MVC 模式不会限制您执行该操作。您可以糖 coat 它具有服务、 mappers 和什么不是,但实际定义的模型是处理业务逻辑,东西,没有任何较少的层。它可以是某个类、 函数或使用 gazillion 对象的完整模块如果这就是您所希望。

总是有一个单独的对象,可实际执行的数据库查询,而不是让它们直接执行模型中更容易︰ 这会特别方便时单元测试 (由于注入模拟数据库依赖关系模型中的 easiness):

class Database {
   protected $_conn;

   public function __construct($connection) {
       $this->_conn = $connection;
   }

   public function ExecuteObject($sql, $data) {
       // stuff
   }
}

abstract class Model {
   protected $_db;

   public function __construct(Database $db) {
       $this->_db = $db;
   }
}

class User extends Model {
   public function CheckUsername($username) {
       // ...
       $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE ...";
       return $this->_db->ExecuteObject($sql, $data);
   }
}

$db = new Database($conn);
$model = new User($db);
$model->CheckUsername('foo');

此外,在 PHP 中,很少需要捕获/重新抛出异常回溯会保留,因为特别是在您的示例情形。只需让引发,而是在控制器中捕捉该异常。

我的结构是非常接近,我认为它只是分离出一些更多。为什么我传递解决连接的原因是因为具有所需数据块在事务中运行。我想要添加的用户,然后将用户添加到角色,而角色,如果一个失败。我无法理清它的唯一方法是通过连接。

-1︰ 它也恰好是完全错误的。模型不是一种抽象的表。

在 Web 的"MVC"中,您可以执行任何您请。

原始的概念(1)描述为业务逻辑模型。它应代表应用程序状态并实施一些数据的一致性。这种方法通常称为"fat 模型"。

大多数 PHP 框架按照更浅表的方法,其中模型是刚的数据库接口。但在这些起码模型仍应验证传入的数据和关系。

无论哪种情况你不很远如果分隔到另一个层的 SQL 资料或数据库调用。这种方法只需要考虑实际数据行为而不是实际的存储 API。(但是不合理划分太细。您如永远不能够替换数据库后端文件存储如果,不提前设计。)

链接是无效的 (404)

在我种情况下我有处理所有直接数据库交互查询,如获取,而且这样的数据库类。如果我不得不将我的数据库从MySQL改为PostgreSQL因此也不存在任何问题。因此增加的额外的一层非常有用。

每个表可以有其自己的类,有其特定的方法,但要真正获得数据,它允许数据库类如何处理它︰

文件Database.php

class Database {
    private static $connection;
    private static $current_query;
    ...

    public static function query($sql) {
        if (!self::$connection){
            self::open_connection();
        }
        self::$current_query = $sql;
        $result = mysql_query($sql,self::$connection);

        if (!$result){
            self::close_connection();
            // throw custom error
            // The query failed for some reason. here is query :: self::$current_query
            $error = new Error(2,"There is an Error in the query.
<b>Query:</b>
{$sql}
");
            $error->handleError();
        }
        return $result;
    }
 ....

    public static function find_by_sql($sql){
        if (!is_string($sql))
            return false;

        $result_set = self::query($sql);
        $obj_arr = array();
        while ($row = self::fetch_array($result_set))
        {
            $obj_arr[] = self::instantiate($row);
        }
        return $obj_arr;
    }
}

表对象 classL

class DomainPeer extends Database {

    public static function getDomainInfoList() {
        $sql = 'SELECT ';
        $sql .='d.`id`,';
        $sql .='d.`name`,';
        $sql .='d.`shortName`,';
        $sql .='d.`created_at`,';
        $sql .='d.`updated_at`,';
        $sql .='count(q.id) as queries ';
        $sql .='FROM `domains` d ';
        $sql .='LEFT JOIN queries q on q.domainId = d.id ';
        $sql .='GROUP BY d.id';
        return self::find_by_sql($sql);
    }

    ....
}

我希望此示例可帮助您创建良好的结构。

"所以如果我有更改为 PostgreSQL 数据库 MySQL 不会有任何问题。"Uhhhmmm 与上面的代码必须改变的巨大问题 imo。

我看到我的答案有意义越来越低之后编辑,和作为时间的推移。但它应该在此处

在该示例中的Database不是类。它只是包装的功能。此外,如何能不能用"表对象类"没有对象?

@tereško 我已阅读的文章很多,他们是很好。但是,我找不到任何地方任何完整框架来研究。您知道"做它右"的其中一个吗?或者至少一个可以做到像您和一些其他人这里上因此说要做吗?谢谢。

我可能是这样,但我想指出的 PDO 几乎解决了无需为了便于以后的更改创建 DB 图层的问题。

多个 oftenly 的大多数应用程序将具有数据、 显示和处理的一部分,我们只把所有那些以字母MV C.

(M)型号有保存应用程序状态的特性,它不知道VC有关的任何事情.

视图 (V)--> 已显示应用程序的格式,只知道 how 到摘要模型并不费心C.

(C) 控制器---> 具有处理应用程序的一部分,用作 M 和 V 之间的布线取决于这两个M,与不同的MV V.

完全没有考虑彼此之间的分离。在将来的任何更改或增强功能可以添加很容易。

内容来源于Stack Overflow How should a model be structured in MVC?
请输入您的翻译

How should a model be structured in MVC?

确认取消