pdoMenu hide collection resources without children

I want to hide all collection resources that have no child-resources in my pdoMenu call. As those are collection resources, their isfolder checkbox is on by default so I can’t easily use something like:

&where=`{"isfolder":"1"}`

Additionally, I need to include all weblink resources, which don’t have children, but should still be shown.

Is there a way to achieve this or is some form of custom snippet necessary?

The resources have a field class_key to store the resource type.
Maybe you can filter by that.

Thank you! So I think now, I’m running into syntax problems. After a bit more research and a look at the xPDOQuery.where doc, I ended up with this:

&where=`{"isfolder":"1","OR:class_key:=":"modWebLink"}`

but it doesn’t work, so probably I got it wrong?

Maybe it’s easier to use a different approach.
Like deciding inside the template if anything should get output at all, based on the placeholders [[+children]] and [[+class_key]].

Also, you may have to set the property &countChildren to 1 to make it work.

So just adding the &countChildren property completely kills my entire navigation. I’m not sure why this would happen, but it seems this approach is probably not the easiest way.

Does the &where approach not work? I think I would prefer having the unwanted resources filtered before displaying them, so I don’t have to mess with the navigation.

What versions of MODX, pdoTools and PHP are you using.


Maybe you can make it work. But sometimes for complicated filtering(AND and OR conditions mixed) it’s difficult to get the structure right.

But maybe I’m just misunderstanding the question:
So you want to exclude the resources with the “class_key” = CollectionContainer that don’t have children, but you can’t use “isfolder” (because it’s always set to true).

In my opinion it’s easier to query all resources and then filter out the collection containers with something like this in the template: (Fenom syntax)

{if !($class_key == 'CollectionContainer' && $children == 0)}
    <li[[+classes]]><a href="[[+link]]" [[+attributes]]>[[+menutitle]]</a>[[+wrapper]]</li>
{/if}

Another possible solution is to use the &resources property.
Run a snippet that returns the IDs of all the collection containers that have no children. Put a dash before each ID, so they get excluded from the result.

[[pdoMenu?
    ...
    &resources=`[[queryContainersWithoutChildren]]`
]]

MODX 2.8.5.
pdoTools 2.13.3
PHP 8.2

Also here is the full call:

[[pdoMenu?
  &parents=`0`
  &templates=`15,23`
  &level=`3`
  &tpl=`nav.category.inner.row`
  &tplInner=`nav.category.inner`
  &tplParentRow=`nav.category.ParentRow`
  &tplInnerRow=`nav.category.tpl`
  &outerClass=`navbar-nav row w-100 justify-content-evenly`
  &countChildren=`1`
]]

Yes. I have Collection resources that don’t have children (yet). And until they get some I don’t want them to be displayed. There are quite a few, so I don’t want to manually hide them.

You are probably right, but I guess I have to fix the call first, before i can try that.

Also thank you for the snippet suggestion, that might be a good solution, if the others fail.

I can’t reproduce any problems with &countChildren=`1` when I test it.

Anyway, in this case it’s possibly even not necessary to use &countChildren=`1` at all. Try testing it without setting &countChildren=`1`.

Just thinking out loud here. For the resources with no children, there will be no resources that have that resource as a parent. I can’t work it out, but I’m pretty sure there’s a single query that will get resources where the class key is something AND the count of resources that have the resource as a parent is >= 1.

I tried adding the mentioned syntax to the parentRow tpl (nav.category.ParentRow), but it removes everything except the very first weblink resource. I feel like something is not working as expected here.


To clarify: when I enable &countChildren it places a big part of my navigation outside of the .container that pdoMenu is called from. So probably something else is interfering?

Here are my templates, in case someone can spot a mistake there:

Templates
// &tpl=`nav.category.inner.row`

<li class="nav-item position-static col-auto">
  <a href="[[+link]]" class="nav-link text-nowrap" role="button">[[+menutitle]]</a>
</li>


// &tplInner=`nav.category.inner`

[[+wrapper]]


// &tplParentRow=`nav.category.ParentRow`

<li class="nav-item dropdown position-static col-auto">
  <a class="nav-link dropdown-toggle" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
    [[+menutitle]]
  </a>
  
  <div class="container start-50 translate-middle-x dropdown-menu w-100 mt-0 p-0 border shadow" aria-labelledby="navbarDropdown" style="border-top-left-radius:0; border-top-right-radius:0;">
    <div class="container">
      <div class="row my-3">
        [[+wrapper]]
      </div>
    </div>
  </div>
</li>


// &tplInnerRow=`nav.category.tpl`

<div class="col-3 mb-3 mb-lg-0">
	<div class="list-group list-group-flush">
	  <a href="[[+link]]" class="list-group-item list-group-item-action">[[+menutitle]]</a>
	</div>
</div>


This sounds like it’s worth a try. Not sure if my knowledge will be sufficient enough for this, though. Will look into it, any more ideas here are definitely appreciated.

No idea what’s going on here.


Anyway: Here is some snippet code to query the collection containers with no children.

<?php
$sql = <<<SQL
SELECT id FROM modx_site_content SC
WHERE class_key IN ('CollectionContainer','Collections\\\\Model\\\\CollectionContainer')
AND published = 1
AND deleted = 0
AND NOT EXISTS (
	SELECT id
	FROM modx_site_content
	WHERE parent = SC.id
	AND published = 1
	AND deleted = 0
);
SQL;

$stmt = $modx->prepare($sql);
$stmt->execute();
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
if (count($ids) > 0) {
    return "-" . implode(",-", $ids);
} else {
    return "";
}
1 Like

Thank you for your efforts! I had to adjust the table name according to my custom prefix which left me a bit head scratching at first, but it works great! Thank you again!

1 Like

I was just going to suggest this simplified version, without the class_key criterion, which looks pretty much like what you have:

$sql = "SELECT id,pagetitle,parent 
   FROM modx_site_content 
   WHERE modx_site_content.id NOT IN (select parent from modx_site_content)";

It should be relatively easy to do this in xPDO, but the MySQL version will be much faster.

1 Like

This topic was automatically closed 2 days after discussion ended and a solution was marked. New replies are no longer allowed. You can open a new topic by clicking the link icon below the original post or solution and selecting “+ New Topic”.