2009/9/1 Matthew Weier O'Phinney <matt...@zend.com>:
> -- Hector Virgen <djvir...@gmail.com> wrote
> (on Tuesday, 01 September 2009, 12:02 PM -0700):
>> I've updated my Collection object and it is working very nicely. I can now
>> iterate through an entity's "child" entities very easily thanks to the lazy
>> loading collection, and the entity has no idea that a mapper even exists:
>>
>> $quizMapper = new QuizMapper();
>> $quiz = $quizMapper->find(123);
>> foreach ($quiz->getQuestions() as $question) {
>>     assert($question instanceof Question); // true
>> }
>>
>> But now I have a problem with relationships in the other direction --
>> specifically, the "belongs to" relationship.
>>
>> Let's say I have a Question object and need to access the Quiz object that it
>> belongs to. Since a Question can only belong to one Quiz, how could I achieve
>> that without a mapper? For example I want to do this:
>>
>> $questionMapper = new QuestionMapper();
>> $question = $questionMapper->find(456);
>> $quiz = $question->getQuiz();
>>
>> How are these types of relationships normally handled? I was thinking of
>> creating a new type of class that is similar to the Collection object, but 
>> only
>> deals with a single entity. For example (simplified for brevity):
>
> Before you get too carried away...
>
> ORM stuff is really, really difficult. DataMappers that deal with a
> single table are fairly easy (which is part of the reason we demonstrate
> the technique in the quick start), but once you start dealing with
> relations (the realm of ORMs -- Object Relational Mappers), it becomes
> quite complicated -- as you're starting to discover.
>
> Benjamin Eberlei is working on a general ORM solution for ZF currently,
> and it's in the incubator. Do yourself a favor and check out his work --
> it's under the Zend_Entity namespace. If you have ideas on how it might
> work, give him a helping hand. :)
>
> If you need stable code, and need it now, try out Doctrine.

Maybe it would be an idea to note this in the quickstart guide, I
think people are easily confused by the data mapper example.

In martin fowlers book it also mentions that data mappers are
difficult to implement, and indeed he says that its better to buy one
than create your own :)

>
>> class My_Domain_Entity_Reference
>> {
>>     protected $_id;
>>     protected $_mapper;
>>
>>     public function __construct($id, My_Domain_Entity_Mapper_Interface 
>> $mapper)
>>     {
>>         $this->_id = $id;
>>         $this->_mapper = $mapper;
>>     }
>>
>>     public function getReference()
>>     {
>>         return $this->_mapper->find($this->_id);
>>     }
>> }
>>
>> Then, I could add this reference to my Question object in the 
>> QuestionMapper's
>> find() method:
>>
>> class QuestionMapper
>> implements My_Domain_Entity_Mapper_Interface
>> {
>>     public function find($id)
>>     {
>>         $row = $this->_questionTable->find($id)->current();
>>         $question = new Question();
>>         /* ... */
>>         $quiz_id = $row->quiz_id;
>>         $quizMapper = new QuizMapper();
>>         $question->addReference('quiz', new My_Domain_Entity_Reference
>> ($quiz_id, $quizMapper));
>>     }
>> }
>>
>> I would then have to update the Question::getQuiz() method to pull the value
>> from its references.
>>
>> Does this seem like a good way to approach the "belongs-to" relationship
>> problem? Thanks again for all your help!
>>
>> --
>> Hector
>>
>>
>> On Sun, Aug 30, 2009 at 3:34 PM, Hector Virgen <djvir...@gmail.com> wrote:
>>
>>     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 <kont...@beberlei.de>
>>     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 
>> <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-tp25193848p25
>>         > >198001.html
>>         > >
>>         > > >> Sent from the Zend Framework mailing list archive at 
>> Nabble.com.
>>         > >
>>         > > --
>>         > >
>>         
>> ----------------------------------------------------------------------
>>         > > [MuTe]
>>         > >
>>         
>> ----------------------------------------------------------------------
>>
>>
>>         --
>>         Benjamin Eberlei
>>         http://www.beberlei.de
>>
>>
>>
>>
>
> --
> Matthew Weier O'Phinney
> Project Lead            | matt...@zend.com
> Zend Framework          | http://framework.zend.com/
>



-- 
----------------------------------------------------------------------
[MuTe]
----------------------------------------------------------------------

Reply via email to