我应该将登录功能的应用程序逻辑放在哪里


Where should I place the application logic for login functionality?

我有一个关于存储相当复杂的应用程序逻辑的最佳位置的问题。

假设我想允许用户登录网站。登录过程应包括以下步骤:

  1. 在表单字段中,对用户的电子邮件进行哈希处理
  2. 在身份验证表中查找用户的电子邮件哈希,以确保用户存在(身份验证表仅存储加密的电子邮件、电子邮件哈希、user_id、和密码哈希)
  3. 如果找到用户,则验证其密码
  4. 重新生成会话 ID
  5. 将新会话存储在数据库中

使用数据映射器模式,我有以下三个模型参与此过程

/User/
 - User.php
 - UserMapper.php
/Auth/
 - Auth.php
 - AuthMapper.php
/Session/
 - Session.php
 - SessionMapper.php

因此,登录用户的函数如下所示:

function login($email, $password)
{
    $security = new 'Lib'Security;
    $authMapper = new 'Models'Auth'AuthMapper($this->db);
    $userMapper = new 'Models'User'UserMapper($this->db);
    $session = new 'Models'Session'Session;
    $sessionMapper = new 'Models'Session'SessionMapper($this->db);
    $email_hash = $security->simpleHash($email);
    if (!$auth = $authMapper->fetchWhere('email_hash', $email_hash))
    {
        echo 'User doesnt exist'; 
        return;
    }
    if (!$auth->verifyPassword($password))
    {
        echo 'Password not correct'; 
        return;
    }
    $user = $userMapper->fetchById($auth->user_id);
    $session->createUserSession($user);
    $sessionMapper->save($session);
}

这里有一些问题。首先是缺乏依赖注入。第二个是,这是一个繁琐的代码块,可以使用我可能想要提供登录功能的每个地方。

那么这个逻辑应该在哪里呢?在控制器中?在用户域对象中?在身份验证域对象中?这似乎有点循环 - 数据映射器的全部意义在于域对象甚至不处理自身的持久性,更不用说其他对象了......它应该放置在/User/或/Auth/模型中的用户或身份验证服务层中吗?

我对这种事情的最佳实践有点迷茫。

另外请记住,我这样做是为了学习目的,所以我不想只使用像Symfony这样的东西。

为了回答我自己的问题,我决定最好的地方是创建一个接受LoginHandlerInterface接口作为构造函数参数的AccountController

然后,帐户控制器如下所示:

namespace App'Controllers;
class AccountController
{
    protected $LoginHandler;
    public function __construct('Framework'Interfaces'LoginHandlerInterface $LoginHandler)
    {
        $this->LoginHandler = $LoginHandler;
    }
    public function login()
    {
        if (/* ...  form validation stuff ... */)
        {
            try 
            {
                $this->LoginHandler->login($email, $password);
            }
            catch ('Framework'Exceptions'Login $e) 
            {
                // Assign login errors to template etc...
            } 
        }
    }
}

然后,无论我最终使用哪种LoginHandler,它都有完成所有登录所需的一切(查找用户,验证密码,更新会话等)。这使我的AccountController保持干净、灵活和可测试。

我通过自动解析构造函数依赖项的 IoC 容器中的配置注入所需的LoginHandler(和RegistrationHandler,我在这里没有显示)。

身份验证应该处理登录,如果它失败返回 false, 如果为 true,则执行会话的逻辑并返回 true。

所以在你的控制器中,你会做类似if(Auth->login($email,$password))

ps:对于这种类型的工作流程,我更喜欢使用单例模式(因为它破坏了单元测试),但我确实认为它更适合您。