CodeIgniter Forums
DMZ 1.7.1 (DataMapper OverZealous Edition) - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forumdisplay.php?fid=20)
+--- Forum: Archived Libraries & Helpers (https://forum.codeigniter.com/forumdisplay.php?fid=22)
+--- Thread: DMZ 1.7.1 (DataMapper OverZealous Edition) (/showthread.php?tid=28550)



DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]sheldonnbbaker[/eluser]
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:
Code:
$widgets = new Widget();
$widgets->get()->widget_version->get();
$this->load->view('', array('widgets' => $widgets));

models/widget.php:
Code:
<?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:
Code:
<?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:
Code:
<?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:
Code:
<?php if($widgets->count()) { ?>

    <?php foreach($widgets as $widget) { ?>
        
        <a>
        
            &lt;?= $widget->name; ?&gt;
            
        </a>
        
        &lt;?php if($widget->widget_versions->exists()) { ?&gt;

            <ul>

            &lt;?php foreach($widget->widget_versions as $widget_version) { ?&gt;

                <li>

                    &lt;?= $widget_version->version; ?&gt;
                    
                    &lt;?php if($widget_version->ratings->exists()) { ?&gt;

                        &lt;?= $widget_version->ratings->id; ?&gt;

                    &lt;?php } else { ?&gt;

                        no widget version ratings

                    &lt;?php } ?&gt;

                </li>

            &lt;?php } ?&gt;

            </ul>

        &lt;?php } else { ?&gt;

            no widget versions

        &lt;?php } ?&gt;
        
    &lt;?php } ?&gt;
    
&lt;?php } else { ?&gt;

    no widgets

&lt;?php } ?&gt;

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


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]OverZealous[/eluser]
@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.


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]sheldonnbbaker[/eluser]
[quote author="OverZealous" date="1270605156"]@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.
[/quote]

How do I find the queries being generated?

[quote author="OverZealous" date="1270605156"]@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.[/quote]

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?


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]TheJim[/eluser]
[quote author="sheldonnbbaker" date="1270606004"]
How do I find the queries being generated?
[/quote]

CodeIgniter's profiler: from your controller run

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

and check the queries at the bottom of the page.

Quote: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

Code:
$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,

Code:
$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.


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]sheldonnbbaker[/eluser]
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:
Code:
$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)?


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]bEz[/eluser]
I heard it from afar... "DEEP RELATIONSHIPS" let's meditate.

If you adhere to the conventions of DMZ db setup, and you eventually wrap yourself with that concept and the schema of your database requirements, you will find that "DR" is so so the answer to this scenario.
I caught this discussion on a whim, and will try to provide some insight if I can. However, the old cliche of "the Manaul has your answer" applies here to save the day.


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]TheJim[/eluser]
Yeah, DMZ provides a lot of magic, but with many deep relationships, there are still considerations and trade-offs, and as you noted, even with hand-crafted SQL it's not entirely simple to take care of everything all in one query.

Good luck putting it all together.


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]TheJim[/eluser]
Oh, I missed that you had a question there.

[quote author="sheldonnbbaker" date="1270613632"]

EDIT: Assuming I use your example to get my widgets:
Code:
$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)?[/quote]

You'd be stuck with running a get on each, since there's not really any way to combine them, and they'd all be has_many.

Code:
$version->comment->get();
$version->rating->get();
...

Well, I guess there's also the option of grabbing, say, all comments in one query and then relating them via PHP logic (easier if you have an in-table foreign key rather than a separate join table). That would be a trade-off of more memory usage for fewer queries. I would probably only go that route if pages are really sluggish though.

With a ton of relationships like that, at some point I'd probably start to ask myself if I need to store it this way. That is, is the rating really part of the comment? Or can more information be added to the Widget_version itself? Do I only care about the aggregate rating (storing a 0-5 as a field of Widget_version, or grabbing the SUM() from the DB rather than each record). That sort of thing.

Of course, not knowing anything about what you're trying to do, I have to give you the benefit of the doubt, and it may just be that you'll have to live with it and optimize in other ways (selecting only necessary fields, using get_iterated, creating good indexes in your DB, etc.). As long as you're not generating mammoth pages though (dozens of widgets with all their associated records, and all the queries that would entail), I think you'll have reasonable performance.


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-06-2010

[eluser]sheldonnbbaker[/eluser]
Thanks a bunch!


DMZ 1.7.1 (DataMapper OverZealous Edition) - El Forum - 04-07-2010

[eluser]Oblique[/eluser]
AD:
Hello everyone.
I've just uploaded new version of HTML table extension.

Now it supports _iterated get's, has better docs and i've made few other tweaks.

Check it out Smile