The setup
I got a custom model (Parent) to be the panel-front for a database backed set of pages. This model is purly for the panel side, and won’t be accesible on the front end, so I can mess with it purly for the panel’s purpose.
For this reason I have a draft page-file representing this panel-front. In the file there is nothing stored other than the page-template (and linked to that its model):
Template: ParentTemplate
I got a blueprint set for that model (Parent) with only a Pages section, which should contain all the database pages.
#partial file for brevety
sections:
pages:
headline: Pages
type: pages
create:
- ChildTemplate
templates:
- ChildTemplate
empty: No pages yet
image: false
text: "{{page.title}}"
info: "{{page.date}}"
layout: list
parent: page
sortBy: page.date desc page.title asc
The problem
The database determines the url/slug for each child. This can contain multiple layers/depths (e.g. Parent/somecategory/childname).
On first sight there doesn’t seem to be a problem. When opening the panel-front (Parent) every child is present in the list. But when I open a childpage for editing/viewing, it will crash if the url/slug of that child has more than 1 layer/depth.
So a child with url/sug test will open just fine, but a child with url test/ing cannot be found.
Analysing
The way Kirby obtains their pages on the panel side is by calling API->page which refers after a few calls to App->page() which looks like this:
/**
* Returns any page from the content folder
*
* @param string|null $id
* @param \Kirby\Cms\Page|\Kirby\Cms\Site|null $parent
* @param bool $drafts
* @return \Kirby\Cms\Page|null
*/
public function page(?string $id = null, $parent = null, bool $drafts = true)
{
if ($id === null) {
return null;
}
$parent = $parent ?? $this->site();
if ($page = $parent->find($id)) {
return $page;
}
if ($drafts === true && $draft = $parent->draft($id)) {
return $draft;
}
return null;
}
The $parent->find($id) call will not find anything because the panel-front is a draft.
The $parent->draft($id) is where it should return the correct result, but where something weird happens. The function looks like follows:
/**
* Searches for a child draft by id
*
* @param string $path
* @return \Kirby\Cms\Page|null
*/
public function draft(string $path)
{
$path = str_replace('_drafts/', '', $path);
if (Str::contains($path, '/') === false) {
return $this->drafts()->find($path);
}
$parts = explode('/', $path);
$parent = $this;
foreach ($parts as $slug) {
if ($page = $parent->find($slug)) {
$parent = $page;
continue;
}
if ($draft = $parent->drafts()->find($slug)) {
$parent = $draft;
continue;
}
return null;
}
return $parent;
}
Basically it splits the slug/url in portions and tries to find the childpage iteratively. On its own there is not really a problem, until a parent has children with a multi-layer/depths url/slug.
In my custom model I can override the find function, which could handle it perfeclty (and it would, if given the full url), but since it only tries to find a single portion of the url/slug. There is no clean way to handle this from a Model-class unfortunately.
Additional thoughts
The decision to split the url/slug is made in the App class basically, which makes it impossible to catch it without override kirby-base classes (something I don’t want to do unless absolutely necessary).
I thought about making dumy pages for the layers in between the panel-front (Parent) and the absolute child. This would solve the panel-url-find-problem, but that would also mean the Pages section would mess up. (Since both rely on the children function in a Model.
Question
I can think of several ways to solve this on Kirby’s end, but that is something I’ll leave up to the devs.
In the meantime, is there a clean way to solve this on my end?
Ps. If it is better to ask this on another platform (since it seems like different members are active on different platforms) please let me know, so I can put up the question there!