Meet isfolder and hasChildren

Pages in MODX can contain other pages, be flagged as a folder, or both. Learn how to use these two concepts to change how MODX behaves.

By Bob Ray  |  November 9, 2021  |  7 min read
Meet isfolder and hasChildren

People are often confused by the difference between isfolder and hasChildren() in MODX Revolution. Understanding which one is which is important if you want to use them in your web pages.

isfolder is a resource field, like pagetitle, alias or published. Its value is always 1 or 0.

The isfolder field matches the "Container" checkbox on the Create/Edit Resource panel.

In other words, when you check the "Container" checkbox, MODX sets the value of the resource's isfolder field to 1. Otherwise MODX sets the field value to 0.

In MODX Revolution, as installed, the isfolder resource field is set automatically based on whether the resource has children or not. When the first child is added below a resource in the tree, isfolder is set to 1. When the last child is removed and the resource has no children, isfolder is set back to 0. In other words, it is always 1 if the resource has children and always 0 if not.

This behavior depends on the auto_isfolder System Setting, which is set to "Yes" by default. If you turn it off, MODX will not alter the isfolder field on its own. So if you uncheck the Container checkbox of a resource and save it, the resource will never become a container, regardless of any children, unless you check the Container checkbox and save it again.

I've never found a use for this feature, but I can imagine one. Suppose you want to create container resources that have a certain look, even when they don't yet have children. A plugin or a snippet in the template could perform optional actions on it based on the value of the resource's isfolder field. For example, you could add a chunk with a "Coming Soon ... " message if the resource was a container and had no children and do something else (e.g., insert a different chunk) if the container resource had children. This is where hasChildren() comes in.

hasChildren()

The hasChildren() method belongs to the modResource object. It ignores the isfolder field and looks for actual children. If it finds any, it returns true. If there are no children, it returns false.

To use hasChildren(), you need a reference to the resource. In a plugin attached to the OnWebPagePrerender event, or in a snippet that runs on the current page, you'd use $modx->resource, which is always set by MODX as a reference to the page being requested. The code would look like this:

resource->hasChildren()) {

    /* Do something */

} else {

    /* Do something else */

}

Having isfolder be independent of whether the resource has children or not does allow you more flexibility in how container documents are displayed. Another thing you might want to do is to insert a Chunk or a Wayfinder menu only in resources that are containers, whether or not they have any children yet.

The way to do this is set the auto_isfolder System Setting to "No", then manually set the "Container" checkbox for any resource you would like to have treated as a container. You can control what happens in container and non-container resources a plugin or snippet, but you can also do it with output modifiers in a tag like this:

[[*isfolder:is=`1`:then=`[[$ContainerChunk]]`:else=``]]

Whatever goes inside the backticks after :then= will be shown if the resource's "Container" checkbox is checked. Whatever is between the backticks after :else= will be shown if it isn't checked (this could be another chunk). The tag above will be replaced by the "ContainerChunk" Chunk if the "Container" checkbox is checked and by nothing at all if it isn't.

But, suppose you turn off the auto_isfolder System Setting because you want to set the Container checkbox manually and have it stay that way regardless of whether a resource has children. No suppose that in addition to that, you also want to perform certain other actions only on resources that actually have children.

hasChildren() to the Rescue

The hasChildren() method of a resource returns the number of children the resource has. If it's 0, the resource is childless. It has nothing to do with the isfolder field or the "Container" checkbox. It simply reports the number of children a resource has.

This one-line snippet will return that value for you to use with Output Modifiers in your HTML code:

resource->hasChildren() != 0;

The snippet will return a 1 (true) if the resource has children and a 0 (false) if not.

Suppose you want to show a particular chunk only if a resource has children and nothing if it doesn't. This code will do it (assuming that you've created the snippet shown just above):

[[!HasChildren:is=`1`:then=`[[$ContainerChunk]]`:else=``]]

Whatever goes inside the backticks after :then= will be shown if the resource has children. Whatever is between the backticks after :else= will be shown if it doesn't have any children.

You might think that HasChildren is a Custom Output Modifier, but it's not. It's a regular MODX snippet, but we're passing its output to the is built-in Output Modifier.

Why the Exclamation Point?

The exclamation point at the beginning of the snippet tag tells MODX to run the snippet and get a fresh version of the results each time the tag is parsed rather than getting the results from the cache. This is necessary because the status of the resource might have changes since the last time the snippet was run. The cached version would contain that result, which might not match the resource's current status.

Things to Remember

Because isfolder is a resource field like pagetitle or content, you can show it (or use it in an output modifier) with this tag in your HTML: [[*isfolder]]

hasChildren(), on the other hand, is a method (function) of the resource object. It can only be used in PHP code, like this:

$numberOfChildren = $modx->resource->hasChildren();

A common mistake

The following code will never work, because hasChildren() is not a method of the MODX object:

$numberOfChildren = $modx->hasChildren();

Actions Based on the Number of Children

Because hasChildren() returns the number of actual children, you can do slightly more sophisticated things with it. You can change the HasChildren snippet above to this:

resource->hasChildren();

Now our snippet returns that actual number of children instead of just a 0 or a 1. With this version, you could show a chunk depending on the number of children. For example, this tag will show the chunk only if the current resources has 3 or more children:

[[!HasChildren:greaterthanorequalto=`3`:then:`[[$SomeChunk]]`:else=``]]

This shorter version does the same thing:

[[!HasChildren:gte=`3`:then:[[$SomeChunk]]:else=``]]

Speeding Things Up

The Output Modifier tag we used above is actually not very efficient because any chunks specified in the tag will be retrieved (and parsed) whether they're going to be used or not.

There are a couple of ways to avoid this. One is to rewrite the tag so that it only gets chunks that are actually used:

[[[[!HasChildren:is=`1`:then=`$someChunk`:else=``]]]]

This is significantly faster, but it's harder to understand and difficult for some people to write. I prefer to put the logic for getting the chunk in the snippet itself, which is usually faster yet and less prone to error. Let's rewrite our tag and make HasChildren work without any Output Modifiers:

[[!hasChildren? &yes=`SomeChunk` &no=`SomeOtherChunk`]]

Notice that there are no longer any ":" tokens in the tag. No Output Modifiers are being called. This saves time because MODX no longer has to parse the tag for Output Modifiers and locate the correct ones before running them. Now, our snippet would look like this:

resource->hasChildren() != 0) {

    return $modx->getChunk($yesChunk);

} else {

    return $modx->getChunk($noChunk);

}

Our snippet is now getting the names of the yes Chunk and the no Chunk from the properties sent in the snippet tag (which are always in the snippet's $scriptProperties array. After checking to see whether the current resource has children, it gets and returns only the chunk that's going to be shown.

If you are really concerned about speed, the snippet can be made a few milliseconds faster by compressing it to this one-line form (shown on three lines to make it easier to follow):

getChunk($modx->resource->hasChildren()
    ? $scriptProperties['yes']
    : $scriptProperties['no']);

In this version, the name of the chunk to get is determined inside the parentheses of the getChunk() call. PHP no longer has to take time to set, and later evaluate, the two variables (which no longer exist). This saves both time and memory, though the difference would probably be trivial.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.