首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PHP: 5层模型

PHP: 5层模型
EN

Stack Overflow用户
提问于 2012-11-29 22:08:36
回答 3查看 1.2K关注 0票数 4

我有关于如何“正确”编写我的应用程序的问题。

我想遵循服务/DAO/映射器/实体模型,我认为我完全理解它,但我发现我想做的一些事情可能会与该模型的一般概念发生冲突。

我有以下数据库表:

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

代码语言:javascript
复制
echo $productEntity->getName();
$productEntity->setPrice(29.9);

我认为能够使用这样的想法将是很棒的:

代码语言:javascript
复制
echo $productEntity->getManufacturer()->getName();

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

代码语言:javascript
复制
$product = $productService->getProduct(5);
$manufacturer = $manufacturerService->getManufacturerByProduct($product);

但我认为“流畅”解决方案要简单得多--它很容易理解,而且使用起来也很有趣。实际上,这是很自然的。我试图通过回调来实现这一点。我将调用制造商服务的回调传递给ProductMapper中的Product实体。

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-12-08 02:07:51

我已经做了几天的研究,我得出的结论是,实现我想要的最好的方法是不再试图自己编写代码,而只是学习使用现有的ORM。因为我一直在尝试做的所有事情都是创建我自己的ORM。我知道这不是个好主意。我将坚持使用Doctrine 2。

票数 3
EN

Stack Overflow用户

发布于 2012-12-02 04:22:43

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

为什么?因为否则你将不得不在你的实体中放置一些数据库代码,并且关于这些层的整个想法是解耦的。

那么,什么是虚拟代理?

根据Martin Fowler (2003)的说法:

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

示例

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

代码语言:javascript
复制
// The interface
interface ManufacturerInterface
{
    public function getName();
}

这就是实体,你可能也在扩展一些泛型模型类:

代码语言:javascript
复制
// The concrete Manufacturer class
class Manufacturer implements ManufacturerInterface
{
    private $name;

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

    public function getName()
    {
        return $this->name;
    }
}

这是代理:

代码语言:javascript
复制
// 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的实例而不是制造商的实例。

数据映射器的实现会有所不同,因此我将给出一个简单的示例:

代码语言:javascript
复制
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)这样的作者同时使用数据映射器和数据访问器。

使用更复杂的数据映射器,您只能拥有XML或YAML、类似Doctrine或Hibernate等语言的文件。

最后,该服务:

代码语言:javascript
复制
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调用数据映射器来创建实体,然后在服务中使用DAO。它实际上更简单,因为您不需要重复两次方法,但这取决于您。

另一个选择

如果您不想实现虚拟代理,但仍然想要一个流畅的接口,那么您可以切换到活动记录模式。有一些库可以完成这项工作,比如PHP-ActiveRecordParis。或者,如果你想自己做,你可以看看herehere。马丁·福勒的这本书也是一个很好的开始。

参考文献

数据访问模式:面向对象应用程序中的数据库交互

票数 9
EN

Stack Overflow用户

发布于 2012-12-02 02:45:50

不要误会我,我不想成为批评家。为什么不将这两个表连接起来,然后让一个类扩展另一个类呢?这样你就可以拥有$this->manufacturer_name = $row['name']了。对于其他制造商行也是如此。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13627570

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档