Thanks, Benjamin. I like your version of the Collection object better that
mine. It seems much more flexible and supports lazy loading without the need
to provide my Quiz object with a mapper. I'm going to update my Collection
class with your idea and post back here if I run into any problems. Thanks
again!

--
Hector


On Sat, Aug 29, 2009 at 12:00 AM, Benjamin Eberlei <[email protected]>wrote:

> Hello,
>
> lazy loads are implemented by some underyling magic. Say you have a Quiz
> with questions and you load the quiz. Instead of adding an array of all the
> questions right away, you build a collection class which implements
> ArrayAccess, Iterator and Countable and takes a PHP Callback which is fired
> upon the first acces of any of those functions. See here:
>
>
> http://framework.zend.com/svn/framework/standard/branches/user/beberlei/Zend_Entity/library/Zend/Entity/LazyLoad/Collection.php
>
> In your example it would look like:
>
> $quiz = $quizMapper->findById(1);
>
> // Inside QuizMapper building the quiz:
> $questionsMapper = new QuestionsMapper();
> $state['questions'] = new Zend_Entity_LazyLoad_Collection(
>    array($questionsMapper, loadByQuizId), array($state['id'])
> );
>
> This way you have a collection inside your quiz, its just not loaded yet
> though. Only upon first access of this pseudo array all the data is loaded.
>
> Implementing LazyLoad for querying is easy, there are some tricks to do it
> right when saving the collection then if you dont want to completly load
> it.
>
> greetings,
> Benjamin
>
> On Saturday 29 August 2009 08:06:06 am Hector Virgen wrote:
> > 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 <[email protected]
> >wrote:
> > > 2009/8/28 Hector Virgen <[email protected]>:
> > > > 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
> > > >
> > > > <[email protected]> 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-tp25193848p25
> > >198001.html
> > >
> > > >> Sent from the Zend Framework mailing list archive at Nabble.com.
> > >
> > > --
> > > ----------------------------------------------------------------------
> > > [MuTe]
> > > ----------------------------------------------------------------------
>
>
> --
> Benjamin Eberlei
> http://www.beberlei.de
>

Reply via email to