栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

在PHP中正确的存储库模式设计?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

在PHP中正确的存储库模式设计?

我以为我会努力回答自己的问题。接下来的内容只是解决我原始问题1-3中的一种方法。

免责声明:在描述模式或技术时,我可能并不总是使用正确的术语。 抱歉

目标:

  • 创建一个用于查看和编辑的基本控制器的完整示例
    Users
  • 所有代码都必须是完全可测试和可模拟的。
  • 控制器不应该知道数据的存储位置(意味着可以更改)。
  • 显示SQL实现的示例(最常见)。
  • 为了获得最佳性能,控制器应该只接收所需的数据,而不能接收其他字段。
  • 为了简化开发,实现应利用某种类型的数据映射器。
  • 实现应具有执行复杂数据查找的能力。

解决方案

我将持久性存储(数据库)交互分为两类: R (读取)和 CUD
(创建,更新,删除)。我的经验是,读取确实是导致应用程序运行缓慢的原因。而且,尽管数据处理(CUD)实际上较慢,但它发生的频率却要低得多,因此也就少了很多问题。

CUD
(创建,更新,删除)很容易。这将涉及使用实际模型,然后将其传递给我

Repositories
以保持持久性。请注意,我的存储库仍将提供Read方法,但仅用于对象创建而不是显示。以后再说。

R
(读取)不是那么容易。这里没有模型,只是重视对象。如果愿意,请使用数组。这些对象可能代表单个模型或许多模型的混合,实际上是任何东西。它们本身并不是很有趣,但是它们是如何产生的。我正在使用我所说的

QueryObjects

编码:

用户模型

让我们从基本的用户模型开始简单。请注意,根本没有ORM扩展或数据库内容。只是纯粹的模特荣耀。添加您的getter,setter,validation等。

class User{    public $id;    public $first_name;    public $last_name;    public $gender;    public $email;    public $password;}

仓库接口

在创建用户存储库之前,我想创建我的存储库界面。这将定义存储库必须遵循的“合同”,以便供我的控制器使用。记住,我的控制器将不知道数据的实际存储位置。

请注意,我的存储库将只包含这三种方法。该

save()
方法仅根据用户对象是否设置了ID来负责创建和更新用户。

interface UserRepositoryInterface{    public function find($id);    public function save(User $user);    public function remove(User $user);}

SQL存储库实现

现在创建我的接口实现。如前所述,我的示例将使用SQL数据库。请注意,使用数据映射器可以避免编写重复的SQL查询。

class SQLUserRepository implements UserRepositoryInterface{    protected $db;    public function __construct(Database $db)    {        $this->db = $db;    }    public function find($id)    {        // Find a record with the id = $id        // from the 'users' table        // and return it as a User object        return $this->db->find($id, 'users', 'User');    }    public function save(User $user)    {        // Insert or update the $user        // in the 'users' table        $this->db->save($user, 'users');    }    public function remove(User $user)    {        // Remove the $user        // from the 'users' table        $this->db->remove($user, 'users');    }}

查询对象接口

现在,我们的存储库负责处理 CUD(创建,更新,删除),我们可以专注于R(读取)。查询对象只是某种类型的数据查找逻辑的封装。他们不是查询生成器。通过像我们的存储库一样抽象它,我们可以更改它的实现并对其进行测试。查询对象的示例可能是

AllUsersQuery
or
AllActiveUsersQuery
或什至
MostCommonUserFirstNames

您可能在想:“我不能只在存储库中为这些查询创建方法吗?” 是的,但是这就是为什么我不这样做的原因:

  • 我的存储库用于处理模型对象。在现实世界中的应用中,
    password
    如果我要列出所有用户,为什么我需要获得该字段?
  • 存储库通常是特定于模型的,但是查询通常涉及多个模型。那么,您将方法放入哪个存储库?
  • 这使我的存储库非常简单-而不是an肿的方法类。
  • 现在,所有查询都组织到各自的类中。
  • 实际上,在这一点上,存在存储库只是为了抽象我的数据库层。

对于我的示例,我将创建一个查询对象以查找“ AllUsers”。这是界面:

interface AllUsersQueryInterface{    public function fetch($fields);}

查询对象实现

在这里,我们可以再次使用数据映射器来帮助加快开发速度。注意,我允许对返回的数据集(字段)进行一次调整。就我想操纵已执行的查询而言,这差不多。记住,我的查询对象不是查询生成器。他们只是执行特定的查询。但是,由于我知道我可能会在许多不同的情况下经常使用此功能,因此我可以指定字段。我永远不想返回不需要的字段!

class AllUsersQuery implements AllUsersQueryInterface{    protected $db;    public function __construct(Database $db)    {        $this->db = $db;    }    public function fetch($fields)    {        return $this->db->select($fields)->from('users')->orderBy('last_name, first_name')->rows();    }}

在继续介绍控制器之前,我想展示另一个示例来说明它的功能。也许我有一个报告引擎,需要为创建报告

AllOverdueAccounts
。对于我的数据映射器来说,这可能很棘手,
SQL
在这种情况下,我可能想写一些实际的东西。没问题,此查询对象如下所示:

class AllOverdueAccountsQuery implements AllOverdueAccountsQueryInterface{    protected $db;    public function __construct(Database $db)    {        $this->db = $db;    }    public function fetch()    {        return $this->db->query($this->sql())->rows();    }    public function sql()    {        return "SELECT...";    }}

这样可以很好地将我对这份报告的所有逻辑归为一类,并且易于测试。我可以嘲笑它,甚至完全使用其他实现。

控制器

现在最有趣的部分-将所有部分组合在一起。请注意,我正在使用依赖项注入。通常,依赖项被注入到构造函数中,但是我实际上更喜欢将它们直接注入到我的控制器方法(路由)中。这样可以最大程度地减少控制器的对象图,而我实际上更容易理解。请注意,如果您不喜欢这种方法,请使用传统的构造方法。

class UsersController{    public function index(AllUsersQueryInterface $query)    {        // Fetch user data        $users = $query->fetch(['first_name', 'last_name', 'email']);        // Return view        return Response::view('all_users.php', ['users' => $users]);    }    public function add()    {        return Response::view('add_user.php');    }    public function insert(UserRepositoryInterface $repository)    {        // Create new user model        $user = new User;        $user->first_name = $_POST['first_name'];        $user->last_name = $_POST['last_name'];        $user->gender = $_POST['gender'];        $user->email = $_POST['email'];        // Save the new user        $repository->save($user);        // Return the id        return Response::json(['id' => $user->id]);    }    public function view(SpecificUserQueryInterface $query, $id)    {        // Load user data        if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) { return Response::notFound();        }        // Return view        return Response::view('view_user.php', ['user' => $user]);    }    public function edit(SpecificUserQueryInterface $query, $id)    {        // Load user data        if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) { return Response::notFound();        }        // Return view        return Response::view('edit_user.php', ['user' => $user]);    }    public function update(UserRepositoryInterface $repository)    {        // Load user model        if (!$user = $repository->find($id)) { return Response::notFound();        }        // Update the user        $user->first_name = $_POST['first_name'];        $user->last_name = $_POST['last_name'];        $user->gender = $_POST['gender'];        $user->email = $_POST['email'];        // Save the user        $repository->save($user);        // Return success        return true;    }    public function delete(UserRepositoryInterface $repository)    {        // Load user model        if (!$user = $repository->find($id)) { return Response::notFound();        }        // Delete the user        $repository->delete($user);        // Return success        return true;    }}

最后的想法:

这里要注意的重要事项是,当我修改(创建,更新或删除)实体时,我正在使用真实的模型对象,并通过存储库执行持久性。

但是,当我显示(选择数据并将其发送到视图)时,我不是在使用模型对象,而是使用普通的旧值对象。我只选择我需要的字段,并且它是经过设计的,以便可以最大限度地提高数据查找性能。

我的存储库保持非常干净,而是将此“混乱”组织到我的模型查询中。

我使用数据映射器来帮助开发,因为为常见任务编写重复的SQL太荒谬了。但是,您绝对可以在需要的地方编写SQL(复杂的查询,报告等)。当您这样做时,它会很好地藏在一个适当命名的类中。

我很想听听您对我的做法的看法!


2015年7月更新:

我在评论中被问到我所有这些都以什么结尾。好吧,实际上相差不远。说实话,我还是不太喜欢存储库。我发现它们对于基本的查询(特别是如果您已经在使用ORM)来说是多余的,并且在处理更复杂的查询时显得凌乱。

我通常使用ActiveRecord样式的ORM,因此大多数情况下,我只会在整个应用程序中直接引用这些模型。但是,在查询比较复杂的情况下,我将使用查询对象使这些查询更可重用。我还应注意,我总是将模型注入方法中,从而使它们在测试中更易于模拟。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/416994.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号