That's definitely true, this design is subject to the n+1 problem. But I
have that covered by using these two solutions:
*1) Identity Map*
*
*
I always check the identity map before querying for a specific domain
entity. This check is handled in the mapper's find() method.
public function find($id)
{
if ($article = $this->_loadIdentity($id)) {
return $article;
}
/* [[ load article from DB ]] */
$this->_storeIdentity($article);
return $article;
}
*2) Domain Entity Caching*
*
*
I also cache domain entities because it's faster to unserialize it than to
query for all the records it needs and construct it from scratch each time.
public function find($id)
{
/* [[ check identity map ]] */
if (!$article = $this->_loadCached($id)) {
/* [[ load article from DB and construct $article ]] */
/* [[ save article in cache ]] */
}
/* [[ store in identity map ]] */
return $article;
}
For more information on my simple little identity map implementation, you
can check out my blog post:
http://www.virgentech.com/blog/2010/02/building-identity-map-php.html
I find it rare that I ever need "just" the title of an article, especially
when it comes to building an index page. I usually also want the
description, published date, who published it, when it was last updated,
which tags it has, etc. I may even want the content itself so I can show it
in a jQuery modal. The way I see it, it's not entirely up to the model as to
how much (or how little) information is displayed -- that's the view's
responsibility. So I allow the view to access as much as it needs, with
lazy-loading within the model for any extra information.
As far as performance is concerned, this has been "good enough" for my data
warehouse app as well as my personal blog.
The added benefit is that when a user views the index page, each article
(that's viewable on the page) is pulled and cached, so when the user clicks
one to see the whole thing, it is pulled directly from cache. It's the same
object, just viewed differently.
--
Hector
On Fri, Apr 16, 2010 at 10:16 AM, Thomas D. <[email protected]> wrote:
> Hi,
>
> Hector Virgen wrote:
> >> But how do you deal with other properties? For example, your articles
> >> have a "isPublished" flag. Now you want a list with n articles, which
> >> are published (isPublished === 1), ordered by another property
> >> "edited_on".
> >
> > I would pass in the criteria as array('published' => true). The mapper
> > then maps that to "WHERE isPublished = 1". I don't support returning n
> > articles in the mapper because my lazy-loading iterator handles that for
> > me:
> >
> > $articles = $mapper->fetchAll(array('published' => true));
> > assert($articles instanceof Default_Model_ArticleCollection); // true
> > assert($articles instanceof SeekableIterator); // true
> > $articles->seekTo(20);
> > $article = $articles->current();
> > assert($article instanceof Default_Model_Article); // true
>
> Does it really perform well? Seems like your architecture requires n+1
> queries if you want to echo a list of titles from the latest n articles (I
> guess your mapper's fetchAll() method will just set the ids for every
> object
> in the collection, matching your criteria. When your collection returns a
> real object, the collection will ask the mapper for that particular
> object).
>
> Lazy-loading sounds good, but wouldn't it be better, if your fetchAll()
> method would fetch every row matching your criteria and cache the data? No
> need to instantiate each object in your result set, when you are just
> preparing the set, this can still happen in your collection... or are you
> still doing this?
>
>
> --
> Regards
> Thomas
>
>
>