We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 22448
    • 241 Posts
    So here's an example from getCollectionGraph rtfm page covering a very common use case: get resources with their related TVs.

    $pages = $modx->getCollectionGraph('modResource', '{"TemplateVarResources":{}}', array('parent'=>12));
     
    foreach ($pages as $p) {
            foreach ($p->TemplateVarResources as $tv) {
                    // Do stuff here with the TV
                    $tv_array = $tv->toArray();
                    $tv->get('value');
            }
    }


    When i read the documentation first i thought: "Great, each resource in the collection will get an attached collection of TVs for easy access."

    So I expected to be able to do something as stimple as this (using the example above):

    $pages = $modx->getCollectionGraph('modResource', '{"TemplateVarResources":{}}', array('parent'=>12));
    $results = array();
    foreach ($pages as $p) {
        $results['pagetitle'] = $p->get('pagetitle');
        $results['date'] = $p->get('createdon');
        $results['tv1'] = $p->TemplateVarResources->get('tv1');
        $results['tv2'] = $p->TemplateVarResources->get('tv2');
    }
    


    But when i ran that i got "calling get() function on a non-object" error. Going back to documentation and some other examples it turns out that you have to iterate through TemplateVarResources with an inner loop to get tv values before they can be used. So i had to create another array in the form 'tv.name'=>'tv.value' just to be able to use tvs with the $p object.

    Granted that i'm new to XPDO and am learning as i go along but the above approach seems rather inelegant.
    Isn't the whole point of getCollectionGraph to elegantly return entities with their relationships? And it would make sense to allow direct named access to those relationships once you have the handle on the main object, wouldn't it.

    Of course it's entirely possible that i'm missing something obvious. And perhaps to satisfy many entity types that getCollectionGraph needs to support it must operate in such a way. But since tvs are such a big part of MODx i would think that they should be treated nicer. Perhaps a getCollectionWithTVsGraph method to make getting resources with their tvs using api easier.

    And while I'm making a wishlist it would be nice to have a simpler way to select which TVs to include in the returned collection. Currently is not possible to do that with the standard getCollectionGraph call. The only way to do that is to define a newQuery separately and pass it into getCollectionGraph method like so (my actual code not related to the example above):

    $criteria = array();
    $criteria['modResource.id:IN'] = $selectedIDs;
    $criteria['TemplateVarResources.tmplvarid:IN'] = array(44,41,67,65,61,66); //specify which TVs to grab
    $criteria = $modx->newQuery('modResource', $criteria);
    $results = $modx->getCollectionGraph('modResource', '{"TemplateVarResources":{"TemplateVar":{}}}', $criteria);
    


    Just some muzings late at night after fighting with getCollectionGraph for a few hours.
      • 3749
      • 24,544 Posts
      The problem isn't with getCollectionGraph(), imo, it's with the architecture of TVs. Be warned that if any of your TVs contains the TV's default value, it will not be retrieved by your search, because it won't be in the modTemplateVarResources table. In addition, if any of them contain the @BINDINGS, or are types that need to be rendered (e.g., image TVs), you'll only get the raw value unless you get the TV itself and call $tv->renderOutput($resourceId), or use $resource->getTVValue(), which gets the TV and calls renderOutput() for you.

      You might look at extending modResource with ClassExtender, which essentially replaces your TVs with fields in a custom database table. They appear on the Create/Edit Resource panel, and you can retrieve them by search criteria *way* faster than you can get TVs.

      http://bobsguides.com/blog.html/2014/06/02/why-extend-modresource/

      http://bobsguides.com/classextender-class.html

        Did I help you? Buy me a beer
        Get my Book: MODX:The Official Guide
        MODX info for everyone: http://bobsguides.com/modx.html
        My MODX Extras
        Bob's Guides is now hosted at A2 MODX Hosting
        • 22448
        • 241 Posts
        Thanks BobRay,

        an interesting idea. I'll look through it. I do like TVs for prototyping and trying different scenarios early in the project when the data architecture is in flux. But once that's settled performance becomes more important.

        I also like the ease and flexibility of TV input modifiers but TV rendering when called from API is a problem. I thought about $resource->getTVValue() but that would be a separate query, would it not? I was trying to minimize the number of database calls, hence the use of getCollectionGraph.

        I guess an argument can be made that even if there are a lot of queries on the page but the page is cached then the performance hit will happen only on the first load. Are there any tried and true caching strategies for Revo?


          • 3749
          • 24,544 Posts
          $resource->getTVValue() is often several queries. One to get the resource (if you don't already have it), one to get the modTemplateVarTemplate value, and another to get the TV's default value if the modTemplateVarTemplate call fails.

          With ClassExtender, you can make a single query with getCollectionGraph() and get all the fields from multiple resources, and for each of them, all the custom fields that used to be in TVs. You can select and sort in that same query by any combination of the fields. You do give up the @ bindings and default values, and you only have the raw values of the fields, but there's nothing to stop you from doing the processing yourself and setting default values just once when you save the custom field rather than every time it's retrieved -- a bonus is that that would all happen in one place: the plugin that saves them when you save a resource. It is blindingly fast compared to, say, a getResources call to do the same thing with TVs, especially if you have a large number of resources.

          With users, BTW, ClassExtender will let you make one query with getCollectionGraph() and get all the user fields, the user profile fields, and the custom fields for multiple users, again searching and sorting by any combination of fields.

          With TVs, you can get the collection of resources with their TV values, but you can't really sort them because some of the TV values may be empty (meant to use the default value), so you have to fill them in by getting the default value of each TV that's empty and then sort the array. Selecting a subset of resources based on TV values can be really slow and involve many queries for the same reason.


          Caching strategies: Some info here: http://rtfm.modx.com/revolution/2.x/developing-in-modx/advanced-development/caching. Any page can be cacheable, and *any* tag can be cached or uncached.



            Did I help you? Buy me a beer
            Get my Book: MODX:The Official Guide
            MODX info for everyone: http://bobsguides.com/modx.html
            My MODX Extras
            Bob's Guides is now hosted at A2 MODX Hosting