应如何模型MVC的结构? [英] How should a model be structured in MVC?

查看:122
本文介绍了应如何模型MVC的结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚开MVC框架的把握和我常常不知道有多少code必须在模型中去。我往往有这样的方法数据访问类:

 公共职能CheckUsername($连接,$用户名)
{
    尝试
    {
        $数据=阵列();
        $数据['用户名'] = $的用户名;        //// SQL
        $的SQL =SELECT用户名FROM。 $这个 - > usersTableName。 其中username =:用户名;        //// Execute语句
        返回$这个 - > ExecuteObject($连接,$ sql中,$的数据);
    }
    赶上(例外$ E)
    {
        扔$ê;
    }
}

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

如果模型对象拥有所有的数据库映射特性以及在code以上还是确定以code分离出来,实际上做的数据库的工作?

我就会有四层?


解决方案

  

<分> 声明:以下是我所理解的在基于PHP的Web应用程序上下文MVC状图案的描述。所有这一切都在内容中使用的外部链接是有解释的术语和概念,和不可以意味着我自己关于这个问题的可信性。


这是我必须弄清楚的第一件事是:的模型是一个层

二:之间是有区别的经典MVC 的,我们在Web开发中使用什么。 这里有些旧的答案我写的,其中简要介绍了他们是如何不同的。

什么一种模式是无法

该模型是不是一个类或任何单个对象。这是一个非常常见的错误,使的(我确实做了,虽然原来的答案写,当我开始学习其他方式)的,因为大多数的框架延续这种误解。

这也不是一个对象 - 关系映射技术(ORM),也不数据库表的抽象。任何人谁告诉你,否则极有可能是试图的卖出的另一种全新的ORM还是一个整体框架。

什么型号是:

在适当的MVC适应,并购包含了所有的领域业务逻辑和的模型层的就是大多三种类型的结构制成:


  • 域对象


      

    一个域对象是纯粹域信息的逻辑容器;它通常重新presents的问题域空间的逻辑实体。通常所说的业务逻辑


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


  • 数据映射器

    这些目的仅用于存储负责。如果在数据库中存储信息,这将是在SQL住哪里。或者,也许你使用XML文件来存储数据,而你的数据映射器的从和到XML文件解析。


  • 服务

    您可以把它们看作更高级别的域对象,但不是业务逻辑的服务的负责的域对象的和的映射器之间的交互的。这些结构最终会与域的业务逻辑交互创建一个公的界面。你可以避开他们,但在一些漏水域逻辑进点球的控制器

    有一个相关的答案,这个问题在 ACL实现问题 - 它可能是有用的。


如何与模型交互?


  

<子>的 prerequisites:手表讲座全球国家和单身不要从清洁code会谈认准的事!


是MVC的模型层和其它部件之间的通信应该发生的,只有通过的服务的。该明确的区分有一些额外的好处:


  • 它有助于执行单一职责原则(SRP)

  • 提供时提供额外的回旋余地的逻辑变更

  • 保持控制器尽可能简单

  • 给出了一个清晰的蓝图,如果你需要一个外部API

要确保两个的最简单的方法查看控制器的实例(即传入的请求)可以访问同一版本的模型层的会为他们提供都具有相同的的ServiceFactory 实例。我会做这样的:

  / *
 *关闭用来提供数据库连接的初始化工作
 * /
$ dbhProvider =功能(){
    比如$ =新\\ PDO('MySQL的:主机=本地主机,数据库名= ******;字符集= UTF-8,
    '**用户名密码**');
    $实例 - &GT;的setAttribute(PDO :: ATTR_ERRMODE,PDO :: ERRMODE_EXCEPTION);
    $实例 - &GT;的setAttribute(PDO :: ATTR_EMULATE_ prePARES,FALSE);
    返回$实例;
};/ *
 *创建基本结构,其将用于
 *与模型层交互
 * /
$ =服务工厂新的服务工厂(
    新DataMapperFactory($ dbhProvider)
    新DomainObjectFactory
    );
$ serviceFactory-&GT; setDefaultNamespace的('\\\\应用服务');/ *
 *初始化路由机制
 * /
$配置= json_de code(
    的file_get_contents(__ DIR__'/config/routes.json'),TRUE);
$路由器=新路由器(新RouteBuilder);
$路由器 - &GT;进口($配置);/ *
 *后?获取URI的一部分符号
 * /
$ URI =使用isset($ _ SERVER ['REQUEST_URI'])
           ? $ _ SERVER ['REQUEST_URI']
           :'/';/ *
 *初始化请求抽象和
 *应用路由图纹到该实例
 * /
$ =请求新请求($ URI);
$路由器 - &GT;路径($请求);/ *
 *观的初始化
 * /
$类='\\\\ \\\\应用\\\\观。 $请求 - &GT; getResourceName();
$视图=新的$类($服务工厂);
$视图 - &GT; setDefaultTemplateLocation(__ DIR__'/模板');/ *
 *控制器的初始化
 * /
$类='\\\\ \\\\应用\\\\控制器。 $请求 - &GT; getResourceName();
$控制器=新的$类($服务工厂,$视图);/ *
 *控制器上执行必要的命令
 * /
$命令= $请求 - &GT; getCommand();
$控制器 - &GT; {$}命令($请求);/ *
 *生成响应
 * /
回声$视图 - &GT;渲染();

这会让你初始化一个不太复杂的MVC应用程序(注意,没有缓存,不支持认证/授权包括在内)。正如你所看到的, $的ServiceFactory 对象都的查看的对象的控制器的对象之间共享,并跟踪初始化的服务。

此外,您可能会注意到,匿名 $ dbhProvider 函数只传递给 DataMapperFactory 实例,这将要创建所有的数据映射器的任何给定的服务中。

通过这个给定的code时,的控制器的实例会改变的模型层的状态,而查看的实例(如每Model2的MVC)将从模型层请求数据

如何建立模型?

由于没有一个单一的模型类(如上所述),你真的不建立模型。相反,你从制作的服务的,它能够执行一定的方法开始。然后实现的域对象映射器

服务方法的一个例子:

这可能是在识别服务的简化身份验证方法(东西ascertains用户的身份)。

但你应该的不可以想直接涉及上述之一,因为作为认证过程的一部分,它应该发生的权利这个例子后的 $服务工厂创建(检查-IF-登录的部分),而认证()方法将被从控制器中调用。和认证将密切互动(但要分开)授权服务。

 命名空间服务;类赞誉
{
    // - 喀嚓 -     / *这是一个例子,而不是一个产品级别的code。
       不要复制粘贴! * /
    公共职能进行身份验证($的用户名,密码$)
    {
        $帐户= $这个 - &GT; domainObjectFactory-&GT;建立(用户);
        $映射器= $这个 - &GT; dataMapperFactory-&GT;建立(用户);        $问责&GT; setUsername($用户名);
        $ mapper-&GT;取($帐户);        如果($问责&GT; matchPassword($密码))
        {
            $状态= $这个 - &GT; dataMapperFactory-&GT;建立('曲奇');
        }
        其他
        {
            $状态= $这个 - &GT; dataMapperFactory-&GT;建立(会议);
        }        $语句&GT;存储($帐户);    }
    // - 喀嚓 -
}

正如你所看到的,在这个级别的抽象,没有任何的数据是从哪里获取的指示。这可能是一个数据库,但也可能只是用于测试的模拟对象。

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

