We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 40045
    • 534 Posts
    I would like to have the same output as $modx->resource->toArray() is giving me for any collection of resources that I want to fetch. When I print $modx->resource->toArray(), I get an array of resource fields AND the corresponding TVs (also in the array, like that (shortened version):

    Array
    (
        [id] => 268
        [type] => document
        [contentType] => text/html
        [pagetitle] => Labs
        [longtitle] => 
        [description] => 
        [alias] => labs
    ...
        [class_key] => modDocument
        [context_key] => web
        [content_type] => 1
        [uri] => labs.html
    ... 
        [tv_name] => Array
            (
                [0] => tv_name
                [1] => 64||65||67||68||66||72||75||69||128||71||74||73||387||477
                [2] => delim
                [3] => 
                [4] => checkboxsortable
            )
    
        [tv2_name] => Array
            (
                [0] => tv2_name
                [1] => TV2 Value
                [2] => default
                [3] => 
                [4] => text
            )
    )
    


    I already tried around with stuff like (based on the RTFM about getCollectionGraph):

    <?php
    $pages = $modx->getCollectionGraph('modResource', '{"TemplateVarResources":{}}', array('parent'=>269));
     
    foreach ($pages as $p) {
            echo '<pre>' . print_r($p->toArray(),1) . '</pre>';
    }
    


    but with no success (actually I could get the tvs, but I have to loop again inside the outer forach loop, I want to avoid that if possible...

    so my question would be, does anybody have an idea how $modx->resource is constructed (or knows where the $modx->resource object is assigned, so I can check the code myself)?

    thank you in advance for any hints!
      • 3749
      • 24,544 Posts
      You can call toArray() on any MODX (xPDO) object. If you want the related objects, you can try setting the fourth argument to true:

      public function toArray($keyPrefix= '', $rawValues= false, $excludeLazy= false, $includeRelated= false) {
      }



      That said, I think the code you're looking for is the getResource() method of modrequest.class.php. It's called on every request and has this code (after first calling toArray():


      if ($tvs = $resource->getMany('TemplateVars', 'all')) {
           /** @var modTemplateVar $tv */
           foreach ($tvs as $tv) {
               $resource->set($tv->get('name'), array(
                   $tv->get('name'),
                   $tv->getValue($resource->get('id')),
                   $tv->get('display'),
                   $tv->get('display_params'),
                   $tv->get('type'),
                ));
          }
      }
      



        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
        • 40045
        • 534 Posts
        Thanks a lot bob for grinding that out of modrequest!!

        I initially was very surprised about the 4th param of toArray() =), I always only used 3...unfortunately it doesn't have the desired effect (eg. showing also the tvs in the array) when I do for example this (just put that code into the console extra to see what happens in your installation), it just shows the "normal" resource fields, but no tvs:

        <?php
        $res = $modx->getObject('modResource', array('id' => 1));
        
        echo '<pre>' . print_r($res->toArray('', false, false, true), 1) . '</pre>';
        


        (while resource with ID = 1 has quite a few TVs, not all of them with values, but at least two with value)

        but this one does what I want:

        <?php
        $resobj = $modx->getObject('modResource', array('id' => 1));
        
        if ( $tvobjs = $resobj->getMany('TemplateVars', 'all') ) {
            foreach ($tvobjs as $tvobj) {
                $tv = $tvobj->toArray();
                $resobj->set($tv['name'], array(
                    $tv['value']
                ));
            }
        }
        
        echo '<pre>' . print_r($resobj->toArray(), 1) . '</pre>';
        


        this works and adds the tvs to the output array. I would like to know if it's actually faster if I work with the array from ->toArray() instead of calling ->get() each time, any clues about that?

        My question regarding the topic would now be, why the first method with toArray() (with 4th param true) doesn't output the tvs?

        To me this would seem to me the most performant way (all the question is just about some performance problems I have, eg. I'm trying to optimize my calls and as the related objects would have been already fetched, I wouldn't need to call many methods again, right?)

        any ideas? [ed. note: exside last edited this post 12 years, 10 months ago.]
          • 3749
          • 24,544 Posts
          I don't know why setting the fourth arg. to true doesn't get them in the array. Maybe it gets them into the DB cache so the getMany() call will go faster.

          There are several alternatives to what you're doing that might be faster (it's hard to know without testing). One would be to use getObjectGraph() to get the resource and the TV values at the same time. Another would be to use straight PDO instead of xPDO, which is almost always the fastest method, though it would give you the raw values of the TVs rather than their rendered values.

          TBH, I pretty much suck at DB tuning, so maybe someone else can help out here.
            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
            • 40045
            • 534 Posts
            Thanks again Bob,

            I tried to get some hard numbers and it seems the way I choose is the worst =)...eg. $someobj->toArray() seems to be the slowest of all..., i tried to create some sort of benchmark but I don't know how reliable it really is, as before, this is run easiest in the console extra:

            <?php
            $query = array('parent' => 15);
            
            $tStart = $modx->getMicroTime();
            $pdoFetch = $modx->getService('pdofetch','pdoFetch',$modx->getOption('pdotools.core_path',null,$modx->getOption('core_path').'components/pdotools/').'model/pdotools/',array());
            if (!($pdoFetch instanceof pdoFetch)) {return '';}
            $tEnd = $modx->getMicroTime();
            echo '<pre>Needed ' . sprintf('%2.4f s', $tEnd - $tStart) . ' to load pdoFetch service</pre>';
            
            // set up the round
            $rounds = 5;
            // prepare the benchmark arrays
            $timingarrays = array('pdofetch' => array(), 'xpdo0' => array(), 'xpdo1' => array(), 'xpdo2' => array(), 'xpdo3' => array(), 'xpdo4' => array(), 'xpdo5' => array(), 'xpdo6' => array());
            
            // start the looping
            for ($i=0;$i<$rounds;$i++) {
            
            	$tStart = $modx->getMicroTime();
            	$resarrs = $pdoFetch->getCollection('modResource', $query);
            
            	foreach ($resarrs as $resarr) {
            		$task = $resarr['pagetitle'];
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['pdofetch'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getCollection('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$task = $resobj->get('pagetitle');
            	}
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo0'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getCollection('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$resarr = $resobj->toArray();
            		$task = $resarr['pagetitle'];
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo1'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getCollection('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$resarr = $resobj->toArray('', true, false, false);
            		$task = $resarr['pagetitle'];
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo2'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getCollection('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$resarr = $resobj->toArray('', true, true, false);
            		$task = $resarr['pagetitle'];
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo3'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getCollection('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$resarr = $resobj->toArray('', true, true, true);
            		$task = $resarr['pagetitle'];
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo4'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getIterator('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$task = $resobj->get('pagetitle');
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo5'][] = $tEnd - $tStart;
            
            	// ------------------------------
            
            	$tStart = $modx->getMicroTime();
            
            	$resobjs = $modx->getIterator('modResource', $query);
            
            	foreach ($resobjs as $resobj) {
            		$resarr = $resobj->toArray('', true, false, false);
            		$task = $resarr['pagetitle'];
            	}
            
            	$tEnd = $modx->getMicroTime();
            	$timingarrays['xpdo6'][] = $tEnd - $tStart;
            }
            
            // get the number of items that was looped trough
            $tStart = $modx->getMicroTime();
            $rescount = $modx->getCount('modResource', $query);
            $tEnd = $modx->getMicroTime();
            echo '<pre>Needed ' . sprintf('%2.4f s', $tEnd - $tStart) . ' to to do $modx->getCount()</pre>';
            
            
            // calculate the avarage time spent
            foreach ($timingarrays as $key => $timingarray) {
            	$average = average($timingarray);
            
            	switch($key) {
            		case 'pdofetch':
            			$test = '$pdoFetch->getCollection()';
            			break;
            		case 'xpdo0':
            			$test = '$modx->getCollection() with $obj->get()';
            			break;
            		case 'xpdo1':
            			$test = '$modx-getCollection() with $obj->toArray()';
            			break;
            		case 'xpdo2':
            			$test = '$modx-getCollection() with $obj->toArray("", true, false, false)';
            			break;
            		case 'xpdo3':
            			$test = '$modx-getCollection() with $obj->toArray("", true, true, false)';
            			break;
            		case 'xpdo4':
            			$test = '$modx-getCollection() with $obj->toArray("", true, true, true)';
            			break;
            		case 'xpdo5':
            			$test = '$modx-getIterator() with $obj->get()';
            			break;
            		case 'xpdo6':
            			$test = '$modx-getIterator() with $obj->toArray("", true, false, false)';
            			break;
            		default:
            			$test = 'undefined';
            			break;
            	}
            
            	echo '<pre>' . $test . ' needed ' . sprintf('%2.4fs', $average) . ' in avarage over ' . $rounds . ' rounds looping trough ' . $rescount . ' resources</pre>';
            }
            
            
            
            function average($arr) {
                $count = count($arr); //total numbers in array
                foreach ($arr as $value) {
                    $total = $total + $value; // total value of array numbers
                }
                $average = ($total/$count); // get average value
                return $average;
            }
            


            in my case I got a result like this:

            RUN1
            Needed 0,0080 s to load pdoFetch service
            Needed 0,0046 s to to do $modx->getCount()
            $pdoFetch->getCollection() needed 0,0145s in avarage over 5 rounds looping trough 38 resources
            $modx->getCollection() with $obj->get() needed 0,6185s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray() needed 0,5654s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, false, false) needed 0,4354s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, true, false) needed 0,4476s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, true, true) needed 0,4470s in avarage over 5 rounds looping trough 38 resources
            $modx-getIterator() with $obj->get() needed 0,4265s in avarage over 5 rounds looping trough 38 resources
            $modx-getIterator() with $obj->toArray("", true, false, false) needed 0,4439s in avarage over 5 rounds looping trough 38 resources
            


            RUN2
            Needed 0,0080 s to load pdoFetch service
            Needed 0,0048 s to to do $modx->getCount()
            $pdoFetch->getCollection() needed 0,0136s in avarage over 5 rounds looping trough 38 resources
            $modx->getCollection() with $obj->get() needed 0,4608s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray() needed 0,6245s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, false, false) needed 0,4478s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, true, false) needed 0,4512s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, true, true) needed 0,4978s in avarage over 5 rounds looping trough 38 resources
            $modx-getIterator() with $obj->get() needed 0,4510s in avarage over 5 rounds looping trough 38 resources
            $modx-getIterator() with $obj->toArray("", true, false, false) needed 0,4546s in avarage over 5 rounds looping trough 38 resources
            


            RUN3
            Needed 0,0079 s to load pdoFetch service
            Needed 0,0046 s to to do $modx->getCount()
            $pdoFetch->getCollection() needed 0,0159s in avarage over 5 rounds looping trough 38 resources
            $modx->getCollection() with $obj->get() needed 0,4639s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray() needed 0,6928s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, false, false) needed 0,4300s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, true, false) needed 0,3911s in avarage over 5 rounds looping trough 38 resources
            $modx-getCollection() with $obj->toArray("", true, true, true) needed 0,4952s in avarage over 5 rounds looping trough 38 resources
            $modx-getIterator() with $obj->get() needed 0,5236s in avarage over 5 rounds looping trough 38 resources
            $modx-getIterator() with $obj->toArray("", true, false, false) needed 0,5480s in avarage over 5 rounds looping trough 38 resources
            


            I know pdoTools is fast (and as you said, going over pure PDO is fastest, and I think this is what pdoTools does...converting xPDO queries as we know and love them to pure PDO...right?) but the test shows such extrem differences that I almost wish that pdoTools was actually part of the core...this is crazy^^...where does all that time get lost in the native methods?? any idea?

            basically what my test says is "go with pdoTools whenever you can" and if not possible use $modx->getIterator() and from there access the fields with ->get() (actually I have to do further tests to see what happens if I access not only one but many fields with get('whatever')...could be that performance goes down when calling get many times, compared to toArray() I mean?) [ed. note: exside last edited this post 12 years, 10 months ago.]
              • 3749
              • 24,544 Posts
              I'm not sure exactly what PdoTools does (I've never looked at the code), or what's taking so much time.

              You might look into this: http://rtfm.modx.com/xpdo/2.x/getting-started/using-your-xpdo-model/retrieving-objects/getcollectiongraph
                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