【问题标题】:Web MVC: how to structure the Model layer? [closed]Web MVC:如何构建模型层? [关闭]
【发布时间】:2012-09-12 18:20:12
【问题描述】:

我将以user 对象为例。用户需要注册、登录、注销、编辑(例如,更改电子邮件)等。

所以一方面我有一个user 对象,其中包括各种类变量(伪、电子邮件等)以及getter 和setter,也许还有一些不处理db 的函数。

另一方面,我有一个 DAO 类,它是通过各种 MySQL / PDO 查询(创建记录、更新、检索信息等)直接处理数据库的对象。

是否有任何理由不让user 对象直接与DAO 对象交互?换句话说,当Controller 请求与现有user 实例相关的数据库查询时(例如,在注册过程中),它是否应该简单地调用user 中的一个函数,而该函数本身又调用DAO 中的一个函数,还是中间应该有一层?

我已经看到控制器调用第三个类与 DAO 交互并将user 实例作为参数传递的示例。有时,第三层负责创建user 实例并处理DAO。在我看来,用于处理DAO 的所有函数都可以驻留在user 对象中。我错过了什么?

【问题讨论】:

    标签: php model-view-controller


    【解决方案1】:

    如果您遵循 MVC 设计模式,那么 没有理由User 实例位于控制器中。它应该是模型层的一部分,而不是泄漏到表示层。

    看来你所说的“控制器”其实更像是一个服务,它应该是模型层的一部分,负责处理domain objects在数据存储相关结构上的交互(mappers,@987654323 @,DAO)。

    基本上,您缺少的是正确的关注点分离。

    控制器应该只是将数据传递给识别或一些用户管理服务,而不是处理域业务逻辑。所述服务应该初始化User 对象,验证数据并尝试保存它。控制器不应该知道这些。

    类似这样的:

    class SomeController
    {
         // ---- snip ----
    
         public function postRegister( $request )
         {
              $accounts = $this->serviceFactory->create('AccountManagement');
              $account->create( $request->getPost('username'),
                                $request->getPost('email'),
                                $request->getPost('password'),
                                $request->getPost('password2') );
    
              $this->view->setState( View::STATE_POST );
         }
    
         // ---- snip ----
    }
    

    class AccountManagement extends Service
    {
         // ---- snip ----
    
        public function addUser( $username, $email, $password, $repeated_password )
        {
    
            $user = $this->domainObjectFactory->create( 'User' );
    
            $user->setNickname( $username );
            $user->setEmail( $email );
            $user->setPassword( $password );
            $user->matchRepeatedPassword( $repeated_password );
    
            if ( $user->isValid() )
            {
                $dao = $this->DAOFactory->create( 'User' );
                $dao->save( $user );
            }
    
            // additional code for saving the error state
            // if domain object turns out to be invalid
            // or DAO returns an error
        }
    
         // ---- snip ----
    }
    

    附注您可能会发现 this post 相关。

    【讨论】:

    • 投反对票的人应该解释原因。
    • 使用类常量有什么好处?像 View::STATE_POST ?除了控制器和视图,你在其他地方使用它吗?为什么不直接使用 $this->view->setState('post');
    • 无,这是过时的帖子。那时我仍然被所有框架垃圾所误导,并认为控制器必须向视图传达一些信息。
    • 但是您是否以类似的方式使用类常量?我的问题是关于常量而不是状态:)
    • 现在我只在模型层中使用类常量来表示状态和错误代码。例如:$user->setStatus(User::STATUS_BANNED);if ($auth->hasError(Error::DUPLICATE_EMAIL)) { ...。我基本上使用类常量来抽象和解释数字状态。它还让我可以在单个位置更改基础值,而无需对所有代码进行 grep 仅将“3”替换为“4”。
    【解决方案2】:

    我认为您正在为模型描述某种适配器或网关,其中您有一个调用适配器的类(它可能与数据库或 XML 数据源交互),然后将结果分配给您的实例模型(即将每个匹配的用户结果分配给您的 User 模型的实例)。

    过去,我采用了您所描述的方法,其中您拥有表示一个项目的模型,然后是一个 DAO 将这些项目保存并检索到数据源(无论是数据库还是其他),并且执行逻辑的控制器。所以一个非常基础的版本:

    UsersDAO.php

    class UsersDAO extends DAO
    {
        public function save(User $user)
        {
            return is_null($user->id) ? $this->insert($user)
                                      : $this->update($user);
        }
    
        public function insert(User $user)
        {
            $sql = "INSERT INTO `users` (`username`, `password`)
                    VALUES (:username, :password)";
    
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':username', $user->username);
            $stmt->bindParam(':password', $user->password);
            $stmt->execute();
        }
    }
    

    用户.php

    class User
    {
        public $id;
        public $username;
        public $password;
        public $email;
    }
    

    UsersController.php

    class UserController extends Controller
    {
        public function register()
        {
            $usersDao = new UserDAO;
    
            if ($_POST) {
                $user = new User;
                $user->username = $_POST['username'];
                $user->password = $_POST['password'];
    
                $userDao->save($user);
            }
        }
    }
    

    希望这会有所帮助。

    【讨论】:

    • -1 :控制器中没有任何代码。您在表示层中有域业务逻辑。而你 User "class" 不提供封装或行为。
    • 哦..您的UsersDAO 代码示例实际上是一个数据映射器not a DAO。 DAO 不直接与存储交互
    • @tereško 随时发布您的解决方案作为答案。另外,当我什至还没有发布表示层的示例时,不知道你怎么能说我在表示层中有域业务逻辑。
    • 控制器是表示层的一部分,仅供参考。
    • 这是正确的方法。 Teresko 非常关注概念抽象理论,以至于他提供的解决方案违背了面向对象编程。 OO 规则 #1 - 使用对象。
    猜你喜欢
    • 2011-08-17
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 2022-01-19
    • 2012-03-09
    相关资源
    最近更新 更多