补充几句:


  1. 数据库表和模型

    虽然有时有直接的1:1:数据库表之间一对一的关系,的域对象的和的映射的,在较大的项目可能比你不太常见预计:


    • 通过使用单一信息​​的域对象的可能会从不同的表映射,而对象本身在数据库中没有持久性。

      例如:的,如果你正在生成月度报告。这从表中不同的收集信息,但没有神奇的 MonthlyReport 数据库表中。


    • 一个单一的映射的可影响多个表。

      例如:的,当你存储从用户数据的对象,这个的域对象的可能含有的其他集合域对象 - 实例。如果你改变它们并存储用户数据映射的将有更新和/或插入多个表项。


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

      例如:的大型系统(想:一个中等大小的社交网络),它可能是务实的,以存储用户的身份验证数据和经常访问的数据分别从较大的内容块,这是很少需要。在这种情况下,你可能仍然有一个用户类,但它包含的信息将依赖的全部细节是否牵强。



  2. 视图是不是一个模板

    查看的MVC中的实例(如果你不使用模式的变化MVP),分别负责presentational逻辑。这意味着每个查看的通常会玩花样至少有几个模板。它获得从模型层的,然后,基于所接收的信息,选择一个模板,并设置值的数据。

    一个你从这个增益可重用性的好处。如果您创建一个的ListView 类的话,用精心编写code,你可以有相同的类交接的用户列表及以下意见的presentation一篇文章。因为它们都具有相同的presentation逻辑。您只需切换模板。

    您可以使用本地PHP模板或使用一些第三方模板发动机。此外,还可能有一些第三方库,它们能完全替代的查看的实例。


  3. 什么老版本的答案?

    唯一的主要的变化是,所谓的模型的旧版本,实际上是一个的服务的。 库类比的其余部分保持了pretty很好。

    这是我看到的是,这将是一个很奇怪的图书馆,因为它会从书返回您的信息,而不是让你触摸这本书本身,否则抽象将开始泄漏唯一的缺陷。我可能会觉得更恰当的比喻。


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

    的MVC结构是由两层组成:presentation和型号。在 presentation层的主要结构是意见和控制器。

    当您正在处理使用MVC设计模式的网站,最好的办法就是有1:1视图和控制器之间的关系。再$ P $每个视图psents在你的网站一整页,它有一个专用的控制器处理所有该特定视图传入的请求。

    例如,要重新present一个开放的文章,你会 \\应用程序\\控制器\\文件 \\应用程序\\查看\\文件。这将包括所有为presentation层的主要功能,当谈到与文章的(当然你可能有一些的不直接相关的文章XHR 组件)


I am just getting a grasp on the MVC framework and I often wonder how much code should go in the model. I tend to have a data access class that has methods like this:

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;
    }
}

My models tend to be an entity class that is mapped to the database table.

Should the model object have all the database mapped properties as well as the code above or is it OK to separate that code out that actually does the database work?

Will I end up having four layers?

解决方案

Disclaimer: the following is a description of how I understand MVC-like patterns in the context of PHP-based web applications. All the external links that are used in the content are there to explain terms and concepts, and not to imply my own credibility on the subject.

The first thing that I must clear up is: the model is a layer.

Second: there is a difference between classical MVC and what we use in web development. Here's a bit of an older answer I wrote, which briefly describes how they are different.

What a model is NOT:

The model is not a class or any single object. It is a very common mistake to make (I did too, though the original answer was written when I began to learn otherwise), because most frameworks perpetuate this misconception.

Neither is it an Object-Relational Mapping technique (ORM) nor an abstraction of database tables. Anyone who tells you otherwise is most likely trying to 'sell' another brand-new ORM or a whole framework.

What a model is:

In proper MVC adaptation, the M contains all the domain business logic and the Model Layer is mostly made from three types of structures:

  • Domain Objects

    A domain object is a logical container of purely domain information; it usually represents a logical entity in the problem domain space. Commonly referred to as business logic.

    This would be where you define how to validate data before sending an invoice, or to compute the total cost of an order. At the same time, Domain Objects are completely unaware of storage - neither from where (SQL database, REST API, text file, etc.) nor even if they get saved or retrieved.

  • Data Mappers

    These objects are only responsible for the storage. If you store information in a database, this would be where the SQL lives. Or maybe you use an XML file to store data, and your Data Mappers are parsing from and to XML files.

  • Services

    You can think of them as "higher level Domain Objects", but instead of business logic, Services are responsible for interaction between Domain Objects and Mappers. These structures end up creating a "public" interface for interacting with the domain business logic. You can avoid them, but at the penalty of leaking some domain logic into Controllers.

    There is a related answer to this subject in the ACL implementation question - it might be useful.

How to interact with a model?

Prerequisites: watch lectures "Global State and Singletons" and "Don't Look For Things!" from the Clean Code Talks.

The communication between the model layer and other parts of the MVC triad should happen only through Services. The clear separation has a few additional benefits:

  • it helps to enforce the single responsibility principle (SRP)
  • provides additional 'wiggle room' in case the logic changes
  • keeps the controller as simple as possible
  • gives a clear blueprint, if you ever need an external API

