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.

DMZ 1.7.1 (DataMapper OverZealous Edition)

March 14, 2010 11:43pm

Subscribe [104]
  • #121 / Mar 31, 2010 5:00pm

    OverZealous

    1030 posts

    @Alface

    It’s in the manual.  There are 3 ways:

    You can set an error using error_message, and not returning anything.  (If there are any errors after validating, then the validation has failed.)

    Alternatively, you can return a custom string from the validation.  Any string value returned is assumed to be an error.

    Finally, you can return FALSE, and DMZ will automatically look up the error from a language line based on the name of the validation rule.

    Again, these are all explained exactly like this in the manual!

  • #122 / Apr 04, 2010 2:21am

    PoetaWD

    97 posts

    Hello Phil,

    After looking this for almost a week I decided to use DATATABLES as my grid plugin.

    It is not as heavy as jqGrid and has a great example page… Also, the main advantage I found is that it does not create a complex container structure in the DOM. Using FIREBUG you can see that most of the grid uses <div> inside <div> + <table> + etc. Datatables only uses a <table></table>.

    It is not very well documented, but using FIREBUG I was able to see the post requests and the results sent from the server, and that is just GREAT because this way I am REALLY learning the concepts of AJAX, JSON, etc…

    This plugin has a search filter that I found very interesting… the question I have is regarding this. (Already found a solution… at the end of the post).

    The plugin send a $POST to the server with the value the user want to search.

    In my object query I will use the like() method to find the objects that have that value in its data.

    The problem is that I can only check one of its column at a time using the like()...

    $obj = new Datatable();
            
            //Check if a post with search data exists
            if($this->input->post('sSearch'))
            {
                $obj->like('engine', $this->input->post('sSearch'));
                $obj->or_like('browser', $this->input->post('sSearch'));
                $obj->or_like('platform', $this->input->post('sSearch'));
                $obj->or_like('version', $this->input->post('sSearch'));
                $obj->or_like('grade', $this->input->post('sSearch'));
            }        
            
            //Execute the query
            $obj->get();

    Is there a better way to perform this search ?

    EDIT: Just found a solution myself. I created a array with the columns of the table and made a foreach loop. I had to do this because of the next feature of datatables, witch is sorting multiple columns.

    $columns = array('engine','browser','platform','version','grade');
    
    if($this->input->post('sSearch'))
            {
                
                foreach($columns as $position => $column)
                {
                    
                    if($position == 0)
                    {
                        $obj->like($column, $this->input->post('sSearch'));
                    }
                    else 
                    {
                        $obj->or_like($column, $this->input->post('sSearch'));
                    }
                }
            }

    Thanks,

    Gabriel

  • #123 / Apr 04, 2010 12:17pm

    OverZealous

    1030 posts

    @PoetaWD
    One tip: make sure you wrap your likes in a group, or the logic may return unexpected results.  What you have there logically is this:

    // Generated query
    WHERE something
        AND col1 LIKE '%val%'
        OR col2 LIKE '%val%'

    Which is actually interpreted as this, due to operator precedence:

    // Most likely incorrect
    WHERE (something AND col1 LIKE '%val%')
        OR col2 LIKE '%val%'

    Also, once you wrap it, you can just or all of the results, without the $position check.  This is because DMZ automatically drops the first operator when grouping items to prevent errors.

    $columns = array('engine','browser','platform','version','grade');
    
    if($this->input->post('sSearch'))
            {
                $obj->group_start();
                
                foreach($columns as $column)
                {
                    $obj->or_like($column, $this->input->post('sSearch'));
                }
    
                $obj->group_end();
            }

    😊

  • #124 / Apr 04, 2010 4:32pm

    PoetaWD

    97 posts

    @Phil : Thanks for the TIP! It works perfectly!

    It is 16:00 and I just woke up! I went to bed 8:00 am, I spent the whole night studying the concepts of AJAX and the grid.

    After that I´ve made a code to generate the grid data automatically, all you need to pass is the name of the object and a array with the columns name.

    Today I found up that I can pass the object name and the columns array as $POST using the javascript.

    Right now I am translating my code (All the comments are in portuguese)...

    After that I will post it here… I think it can help other people like me.

    Maybe It is a good idea to make a plugin with that code… what do you think?

  • #125 / Apr 04, 2010 7:27pm

    PoetaWD

    97 posts

    Its Done !

    Please take a minute of yout time to look at this:

    http://www.spassus.net/cidatatables/

    A Data Grid getting json data DIRECT from database without any other CODING!

    All you need to do is specify in your Datatable initialization the table(Datamapper Object) from your database and the columns you want to show!

    Here is the controller:

    http://spassus.net/cidatatables/datatable.zip  (Too big to post here)

    or in PasteBin:

    http://pastebin.com/rnATM6Kh

    @PHIL: This is thanks to you man! I hope that it shows you that I am learning how to script and how great teacher you are! Also, I hope you see that I learned my lesson about that incident we had… If I were you I would be DAMN proud of what I done with DMZ and all the effort you give to this comunnitie!

    I am just learning PHP and JAVASCRIPT.. if you have ANY suggestion to improve this code please tell me!

    BTW: Just try holding down the SHIFT key to SORT multiple columns!

  • #126 / Apr 04, 2010 8:14pm

    OverZealous

    1030 posts

    @PoetaWD
    That looks great!  Your example works really well.

    One thing you might need to tweak is that it is reporting the incorrect number of rows (it shows the number of visible rows, instead of the total number of rows).  See the bottom left where it says “out of XXX unfiltered rows”.

    Make sure you add it to the wiki category!  😊

    Edit: Well, it’s not really an extension, so if you decide to convert it into an extension, then you can add it to the above! 😉

    (Note: it seems like this would be pretty easy to convert into an extension.)

  • #127 / Apr 04, 2010 9:13pm

    PoetaWD

    97 posts

    @PoetaWD
    That looks great!  Your example works really well.

    Its not really a extesion YET… just a example controller… I will improve it more and I will sure make a extension out of it!

    I will also re-check the JSON being output.

    It really means A LOT for me you saying that my example looks good…

    My plans for the extension:

    #1 - Automatically add the GRID in the view file (JavaScrits and HTML markups).
    Example: The user would just need to do like this:

    <?php echo $obj->grid_generate() ?>

    #2 - The user would specify what columns he wants in the grid passing a array
    Example:

    $obj->grid_columns(array('engine' => 'Rendering Engine','browser' => 'Browser','platform' => 'Platform(s)','version' => 'Engine Version'));

    #3 - He would also be able to add a column to the existing column array using this method:
    Example:

    $obj->grid_add_columns('grade', 'CSS Grade');

    The user would also be able to send a array.

    #4 - Add custom column DATA. The user might want to add data that is not acctually in that object table, something like a property from a related object;
    Example:

    $obj->grid_add_related_column('developer','name','Developer Name')

    Example:

    $obj->grid_add_custom_column('SOMETHING')

    #5 - Add support for other grids like: jqGrid, extjs Grid, dijit Grid…
    Example:

    $obj->set_grid_type('dijit');


    I think that would make a good extension… what do you think ? Any other Ideas? Do you think that it would be WASTE of my time?

    EDIT: I dont really know how to do it, I would have to study some dmz examples to do it.. and, of course, get some help from you. 😛

    Thanks

  • #128 / Apr 05, 2010 8:07pm

    TheJim

    35 posts

    @OverZealous

    Hey, Phil, related to an ITFK issue I had mentioned a while back and got a fix in for, I forgot to make sure this part of the fix made it into DMZ:

    DMZ 1.7.1 Line 1786
    
    if(isset($this->has_one[$rf]) && in_array($other_column, $this->fields))
    {
        if($this->{$other_column} != $o->id)
        {
            // ITFK: store on the table
            $this->{$other_column} = $o->id;
    
            // unset, so that it doesn't get re-saved later.
            unset($objects[$index]);
    
            // Remove reverse relationships for one-to-ones
            $this->_remove_other_one_to_one($rf, $o);
        }
    }

    If the ITFK field doesn’t have to be changed, then the object in the list isn’t unset.  I propose:

    if(isset($this->has_one[$rf]) && in_array($other_column, $this->fields))
    {
        // unset, so that it doesn't get re-saved later.
        unset($objects[$index]);
    
        if($this->{$other_column} != $o->id)
        {
            // ITFK: store on the table
            $this->{$other_column} = $o->id;
    
            // Remove reverse relationships for one-to-ones
            $this->_remove_other_one_to_one($rf, $o);
        }
    }

    This is a bit obscure and probably doesn’t affect many people, and in general I believe all it means is an extra call to the save function when _save_relation comes across the object.  What exposed it for me is some extra functionality added to an inherited save function that actually ended up crashing the page load with some infinite recursion in this particular circumstance.  So, undoubtedly most people wouldn’t see much of a negative effect, but I think it’s the “more correct” way to just remove it in _save_itfk.

    Thanks,

    Jim

  • #129 / Apr 06, 2010 12:08am

    OverZealous

    1030 posts

    @TheJim

    That’s another great find.  I’ll try to remember it when I next get around to working on DMZ.  Right now, I’m in crazy-what-am-I-doing-here-I-need-to-pack mode for this job.

    Oh, and to make sure that my stress levels are at an all-time high, the netbook I was planning on using for my interim computer while away from home decided to die today.  Now I have to see if Dell can overnight a replacement SSD, and that I can get it re-setup before I leave Friday morning.

    if($weather_type == RAIN) {
        $weather_amount = POUR;
    }

    😊

  • #130 / Apr 06, 2010 5:11pm

    TheJim

    35 posts

    It can’t ever be simple, can it?  Anyway, good luck with the new job, the move, and sorting out everything that comes along with that.

  • #131 / Apr 06, 2010 5:36pm

    sheldonnbbaker

    27 posts

    This looks great - just started out a few days ago.

    I still haven’t been able to do anything with it as I’m not getting the results I expected.

    I have a widgets table (contains widgets), a widget_versions table (contains versions of widgets, e.g., 1.0.0, 2.1.5, etc.) with fields id, widget_id, and a ratings_widget_versions table with fields widget_version_id and rating_id.

    My controller:

    $widgets = new Widget();
    $widgets->get()->widget_version->get();
    $this->load->view('', array('widgets' => $widgets));

    models/widget.php:

    <?php class Widget extends DataMapper {
    
        var $has_one = array();
        var $has_many = array('widget_version');
    
        function __construct($id = NULL)
        {
            parent::__construct($id);
        }
    }

    models/widget_version.php:

    <?php class Widget_version extends DataMapper {
    
        var $has_one = array('widget');
        var $has_many = array('rating');
    
        function __construct($id = NULL)
        {
            parent::__construct($id);
        }
    }

    models/rating.php:

    <?php class Rating extends DataMapper {
    
        var $has_one = array();
        var $has_many = array('workflow_version');
    
        function __construct($id = NULL)
        {
            parent::__construct($id);
        }
    }

    And my view:

    <?php if($widgets->count()) { ?>
    
        <?php foreach($widgets as $widget) { ?>
            
            <a>
            
                <?= $widget->name; ?>
                
            </a>
            
            <?php if($widget->widget_versions->exists()) { ?>
    
                <ul>
    
                <?php foreach($widget->widget_versions as $widget_version) { ?>
    
                    <li>
    
                        <?= $widget_version->version; ?>
                        
                        <?php if($widget_version->ratings->exists()) { ?>
    
                            <?= $widget_version->ratings->id; ?>
    
                        <?php } else { ?>
    
                            no widget version ratings
    
                        <?php } ?>
    
                    </li>
    
                <?php } ?>
    
                </ul>
    
            <?php } else { ?>
    
                no widget versions
    
            <?php } ?>
            
        <?php } ?>
        
    <?php } else { ?>
    
        no widgets
    
    <?php } ?>

    I end up getting the widgets displayed in a list - but not the widget versions or ratings, which I am expecting.

    Anything I’m doing wrong here?

    Thanks

  • #132 / Apr 06, 2010 5:52pm

    OverZealous

    1030 posts

    @sheldonbaker
    Your query doesn’t look up every widget_version for every widget - look at the queries being generated to see this.  You are only looking up the widget_version for the top-level widget.

    Instead, you probably want to include the widget_version alongside the main query (if it is $has_one related).  For this see include_related.

    Alternatively, if there are more than one widget_version per widget, you need to call $widget->widget_version->get() inside the foreach loop, on every widget.

  • #133 / Apr 06, 2010 6:06pm

    sheldonnbbaker

    27 posts

    @sheldonbaker
    Your query doesn’t look up every widget_version for every widget - look at the queries being generated to see this.  You are only looking up the widget_version for the top-level widget.

    How do I find the queries being generated?

    @sheldonbaker
    Alternatively, if there are more than one widget_version per widget, you need to call $widget->widget_version->get() inside the foreach loop, on every widget.

    There is more than one per widget - there isn’t any way to do without calling get() in the foreach? Does that increase the number of queries or simply return an existing object?

  • #134 / Apr 06, 2010 7:45pm

    TheJim

    35 posts

    How do I find the queries being generated?

    CodeIgniter’s profiler: from your controller run

    $this->output->enable_profiler(TRUE);

    and check the queries at the bottom of the page.

    There is more than one per widget - there isn’t any way to do without calling get() in the foreach? Does that increase the number of queries or simply return an existing object?

    Calling get() in foreach would increase the number of queries.  That’s why Phil first recommended using include_related.  As long as you have either one-to-many or one-to-one relationships, you can use include_related.  The way your models are set up, you could do something like

    $versions = new Widget_version();
    $versions->include_related('widget')->get();
    foreach ($versions as ...

    and get all your versions and widgets in one result set.  Now, of course, to display widget-by-widget, you’d need extra logic.  And ratings wouldn’t lend themselves to include_related, as you’ve defined a many-to-many relationship.  Also with ratings, I think you might want to take a look at “workflow_version” in your relationship, for starters.

    To elaborate a little on what Phil said earlier about not loading a Widget_version for each Widget,

    $widgets->get()->widget_version->get();

    does not do what you seem to think (judging by your later code and comment).  The confusion likely has to do with the dual nature of DMZ objects as database records and iterators.  So, $widgets->get() loads all records from your widgets table and stores them such that they can be iterated over, however, when you access the widget_version field on the object, it applies to the object-as-a-record (not to all returned records).  A Widget_version object is instantiated with its parent set to the first record stored in $widgets.  Then when get() is called on widget_version, only Widget_version records related to that first Widget are returned.  Later, when you’re iterating over all widgets, you’re dealing with a set of individual objects which don’t refer to the original object’s related Widget_versions you pulled from the database.  That’s why you get all the widgets fine, but see no version information at all.  That’s a little tricky, so I hope that my explanation makes sense.

    You might need some more time with the manual and and picking apart the example application.  Or, depending on how you learn best, turning on the profiler so you can see if the generated queries are doing what you think they should might be your best option.

  • #135 / Apr 06, 2010 8:13pm

    sheldonnbbaker

    27 posts

    Thanks for the help.

    I’ve gone over the example app and the documentation and have been fooling with this for a while, unable to get a basic setup going (as you can see). I guess I was expecting to be able to get very deep relationships very easily (my whole app will have many deep relationships like this - this is but a simple example).

    After thinking about it though, it’s really not even that easy with plain MySQL (I assume one would have to do a bunch of GROUP_CONCAT’ing and PHP trickery to get, for example, 0 or more ratings, comments, and descriptions inside every widget_version array inside every widget array inside every user group array, etc.).

    And from what you’re suggesting, the best way to go about very deep relationships is to find the deepest ‘has_one’ model and get the queries from there, and do whatever logic is needed afterwards.

    EDIT: Assuming I use your example to get my widgets:

    $versions = new Widget_version();
    $versions->include_related('widget')->get();
    foreach ($versions as ...

    What would be a good way to get the ratings (and eventually comments, descriptions, etc.) for that widget_version (they would be has_many relationships)?

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

ExpressionEngine News!

#eecms, #events, #releases