問題描述
我有一個帶有域對象和數據映射器的 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()
方法,以獲取特定表的記錄 - 如 users
- 通過給定的 id
值,例如用戶身份.但這意味著我總是從單個表中獲取記錄,而無法使用任何左連接來從與給定 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 yourPRIMARY 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).
這個觀點有點爭議:)
據我所知,調用所有主列 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:數據映射器模式:類設計的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!