We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 17883
    • 1,039 Posts
    I just finished a version where you can sortby=`resources` and the order of resources passed via &resources is used for the sort order of returned results. I needed it for a custom sorting output filter with which you can sort any list of values via drag&drop. Just stay tuned, have to learn Git a bit and pull a request. Will post it here for you directly after a couple of tests.
      • 17883
      • 1,039 Posts
      Is that something you could need?

      http://screenr.com/Gkk

      I attached the modified getResources version. Here you can pass in your resources and they will be displayed in that particular order (&resources=`2,12,4` will display the resources in that order). Just use &sortby=`resources` and don`t set a parent. Just to be sure name it "getResourcesNew" or something like that and don’t overwrite the original one.
      <?php
      /**
       * getResources
       *
       * A general purpose Resource listing and summarization snippet for MODx 2.0.
       *
       * @author Jason Coward
       * @copyright Copyright 2010, Jason Coward
       * @version 1.2.2-pl - October 18, 2010
       *
       * 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.]
       *
       * where - (Opt) A JSON expression of criteria to build any additional where clauses from. An example would be
       * &where=`{{"alias:LIKE":"foo%", "OR:alias:LIKE":"%bar"},{"OR:pagetitle:=":"foobar", "AND:description:=":"raboof"}}`
       *
       * sortby - (Opt) Field to sort by [default=publishedon]
       * sortbyTV - (opt) A Template Variable name to sort by (if supplied, this precedes the sortby value) [default=]
       * 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]
       * sortdirTV - (Opt) Order which to sort by a TV [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]
       *
       * 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]
       * outputSeparator - (Opt) An optional string to separate each tpl instance [default="\n"]
       *
       */
      if (!function_exists('cmp')) 
      { 
      	function cmp($a, $b) 
      	{
      		$pos_a=array_search($a->id,$GLOBALS['resources']); 
      		$pos_b=array_search($b->id,$GLOBALS['resources']); 
      		if ($pos_a == $pos_b) 
      		{ 
      			return 0; 
      		} 
      		return ($pos_a < $pos_b)?-1:1; 
      	} 
      } 
      $output = array();
      $outputSeparator = isset($outputSeparator) ? $outputSeparator : "\n";
      
      /* 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.';
      $depth = isset($depth) ? (integer) $depth : 10;
      $parents = (!empty($parents) || $parents === '0') ? explode(',', $parents) : '';
      // if no parents set but resources, leave parents empty
      $parents = (is_array($parents)) ? $parents : ($resources!='' ? '' : array($modx->resource->get('id')));
      if(!empty($parents)){
      $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();
      
      $where = !empty($where) ? $modx->fromJSON($where) : array();
      $showUnpublished = !empty($showUnpublished) ? true : false;
      $showDeleted = !empty($showDeleted) ? true : false;
      $sortby = isset($sortby) ? $sortby : 'publishedon';
      $sortbyTV = isset($sortbyTV) ? $sortbyTV : '';
      $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';
      $sortdirTV = isset($sortdirTV) ? $sortdirTV : 'DESC';
      $limit = isset($limit) ? (integer) $limit : 5;
      $offset = isset($offset) ? (integer) $offset : 0;
      $totalVar = !empty($totalVar) ? $totalVar : 'total';
      
      /* build query */
      $contextResourceTbl = $modx->getTableName('modContextResource');
      // echo "parents: ".$parents." | sortby: ".$sortby."| resources: ".$resources."<br>";
      /* multiple context support */
      if (!empty($context)) {
          $context = explode(',',$context);
          $contexts = array();
          foreach ($context as $ctx) {
              $contexts[] = $modx->quote($ctx);
          }
          $context = implode(',',$contexts);
          unset($contexts,$ctx);
      } else {
          $context = $modx->quote($modx->context->get('key'));
      }
      // Build query outside newQuery depending on if parents are set
      $query=array();
      if(!empty($parents)) $query[] = "`modResource`.`parent` IN (" . implode(',', $parents) . ")";
      $query[] = "(`modResource`.`context_key` IN ({$context}) OR EXISTS(SELECT 1 FROM {$contextResourceTbl} `ctx` WHERE `ctx`.`resource` = `modResource`.`id` AND `ctx`.`context_key` IN ({$context})))";
      $criteria = $modx->newQuery('modResource', $query);
      if (empty($showDeleted)) {
          $criteria->andCondition(array('deleted' => '0'));
      }
      if (empty($showUnpublished)) {
          $criteria->andCondition(array('published' => '1'));
      }
      if (empty($showHidden)) {
          $criteria->andCondition(array('hidemenu' => '0'));
      }
      if (!empty($hideContainers)) {
          $criteria->andCondition(array('isfolder' => '0'));
      }
      /* include/exclude resources, via &resources=`123,-456` prop */
      if (!empty($resources)) {
          $resources = explode(',',$resources);
          // don`t know why, but resources is empty after this function, so save them in new variable
          $globalresources = $resources;
          $include = array();
          $exclude = array();
          foreach ($resources as $resource) {
              $resource = (int)$resource;
              if ($resource == 0) continue;
              if ($resource < 0) {
                  $exclude[] = abs($resource);
              } else {
                  $include[] = $resource;
              }
          }
          if (!empty($include) && !empty($parents)) {
              $criteria->orCondition(array('modResource.id:IN' => $include),null,10);
          }
          if (!empty($include) && empty($parents)) {
              $criteria->andCondition(array('modResource.id:IN' => $include),null,10);
          }
          if (!empty($exclude)) {
              $criteria->andCondition(array('modResource.id NOT IN ('.implode(',',$exclude).')'));
          }
      }
      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);
                  }
              }
          }
      }
      if (!empty($where)) {
          $criteria->where($where);
      }
      
      $total = $modx->getCount('modResource', $criteria);
      $modx->setPlaceholder($totalVar, $total);
      
      $columns = $includeContent ? $modx->getSelectColumns('modResource', 'modResource') : $modx->getSelectColumns('modResource', 'modResource', '', array('content'), true);
      $criteria->select($columns);
      if (!empty($sortbyTV)) {
          $criteria->leftJoin('modTemplateVar', 'tvDefault', array(
              "tvDefault.name" => $sortbyTV
          ));
          $criteria->leftJoin('modTemplateVarResource', 'tvSort', array(
              "`tvSort`.`contentid` = `modResource`.`id`",
              "`tvSort`.`tmplvarid` = `tvDefault`.`id`"
          ));
          $criteria->select("IFNULL(`tvSort`.`value`, `tvDefault`.`default_text`) AS `sortTV`");
          $criteria->sortby("`sortTV`", $sortdirTV);
      }
      // only sort if necessary
      if ($sortby != 'resources') $criteria->sortby($sortby, $sortdir);
      if (!empty($limit)) $criteria->limit($limit, $offset);
      
      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);
      // new resources sort is called here. Couldn`t find a proper way to pass the resources in the function, so used $GLOBALS
      if(is_array($globalresources) && $sortby=='resources') 
      {
      	$GLOBALS['resources'] = $globalresources;
      	usort($collection,'cmp');
      }
      /* include parseTpl */
      include_once $modx->getOption('getresources.core_path',null,$modx->getOption('core_path').'components/getresources/').'include.parsetpl.php';
      
      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++;
      }
      
      /* output */
      $output = implode($outputSeparator, $output);
      $toPlaceholder = $modx->getOption('toPlaceholder',$scriptProperties,false);
      if (!empty($toPlaceholder)) {
          $modx->setPlaceholder($toPlaceholder,$output);
          return '';
      }
      return $output;




        • 22303 MODX Staff
        • 10,725 Posts
        Quote from: gadamiak at Nov 09, 2010, 02:55 PM

        I need advice on how to use getResources to push featured resources to top of the list. Resources can be marked as "featured" with a TV, say [tt]res.featured[/tt] with value of 0 or 1.

        I need those "featured" to show on top of the list regardless sorting criteria. The number of featured items can vary from 0 to few.

        What should be the snippet call to achieve this? Is that feasible?
        Why not just call it once for the featured, and once to get the remaining unfeatured by the same criteria?
          • 36541
          • 222 Posts
          Quote from: OpenGeek at Nov 10, 2010, 12:25 AM

          Why not just call it once for the featured, and once to get the remaining unfeatured by the same criteria?

          The tricky part is the length of the list, which must be constant, ie. set with `&limit`. Because the number of featured items can vary I should set the length of the unfeatured list dynamically to use this approach.

          Maybe a multi-sort solution would do best?
            This is the web: the only thing you know about who will come is that you don't know who will come.
            • 36541
            • 222 Posts
            Quote from: MadeMyDay at Nov 09, 2010, 03:20 PM

            I just finished a version where you can sortby=`resources` and the order of resources passed via &resources is used for the sort order of returned results.

            Great idea! I opened an issue on "natural" sorting, but your solution goes a step further. It’s really nice and intuitive to use the `resources` parameter as a source for sorting! I’ll definitely check it very soon.

            After skimming the code it seems you got rid of obligatory `parents` parameter when used with `resources`? That would deserve a pull request on its own. smiley


            I needed it for a custom sorting output filter with which you can sort any list of values via drag&drop.

            I watched the screencast, and it looks very promising. It it possible to use it with any input list or only with checkbox?
              This is the web: the only thing you know about who will come is that you don't know who will come.
              • 22303 MODX Staff
              • 10,725 Posts
              Quote from: gadamiak at Nov 10, 2010, 05:49 AM

              Quote from: OpenGeek at Nov 10, 2010, 12:25 AM

              Why not just call it once for the featured, and once to get the remaining unfeatured by the same criteria?

              The tricky part is the length of the list, which must be constant, ie. set with `&limit`. Because the number of featured items can vary I should set the length of the unfeatured list dynamically to use this approach.

              Maybe a multi-sort solution would do best?
              Gotcha; yeah, that does indeed need multi-sort. You can hackishly use multisort with any of the resource fields by just doing
              &sortby=`isfolder DESC, menuindex` &sortdir=`ASC`

              Currently, If you use sortby and sortbyTV at the same time, the sorting on the TV will take precedence over the sortby parameter, so you can already accomplish this if you are sorting by a TV and then by a resource field (or a few).
                • 36541
                • 222 Posts
                Quote from: OpenGeek at Nov 10, 2010, 08:50 AM

                Gotcha; yeah, that does indeed need multi-sort. You can hackishly use multisort with any of the resource fields by just doing
                &sortby=`isfolder DESC, menuindex` &sortdir=`ASC`

                Currently, If you use sortby and sortbyTV at the same time, the sorting on the TV will take precedence over the sortby parameter, so you can already accomplish this if you are sorting by a TV and then by a resource field (or a few).

                Thanks, that did the trick!

                Both multi-sort and `sortbyTV` are missing in documentation. It needs update, I guess smiley
                  This is the web: the only thing you know about who will come is that you don't know who will come.
                  • 9102 ☆ A M B ☆
                  • 318 Posts
                  I just finished a version where you can sortby=`resources` and the order of resources passed via &resources is used for the sort order of returned results.

                  Thanks, MadeMyDay, that’s awesome, you... well, made my day. I needed this and was considering whether to try writing it myself or wait for this feature to be included in the main version.

                  OpenGeek, I for one would strongly support rolling this into the main version so we don’t have to maintain two versions. This feature is quite useful in a variety of contexts.
                    • 36624
                    • 535 Posts
                    hello, is there a way to random the output order with getResources (like ditto randomize=`1`) ?
                      CTRL+SHIFT+U - Clear Cache
                      CTRL+SHIFT+H - Hiding Heft Panel
                      CTRL+SHIFT+N - Fast Create Resource
                      CTRL+ALT+P - Preview Recource (in edit resorce window)
                      CTRL+ALT+S - Save
                      • 36624
                      • 535 Posts
                      ok, i found the solution here: http://modxcms.com/forums/index.php?topic=55071.0

                      &sortby=`RAND()`
                        CTRL+SHIFT+U - Clear Cache
                        CTRL+SHIFT+H - Hiding Heft Panel
                        CTRL+SHIFT+N - Fast Create Resource
                        CTRL+ALT+P - Preview Recource (in edit resorce window)
                        CTRL+ALT+S - Save