Issue with PHP-Based Blueprint and Filtered Page Sections in Kirby

Hello everyone,

I’m currently working on a plugin and want to create a PHP-based blueprint while using filtered page sections, as described in the documentation:
PHP-based blueprints | Kirby CMS.

However, I’m facing an issue when multiple pages use the same template (e.g., “notes”). The page() function should return the currently active page, while page('notes') retrieves the page with the given slug. In my tests, this always returns the same page instead of the one currently open in the panel.

To work around this, I tried determining the current page’s slug using kirby()->request()->path() and extracting it with a custom function. However, this approach causes errors in the site blueprint, even though I only use the function in a different page blueprint.

Here is my function:

function getPageFromPath(int $index = 2): Page|null
{
    $path = kirby()->request()->path();
    $segments = explode('/', $path);
    $slug = $segments[$index] ?? null;

    return $slug ? page($slug) : null;
}

// Example usage
$page = getPageFromPath();

Error Message:

When using this function in a page blueprint, the following error appears in the site blueprint:

The section "published" could not be loaded: Call to a member function title() on null

This suggests that the function may not always return a valid page object, leading to issues when trying to access properties like title().

Has anyone encountered a similar issue? How can I reliably create a PHP-based blueprint that always retrieves the currently active page in the panel without causing errors in the site blueprint?

Thanks in advance!

from @tobimori

/**
	 * Helper method the get the current page from the URL path,
	 * for use in programmatic blueprints
	 */
	public static function currentPage(): Page|null
	{
		$path = App::instance()->request()->url()->toString();
		$matches = Str::match($path, "/pages\/([a-zA-Z0-9-_+]+)\/?/m");
		$segments = Str::split($matches[1], '+');

		$page = App::instance()->site();
		foreach ($segments as $segment) {
			if ($page = $page->findPageOrDraft($segment)) {
				continue;
			}

			return null;
		}

		return $page;
	}

Thank you for your quick response and help! I’ve adopted your solution.

Is it possible to create PHP classes within a plugin and use them in PHP-based blueprints? I was thinking of a utility class containing some helper functions that might also be needed in other PHP blueprints.

So far, I’ve struggled with using my PHP classes within my plugin unless they were PageModels. Do you have any suggestions on how to achieve this?

i would put the classes in a seperate directory and “autoload” that with the composer psr4 autoloding.

alternativly the Kirby core also has a load()-helper that you could use in a dedicated plugin (name it 1_classes or similar to make it load before the others).

Yes, the alternative method is quite similar to the one in this guide: Page models | Kirby CMS, isn’t it? The idea of using the load() helper in a dedicated plugin to autoload classes before the others seems very close to what’s explained there.

I would now create a src folder and add a PHP class with my helper functions. Then, I would load it once in the plugin’s index.php file using the load() method so that the helper functions can be used throughout the plugin. Is that correct?

src/Auxiliary.php

<?php

namespace KirbyPluginName;

use Kirby\Cms\App;
use Kirby\Cms\Page;
use Kirby\Toolkit\Str;

class Auxiliary
{
    /**
     * Helper method the get the current page from the URL path,
     * for use in programmatic blueprints
     */
    public static function currentPage(): Page|null
    {
        $path = App::instance()->request()->url()->toString();
        $matches = Str::match($path, "/pages\/([a-zA-Z0-9-_+]+)\/?/m");
        $segments = Str::split($matches[1], '+');

        $page = App::instance()->site();
        foreach ($segments as $segment) {
            if ($page = $page->findPageOrDraft($segment)) {
                continue;
            }

            return null;
        }

        return $page;
    }
}

index.php:

load([
    'Auxiliary' => 'src/Auxiliary.php',
], __DIR__);

blueprints/pages/nodes.php:

<?php

use KirbyPluginName\Auxiliary;

$sections = [];
if ($page = Auxiliary::currentPage()) {
    foreach ($page->employees()->toStructure() as $employee) {
        $sections['section_' . $employee->uuid()] = [
            'type'  => 'pages',
            'label' => 'Pages with tag ' . $employee->lastname(),
            // note the required quotes around the `$tag` variable
            'query' => "page.children.filterBy('employee', '". $employee->uuid() . "')",
        ];
    }
}

return [
    'title' => 'Nodes Manager',
    'description' => '.....',
    'tabs' => [
        'main' => [
            'label' => 'Main',
            'columns' => [
                'main' => [
                    'width' => '2/3',
                    'sections' => $sections
                ],
                'sidebar' => [
                    'width' => '1/3',
                    'sections' => []
                ]
            ]
        ],
        'settings' => []
    ]
];

result:

Class "KirbyPluginName\Auxiliary" not found

I removed the namespace from the PHP class and also removed the import in nodes.php, and it’s working again now.

I think that load() would have needed the namespace within the key to make the namespace work within the PHP blueprint