The easiest way to make sure that both View and Controller instances (for that incoming request) have access to the same version of the Model Layer would be to provide them both with the same ServiceFactory instance. I would do it like this:

/*
 * 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('Application\\Service');

/*
 * 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 = '\\Application\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory);
$view->setDefaultTemplateLocation(__DIR__ . '/templates');

/*
 * Initialization of Controller
 */
$class = '\\Application\\Controller\\' . $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();

This would let you initialize a not-too-complicated MVC application (notice that there is no caching nor authentication/authorization included). As you can see, the $serviceFactory object is shared between both the View object and Controller object, and keeps track of initialized services.

Also, you might notice that the anonymous $dbhProvider function is passed only to the DataMapperFactory instance, which would be creating all the Data Mappers within any given service.

With this given code, the Controller instance would change the state of the Model Layer, and the View instance (as per Model2 MVC) would request data from the Model Layer.

How to build the model?

Since there is not a single "Model" class (as explained above), you really do not "build the model". Instead you start from making Services, which are able to perform certain methods. And then implement Domain Objects and Mappers.

An example of a service method:

This might be a simplified authentication method in a recognition service (something that ascertains a user's identity).

But you should not think of this example as directly related to the one above, because as part of the authentication process, it should happen right after the $serviceFactory was created (the check-if-logged-in part), while the authenticate() method would be called from within the controller. And the authentication would closely interact with (but be separate from) the authorization service.

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 --
}

As you can see, at this level of abstraction, there is no indication of where the data was fetched from. It might be a database, but it also might be just a mock object for testing purposes.

P.S. This would also be the part where caching is introduced. For example, as an additional Mapper.

Some additional comments:

  1. Database tables and model

    While sometimes there is a direct 1:1:1 relationship between a database table, Domain Object, and Mapper, in larger projects it might be less common than you expect:

    • Information used by a single Domain Object might be mapped from different tables, while the object itself has no persistence in the database.

      Example: if you are generating a monthly report. This would collect information from different of tables, but there is no magical MonthlyReport table in the database.

    • A single Mapper can affect multiple tables.

      Example: when you are storing data from the User object, this Domain Object could contain collection of other domain objects - Group instances. If you alter them and store the User, the Data Mapper will have to update and/or insert entries in multiple tables.

    • Data from a single Domain Object is stored in more than one table.

      Example: in large systems (think: a medium-sized social network), it might be pragmatic to store user authentication data and often-accessed data separately from larger chunks of content, which is rarely required. In that case you might still have a single User class, but the information it contains would depend of whether full details were fetched.

  2. A view is not a template

    View instances in MVC (if you are not using the MVP variation of the pattern) are responsible for the presentational logic. This means that each View will usually juggle at least a few templates. It acquires data from the Model Layer and then, based on the received information, chooses a template and sets values.

    One of the benefits you gain from this is re-usability. If you create a ListView class, then, with well-written code, you can have the same class handing the presentation of user-list and comments below an article. Because they both have the same presentation logic. You just switch templates.

    You can use either native PHP templates or use some third-party templating engine. There also might be some third-party libraries, which are able to fully replace View instances.

  3. What about the old version of the answer?

    The only major change is that, what is called Model in the old version, is actually a Service. The rest of the "library analogy" keeps up pretty well.

    The only flaw that I see is that this would be a really strange library, because it would return you information from the book, but not let you touch the book itself, because otherwise the abstraction would start to "leak". I might have to think of a more fitting analogy.

  4. What is the relationship between View and Controller instances?

    The MVC structure is composed of two layers: presentation and model. The main structures in the Presentation layer are views and controller.

    When you are dealing with websites that use MVC design pattern, the best way is to have 1:1 relation between views and controllers. Each view represents a whole page in your website and it has a dedicated controller to handle all the incoming requests for that particular view.

    For example, to represent an opened article, you would have \Application\Controller\Document and \Application\View\Document. This would contain all the main functionality for presentation layer, when it comes to dealing with articles (of course you might have some XHR components that are not directly related to articles).

这篇关于应如何模型MVC的结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