Dynamic templates in blueprints?

Hi folks,

some days ago I had an issue (see Changing a blueprint on the fly when creating a subpage) which eventually could be solved by kind support from @pixelijn - thank you once again.

In the meantime, another question arises.
I have a generic blueprint (and template) for overview pages. Each overview page contains links to other parts of the website. Those linked pages will generally contain payload information (of different types), but could also be overview pages themselves. Those linked subpages must therefore have different blueprints.

Thus I’d like to specify which blueprint has to be employed when creating a child page from a particular overview page, e. g.:

overview page => desired child’s blueprint
overview (products) => overview (productgroup)
overview (productgroup) => product
overview (services) => service

and so on.

I have a page model for my overview page, so I thought that I could employ a similar approach as to my former label question. I tried

subpages:
  type: pages
  headline: "{{ page.label('subpage-header') }}"
  templates: "{{ page.templates() }}"

but unfortunately, that does not work out:
grafik

How can I specify a kind of query in the templates key of my overview blueprint? Or how should I tackle this problem?

(Sorry for this somewhat lengthy question. I didn’t know how to make the problem clear in a terser way.)

What is in your model? And for which page type did you create the model?

However, I think the template options doesn’t support query language

In my model OverviewPage for my overview pages I have the necessary devices to determine the variable parts for the different variants of the blueprint (not only page.label("label_name")), but others too. This all works very well.

I was afraid to get that answer.

I thought of going along with specialized sections. But then, I’d have to copy/paste large parts of my blueprint just for one value, which I don’t like, because it would not be DRY.

Any suggestions how I could work around this problem?

Is it perhaps possible to have a query on a section in a blueprint?

section:
  subpages:
    query: "{{ page.subpagetype() }}"

Nope, sections do not have a query option. And if they did, it wouldn’t help you, because the query doesn’t set what template would be used at page creation.

To prevent having to duplicate everything when using different template (which would be simplest thing to do), you can create reusable blueprint parts (tabs, sections, fields, field groups):

Yes, I know of that re-using stuff, and I use it well. But I have to re-use a rather large blueprint, and I have to replace only a handful values relatively deep in the yaml structure. My question is how to achieve this.

What you could do though, would be to overwrite the createChild() method in your overview page template and assign the template conditionally depending on the parent (in that case, hiding the template in the dialog would probably make sense).

Just to make sure I fully understand you: Did you mean the createChild() method in my OverviewPage model (not in a template)? I will dig into the docs for this. Thank you so far.

Yes, exactly.

Overwriting the createChild() method does not help, because the child page gets created anyway. createChild() on the other hand yields a new page object. How should I replace the current page by some new page object? I did not find a viable solution for this in the docs.

I did not manage all of the issues connected to my original problem. But the specific question I posted has a solution.

Instead, I’m using now a hook to accomplish the task:

return [
    "hooks" => [
        "page.create:after" => function ($page) {
            if (Str::endswith(($parent = $page->parent())->blueprint()->name(), "overview")) {
                $page->changeTemplate($parent->flexibleTemplate());
            }
         },
    ],
];

That one does the trick. Thank you, @texnixe, for pointing me into the right direction.

Well, yes, it get’s created, but you can set the template at page creation, whereas with the hook, the page is first created and then the template is changed.

Note that this is the model for the parent page!

<?php
class OverviewPage extends Page
{
    public function createChild(array $props)
    {
        $props = array_merge($props, [
            'url'    => null,
            'num'    => null,
            'parent' => $this,
            'site'   => $this->site(),
            'template' => 'sometemplate', // set this according to your condition
        ]);

        $modelClass = Page::$models[$props['template']] ?? Page::class;
        return $modelClass::create($props);
    }
}
1 Like

Ok, this works too. Thank you, that solution is definitely superior to my workaround using a hook.

public function createChild(array $props): Page {
    $props = array_merge($props, [
        'url'      => null,
        'num'      => null,
        'parent'   => $this,
        'site'     => $this->site(),
        'template' => $this->flexibleTemplate(),
    ]);   
    $modelClass = Page::$models[$props['template']] ?? Page::class;
    return $modelClass::create($props);
}

The solution seems clear and straight forward. Unfortunately I could not make up this solution myself, given the sparse documentation for createChild(). The referenced documentation for Page::create() does not help either, because it only enumerates the properties without telling me how to set them.

Even now it works I have no clue why the props have to be set the way they are set, nor how to set those props, why to set the $modelClass, how to construct that return value.

Have I overlooked some crucial information in the docs?