Related pages snippet to show all resources that use a particular template

Hi there and Happy Easter all!

I want to setup a relatedPages TV which lists all my products. I’ve found a snippet which I can list all resources form a particular parent, but my products are spread around the site, so this doesn’t do what I need.

I have two main folders of products:
Women (4)
Men (5)

But each folder then contains another folder for my product categories (shirts, jackets, shoes etc).

So the TV I found has this in the input options:

@EVAL return $modx->runSnippet('listMyResources',array('parent' => 4));

Then the snippet I found is this:

<?php
$parent = $modx->getOption('parent',$scriptProperties,4);
$parentObj = $modx->getObject('modResource',$parent);
if (!($parentObj instanceof modResource)) { return ''; }
$resArray = $parentObj->getMany('Children');
$resources = array();
foreach($resArray as $res) {
  if ($res instanceof modResource) {
    $resources[] = $res->get('pagetitle') . '==' . $res->get('id');
  }
}
$out = implode("||",$resources);
return $out;

So this will find all the children from the parent 4, but all this does is display the categories within women (4) and no actual products because it only finds the direct children.

So, ideally I would pull in all children and grandchildren, bypassing containers.

Also, I need to set the ultimate parent as either Men or Women, so that I don’t pull in a list of men’s shoes as a related product option of a women’s dress.

Can anyone push me in the right direction?

Thanks!
Andy

Not 100% sure if I understand what you need, but it sounds like the pdoTools Extra, with pdoResources should be able to do what you want. You can specifiy the &where property for example to only show specific templates:

&where=`{ "template:IN" : [ 1,2,3 ] }`

Thanks. But the Mansger needs to be able to select which products are related products from a listbox ideally. With pdoresources they can’t control which products are related can they?

Oh, so you want this selection to be happening in the manager then? Depending on the size of your shop, I’m not sure how useful it would be to select them manually, but yeah, pdoResources si probably the wrong tool then. You could still us it to fill like your listbox, though.

Use $modx->getChildIDs() to fetch the resource IDs; that comes from cache and is quite speedy. Then feed that into a query to apply your other filters.

E.g.

$parent = $modx->getOption('parent', $scriptProperties, 4);
$ids = $modx->getChildIDs($parent, 10, ['context' => 'web']);

$c = $modx->newQuery('modResource');
$c->where([
  'id:IN' => $ids,
  'AND:deleted:=' => false,
  'AND:template:=' => 9, // replace with id of template
]);

$results = [];
foreach ($modx->getIterator('modResource', $c) as $resource) {
    $results[] = $resource->get('pagetitle') . '==' . $resource->get('id');
}
return implode('||', $results);
1 Like

This is brilliant - thank you Mark.
The only problem I have now is that this brings in all the Women’s products - (because 4 is the women’s section parent). Which is great for all the women’s product resources, but for the men’s products, it will also only show the women’s products.

I guess i could duplicate the template, and have a men-only template, and duplicate the snippet, but I’m always weary of duping templates/snippets in case changes are required later on.

I can’t think of another way of identifying the men’s section apart from the parent ID though?

Could call getChildIDs twice, one for each parent, and merge those arrays into $ids?

Thanks. I’m not really sure how to do this.
I would only want the men’s resources to show on men’s pages, and women’s products to show on women’s pages - but I don’t really have a differentiator (apart from the ultimate parent) to know which resources to pull in each time?

Then determine that ultimate parent ID in your snippet. You can probably use $modx->getParentIds() and apply a little logic (and error handling!) to retrieve the parentId at the appropriate level in the heirarchy.

Hi thanks. Yep that’s exactly what I need to do. I just don’t know how to do it!

Ok - try something like this. Expanding Mark’s example a little:

//  call this with optional params:
//  [[snippet? &template=`9` &parents=`4,5`]]

// ID of the current resource
$currentId = $modx->resource->id;

// get the parents of the current page. includes '0' as the site root. 
// you may want the mgr context, whatever works for you.
$parents = $modx->getParentIds($currentId, 10, ['context' => 'web']);

// the templateId of a product
$productTemplateId = $modx->getOption('template', $scriptProperties, 9);

// the IDs of the men/women folders (resources)
$validParentsOpts = $modx->getOption('parents', $scriptProperties, "4,5");

// make an array of the validParentsOpts
$validParents = explode(",", $validParentsOpts); 

// make sure there's some kind of parent. some better error handling would 
// not go amiss here!
if ( count($validParents) < 1 || count($parents) < 1 ) {
    // bork
    return false;
} else {
    $categoryParent = false; // initialise
    // we don't care if it's men or women at this point, 
    // just that one of validParents exists in the heirarchy:
    foreach ($parents as $parent) {
        if ( in_array($parent, $validParents) ) {
            // we've found the right parent resource ($parent)
            $categoryParent = $parent;
        }

        // break out of the foreach to save unnecessary processing
        if ($categoryParent !== false) {
            break;
        }
    }

    // if one of the valid parents has been found, get the childIds.
    if ( $categoryParent !== false ) {

        $ids = $modx->getChildIDs($categoryParent, 10, ['context' => 'web']);

        $c = $modx->newQuery('modResource');
        $c->where([
          'id:IN' => $ids,
          'AND:deleted:=' => false,
          'AND:template:=' => $productTemplateId, // remove this for any template
        ]);
        
        $results = [];
        foreach ($modx->getIterator('modResource', $c) as $resource) {
            $results[] = $resource->get('pagetitle') . '==' . $resource->get('id');
        }
        return implode('||', $results);


    } else {
        return false;
    }
}

I’ve used your IDs as defaults, but you can override these in the snippet call for a bit of flexibility. This is obviously untested other than verifying that it’s synactically correct, so test thoroughly!!

Andy

Thanks so much for this.
I’ve set up a new snippet called ‘listMyProductsNew’ with the code you kindly provided, and have added a Listbox TV - with the following input options:
@EVAL return $modx->runSnippet('listMyProductsNew');

But the listbox in the resource is empty.
I think the above input option is wrong maybe?

Thanks again so much for your help
Andy

Right - I got it working by changing the template value in this line:
$productTemplateId = $modx->getOption('template', $scriptProperties, 9);

to

$productTemplateId = $modx->getOption('template', $scriptProperties, 3);

3 being the product template.

I will test further.

Thanks again

It’s configurable; just give the “template” parameter in the snippet a value of 3.

@EVAL return $modx->runSnippet('listMyProductsNew', array('template' => '3'));

Ah gotcha. Thank you!

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.