我有一个带有域对象和数据映射器的Web MVC应用程序。数据映射器的类方法包含所有数据库查询逻辑。我试图避免镜像任何数据库结构,因此,在构造sql语句时实现最大的灵活性。因此,原则上,我试图不使用任何ORM或ActiveRecord结构/模式。
我举个例子:
通常,我可以有一个由所有特定数据映射器类继承的抽象类AbstractDataMapper
- 比如UserDataMapper
类。然后我可以在findById()
中定义AbstractDataMapper
方法,以获取特定表格的记录,例如users
- 按给定的id
值,例如用户身份。但是这意味着我总是从单个表中获取记录,而不可能使用任何左连接来从与给定id
- 用户ID相对应的其他表中获取一些其他详细信息。
所以,我的问题是: 在这些条件下 - 我自己有义务,我应该实现一个抽象的数据映射器类,还是每个数据映射器类应该包含它自己的数据访问层的完全“专有”实现?
我希望我能清楚表达自己的想法。请告诉我,如果我不清楚或你有任何问题。
非常感谢您的时间和耐心。
答案 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;你的地图制作者的场景:
你的原创想法在一个小规模的项目中可以正常工作(有一个或两个映射器是一个"边缘情况")。但是对于一个大型项目,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()
语法糖。
糟糕的主意。你基本上打破LSP。