如果您想坚持使用 Data Mapper 模式,您可以从数据库中加载产品及其所有依赖项(制造商、库存、税收),也可以使用延迟加载。第一个选项不是一个好习惯。但是要使用延迟加载,您需要通过虚拟代理获得一个额外的层。
为什么?因为否则你将不得不在你的实体中放置一些数据库代码,而关于这些层的整个想法就是解耦。
那么,什么是虚拟代理?
根据 Martin Fowler (2003) 的说法:
虚拟代理是一个看起来像应该在字段中的对象,但实际上不包含任何内容的对象。只有当它的方法之一被调用时,它才会从数据库中加载正确的对象。
例子
该接口定义了将由真实实体和虚拟代理实现的方法:
// The interface
interface ManufacturerInterface
{
public function getName();
}
这是实体,您可能也在扩展一些通用模型类:
// The concrete Manufacturer class
class Manufacturer implements ManufacturerInterface
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
这是代理:
// The proxy
class ManufacturerProxy implements ManufacturerInterface
{
private $dao;
private $manufacturer;
private $manufacturerId;
public function __construct($dao, $manufacturerId)
{
$this->dao = $dao;
// set manufacturer to NULL to indicate we haven't loaded yet
$this->manufacturer = null;
}
public function getName()
{
if ($this->manufacturer === null)
{
$this->lazyLoad();
}
return $this->manufacturer->getName();
}
private function lazyLoad()
{
$this->manufacturer = $this->dao->getById($this->manufacturerId);
}
}
当实例化与制造商有某种关系的其他类(如产品)时,数据映射器将使用代理。因此,当一个产品被实例化时,它的字段被填充,并且制造商字段接收到 ManufacturerProxy 的实例而不是 Manufacturer。
Data Mapper 的实现会有所不同,所以我举一个简单的例子:
class ProductMapper {
private $dao;
private $manufacturerDao;
// .......
public function find($id) {
// call the DAO to fetch the product from database (or other source)
// returns an associative array
$product = $this->dao->find($id);
// instantiate the class with the associative array
$obj = new Product($product);
// create the virtual proxy
$obj->manufacturer = new ManufacturerProxy($this->manufacturerDao, $product['manufacturer_id']);
return $obj;
}
}
正如我所说,上面的例子非常简单。在您使用 DAO 时,我已将其包含在内,但像 Martin Fowler 这样的作者将处理 SQL 查询或任何其他底层技术的任务交给了 Data Mapper。但是也有像 Nock (2004) 这样的作者同时使用了 Data Mapper 和 Data Accessor。
使用更复杂的 Data Mapper,您只能拥有 XML 或 YAML 等语言的文件,类似 Doctrine 或 Hibernate。
刚刚结束,服务:
class ProductService {
private $productMapper;
/**
* Execute some service
* @param {int} $productId The id of the product
*/
public function someService($producId) {
// get the product from the database
// in this case the mapper is handling the DAO
// maybe the data mapper shouldn't be aware of the DAO
$product = $this->productMapper->find($productId);
// now the manufacturer has been loaded from the database
$manufacturerName = $product->manufacturer->getName();
}
}
也许您可以让 DAO 调用 Data Mapper 来创建实体,然后在服务中使用 DAO。它实际上更简单,因为您不需要重复两次方法,但这取决于您。
替代方案
如果你不想实现虚拟代理,但仍然想要一个流畅的界面,你可以切换到 Active Record 模式。有一些库可以完成这项工作,例如PHP-ActiveRecord和Paris。或者如果你想自己做,你可以看看这里和这里。Martin Fowler 的书也是一个好的开始。
参考
福勒,马丁。企业应用架构模式。艾迪生-韦斯利,2003 年。诺克,克利夫顿。数据访问模式:面向对象应用程序中的数据库交互。艾迪生-韦斯利,2004 年。