Thank you for the replies, Dmytro and Keith.
I just finished reading over the Zend_Db_Mapper proposal, and it is looking
really nice. It seems to be similar to what I've been putting together,
which makes me feel confident that I'm at least somewhat on the right track.

I can agree that the entity should not know about the mapper, but without it
I wasn't able to figure out a way for the entity to support lazy loading
(where would it load from?). This was the part I struggled with the most,
because I wanted my entities to know about their relationships:

$quiz->getOwner(); // lazy loads the owner as a User entity

Unless I'm missing something, it seems important that a quiz knows about its
owner if even just for saving purposes:

class QuizMapper
{
    /* ... */
    public function save(Quiz $quiz)
    {
        $data = array(
            'title' => $quiz->title,
            'owner_id' => $quiz->owner->id
        );
        // add code to save to persistent storage
    }
}

I suppose I could just test if the quiz has an owner object, and save its ID
if it does, but then I have to account for a quiz that doesn't have an owner
-- would its value be set to false?

I think maybe shadow data can help in this case by resorting to the shadow
value if owner object doesn't exist, allowing me to re-save an entity
without ever having to load the owner object at all.

Dmytro, regarding your question about where the collection gets its IDs, it
is provided by the mapper. For example, my QuizMapper may have a method that
returns all quizzes owned by a user. But instead of returning an array of
instantiated Quiz objects, it returns a single Collection object that
produces Quiz entities when iterated:

class QuizMapper
{
    /* ... */
    public function findByUser(User $user)
    {
        $db = $this->getDatabase()
        $select = $db->select()
            ->from('quizzes', array('quiz_id'))
            ->where('user_id = ?', $user->id)
        ;
        $ids = $db->fetchCol($select);
        $collection = new My_Entity_Collection();
        $collection->setMapper($this);
        $collection->setIds($ids);
        return $collection;
    }
}

This allows me to create specialty methods that return entities based on any
criteria (like for searches, etc) and the collection can be paginated with
Zend_Paginator. From what I understand, the Zend_Db_Mapper proposal also has
a collection that is similar to this one.

What I was also thinking of doing was expanding on the Collection idea and
making a class that contains information on how to build a reference entity
using an implementation of the Value Holder type of lazy loading. All this
class would contain is a mapper, an ID, and a factory method to use the
mapper to create the entity. Something like this:

class My_Entity_Reference
{
    protected $_id;
    protected $_mapperClass;

    public function __construct($id, $mapperClass)
    {
        $this->_id = $id;
        $this->_mapperClass = $mapperClass;
        $this->_method = $method;
    }

    public function getValue()
    {
        $mapper = new $this->_mapperClass();
        return $mapper->find($this->_id);
    }
}

I could then use this reference object to give the entity a way to lazy-load
a single resource without giving the entity access to a mapper. Perhaps this
would be a good alternative to giving the entity a mapper to work with? Or
should I be relying on shadow data? Maybe I could use both ideas together
and use the Reference object as a shadow data by adding a getId() method. So
many ways to go! :)

--
Hector


On Fri, Aug 28, 2009 at 4:45 PM, keith Pope <mute.p...@googlemail.com>wrote:

