Faster alternative to `index()`

$page->index() is known to be a slow function under certain conditions. Specifically, if you have many subpages under a single folder.

I’m trying to improve the performance of Shopkit, and I noticed that some of my clients really love putting 100+ products under a single category :grimacing:

So I made an alternative to index() for this situation that is about 5x faster (of course, the speed depends on how many pages you have, how they are organized, and how you plan to filter them).

I replaced this…

$products = page('shop')->index()->visible()->filterBy('template','product');

… with this:

function findNestedPages($parent, $template){
    $collection = new Pages();
    if ($parent->hasVisibleChildren()) {
        $children = $parent->children()->visible();
        foreach ($children as $child) {
            // Run this function recursively on each child page
            // The return value feeds back into the first $collection
            $collection->add(findNestedPages($child, $template));
    return $collection;
$products = findNestedPages(page('shop'), 'product');

findNestedPages() is a recursive function that works much like Bastian’s treemenu snippet.

I hope this is helpful for some of you to shave a few milliseconds off the load time!


Why is it faster? Is it because it first grabs the visible pages and then filter by template? Two steps instead of one.

So how is it solved by the native function? Does it run filterBy first?

The native index() function has a few main differences from findNestedPages(), from what I can see:

  • It deals with Children and Page objects (Mine sets a general Collection object)
  • It saves values into the cache as it iterates through all the pages, and fetches the final result from cache as well. (Mine saves the collection in a variable inside the function.)
  • It doesn’t try to be too smart, so it doesn’t to any pre-filtering. (Mine has a couple assumptions built-in, so it can exit early for invisible pages and pages with non-matching templates never get added to the return value)
  • It checks for a null input variable to prevent PHP errors

I’m not sure which of these makes the most impact in terms of load time. Even though the native function uses a cache, I didn’t find it to be faster after a page refresh in my tests.

Note: I find that it’s faster in general to put visible() first when you’re chaining multiple filtering and sorting methods. It’s a very efficient function, and seems to make it easier for the following methods to run.

1 Like

this would also be great for autoid plugin by at @helllicht – which is missing something like a getPageByAutoID(). they could change filtering by template to check autoid.