从其他类访问登录用户对象的最佳实践

时间:2014-10-21 07:15:33

标签: php mysql session pdo

这是关于从另一个类存储和访问对象的最佳实践的问题。

我在 PHP 中使用了一个简单的自制MVC范例,该类名为 User ,并且有自己的方法和vars,基本上可以用作数据库抽象层

通过调用{strong> new 来实例化此类,该函数从给定$ userID的数据库中检索数据,或者如果没有具有该ID的用户则抛出异常。< / p>

每个页面都有自己的User($userID)类来管理页面内容,在某些情况下,页面需要调用WebViewController依赖函数,例如$loggedInUser,这可能看起来像这样:

WebViewController->displayUserFriends()

是否存在一种(遵循最佳实践)方式将LoggedInUser存储为一种全局对象,因此可以在任何类或{{1}内部访问它没有在它使用的每个类中实例化

2 个答案:

答案 0 :(得分:1)

您问题的最佳解决方案是使用dependency injection。由于您可能需要多个控制器中的当前用户,因此最好创建一个AuthenticationService(或类似),提供检查用户是否已登录并获取当前登录用户的方法并封装该常用功能。然后,您可以在所需的所有控制器中注入服务实例。

有几个独立的PHP依赖注入库:

答案 1 :(得分:0)

我认为这是一个意见问题,真的。尽管如此,我将在这里提出两个可行的选择......

选项1

...是创建一个包含当前用户的单例:

class SessionHolder
{
    private $user;

    public static function getCurrentUser()
    {
        return self::$user;
    }

    public static function setCurrentUser(User $user)
    {
        self::$user = $user;
    }
}

用法:

//Authentication does following:
SessionHolder::setCurrentUser($user);
//web view controller does following:
SessionHolder::getCurrentUser();

显而易见的优点是,在任何框架中都可以很容易地将其合并。缺点是使用单例通常是一个坏主意,因为它们使得测试代码非常困难(你不能mock它们)。跟踪您的依赖关系也很困难。要回答问题&#34;我的控制器是否依赖于活跃用户?&#34;,您需要手动搜索您的代码。

我认为测试部分非常重要,因为你迟早会在工作中偶然发现TDD(如果你还没有),因此,了解它是件好事。如何处理它的特质。

选项2

使用服务层。我认为这是构建项目最好的方法之一。例如,我经常在我的项目中使用以下结构:

  • 控制器 - 充当服务的代理,收集来自_GET_POST和诸如此类的用户输入。
  • 服务 - 例如包含业务逻辑 - 应将哪些项目放入客户的订单中。
  • 模型/ DAO层 - 包含数据库逻辑,就像如何获取数据一样。

使用这样的架构,您可以按以下方式构建代码:

  • AuthService::getCurrentUser() - 返回当前登录的用户。
  • UserService::getUserFriends(User $user) - 为任意用户返回朋友。
  • UserService::getUserFriendsForCurrentUser() - 为活跃用户返回朋友。
  • UserService::getUserFriendsForCurrentUser()使用AuthService::getCurrentUser()
  • UserService现在取决于AuthService
  • 最后,您的控制器只需调用$this->userService->getUserFriendsForCurrentUser()即可获取它们,并可选择装饰它们,然后再将它们发送到视图层。

因此,您的代码可能如下所示:

class UserService
{
    private $authService;

    public function __construct(AuthService $authService)
    {
        $this->authService = $authService;
    }

    public function getUserFriends(User $user)
    {
        //...use DAO / models here...
    }

    public function getUserFriendsForCurrentUser()
    {
        $user = $this->authService->getCurrentUser();
        if (!$user)
            throw new DomainException('Must be logged in to do that.');

        return $this->getUserFriends($user);
    }
}

优点:您的代码是可测试的,代码也看起来很干净。通过浏览构造函数,依赖性是显而易见的。业务逻辑很好地与控制器分开,控制器就像代理一样。

缺点:您需要生成更多的样板代码。它也不是真正的MVC。但是,既然你自己说你使用了自制的MVC模式,我认为这种方法也值得一试。


作为旁边节点 - 如您所见,我们使用构造函数将AuthService注入UserService。我认为值得一些评论:

  • 我们还没有使用静态方法单AuthService,因为我们想测试我们的UserService类。为了测试它,我们可能会或可能不想使用模拟。在这种特殊情况下,我们想要模拟AuthService。这样做很简单,只需在测试中实现AuthService的变体,并使用此存根创建UserService,而不是真实AuthService。如果您希望了解有关使代码可测试的良好实践的更多信息,请参阅有关TDD的更多信息,因为我不相信这是在这个问题的范围内。

  • 重要的是要知道,每当您需要使用它时,您不应该手动构建UserService所有依赖项。这就是依赖注入的用武之地 - 您只需声明UserService需要的内容,然后使用对象工厂来检索其实例。 (理想情况下,您需要在整个项目中调用对象工厂一次,并且它将在运行时基础上构建整个依赖关系树。)有关进一步阅读,请查看PHP-DI。它也在Github上。