> 2009/8/28 Hector Virgen <djvir...@gmail.com>:
> > Thanks for the reply, Tim. So you're saying I'll need one or more mappers
> > for my Quiz entity depending on how much information I want to prefill
> the
> > quiz with? For example I'll have classes like:
> >
> > QuizSimpleMapper
> > QuizWithQuestionsMapper
> >
> > Let's say we allow the users to tag their quizzes from a set of global
> tags,
> > and they can use as many tags as they want. Would I then need more
> mappers?
> >
> > QuizWithTagsMapper
> > TagMapper
> > TagWithQuizzesMapper
> >
> > Then, if I need a Quiz with its questions and its tags, do I need to
> create
> > yet another mapper?
> >
> > QuizWithQuestionsAndTagMapper
> >
> > This is becoming overwhelming. I feel like this complexity could be
> > simplified if the entity had access to the mapper that created it,
> allowing
> > it to pull in more data in a lazy-loading fashion. But as you said, that
> > goes against the design pattern.
> > What I have been working on lately has been an "entity collection" object
> > which contains a mapper instance and a list of IDs. It implements the
> > SeekableIterator and Countable interfaces and uses lazy-loading to load
> the
> > actual entity on demand when My_Model_Entity_Collection::current() is
> > called. This seems to help so that objects aren't created until they're
> > accessed, and they can be accessed without any additional work on the
> > object. Any thoughts on this?
> > --
> > Hector
>
> I would say that the quickstart is only a pointer to how a mapper can
> work, once you start looking at relationships that are not simple you
> will need to create more and more infrastructure to handle them. The
> idea of a data mapper is to help create a Domain Model, this also will
> require components to manage object life cycle such as unit of work
> and identity map patterns.
>
> I would suggest reading up on domain model, Eric Evans Domain Driven
> Design is probably the best book to read on this.
>
> Also check out the Zend_Db_Mapper proposal, you will see from this how
> involved creating a data mapper is :) The good news is this proposal
> has been approved for development which is very good news for the
> framework. There is code checked into the SVN for this component too,
> which is worth a read through.
>
> >
> >
> > On Fri, Aug 28, 2009 at 3:32 PM, Tim Navrotskyy
> > <dmytro.navrots...@gmail.com> wrote:
> >>
> >> Hi,
> >>
> >>
> >> Hector Virgen wrote:
> >> >
> >> > The example in the Zend Framework Quick Start [1] involves only a
> single
> >> > model and a single mapper, which is fine -- the pattern works
> >> > beautifully
> >> > in
> >> > isolation. But in my application I have many models each with their
> own
> >> > mappers, and the models are related to each in one-to-one,
> one-to-many,
> >> > and
> >> > many-to-many relationships.
> >> >
> >>
> >> I think the data mapper pattern is not implemented right in the
> QuickStart
> >> guide which leads to series of questions like yours. Even on the
> >> http://martinfowler.com/eaaCatalog/dataMapper.html reference page  we
> see:
> >>
> >>
> >> > A layer of Mappers (473) that moves data between objects and a
> database
> >> > while keeping them independent of each other and the mapper itself.
> >> >
> >>
> >> According to this statement the class Quiz may not depend on any Data
> >> Mapper, including the QuestionMapper.
> >>
> >> The client code (e.g. action controller) using a Data Mapper may look
> like
> >> this:
> >>
> >> //fetching questions for some quiz
> >> $questions = $questionMapper->findByQuiz($quiz);
> >>
> >> $questions is now a collection of objects with complete question
> >> informaton
> >> including the answer choices.
> >>
> >> The client doesn't ask the Quiz Entity to get the questions, but the
> >> mapper.
> >> The question mapper may communicate with the AnswerChoice mapper to
> fetch
> >> the choices and populate the questions with them.
> >>
> >> Implementing it this way you make your Model independent of the Mapper
> and
> >> therefore the storage type.
> >>
> >>
> >> Hector Virgen wrote:
> >> >
> >> > Also, since each question belongs to a quiz, should the Question
> object
> >> > contain an instance of the Quiz object it belongs to?
> >> >
> >>
> >> I think it's acceptable if you use this backreference somewhere.
> >>
> >>
> >> Hector Virgen wrote:
> >> >
> >> > If I follow this pattern, then when I "find" a single AnswerChoice
> >> > object,
> >> > the AnswerChoiceMapper would load the parent Question object, which
> >> > loads
> >> > the parent Quiz object, which loads the parent User object, etc. This
> >> > seems
> >> > to be inefficient, especially when iterating through multiple answer
> >> > choices.
> >> >
> >>
> >> With the Data Mapper layer decoupled from your model you may now
> implement
> >> another mapper for each use case which pulls exactly as many
> dependencies
> >> as
> >> needed. You could still use techniques you mentioned like Identity Map
> or
> >> Lazy Loading. Martin Fowler describes them very good in
> >> http://martinfowler.com/books.html#eaa PoEAA .
> >>
> >> Tim.
> >> --
> >> View this message in context:
> >>
> http://www.nabble.com/Data-Mappers-and-Relational-Modelling-tp25193848p25198001.html
> >> Sent from the Zend Framework mailing list archive at Nabble.com.
> >>
> >
> >
>
>
>
> --
> ----------------------------------------------------------------------
> [MuTe]
> ----------------------------------------------------------------------
>

Reply via email to