我在这里发布了一个幽灵,这让我感到困惑多年。这是一个关于如何构建正确模型,正确对象的问题。
让我解释一下。假设我有一篇文章。文章有标题,评级,正文和评论。
类Comment有作者,时间戳,文本。
文章可以有0个或更多评论。到现在为止还挺好。这个概念没问题。但...
这是我感到困惑的地方,因为我不需要加载评论信息,当我有大量文章和大量评论时,这会产生显着的性能差异。
我应该建两个型号吗?一个用于Article,一个用于ArticlesInList?我应该将评论的负载委托给懒惰模式(这可能),只在必要时检索它们吗?
面对和解决这个问题的正确方法是什么?
THX。
答案 0 :(得分:4)
在尝试为业务对象建模时,需要做出许多权衡。
考虑到你的例子,我可以想到一些方法,主要围绕延迟加载注释。如果我有理由相信事情不会变得更复杂,我就是这样做的:
首先,您为Article和Comment创建实体,它们只表示数据库中每个表中的数据。写二传手和吸气剂。在Article上实现loadComments()方法。
实现一个或多个Collection类,例如ArticleCollection。您可能有一个服务类可以获取符合某些条件的文章。 ArticleService :: fetchArticles()将返回没有加载注释的文章。然后实现ArticleCollection的loadComments()方法,该方法加载集合中所有文章的所有注释。首先,这可以迭代调用loadComments的文章 - 但您可以稍后用单查询实现替换它。
这是Article和ArticleCollection的开头。如果您实现了CommentCollection类,则可以使用Article中的内容来保存注释等。
<?php
/**
* Extends a base model class that provides database-related methods -- not ideal,
* but trying to stay focused here.
*/
class Article extends Model {
private $_data;
private $_fields = array('id','title','body','author');
/**
* Constructor can take an array of values to initialize.
*/
public function __construct($data=null){
if (is_array($data)){
foreach($this->_fields as $field){
$this->_data[$field] = $data[$field];
}
}
}
public function getId(){ return $this->_data['id']; }
// more getters, and setters, here.
public function loadComments(){
$result = $this->query('SELECT * FROM Comment WHERE article_id = ' . $this->getId());
$this->_comments = array();
foreach($result as $c){
//instantiate a new comment (imagine Comment's constructor is very similar to Article's
$this->_comments[] = new Comment($c);
}
}
}
class ArticleCollection extends Model {
/**
* An array of Articles, indexed by article_id
*/
private $_articles = array();
/**
* Naive implementation. A better one would grab all article IDs from $this->_articles, and
* do a single query for comments WHERE article_id IN ($ids), then attach them to the
* right articles.
*/
public function loadComments(){
foreach($this->_articles as $a){
$a->loadComments();
}
}
/**
* Add article to collection
*/
public function addArticle(Article $article){
if (empty($article->id)) throw new \Exception('Can\'t add non-persisted articles to articlecollection!');
$this->_articles[$article->id] = $article;
}
}
以上是非常基本的 - 您可以应用其他设计模式来分解您的数据库访问,因此它不是那么紧密耦合。但我只是想以一种理智的方式描述一种懒惰加载你的评论的策略。
一些最终的建议:不要陷入许多框架所做的陷阱,并认为数据库和模型中的表之间存在一些神圣的相关性。模型只是对象。他们可以做不同类型的事情(代表一个简单的事情,如评论或用户),或代表像服务于这些简单事物的服务,或者它们可以是诸如个别事物的组(集合)之类的事物
一个有趣的练习就是编写类,并用虚拟数据填充它们。尽力完全忘记涉及数据库。支持您需要的用例的工艺对象。然后,完成后,找出如何在数据库中保存和加载数据。
答案 1 :(得分:0)
我认为没有最佳方式,但有些方法比其他方式更好。无论如何这是我的两分钱:
首先,我想创建2个类,但一个用于文章,第二个用于评论。通过这种方式,你(在我看来)符合 Demetra法则(Law of Demetra)。现在,在您的控制器中,您可以检索文章列表(没有评论......没有性能),并且在您需要时,对于每篇文章,您都可以使用模型Comment来检索链接的评论。此外,您必须牢记软件工程的原则:“低coupling,高cohesion ”
这是我的意见。我希望它可以帮到你。
答案 2 :(得分:0)
这取决于框架,但您的业务需求非常有意义。以下是我将如何构建事物(关于Agile Toolkit的逻辑):
业务逻辑始终反映您的真实对象,例如文章和评论。它不受演示要求的影响:
class Model_Article extends Model_Table {
function init(){
parent::init();
$this->addField('title');
$this->addField('rating')->type('int');
$this->addField('body')->type('text');
}
function getComments(){
return $this->add('Model_Comment')
->setMasterField('article_id',$this->get('id'));
}
}
class Model_Comment extends Model_Table {
function init(){
parent::init();
$this->addField('name');
$this->addField('body')->type('text');
$this->addField('article_id')->refModel('Model_Article');
}
}
在Agile Toolkit演示文稿由“页面”类控制。在您的情况下,您需要2页,但两个页面都依赖于两个模型:
class page_article extends Page {
function init(){
parent::init();
$m=$this->add('Model_Article')->loadData($_GET['id']);
$this->add('View',null,null,array('view/article/body'))
->setModel($m);
$this->add('MVCLister',null,null,array('view/article/comments'))
->setModel($m->getComments());
}
}
class page_article_comment extends Page {
$m=$this->add('Model_Comment')->loadData($_GET['id']);
$this->add('View',null,null,array('view/comment/header'))
->setModel($m->getRef('article_id'));
$this->add('View',null,null,array('view/comment/full'))
->setModel($m);
}
此代码依赖于4个HTML模板,其中包含诸如等等的标签