数据映射器通常是什么样子?

8
我有一个名为Cat的表格和一个名为Cat的PHP类。现在我想创建一个CatDataMapper类,以便Cat扩展CatDataMapper
我希望该数据映射器类提供基本的ORM功能,以及用于创建、编辑和删除Cat的功能。
为此,也许了解这种模式非常好的人可以给我一些有用的建议?我觉得只提供一些像update()、delete()、save()之类的函数可能会有点太简单了。
我意识到数据映射器有这个问题:首先创建Cat实例,然后初始化所有变量,如名称、毛色、眼色、咕噜声、喵声、服务员等。当一切都设置好后,调用从CatDataMapper继承的save()函数。这很简单;)
但现在,真正的问题是:您查询数据库以获取带有大量猫数据的普通无聊结果集。
PDO具有一些ORM功能来创建Cat实例。假设我使用它,甚至假设我有一个mapDataset()函数,它接受一个关联数组。但是,一旦我从数据集中获得了我的Cat对象,我就有了冗余数据。同时,二十个用户可以从数据库中获取相同的猫数据并编辑猫对象,即重命名猫,并将其保存(save()),而另一个用户仍然想设置其他毛色。当他们都保存自己的编辑时,一切都混乱了。
嗯...好的,为了使这个问题真正简洁:在这里有什么好的做法?
5个回答

12

来自 PoEA中的DataMapper

Data Mapper是一种软件层,它将内存中的对象与数据库分离。它的职责是在两者之间传输数据,并使它们彼此隔离。使用Data Mapper时,内存中的对象甚至不需要知道存在数据库;它们不需要SQL接口代码,也不需要了解数据库模式。(数据库模式总是不知道使用它的对象。)由于它是一种Mapper(473),因此领域层甚至不知道Data Mapper本身。

因此,Cat不应该扩展CatDataMapper,因为那会创建一个is-a关系并将Cat绑定到持久化层。如果您想以这种方式处理Cats的持久性,可以查看 ActiveRecord 或任何其他数据源架构模式。

通常在使用领域模型时使用DataMapper。简单的DataMapper只需基于字段映射数据库表到内存中的等效类。但是,当需要DataMapper时,通常不会有这样简单的关系。表将不会1:1地映射到您的对象。相反,多个表可能形成一个对象聚合体,反之亦然。因此,仅实现CRUD方法可能很具有挑战性。

除此之外,它是更为复杂的模式之一(在 PoEA 中占据了 15 页),通常与Repository pattern等模式结合使用。请查看页面右侧的相关问题栏以获取类似问题的答案。
至于您关于多个用户编辑同一 Cat 的问题,这是一个常见的Concurrency问题。其中一种解决方法是locking the row,当有人编辑时进行锁定。但像所有事情一样,这也可能lead to other issues

我有一个关于数据映射器(Data mapper)和表数据网关(Table Data gateway, TDG)的问题。TDG模式中,表格映射1:1与一个表格,但在一些TDG类方法中,我们需要将主表格与其他表格连接起来(与某些父表格连接以选择额外信息)。这样做是否违反了Table Data gateway模式?在我看来,只要您在TDG类方法中添加了其他数据库表格(即使是连接),或者将您的模型(领域模型或表数据)类分成两个部分,该类就变成了Data mapper类。在我看来,Data Mapper只是您的TDG类的正常演变。 - Centurion
此外,如果您实现的数据源类能够连接到各种来源(数据库或某些Web服务),那么在我看来,您已经实现了数据映射器类。 - Centurion
Fowler在POEAA中指出,TDG可能包含访问多个表/视图的代码,因此我猜您也可以在其中进行JOIN操作。但是,当然这意味着返回的结果将不再与表格1:1匹配,您还需要为那些行编写专门的Update和Insert查询,除了通用的查询。我可以看出这让您得出它看起来像Data Mapper的结论,但是Data Mapper的目的确实比仅此复杂得多。它不仅仅是JOIN => DataMapper。请参阅http://css.dzone.com/books/practical-php-patterns/practical-php-patterns-data。 - Gordon
在这种情况下,我认为更像是一个远程门面。但为什么不直接买这本书呢?毕竟它是一部经典之作。 - Gordon
当然 :) 谢谢。此外,与具有相关知识的人讨论总是一个好习惯。 - Centurion
显示剩余3条评论

2
如果您依赖像DoctrinePropel这样的ORM,基本原则是创建一个静态类来从数据库获取实际数据(例如,Propel将创建CatPeer),然后对等类检索的结果将被“水化”为Cat对象。
水化过程是将“普通乏味”的MySQL结果集转换为具有getter和setter的漂亮对象的过程。
因此,对于检索,您将使用类似CatPeer::doSelect()的东西。然后对于新对象,您首先会实例化它(或从DB中检索实例):$cat = new Cat(); 插入只需简单地执行:$cat->save(); 这相当于插入(如果对象已经存在于db中,则为更新... ORM应该知道如何通过使用主键的存在或不存在来区分新对象和现有对象)。

2
在PHP<5.3中实现数据映射器非常困难,因为您无法读取/写入受保护/私有字段。在加载和保存对象时,您有几个选择:
  1. 使用某种解决方法,例如将对象序列化,修改其字符串表示形式,并使用unserialize将其带回
  2. 使所有字段公共
  3. 保持它们是私有/受保护的,并为每个字段编写mutator/accessor
第一种方法可能会因为新版本而破坏,并且是非常粗糙的hack,第二种被认为是(非常)不好的做法。
第三个选项也被认为是不好的做法,因为您不应该为所有字段提供getter/setter,只需提供需要的字段即可。从纯DDD(领域驱动设计)的角度来看,您的模型会“损坏”,因为它包含仅由于持久性机制而需要的方法。 这也意味着现在您必须描述另一个映射以获取字段-> setter方法,而不是字段->表列。
PHP 5.3引入了通过使用反射访问/更改所有类型的字段的能力:

http://hu2.php.net/manual/en/reflectionproperty.setaccessible.php

通过这种方式,你可以实现真正的数据映射器,因为不需要为所有字段提供mutator。

1
PDO具有一些ORM功能来创建Cat实例。假设我使用了那个,或者甚至可以说我有一个mapDataset()函数,它接受一个关联数组。然而,一旦我从数据集中获得了我的Cat对象,我就有了冗余数据。同时,二十个用户可以从数据库中获取相同的猫数据并编辑猫对象,即重命名猫,并将其保存(save()),而另一个用户仍在考虑设置另一种毛色。当他们所有人保存他们的编辑时,一切都乱了。

为了跟踪数据状态通常使用IdentityMap和/或UnitOfWork来跟踪映射实体上的所有不同操作...在请求周期结束时,然后执行所有操作。


0
保持回答简短: 您有一个Cat实例。(可能扩展了CatDbMapper或Cat3rdpartycatstoreMapper) 您调用:
$cats = $cat_model->getBlueEyedCats();
//then you get an array of Cat objects, in the $cats array

如果你不知道该用什么,可以看一下一些PHP框架以更好地理解。


我认为你描述的是ActiveRecord,而不是DataMapper。 - Denis Pshenov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接