我有一个带有域对象和数据映射器的 Web MVC 应用程序。数据映射器的类方法包含所有数据库查询逻辑。我试图避免镜像任何数据库结构,因此,在构建 sql 语句时实现最大的灵 active 。因此,原则上,我尽量不使用任何 ORM 或 ActiveRecord 结构/模式。
举个例子:
通常,我可以有一个抽象类 AbstractDataMapper 由所有特定数据映射器类继承 - 如 UserDataMapper 类。然后我可以在 AbstractDataMapper 中定义一个 findById() 方法,通过给定的 id 值,例如用户身份。但这意味着我总是从单个表中获取记录,而不可能使用任何左连接来从对应于给定 id - 用户 id 的其他一些表中获取一些其他详细信息.
所以,我的问题是: 在这些条件下(我自己有义务),我应该实现一个抽象数据映射器类,还是每个数据映射器类都应该包含其自己完全“专有”的数据访问层实现?
我希望我能把我的想法表达清楚。如果我不清楚或者您有任何疑问,请告诉我。
非常感谢您的时间和耐心等待。
最佳答案
如果我理解你的意思......
让所有的具体映射器从一个公共(public)类继承 SQL 有几个您忽略的问题:
id 作为所有 PRIMARY KEY 列的名称现在,我将尝试解压其中的每一个。
要创建共享的 findById() 方法,唯一实用的方法是围绕以下内容构建它:
"SELECT * FROM {$this->tableName} WHERE id = :id"
主要问题实际上是通配符 * 符号。
使用数据映射器填充实体有两种主要方法:使用 setter 或使用反射。在这两种情况下,参数/ setter 的“名称”都由您选择的列暗示。
在普通查询中,您可以执行类似SELECT name AS fullName FROM ... 的操作,这样您就可以使用查询重命名 字段。但有了“统一的方法”,就没有好的选择了。
id获取数据吗?所以,问题是,除非你有一个 mapper-per-table 结构(在这种情况下,事件记录开始看起来像是实用选项),否则你最终会遇到一些(非常常见的)“边缘案例”场景映射器:
您最初的想法在小规模项目中效果很好(一个或两个映射器是“边缘案例”)。但是对于大型项目,findById() 的使用将是异常(exception)而不是常态。
要在父类(super class)中实际获得此 findById() 方法,您需要一种方法将表名传递给它。这意味着,您的类定义中有类似 protected $tableName 的内容。
您可以通过在抽象映射器类中使用 abstract function getTableName() 来缓解它,该类在实现时返回一个全局常量值。
但是,当您的映射器需要处理多个表时会发生什么。
对我来说,这似乎是一种代码味道,因为信息实际上跨越了两个边界(找不到更好的词)。当此代码中断时,将为父类(super class)中的 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() 语法糖。
坏主意。你基本上打破了LSP .
关于PHP MVC : Data Mapper pattern: class design,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44855707/