RE: [fw-general] Data Mappers and Relational Modelling
Hi, Tried to post this as a comment on the wiki, but its slow/dying or dead. One thing I think that could simplify some of this, is telling the back end where to put values, rather than ask for them and having to juggle them about. Especially when have to deal with joins and particularly self joins, as each set of fields can be bound to their own scope. The also an added advantage is that PDO MySQLi backends can be told where to put column values, so PDOStatement::fetch(PDO::FETCH_BOUND) or MySQLI_STMT::fetch() respectively can do most of the actual mapping. A crude example (which is all kinds of wrong in other aspects... ) is below.. Jared ?php function query(PDO $pdo, $sql, array $refs = null, array $params = null) { $query = $pdo-prepare($sql); if ($query-execute($params)) { if ($refs) { $i = 1; foreach($refs as $ref) $query-bindColumn($i++, $ref); } return function() use ($query) { return $query-fetch(\PDO::FETCH_BOUND); }; } return false; } function findAllAndBoss(PDO $pdo) { $e = array(); $b = array(); $iterator = query($pdo, 'SELECT e.id, e.name, e.email, b.id, b.name, b.email FROM Employee e LEFT JOIN Employee b ON e.boss = b.id') array( $e['id'], $e['name'], $e['email'], $b['id'], $b['name'], $b['email'])); return function() use ($e, $b, $iterator) { if ($iterator()) { // IdentityMap lookup omitted... $employee = new Employee(); $employee-setState($e); // IdentityMap lookup omitted... if (isset($b['id'])) { $boss = new Employee(); $boss-setState($b); } else $boss = null; return array($employee, $boss); } return false; }; }
Re: [fw-general] Data Mappers and Relational Modelling
Hello Chris, there is an example/readme at www.beberlei.de/zendentity which is a quickstart rewrite of the bug model in the Zend_Db manual. It has some quirks, but a working (though slightly different) demo is in the http://framework.zend.com/svn/framework/standard/incubator/demos/Zend/Entity repository. Sadly ZendCon and the development of zend Entity are on a bad timeline, i haven't tried to get any talk about it into, and since i am from germany i won't be at ZendCon unless someone (Zend ;)) pays me to come ;) greetings, Benjamin On Thu, 3 Sep 2009 16:49:34 -0700 (PDT), Chris Murray cmur...@murtek.com wrote: Yes, it is difficult, especially doing complex validation on nested forms. I'm in the late stages of building an ORM/MDA framework on top of ZF. It's quite functional, but I'd much prefer to work with others doing the same thing and use a common code base that is certainly going to be more robust and sophisticated than what I've built. I've downloaded the latest Zend_Entity code. Is there an example or readme somewhere so I can try to map my MDA definitions into Zend_Entity? BTW, I'll be attending ZendCon so hope to learn more about it there.
Re: [fw-general] Data Mappers and Relational Modelling
Yes, it is difficult, especially doing complex validation on nested forms. I'm in the late stages of building an ORM/MDA framework on top of ZF. It's quite functional, but I'd much prefer to work with others doing the same thing and use a common code base that is certainly going to be more robust and sophisticated than what I've built. I've downloaded the latest Zend_Entity code. Is there an example or readme somewhere so I can try to map my MDA definitions into Zend_Entity? BTW, I'll be attending ZendCon so hope to learn more about it there. -- View this message in context: http://www.nabble.com/Data-Mappers-and-Relational-Modelling-tp25193848p25286031.html Sent from the Zend Framework mailing list archive at Nabble.com.
Re: [fw-general] Data Mappers and Relational Modelling
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
Re: [fw-general] Data Mappers and Relational Modelling
-- 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. 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
Re: [fw-general] Data Mappers and Relational Modelling
Thanks for the reply, Matthew. I have looked over his proposal but only briefly. I'll have to examine it more closely to see how the relationships are handled. I've found (based on my limited experience) that the data mapper pattern is more of an idea than a set of abstract classes. For example, my mapper interface only requries a find() method, and my abstract entities are nothing more than magic getters/setters for a protected $_data array. This is why I have been interested lately in learning how everything works, since the pattern must be re-implemented for each entity/mapper. Thanks again for the tips. Looks like I have some more reading to do :) -- Hector On Tue, Sep 1, 2009 at 1:55 PM, Matthew Weier O'Phinney matt...@zend.comwrote: -- 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. 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);
Re: [fw-general] Data Mappers and Relational Modelling
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.dewrote: 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
Re: [fw-general] Data Mappers and Relational Modelling
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.comwrote: 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
Re: [fw-general] Data Mappers and Relational Modelling
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.comwrote: 2009/8/28 Hector Virgen djvir...@gmail.com: Thanks for the reply, Tim. So you're saying I'll need one or more
Re: [fw-general] Data Mappers and Relational Modelling
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.
Re: [fw-general] Data Mappers and Relational Modelling
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 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.
Re: [fw-general] Data Mappers and Relational Modelling
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] --