PHP:5层模型

gro*_*gor 4 php service dao design-patterns model

我对如何"正确"编写我的应用程序有疑问.

我想遵循服务/ DAO/mapper/entity模型,我认为我完全理解它,但我发现我想做的一些事情可能会与这个模型的一般概念相冲突.

我有这些数据库表: 产品表及其相关表 - 制造商

我有简单的ProductDbMapper,它从数据库获取产品的数据并将它们作为Product实体返回,所以我可以像这样使用它:

echo $productEntity->getName();
$productEntity->setPrice(29.9);
Run Code Online (Sandbox Code Playgroud)

我认为能够像这样使用一些想法会很棒:

echo $productEntity->getManufacturer()->getName();
Run Code Online (Sandbox Code Playgroud)

"getManufacturer()"方法只是通过id查询制造商的另一个服务/ DAO.我知道如何获得产品制造商的正确方法是:

$product = $productService->getProduct(5);
$manufacturer = $manufacturerService->getManufacturerByProduct($product);
Run Code Online (Sandbox Code Playgroud)

但我认为"流畅"的解决方案要简单得多 - 它易于理解且使用起来很有趣.其实这很自然.我试图通过回调实现这个.我在ProductMapper中传递了将制造商的服务调用到Product实体的回调.

问题是看起来我正在尝试遵循5层模型,同时我试图避免它.所以我的问题是:这看起来是一个很好的解决方案吗?是否有意义?我怎样才能以更好的方式实现同​​样的(魔术)?

may*_*din 9

如果您想坚持使用Data Mapper模式,您可以从数据库加载产品及其所有依赖项(制造商,库存,税),也可以使用延迟加载.第一种选择不是一种好的做法.但是要使用延迟加载,您将需要通过虚拟代理获得的附加层.

为什么?因为否则你必须在你的实体中放入一些数据库代码,而关于这些层的整个想法是脱钩的.

那么,什么是虚拟代理?

根据Martin Fowler(2003)的说法:

虚拟代理是一个对象,它看起来像应该在字段中的对象,但实际上并不包含任何内容.只有在调用其中一个方法时,它才会从数据库中加载正确的对象.

该接口定义了由真实实体和虚拟代理实现的方法:

// The interface
interface ManufacturerInterface
{
    public function getName();
}
Run Code Online (Sandbox Code Playgroud)

这是实体,您可能也在扩展一些通用模型类:

// The concrete Manufacturer class
class Manufacturer implements ManufacturerInterface
{
    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是代理:

// 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在实例化与制造商有某种关系的其他类时,数据映射器将使用代理,例如产品.因此,当实例化产品时,它的字段被填充,制造商字段接收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;
    }
}
Run Code Online (Sandbox Code Playgroud)

正如我所说,上面的例子非常简单.我在你使用它时已经包含了DAO,但像Martin Fowler这样的作者将处理SQL查询或任何其他底层技术的任务交给了Data Mapper.但也有像Nock(2004)这样的作者同时使用数据映射器和数据访问器.

使用更复杂的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();
    }
}
Run Code Online (Sandbox Code Playgroud)

也许您可以让DAO调用Data Mapper来创建实体,然后在服务中使用DAO.它实际上更简单,因为您不需要重复两次方法,但这取决于您.

替代方案

如果您不想实现虚拟代理,并且仍然需要流畅的界面,则可以切换到Active Record模式.有些库可以完成这项工作,比如PHP-ActiveRecordParis.或者如果你想自己做,你可以在这里这里看看.Martin Fowler的这本书也是一个好的开始.

参考

福勒,马丁.企业应用架构模式.Addison-Wesley,2003.NOCK,CLIFTON.数据访问模式:面向对象应用程序中的数据库交互.艾迪生 - 韦斯利,2004年.