We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 34120
    • 236 Posts
    Hi,
    I have a page with a simple getResources call to display content but on the same page I also need to populate an option list using a TV from the resources.
    If it was a straightforward getResources call I'd do something like this in the a tpl:
    <option value="[[+country]]">[[+country]]</option>

    The problem is I need to filter out any duplicates first. I was thinking of calling getResources from a snippet using runSnippet and then filtering the results. I've also looked at skipping getResources and doing something like this and then getting the TVs:
     $children = $modx->resource->getMany('Children');

    Any advice much appreciated.

    Finally, does modxcloud handle errors differently? I don't seem to be getting any errors, just a blank screen when something isn't right

    Thanks
      • 34120
      • 236 Posts
      Here's what I've got, this returns the TVs that I need:
      <?php 
      $children = $modx->resource->getMany('Children');
      
      if (!$children) { return false; }
      
      // Check if there is a tpl set, if not return an error
      $tpl = $modx->getOption('tpl', $scriptProperties, '');
      
      if (!$tpl) { return 'No template given.'; }
      
      foreach($children as $child) {
         	$out = array(
            'country' => $child->getTVValue('country')
            );
          // ... and adding it to the output as placeholders in the chunk
          $o .= $modx->getChunk($tpl, $out);
      }
      return $o;
      


      Now I need to combine it with something like this to filter the duplicates:
      $property_types = array();
      foreach($search_results_unique as $filter_result){
          if ( in_array($filter_result['property_type'], $property_types) ) {
              continue;
          }
          $property_types[] = $filter_result['property_type'];
          echo $filter_result['property_type'];
      }
        • 3749
        • 24,544 Posts
        When you call getResources with runSnippet(), you get back one giant string containing the full output. Applying your filter to that would be extremely difficult.

        Using getMany('Children'), should work, though it may not be very fast.

        Can you give a detailed example of the what's in the TVs and what a duplicate means? I don't know if you mean a duplicate value across multiple resources, multiple TVs, or within a TV.
          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
          • 34120
          • 236 Posts
          Thanks Bob,
          Each resource has a country TV assigned to it, there could be 3 resources each with 'France' assigned but I only want France to appear once in the option list. Selecting France in the option list would then return the 3 resources, hope that makes sense.

          Anyway after a bit of a battle I've got something which seems to be working:
          <?php 
          
          $children = $modx->resource->getMany('Children');
          
          if (!$children) { return false; }
          // Check if there is a tpl set, if not return an error
          $tpl = $modx->getOption('tpl', $scriptProperties, '');
          if (!$tpl) { return 'No template given.'; }
          
          $filter_countries = array();
          foreach($children as $child) {
              $country = $child->getTVValue('country');
              if ( in_array($country, $filter_countries) ) {
                  continue;
              }
              $filter_countries[] = $country;
              $out = array(
                  'country' => $country
              );
              $o .= $modx->getChunk($tpl, $out);
          }
          return $o;


          I'd welcome your thoughts on this, what do you think?
            • 3749
            • 24,544 Posts
            If the speed of the search is acceptable, you may want to leave it, but you're making a separate query to the DB for each resource (sometimes two), so it would be a lot faster if you could avoid that.

            If I'm understanding what you're doing, something like this (semi-tested) would get them all in a single query, making both your users and your host a lot happier:


            $o = '';
            $tvId = 23; /* ID of the country TV */
            $countries = array();
            $result = array();
            
            $docIds = $modx->getChildIds(38);
            if (empty($docIds) { 
                return false; 
            }
            // Check if there is a tpl set, if not return an error
            $tpl = $modx->getOption('tpl', $scriptProperties, '');
            if (!$tpl) { return 'No template given.'; }
            
            
            $c = $modx->newQuery('modTemplateVarResource');
            $c->select(array('value'));
            $c->distinct(true);
            $c->sortby('value');  /* (optional) */
            $c->where(
                array(
                    'tmplvarid' => $tvId,
                    'contentid:IN' => $docIds,
                    'value:!=' => '',
                )
            );
            
            if ($c->prepare() && $c->stmt->execute()) {
                $result = $c->stmt->fetchAll(PDO::FETCH_ASSOC);
            }
            $countries = array_map('current', $result);
            
            foreach($countries as $country) {
                $o .= $modx->getChunk($tpl, array('country' => $country));
            }
            


            This will be very fast compared to your method, but with one catch -- the TV cannot have a default value. All the TV values must be entered manually.


              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
              • 34120
              • 236 Posts
              Nice! Thanks Bob, this looks great. I'll give it a go later on.
                • 1887
                • 50 Posts
                Quote from: BobRay at Jan 29, 2015, 03:32 AM

                $o = '';
                $tvId = 23; /* ID of the country TV */
                $countries = array();
                $result = array();
                
                $docIds = $modx->getChildIds(38);
                if (empty($docIds) { 
                    return false; 
                }
                // Check if there is a tpl set, if not return an error
                $tpl = $modx->getOption('tpl', $scriptProperties, '');
                if (!$tpl) { return 'No template given.'; }
                
                
                $c = $modx->newQuery('modTemplateVarResource');
                $c->select(array('value'));
                $c->distinct(true);
                $c->sortby('value');  /* (optional) */
                $c->where(
                    array(
                        'tmplvarid' => $tvId,
                        'contentid:IN' => $docIds,
                        'value:!=' => '',
                    )
                );
                
                if ($c->prepare() && $c->stmt->execute()) {
                    $result = $c->stmt->fetchAll(PDO::FETCH_ASSOC);
                }
                $countries = array_map('current', $result);
                
                foreach($countries as $country) {
                    $o .= $modx->getChunk($tpl, array('country' => $country));
                }
                



                This code is close to what I need, except instead of returning the countries, I want to return the ids for those children after they have been deduplicated.

                I have a number of bookable events, most of which have a morning and afternoon session. I have created a TV called training_group which holds the date in YYYYMMDD format so that I can group multiple sessions together. I would like the user to select a location / date sequence before offering them a morning or afternoon session which is why I need to remove duplicates.

                The next step is to come up with a list of ids a deduplicated list of training_group values. Tweaking the code above gives me a list of deduped training_groups but how do I get the resource Ids instead of the values?

                Any points gratefully received.

                James

                  • 3749
                  • 24,544 Posts
                  Try changing this:

                  $c->select(array('value'));


                  To this:

                  $c->select(array('contentid'));


                  that holds the resource ID.

                    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
                    • 1887
                    • 50 Posts
                    Quote from: BobRay at Mar 23, 2015, 01:18 AM
                    Try changing this:

                    $c->select(array('value'));


                    To this:

                    $c->select(array('contentid'));


                    that holds the resource ID.


                    Aah many thanks for this answer and the code above.

                    I have to say I love MODX. I regularly take on challenges which are outside of my comfort zone and each time I find a good solution to solve them. There are not many platforms that enable one in that way.

                    James
                      • 1887
                      • 50 Posts
                      Hmm spoke to fast. I want to dedup by value but return the id(s) of the results.

                      IDs are of course unique so the duplicates are not returned.

                      So if I have a list of deduplicated values, how do I find their IDs?