PHP MVC:Data Mapper模式:类设计 [英] PHP MVC: Data Mapper pattern: class design

查看:102
本文介绍了PHP MVC:Data Mapper模式:类设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a web MVC application with domain objects and data mappers. The class methods of the data mappers contain all database querying logic. I'm trying to avoid mirroring any database structure and, therefore, to achieve the maximum flexibility in constructing the sql statements. So, in principle, I'm trying to not make use of any ORM or ActiveRecord structure/pattern AT ALL.

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

Let me give you an example: Normally, I could have an abstract class AbstractDataMapper inherited by all specific data mapper classes - like the UserDataMapper class. And then I could define a findById() method in AbstractDataMapper, to fetch a record of a specific table - like users - by a given id value, e.g. user id. But this would imply that I'd always fetch a record from a single table, without the possibility to use any left joins to also fetch some other details from some other tables corresponding to the given id - user id.

所以,我的问题是: 在这些条件下(我本人必须这样做),我应该实现一个抽象的数据映射器类,还是每个数据映射器类都应包含其自己的数据访问层的完全专有"实现?

So, my question is: Under these conditions - to which I myself obliged to, should I implement an abstract data mapper class, or each data mapper class should contain its own completely "proprietary" implementation of the data-access layer?

我希望我能表达清楚我的想法.如果我不清楚或您有任何疑问,请告诉我.

I hope I could express my idea clear. Please tell me, if I was somehow unclear or you have any questions.

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

Thank you very much for your time and patience.

推荐答案

如果我理解您的意思...

If I understood your point ...

让所有具体的映射器从一个通用类继承SQL时,您会遗漏几个问题:

Having all your concrete mappers inheriting SQL from a common class has several issues that you have missed:

    域对象中的
  • 参数名称取决于列的名称
  • 映射器中有一个获取方法",没有对应的表
  • 您仍然具有超类所期望的配置(表名)
  • 数据库模式必须以id作为所有PRIMARY KEY列的名称
  • parameter names in your domain objects depend on the names of columns
  • there is a "fetching method" in mappers, that don't have a corresponding table
  • you still have configuration (table name), that is expected by superclass
  • the DB schema must have id as name for all of your PRIMARY KEY columns

现在,我要尝试解开每个包装.

Now, I'm gonna try to unpack each of those.

要创建共享的findById()方法,唯一实用的方法是围绕以下内容进行构建:

To create a shared findById() method, the only pragmatic approach is to build it around something like this:

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

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

The main issue actually is the wildcard * symbol.

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

There are two major approaches for populating an entity using a data mapper: use setters or use reflection. In both cases the "names" of a parameters/setters is implied by columns, that you have selected.

在常规查询中,您可以执行类似SELECT name AS fullName FROM ...的操作,该查询使您可以将查询用于重命名字段.但是采用统一方法",没有好的选择.

In a normal query you can do something like SELECT name AS fullName FROM ..., which lets you to use the query for re-naming the fields. But with a "unified approach", there are no good options.

问题是,除非您具有每表一个映射器的结构(在这种情况下,活动记录开始看起来像实用选项),否则最终将遇到几种(真正常见的)边缘情况"方案映射器:

So, the thing is, unless you have a mapper-per-table structure (in which case an active record starts look like pragmatic option), you will end up with few (really common) "edge case" scenarios for your mappers:

  • 仅用于保存数据
  • 使用集合而非单个实体进行交易
  • 聚合来自多个表的数据
  • 使用具有组合键的表
  • 实际上不是表,而是SQL视图
  • ...或上述各项的组合

您的原始想法在一个小型项目中就可以很好地工作(一个或两个映射器是边缘案例").但是对于大型项目,findById()的使用将是一个例外,而不是常规.

Your original idea would work just fine in a small scale project (with one or two mappers being an "edge case"). But with a large project, the usage of findById() will be the exception not the norm.

要在超类中实际获得此findById()方法,您将需要一种与表名称进行通信的方法.这意味着您在类定义中有类似protected $tableName的内容.

To actually get this findById() method in the superclass, you will need a way to communicate the table name to it. Which would mean, that you have something like protected $tableName in you class definition.

您可以通过在抽象映射器类中添加abstract function getTableName()来缓解这种情况,该类在实现时会返回全局常数的值.

You can mitigate it by having abstract function getTableName() in your abstract mapper class, which, when implemented, returns a value of global constant.

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

But what happens, when your mapper need to work with multiple tables.

在我看来,这是一种代码气味,因为信息实际上跨越了两个边界(因为缺少更好的单词).当此代码中断时,将在超类中显示SQL错误,而不是错误的起源(特别是如果使用常量的话).

It seems like a code smell to me, because information actually crosses two boundaries (for lack of better word). When this code breaks, the error will be shown for SQL in the superclass, which isn't where the error originated from (especially, if you go with constants).

这是更具争议性的观点:)

This is a bit more controversial opinion :)

据我所知,调用所有主列id的做法来自各种ORM.由此产生的惩罚仅适用于可读性(和代码维护).考虑以下两个查询:

As far as I can tell, the practice of calling all primary columns id comes from various ORMs. The penalty, that this incurs, applies only to readability (and code maintenance). Consider these two queries:

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"在什么情况下代表什么变得越来越困难.

As the DB schema grows and the queries become more complex, it gets harder and harder to actually keep track of, what the "id" stands for in what case.

我的建议是,当列是主键时还是外键,请尝试使用相同的名称(可能的话,因为在某些情况下,例如关闭表",它是不可行的.)基本上,所有列相同类型的商店ID应该具有相同的名称.

My recommendation would be to try same name for column, when it is a primary as when it is a foreign key (when possible, because in some cases, like for "closure tables, it's not viable). Basically, all columns that store IDs of same type, should have the same name.

作为次要好处,您可以获得USING()语法糖.

As a minor bonus, you get the USING() syntax sugar.

坏主意.您基本上是在破坏 LSP .

Bad idea. You are basically breaking LSP.

这篇关于PHP MVC:Data Mapper模式:类设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