3 php oop model-view-controller design-patterns data-mapping
我有一个带有域对象和数据映射器的 Web MVC 应用程序。数据映射器的类方法包含所有数据库查询逻辑。我试图避免镜像任何数据库结构,因此在构造 sql 语句时实现最大的灵活性。因此,原则上,我尝试完全不使用任何 ORM 或 ActiveRecord 结构/模式。
让我举个例子:通常,我可以有一个AbstractDataMapper由所有特定数据映射器类继承的抽象类 - 就像该类一样UserDataMapper。然后我可以findById()在 中定义一个方法,通过给定值AbstractDataMapper(例如用户 ID)获取特定表的记录。但这意味着我总是从单个表中获取记录,而无法使用任何左连接从与给定用户 ID 对应的其他表中获取一些其他详细信息。usersidid
所以,我的问题是:在这些条件下 - 我自己有义务这样做,我应该实现一个抽象数据映射器类,还是每个数据映射器类应该包含其自己的数据访问层的完全“专有”实现?
我希望我能清楚地表达我的想法。如果我不清楚或者您有任何疑问,请告诉我。
非常感谢您的时间和耐心。
如果我明白你的意思...
让所有具体映射器从公共类继承 SQL 有几个您忽略的问题:
id的名称PRIMARY KEY现在,我将尝试逐一解开包装。
要创建共享findById()方法,唯一实用的方法是围绕以下内容构建它:
"SELECT * FROM {$this->tableName} WHERE id = :id"
Run Code Online (Sandbox Code Playgroud)
主要问题实际上是通配符*。
使用数据映射器填充实体有两种主要方法:使用 setter 或使用反射。在这两种情况下,参数/设置器的“名称”都由您选择的列隐含。
在普通查询中,您可以执行类似的操作SELECT name AS fullName FROM ...,它允许您使用查询来重新命名字段。但对于“统一方法”来说,没有什么好的选择。
id每个映射器都可以通过?获取数据。所以,问题是,除非你有一个每表映射器结构(在这种情况下,活动记录开始看起来像是实用的选项),否则你的映射器最终会遇到一些(非常常见的)“边缘情况”场景:
您最初的想法在小型项目中效果很好(一两个映射器是“边缘情况”)。但对于大型项目,使用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'
Run Code Online (Sandbox Code Playgroud)
随着数据库模式的增长和查询变得越来越复杂,实际跟踪“id”在什么情况下代表什么变得越来越困难。
我的建议是尝试对列使用相同的名称,当它是主键时和作为外键时(如果可能的话,因为在某些情况下,例如“封闭表,它是不可行的)。基本上,所有存储 ID 的列同一类型,应具有相同的名称。
作为一个小好处,你可以获得USING()语法糖。
馊主意。你基本上破坏了LSP。