ExpressionEngine CMS
Open, Free, Amazing

Thread

This is an archived forum and the content is probably no longer relevant, but is provided here for posterity.

The active forums are here.

DataMapper 1.6.0

September 05, 2008 12:32pm

Subscribe [115]
  • #511 / Feb 13, 2009 2:27pm

    OverZealous

    1030 posts

    @tdtank59

    How I’m doing it requires a little reversal of thought.  Instead of using a join table, use a dedicated model for the join, with two one-to-N relationships.  Then you can include whatever information you want in that table.

    It isn’t seamless yet, but I’m too busy to worry about making it better.  You can still perform some very slick queries, though:

    // relationship: user <- vote -> song
    // get all votes and usernames for a song
    $song->vote->join_related("user", array("name"))->get();
    foreach($song->vote as $v) {
        echo($v->user_name . ": " . $v->rating);
    }
    
    // get all votes for this user
    $user->vote->join_related("song", array("title", "artist"))->get();
    
    // get all voting users for this song
    $user = new User();
    // this will lookup the users themselves, based on $song
    $user->where_related("vote", "song_id", $song->id);
    
    // Same as above, but add in the rating.  Note: this is a where the "hack" comes in
    $user->where_related("vote", "song_id", $song->id);
    $user->select("votes.rating as vote_rating");
    $user->get(); // this is now similar to the first query, but using the User model.
  • #512 / Feb 13, 2009 2:32pm

    OverZealous

    1030 posts

    @macigniter
    No.  If you think about it, what you did was ask DM to look up all Products whose Client was not set.  Most likely you are actually returning an array of ALL products.  When you perform a $object->related->get(), and $related isn’t set, $related is an actual object, just with no ID.

    You need to verify that User has a Client before running the query.  It can be this simple:

    $p = new Product();
    if($user->client->exists()) {
        $p->get_by_related($user->client);
    }
    // $p->all may or may not have any items
  • #513 / Feb 13, 2009 2:39pm

    macigniter

    244 posts

    Good point, Phil 😊 If I wouldn’t be that brain-dead after hours of coding I could’ve realized it myself. I am actually using your solution already. Just didn’t know why DataMapper needed the extra ->exists() check. But it all sounds logical now. I better shut down that computer and get a drink now (it’s time for it in Germany) 😉

    Thanks!

  • #514 / Feb 13, 2009 4:13pm

    set_value() helper misbehaves when DataMapper is loaded

    I noticed that the set_value() helper wasn’t working as expected on an application I’m developing. It always returned the value from the $default parameter, even when there were a posted value. Then, I tried to isolate the problem.

    I created a controller just to test this behavior:

    class Test extends Controller {
        
        function index()
        {
            $this->load->helper('form');
            
            $data['name'] = 'Foo';
            
            $this->load->view('test', $data);
        }
    }

    And a view that contains only this code inside the <body> element:

    <?= form_open('test') ?>
    <?= form_input('name', set_value('name', $name)) ?>
    <?= form_submit('submit', 'Submit') ?>
    <?= form_close() ?>

    The first time the view is displayed, it shows de default $name value (‘Foo’), as expected. But if I change the field to something else, like ‘Bar’ for instance, and submit the form, it displays ‘Foo’ again instead of ‘Bar’.

    When I disable DataMapper in config/autoload.php, set_value() works as expected. My conclusion is that DataMapper is causing set_value() to misbehave in some way. Or am I making something wrong here?

  • #515 / Feb 13, 2009 4:20pm

    OverZealous

    1030 posts

    I don’t use that function, and I’m not sure why this should matter, but you might try autoloading the form_validation library BEFORE autoloading the DataMapper library in your CodeIgniter Autoload config.

    It’s the only connection I can find between set_value and DataMapper.  (Why is a method [set_value] that can be so simple so complex?  It boggles…)

    Update
    After digging into Form_Validation a bit more, I’m not sure you can use Form_Validation alongside DataMapper, because they do almost the same thing.  What is happening, I believe, is that set_value sees that Form_Validation is already loaded, and therefore reverts to using that.

    Instead, you’ll need to write your own helper to handle set_value.  It’s a very simple function:

    function set_value($field, $default = '') {
        return isset($_POST[$field]) ? $_POST[$field] : $default;
    }

    If you are using DataMapper for the information, you can skip the set_value stuff altogether.  Just pass the object itself in, even if it is new.  (You could potentially set default values on a new object, if you need).

    // controller
    $u = new User();
    if(isset($this->input->post('id'))) {
        // attempt to save
    } else {
        // set defaults
        $u->favorite_color = 'Blue';
    }
    $this->load->view('edit_user', array('user' => $u);
    
    //...
    
    // view
    <?= form_input('name', set_value('name', $u->name)) ?>
  • #516 / Feb 13, 2009 4:52pm

    Phil, thanks for the quick reply.

    Although I didn’t do it in my example, I actually use DataMapper as you pointed: setting default values in objects. Even so, sometimes I need to use the set_value helper. I’ve already written a replacemet for set_value() in MY_form_helper.php, but It would be nice if I could use the native one.

  • #517 / Feb 13, 2009 4:54pm

    OverZealous

    1030 posts

    The problem is the native one.  It assumes that if Form_Validation is loaded, you are using it in the form.

    There is no way for DataMapper to remove that assumption - and DataMapper uses Form_Validation to validate the objects, so Form_Validation is always loaded.

  • #518 / Feb 14, 2009 5:54am

    dobomode

    7 posts

    I was having troubles saving new objects in the db. It was inserting the row, but not updating the id. As a result, I was able to save the objects, but any subsequent save’s that referenced those objects (like trying to save a relation) failed.

    I narrowed down the issue to the DataMapper function save():

    On line 610:

    // Complete auto transaction
                        $this->_auto_trans_complete('save (insert)');
    
                        // Assign new ID
                        $this->id = $this->db->insert_id();

    ... needs to be changed to:

    // Assign new ID
                        $this->id = $this->db->insert_id();
    
                        // Complete auto transaction
                        $this->_auto_trans_complete('save (insert)');

    The last inserted id was requested after the completion of the transaction which made the id = 0.

    This is a silly bug that unfortunately took me nearly 4 hours to track down, *ugh*!

  • #519 / Feb 14, 2009 5:59am

    OverZealous

    1030 posts

    What database are you using?  I have been using the last id for a long time, as, I’m sure, have many others, without issue.

  • #520 / Feb 14, 2009 1:40pm

    dobomode

    7 posts

    I am using PHP 5.2.6 with MySQL 5.1.31 on a Mac OS X Leopard 10.5.6 machine.
    In datamapper.php, I have $config[‘auto_transaction’] = TRUE;

  • #521 / Feb 14, 2009 5:47pm

    OverZealous

    1030 posts

    Ahh - I didn’t think about the auto transaction issue.  I handle all of my transactions by hand.

    I’ll update that on my development version.  Thanks for finding the bug!

  • #522 / Feb 14, 2009 7:28pm

    dobomode

    7 posts

    You are welcome. 😊
    Do you think that the auto transaction mechanism could have an impact on any of the other functions of datamapper?

    By the way, what are the benefits of enabling auto transactions?

  • #523 / Feb 14, 2009 7:32pm

    OverZealous

    1030 posts

    I don’t have the answers to that one (DataMapper is not my code - I just hack on it 😉)

    I do transactions by hand because I’m usually saving multiple objects, and I need all to succeed or all to fail (and I use PostGreSQL).  Oh, and I’m used to it.

    That being said, I doubt that you’ll see too many other issues with the auto transaction mechanism.

  • #524 / Feb 15, 2009 6:45am

    DominixZ

    23 posts

    I have one suggest that I think DataMapper should have that is “Get_array_result”. it will be nice to use with json_encode. That would be easy to do that today i must convert data from DataMapper to array and use json_encode that isn’t nice

  • #525 / Feb 15, 2009 12:57pm

    OverZealous

    1030 posts

    While I wouldn’t add that in to DataMapper directly*, if this is something you need, you could add it to a DataMapper extension class, and then extend this class instead (which I do for a lot of my code).

    The code could be really simple:

    // untested
    function get_array_result($limit = NULL, $start = NULL) {
        $this->get($limit, $start);
        $result = array();
        foreach($this->all as $item) {
            $item_values = array();
            foreach($this->fields as $field) {
                $item_values[$field] = $item->{$field};
            }
            $result[] = $item_values;
        }
        return $result;
    }

    Add this to your own extension of DataMapper, and then use that as the base class for your models.  I highly recommend this anyway, because there are always going to be features that you want in all of your models, but might not fit in with the DataMapper core design.  (I created a new model called DataMapperExt in the Models folder.  It shouldn’t ever be instantiated directly, but use it as the base class for all your other models.)

    * I wouldn’t add it to the general distribution because the $item->all array provides the same basic features, and I think it would confuse the correct way to use DataMapper.

    Obviously, you can’t use json_encode() with the ->all array, for a variety of reasons, but I also don’t think you necessarily want to send every field within an object over JSON every time.  For my code, I have dedicated classes I use to manage my JSON responses, and I use these to handle the conversion, so I can specify which fields to send.

.(JavaScript must be enabled to view this email address)

ExpressionEngine News!

#eecms, #events, #releases