PHP MVC:数据映射器模式:类设计

时间:2017-06-30 22:46:22

标签: php oop design-patterns model-view-controller data-mapping

我有一个带有域对象和数据映射器的Web MVC应用程序。数据映射器的类方法包含所有数据库查询逻辑。我试图避免镜像任何数据库结构,因此,在构造sql语句时实现最大的灵活性。因此,原则上,我试图不使用任何ORM或ActiveRecord结构/模式。

我举个例子: 通常,我可以有一个由所有特定数据映射器类继承的抽象类AbstractDataMapper - 比如UserDataMapper类。然后我可以在findById()中定义AbstractDataMapper方法,以获取特定表格的记录,例如users - 按给定的id值,例如用户身份。但是这意味着我总是从单个表中获取记录,而不可能使用任何左连接来从与给定id - 用户ID相对应的其他表中获取一些其他详细信息。

所以,我的问题是: 在这些条件下 - 我自己有义务,我应该实现一个抽象的数据映射器类,还是每个数据映射器类应该包含它自己的数据访问层的完全“专有”实现?

我希望我能清楚表达自己的想法。请告诉我,如果我不清楚或你有任何问题。

非常感谢您的时间和耐心。

1 个答案:

答案 0 :(得分:4)

如果我明白你的意思......

让所有具体的映射器从公共类继承SQL有几个你错过的问题:

  • 域对象中的参数名称取决于列的名称
  • 有一个"提取方法"在地图制作者中,没有相应的表格
  • 您仍然拥有超类
  • 所需的配置(表名)
  • 数据库架构必须以id作为所有PRIMARY KEY
  • 的名称

现在,我要尝试打开每一个。

参数和列名称

要创建共享的findById()方法,唯一务实的方法是围绕这样的方法构建它:

"SELECT * FROM {$this->tableName} WHERE id = :id"

主要问题实际上是通配符*符号。

使用数据映射器填充实体有两种主要方法:使用setter或使用反射。在这两种情况下,"名称"您选择的列隐含了参数/设置者的信息。

在普通查询中,您可以执行SELECT name AS fullName FROM ...之类的操作,这样您就可以使用重新命名字段的查询。但是采用统一的方法",没有好的选择。

每个映射器都可以按id

获取数据

所以,问题是,除非你有一个mapper-per-table结构(在这种情况下,活动记录开始看起来像实用选项),你最终会得到一些(非常常见的)"边缘情况&# 34;你的地图制作者的场景:

  • 仅用于保存数据
  • 处理集合而不是单一实体
  • 聚合来自多个表的数据
  • 使用具有复合键的表
  • 它实际上不是一个表,而是一个SQL视图
  • ......或上述
  • 的组合

你的原创想法在一个小规模的项目中可以正常工作(有一个或两个映射器是一个"边缘情况")。但是对于一个大型项目,findById()的使用将是例外,而不是常态。

独立养育?

要在超类中实际获取此findById()方法,您需要一种方法将表名称传递给它。这意味着,您在类定义中有类似protected $tableName的内容。

您可以通过在抽象映射器类中使用abstract function getTableName()来缓解它,在实现时,它会返回一个全局常量值。

但是当您的映射器需要使用多个表时会发生什么。

对我来说,这似乎是一个代码嗅觉,因为信息实际上跨越了两个边界(缺少更好的单词)。当这段代码中断时,将在超类中显示SQL的错误,这不是错误源自的地方(特别是,如果你使用常量)。

命名主键

这是一个更有争议的意见:)

据我所知,调用所有主要列id的做法来自各种ORM。这引起的惩罚仅适用于可读性(和代码维护)。考虑这两个问题:

SELECT ar.id, ac.id 
  FROM Articles AS ar LEFT JOIN 
       Accounts AS ac ON ac.id = ar.account_id 
 WHERE ar.status = 'published'

SELECT ar.article_id, ac.account_id 
  FROM Articles AS ar LEFT JOIN 
       Accounts AS ac USING(account_id)
 WHERE ar.status = 'published'

随着数据库架构的增长和查询变得越来越复杂,实际上越来越难以跟踪," id"代表什么情况。

我的建议是为列尝试相同的名称,当它是主键时,如果它是外键(如果可能的话,因为在某些情况下,比如"关闭表,它不是可行)。基本上,存储相同类型ID的所有列应具有相同的名称。

作为小额奖励,您获得USING()语法糖。

TL; DR

糟糕的主意。你基本上打破LSP