2

我对Data Mapper 模式很陌生。我想我错过了一些东西,因为对我来说,它对于比最基本的例子更复杂的所有事情都失败了。

假设我有一个由页面组成的简单网站:

class Page {
    private $id     = null;
    private $parent = null;
    private $title  = null;
    private $body   = null;

    public function setId($id)              { $this->id     = (int) $id; }
    public function setParent(Page $parent) { $this->parent = $parent;   }
    public function setTitle($title)        { $this->title  = $title;    }
    public function setBody($body)          { $this->body   = $body;     }

    public function getId()     { return $this->id;     }
    public function getParent() { return $this->parent; }
    public function getTitle()  { return $this->title;  }
    public function getBody()   { return $this->body;   }
}

现在我想实例化树深处的第 7 页:

  • 第 1 页
    • 第2页
      • 第 3 页
        • 第 4 页
          • 第 5 页
            • 第 6 页
              • 第 7 页
使用数据映射器模式,我将使用以下映射器类:

class PageMapper {
    public function fetch($id) {
        //...
        $data = $db->fetchRow("SELECT * FROM `pages` WHERE `id` = ?", $id);
        $page = new Page();
        $page->setId($data['id']);
        $page->setTitle($data['title']);
        $page->setBody($data['body']);
        if ($data['parent_id'] !== null) {
            $page->setParent(
                $this->fetch($data['parent_id']);
            );
        }
    }

    public function save(Page $page) {
        //...
    }
 }

问题很明显:我必须一直实例化所有父页面,直到根页面。

现在想象一个 Page 需要知道它的子节点。这意味着,如果我想实例化根 Page,我将不得不加载整个 tree

一种解决方案可能是仅按需加载父/子,但这意味着域对象必须了解映射器才能从中提取数据......并且分离将消失。

域模型(Page类的实例)应该对 Data Mapper 层(分离)一无所知,但仍然能够执行这些任务(检索父/子)。

在这些情况下是否可以实现关注点分离?如果是,如何?

4

1 回答 1

1

看看 Doctrine,一个实现数据映射器模式的 orm 框架:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-self-reference

您的方法的问题不是整个树(子)的实例化,而是它将使用的查询数量。

查看教义方法,您将有一个 children 属性,该属性通过一个查询立即加载。

我也绝不会建议为 orm 实施自制的解决方案。

一个可能的解决方案是使用闭包:

class Page {
//...
    public function setParents(Closure $pages)
    {
        $this->parents = $pages;
    } 
}

class PagesMapper {

public function fetch() {
//fetch the page ...
$parents = function($parent) use($id, $db) {
        parents = $db->query(/* select parent... */);
        $pages = array();
        foreach($parents as $parent) {
            $page = new Page(); 
            $page->id = $parent->id;
            //...
            $pages[] = $page;
        }
        return $pages;
    };
    $page->setParents($parents);
    return $page;
}
}

所以页面域将不知道持久层。

于 2013-03-17T10:01:59.703 回答