We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 2611
    • 394 Posts
    Hi y’all,

    I needed to sort by a TV (sort by template variables) with getResources and couldn’t find a way in the manual to do so, so i made a way.
    Note that it’s not very neet, i know that...it’s kind of an emergency solution, but i thought that if anyone might
    be interested he/she could at least get it =D.

    <?php
    /**
     * getResourcesCustom
     *
     * A general purpose Resource listing and summarization snippet for MODx 2.0.
     *
     * @author Jason Coward
     * @copyright Copyright 2009, Jason Coward
     * @version 1.0.0 - December 28, 2009
     *
     * TEMPLATES
     *
     * tpl - Name of a chunk serving as a resource template
     * [NOTE: if not provided, properties are dumped to output for each resource]
     *
     * tplOdd - (Opt) Name of a chunk serving as resource template for resources with an odd idx value
     * (see idx property)
     * tplFirst - (Opt) Name of a chunk serving as resource template for the first resource (see first
     * property)
     * tplLast - (Opt) Name of a chunk serving as resource template for the last resource (see last
     * property)
     * tpl_{n} - (Opt) Name of a chunk serving as resource template for the nth resource
     *
     * SELECTION
     *
     * parents - Comma-delimited list of ids serving as parents
     *
     * depth - (Opt) Integer value indicating depth to search for resources from each parent [default=10]
     *
     * tvFilters - (Opt) Delimited-list of TemplateVar values to filter resources by. Supports two
     * delimiters and two value search formats. THe first delimeter || represents a logical OR and the
     * primary grouping mechanism.  Within each group you can provide a comma-delimited list of values.
     * These values can be either tied to a specific TemplateVar by name, e.g. myTV==value, or just the
     * value, indicating you are searching for the value in any TemplateVar tied to the Resource. An
     * example would be &tvFilters=`filter2==one,filter1==bar%||filter1==foo`
     * [NOTE: filtering by values uses a LIKE query and % is considered a wildcard.]
     * [NOTE: this only looks at the raw value set for specific Resource, i. e. there must be a value
     * specifically set for the Resource and it is not evaluated.]
     *
     * sortby - (Opt) Field to sort by [default=publishedon]
     * sortbyAlias - (Opt) Query alias for sortby field [default=]
     * sortbyEscaped - (Opt) Escapes the field name specified in sortby [default=0]
     * sortdir - (Opt) Order which to sort by [default=DESC]
     * limit - (Opt) Limits the number of resources returned [default=5]
     * offset - (Opt) An offset of resources returned by the criteria to skip [default=0]
     * 
     * sortbytv - (Opt) ID of template variable to sort by
     * tvmodifier - Case of the modifier switch to preprocess the TV's value before sorting
     * tvoperator - Either: >, >=, <, <=
     * tvoperand - Value to compare to (uses the processed TV value from tv modifier!)
     *
     * OPTIONS
     *
     * includeContent - (Opt) Indicates if the content of each resource should be returned in the
     * results [default=0]
     * includeTVs - (Opt) Indicates if TemplateVar values should be included in the properties available
     * to each resource template [default=0]
     * processTVs - (Opt) Indicates if TemplateVar values should be rendered as they would on the
     * resource being summarized [default=0]
     * tvPrefix - (Opt) The prefix for TemplateVar properties [default=tv.]
     * idx - (Opt) You can define the starting idx of the resources, which is an property that is
     * incremented as each resource is rendered [default=1]
     * first - (Opt) Define the idx which represents the first resource (see tplFirst) [default=1]
     * last - (Opt) Define the idx which represents the last resource (see tplLast) [default=# of
     * resources being summarized + first - 1]
     *
     */
    $output = array();
    
    /* set default properties */
    $tpl = !empty($tpl) ? $tpl : '';
    $includeContent = !empty($includeContent) ? true : false;
    $includeTVs = !empty($includeTVs) ? true : false;
    $processTVs = !empty($processTVs) ? true : false;
    $tvPrefix = isset($tvPrefix) ? $tvPrefix : 'tv.';
    $parents = isset($parents) ? explode(',', $parents) : array($modx->resource->get('id'));
    $depth = isset($depth) ? (integer) $depth : 10;
    $children = array();
    foreach ($parents as $parent) {
        $pchildren = $modx->getChildIds($parent, $depth);
        if (!empty($pchildren)) $children = array_merge($children, $pchildren);
    }
    if (!empty($children)) $parents = array_merge($parents, $children);
    
    $tvFilters = !empty($tvFilters) ? explode('||', $tvFilters) : array();
    
    $sortby = isset($sortby) ? $sortby : 'publishedon';
    $sortbyAlias = isset($sortbyAlias) ? $sortbyAlias : 'modResource';
    $sortbyEscaped = !empty($sortbyEscaped) ? true : false;
    if ($sortbyEscaped) $sortby = "`{$sortby}`";
    if (!empty($sortbyAlias)) $sortby = "`{$sortbyAlias}`.{$sortby}";
    $sortdir = isset($sortdir) ? $sortdir : 'DESC';
    $limit = isset($limit) ? (integer) $limit : 5;
    $offset = isset($offset) ? (integer) $offset : 0;
    $totalVar = !empty($totalVar) ? $totalVar : 'total';
    $tvmodifier = !empty($tvmodifier) ? $tvmodifier : '';
    $sortbytv = !empty($sortbytv) ? $sortbytv : '';
    $tvoperator = !empty($tvoperator) ? $tvoperator : ''; 
    $tvoperand = !empty($tvoperand) ? $tvoperand : '';  
    
    if ($tvoperator != '') {
        $tvLimit = $limit;
        $limit = null;    
    }
    
    /* build query */
    $contextResourceTbl = $modx->getTableName('modContextResource');
    $context = empty($context) ? $modx->quote($modx->context->get('key')) : $modx->quote($context);
    $criteria = $modx->newQuery('modResource', array(
        'deleted' => '0'
        ,'published' => '1'
        ,"`modResource`.`parent` IN (" . implode(',', $parents) . ")"
        ,"(`modResource`.`context_key` = {$context} OR EXISTS(SELECT 1 FROM {$contextResourceTbl} `ctx` WHERE `ctx`.`resource` = `modResource`.`id` AND `ctx`.`context_key` = {$context}))"
    ));
    
    if (empty($showHidden)) {
        $criteria->andCondition(array('hidemenu' => '0'));
    }
    if (!empty($hideContainers)) {
        $criteria->andCondition(array('isfolder' => '0'));
    }
    if (!empty($tvFilters)) {
        $tmplVarTbl = $modx->getTableName('modTemplateVar');
        $tmplVarResourceTbl = $modx->getTableName('modTemplateVarResource');
        $conditions = array();
        foreach ($tvFilters as $fGroup => $tvFilter) {
            $filterGroup = count($tvFilters) > 1 ? $fGroup + 1 : 0;
            $filters = explode(',', $tvFilter);
            foreach ($filters as $filter) {
                $f = explode('==', $filter);
                if (count($f) == 2) {
                    $tvName = $modx->quote($f[0]);
                    $tvValue = $modx->quote($f[1]);
                    $conditions[$filterGroup][] = "EXISTS (SELECT 1 FROM {$tmplVarResourceTbl} `tvr` JOIN {$tmplVarTbl} `tv` ON `tvr`.`value` LIKE {$tvValue} AND `tv`.`name` = {$tvName} AND `tv`.`id` = `tvr`.`tmplvarid` WHERE `tvr`.`contentid` = `modResource`.`id`)";
                } elseif (count($f) == 1) {
                    $tvValue = $modx->quote($f[0]);
                    $conditions[$filterGroup][] = "EXISTS (SELECT 1 FROM {$tmplVarResourceTbl} `tvr` JOIN {$tmplVarTbl} `tv` ON `tvr`.`value` LIKE {$tvValue} AND `tv`.`id` = `tvr`.`tmplvarid` WHERE `tvr`.`contentid` = `modResource`.`id`)";
                }
            }
        }
        if (!empty($conditions)) {
            foreach ($conditions as $cGroup => $c) {
                if ($cGroup > 0) {
                    $criteria->orCondition($c, null, $cGroup);
                } else {
                    $criteria->andCondition($c);
                }
            }
        }
    }
    
    $total = $modx->getCount('modResource', $criteria);
    $modx->setPlaceholder($totalVar, $total);
    
    $criteria->sortby($sortby, $sortdir);
    if (!empty($limit)) $criteria->limit($limit, $offset);
    $columns = $includeContent ? '*' : $modx->getSelectColumns('modResource', 'modResource', '', array('content'), true);
    $criteria->select($columns);
    if (!empty($debug)) {
        $criteria->prepare();
        $modx->log(modX::LOG_LEVEL_ERROR, $criteria->toSQL());
    }
    $collection = $modx->getCollection('modResource', $criteria);
    
    $idx = !empty($idx) ? intval($idx) : 1;
    $first = empty($first) && $first !== '0' ? 1 : intval($first);
    $last = empty($last) ? (count($collection) + $idx - 1) : intval($last);
    
    /* include parseTpl */
    include_once $modx->getOption('core_path').'components/getresources/include.parsetpl.php';
    
    if ($sortbytv != '') {
        
        switch($tvoperand) {
            case 'time':
                $tvoperand = time();
                break;    
            case 'daystart':
                $tvoperand = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
                break;
        }
        
        function getResourcesSort($a, $b) {
            return strcmp($a["tvvalue"], $b["tvvalue"]);
        }
        
        $outputArray = array();
        foreach($collection as $resourceId => &$resource) {
            $tvValue = $resource->getOne('TemplateVarResources', array('tmplvarid' => $sortbytv, 'contentid' => $resource->get('id')));
            
            if ($tvValue != null) {
                switch($tvmodifier) {
                    case 'strtotime':
                        $tvValue = strtotime($tvValue->get('value'));
                        break;    
                    default:
                        $tvValue = $tvValue->get('value');
                        break;
                }
            } else {
                $tvValue = '';    
            }        
            
            $add = true;
            if ($tvoperator != '') {
                $add = false;
    
                switch($tvoperator) {
                    case '>';
                        if ($tvValue > $tvoperand) {
                            $add = true;
                        }
                        break;
                    case '>=';
                        if ($tvValue >= $tvoperand) {
                            $add = true;
                        }
                        break;
                    case '<':
                        if ($tvValue < $tvoperand) {
                            $add = true;
                        }
                        break;
                    case '<=':
                        if ($tvValue <= $tvoperand) {
                            $add = true;
                        }
                        break;    
                }
            }
            
            if ($add) {
                $outputArray[$resourceId] = array(
                    'tvvalue' => $tvValue,
                    'resource' => $resource
                );
            }
        }
        
        usort($outputArray, 'getResourcesSort');
        
        $collection = array();
        foreach($outputArray as $resourceId => $resource) {
            $collection[$resourceId] = $resource['resource'];    
            if (sizeof($collection) == $tvLimit) {
                break;    
            }
        }
        
        if (strtolower($sortdir) == 'desc') {
            $collection = array_reverse($collection);    
        }
    }
    
    foreach ($collection as $resourceId => $resource) {
        $tvs = array();
        if (!empty($includeTVs)) {
            $templateVars =& $resource->getMany('TemplateVars');
            foreach ($templateVars as $tvId => $templateVar) {
                $tvs[$tvPrefix . $templateVar->get('name')] = !empty($processTVs) ? $templateVar->renderOutput($resource->get('id')) : $templateVar->get('value');
            }
        }
        $odd = ($idx & 1);
        $properties = array_merge(
            $scriptProperties
            ,array(
                'idx' => $idx
                ,'first' => $first
                ,'last' => $last
            )
            ,$resource->toArray()
            ,$tvs
        );
        $resourceTpl = '';
        $tplidx = 'tpl_' . $idx;
        if (!empty($$tplidx)) $resourceTpl = parseTpl($$tplidx, $properties);
        switch ($idx) {
            case $first:
                if (!empty($tplFirst)) $resourceTpl = parseTpl($tplFirst, $properties);
                break;
            case $last:
                if (!empty($tplLast)) $resourceTpl = parseTpl($tplLast, $properties);
                break;
        }
        if ($odd && empty($resourceTpl) && !empty($tplOdd)) $resourceTpl = parseTpl($tplOdd, $properties);
        if (!empty($tpl) && empty($resourceTpl)) $resourceTpl = parseTpl($tpl, $properties);
        if (empty($resourceTpl)) {
            $chunk = $modx->newObject('modChunk');
            $chunk->setCacheable(false);
            $output[]= $chunk->process(array(), '<pre>' . print_r($properties, true) .'</pre>');
        } else {
            $output[]= $resourceTpl;
        }
        $idx++;
    }
    return implode("\n", $output);
    ?>


    In the snippet call:

    sortbytv = The ID belonging to the TV you want to sort on
    tvmodifier = A pre-processor for TV values (i had to convert back to timestamp so)
    tvoperator - Either: >, >=, <, <=
    tvoperand - Value to compare to (uses the processed TV value from tv modifier!)

    It uses the default sortdir to sort.

    You can create your modifiers/tvoperands in the script itself...in the switch.

    I needed to sort by a TV that held a custom date only showing those today and forward, so:

    [!getResourcesCustom? &parents=`3`&tpl=`chunkName`&sortbytv=`3`&tvmodifier=`strtotime`&tvoperator=`>`&tvoperand=`daystart`]

    This script uses an array limiter which means it does get ALL the resources to loop through. Im using this on small sites
    (less then 500 resources) so it’s fine by me, but make that disision for yourself. (Maybe enable caching if its possible)


    Update (sept, 14th, 2010):
    Fixed a little bug causing random sorted TV values smiley
      Follow me on twitter: @b03tz
      Follow SCHERP Ontwikkeling on twitter: @scherpontwikkel
      CodeMaster
      • 7455
      • 2,204 Posts
      nice work m8
        follow me on twitter: @dimmy01
        • 18463
        • 121 Posts
        Martijn van Turnhout Reply #3, 13 years, 8 months ago
        Great work! So, contrary to Ditto, getResources can’t order its output by a TV yet? That’s a shame. Think I’m gonna wait a while for the Revo add-ons to catch up with their Evo counterparts...
          • 2611
          • 394 Posts
          Yeah you should! I couldnt find a way at least... smiley
            Follow me on twitter: @b03tz
            Follow SCHERP Ontwikkeling on twitter: @scherpontwikkel
            CodeMaster