SitePoint Sponsor

User Tag List

Results 1 to 19 of 19
  1. #1
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Correct use of data mapper - build object from request or load from database

    Hi guys,

    Right now I use data mappers in my applications and I've kept them simple. They have a save() method which accepts an object, checking if the object has an id or not - if it hasn't, it creates a new object in the database, if it has, it updates the object with that id.

    However the id and any other object attributes to insert/update all come from the request (the id is a hidden field in a form), so I either have the option of building an object from the request and saving it, or loading the object from the database and running its set() commands and then saving it.

    I.e.

    PHP Code:
    $mapper = new ArticleMapper;
    $id $idFromRequest;
    $article $mapper->findById($id);
    $article->setTitle($titleFromRequest);
    $article->setBody($bodyFromRequest);
    $mapper->save($article); 
    vs.

    PHP Code:
    $article makeArticleFromRequest(); //article will have an id of 0 if new object to insert, integer otherwise
    $mapper = new ArticleMapper;
    $mapper->save($article); 
    Obviously the second way seems a lot more flexible, has one less query and - possibly importantly? - means that request handler / controller doesn't need to know about any of the object's attributes and can leave that to the mapper, and the factory which creates the object from the request.

    Is there anything wrong with using the second version as opposed to the first, which I see used commonly?

  2. #2
    Grumpy Minimalist
    Join Date
    Jul 2006
    Location
    Ontario, Canada
    Posts
    424
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What does the ID represent in your design?

    Generally, a domain object's ID refers to its unique identifier stored in the database (ie. the primary key in most cases); but you seem to be using it differently. Normally, a new object is created in memory without a key and then the data mapper saves it to the database, assigns it an ID (whether the database determines this or the application determines it is another issue), and then stores that ID in the object within memory.

    Something like this:
    PHP Code:
    $article = new Article($titleFromRequest$bodyFromRequest);
    //$article now has an ID of null

    $mapper $application->getMapper('Article');
    $mapper->save($article);
    //$article now has an ID of, for example, 123 

  3. #3
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Tarh View Post
    What does the ID represent in your design?

    Generally, a domain object's ID refers to its unique identifier stored in the database (ie. the primary key in most cases); but you seem to be using it differently. Normally, a new object is created in memory without a key and then the data mapper saves it to the database, assigns it an ID (whether the database determines this or the application determines it is another issue), and then stores that ID in the object within memory.

    Something like this:
    PHP Code:
    $article = new Article($titleFromRequest$bodyFromRequest);
    //$article now has an ID of null

    $mapper $application->getMapper('Article');
    $mapper->save($article);
    //$article now has an ID of, for example, 123 

    Regarding the ID, yes that's exactly how it works.. the ID is passed in the request - it may be empty/null in the case of a new object, in which case the 'save' function will return the id of the newly inserted object (which I can then assign to the object in memory).

    My question was really, when updating objects in a database which have ids, do I load the object from the database and use its setter methods to set its attributes to new values and then save this object again, OR do I build an object from the request (including a null or numeric ID as needed) and then save this object, thus 'overwriting' any previous object with the same ID but with the new attributes?

    Sorry if my question isn't clear!!

  4. #4
    Grumpy Minimalist
    Join Date
    Jul 2006
    Location
    Ontario, Canada
    Posts
    424
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah; now I understand your question!

    The first option is "correct." Otherwise, you're taking some control away from your data mapper and lose the potentially valuable assumption that all existing objects are loaded through your mapper. Also, the second option requires that you know everything required to recreate the object in memory based on your request alone which, except in very rare applications (if any at all), means that you won't be able to use it all the time. Hence, if you go with option #2, you'll still be using option #1 frequently in your application.

  5. #5
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Tarh View Post
    Ah; now I understand your question!

    The first option is "correct." Otherwise, you're taking some control away from your data mapper and lose the potentially valuable assumption that all existing objects are loaded through your mapper. Also, the second option requires that you know everything required to recreate the object in memory based on your request alone which, except in very rare applications (if any at all), means that you won't be able to use it all the time. Hence, if you go with option #2, you'll still be using option #1 frequently in your application.
    Thanks for the answer , I knew there was a reason why all the code examples I saw went with the #1 option.

    Also, there are many instances in my apps where I have had to use both options, i.e. sometimes I cannot instantiate an object from a request because I'm only updating one or two fields, and these fields alone cannot constitute an object - so I have to use #1 anyway.

    However, often when I'm creating a new object from a request - i.e. I'd get a submission from a form which contains a lot of attributes, I usually pass the request to a Factory object which has a instanceFromRequest() method which takes the request object as a parameter and creates a new object based on the request (with an id of null since it'd be a new object).

    Is this a good practice for creating new objects, or should I instantiate the new object a different way? Maybe just using the factory's instance() method which takes the request values as a parameter

    i.e.

    PHP Code:
    $factory = new ArticleFactory();
    $article $factory->instance($request->get('title'), $request->get('body'));
    //save $article 
    instead of

    PHP Code:
    $factory = new ArticleFactory();
    $article $factory->instanceFromRequest($request);
    //save $article 

  6. #6
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Though I've just realised after typing that the factory would instantiate objects with null ids (new objects) as well as existing objects from the database (with ids).

    So would my factory look something like:

    PHP Code:
    class ArticleFactory {
      function 
    instance($title$body$id 0) {
          return new 
    Article($id$title$body);
      }
    }

    //called in the 'new article' method in a controller
    function newArticle() {
      
    $factory = new ArticleFactory;
      
    //article id should be zero since it's a new instance
      
    $article $factory->instance($request->get('title'), $request->get('body'));
      
    $mapper = new ArticleMapper;
      
    $mapper->save($article);
    }

    //called in an article mapper to retrieve an existing article
    function findById($id) {
       
    $sql "SELECT ... WHERE id = $id'";
       
    $row $db->query($sql); //$row is an associative array
       //then
       
    $factory = new ArticleFactory;
       
    $article $factory->instance($row['title'], $row['body'], $row['id']);
       
    //or ?
       
    $article $factory->instanceFromArray($row);

    But I suppose an instanceFromArray() function is as bad as an instanceFromRequest()? How do we decouple object instantiation from the methods that might need to instantiate objects and give all that responsibility to a factory?

  7. #7
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    My preference would be for grabbing the object from the DB, updating it in memory, and then saving it.

  8. #8
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    My preference would be for grabbing the object from the DB, updating it in memory, and then saving it.
    But if were using some locking mechanism, such as optimistic locking via versioning/timestamping then version of the object that the user edits, wouldn't necessarly be the version that is updated.

  9. #9
    Grumpy Minimalist
    Join Date
    Jul 2006
    Location
    Ontario, Canada
    Posts
    424
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In such cases it's better to use pessimistic locking to begin with.

  10. #10
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Pessimistic locking is alot harder to implement correctly, involving explicit locking of rows, which imo is generally bad idea for web applications.

    Easier to serialize an object + version to a html form, and rebuild the instance from the POST.

  11. #11
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    My preference would be for grabbing the object from the DB, updating it in memory, and then saving it.
    Mine wouldn't. That would make the procedure non-re-entrant. (Is that a word?)

  12. #12
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    Mine wouldn't. That would make the procedure non-re-entrant. (Is that a word?)
    What do you mean exactly?

  13. #13
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by old_iron View Post
    What do you mean exactly?
    Doing the same thing multiple times might have a different end-result than doing it just once. Some times that's just how things are, but if you have the choice between making a procedure in such a way that it is safe to call multiple times, and in a way so it's not, the first is generally preferable.

  14. #14
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    348
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm still not sure what you mean. If you build an object from a request, which includes giving it an identifier somehow which would tell the database to update or insert as needed, would that operation not have the same result even if you instantiated the same object many times from the same request within a single function call?

  15. #15
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    Doing the same thing multiple times might have a different end-result than doing it just once. Some times that's just how things are, but if you have the choice between making a procedure in such a way that it is safe to call multiple times, and in a way so it's not, the first is generally preferable.
    Like old_iron, I'm not sure why the procedure wouldn't be safe to call multiple times. Could you enlighten us?

  16. #16
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    Like old_iron, I'm not sure why the procedure wouldn't be safe to call multiple times. Could you enlighten us?
    OK, reading it again, perhaps it doesn't really apply to this situation. What I meant was that if you have a request that contains only some values, then the end result depends on the context. This means that if you repeat the exact same request later on, then you might get a different result. If you sent all the data in the request, then the end result would always be the same. This makes the complete system slightly less complex. Another way of looking at the same thing is, that the first case is more focused on mutating state than the latter.

  17. #17
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    OK, reading it again, perhaps it doesn't really apply to this situation. What I meant was that if you have a request that contains only some values, then the end result depends on the context. This means that if you repeat the exact same request later on, then you might get a different result. If you sent all the data in the request, then the end result would always be the same. This makes the complete system slightly less complex. Another way of looking at the same thing is, that the first case is more focused on mutating state than the latter.
    But in some situations you want the second request later on to fail. Otherwise it could revert someone else's work.

  18. #18
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren View Post
    But in some situations you want the second request later on to fail. Otherwise it could revert someone else's work.
    True. But at least it's internally consistent.

    Actually, choosing between the two is probably very circumstantial.

  19. #19
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    True. But at least it's internally consistent.

    Actually, choosing between the two is probably very circumstantial.
    For when a Session/UnitOfWork spans multiple Http requests, and therefore multiple database transactions.


Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •