Show other pages as checkboxes

Continuing the discussion from Fetch query from parent page:

I have a restaurant site that has several locations. All the locations share basically the same menu, with some exceptions. My content directory looks somewhat like this:


The client can go to a city location and select checkboxes from the master menu, making adding common menu items a breeze. However, if they add a new menu child page, or menu category, it doesn’t show up in the blueprint for the location. Right now in my /site/blueprints/locations.yml I have to spell out each checkbox query to find a particular menu child or category page, such as the following. (note the “page: menu/starters” that I hard code)

    label: Menu Section
    type: checkboxes
    options: query
        page:  menus/starters
        fetch: visibleChildren

This works fine, except when the client wants to add a menu section. They add a page under /menus, but because it isn’t coded into the locations blueprint, the new menu items never show up as checkboxes there. Is there a way to grab all the visible menu children and display checkboxes for each in the locations blueprint. I thought of showing pages based on template, but I’d to separate these by category or there will be hundreds of un-separated checkboxes in the panel.

Right now it seems you have a field for each menus’ children, i.e. starters, soups etc. So basically what you are asking then is to generate checkbox fields on the fly? This is not possible.

You could grab all grandchildren of the menus folder, but as you already said, then you would end up with a very long list of checkboxes, which does not make sense.

The only thing I can currently think of is to try and generate the blueprint with a script that is triggered by a panel hook. Never tried that but should be possible somehow.

Edit: I just realized that you marked this thread as solved. How did you solve it then?

Sorry–didn’t mean to mark as solved when I created the question. It’s not solved, but thanks for the idea! I’ve never ventured into the panel hook area, but may give it a try.

You could also try to create a custom field that grabs all the subpages and their children and transforms the subpages into sections (headlines, tabs) under which the items are listed. Don’t know, if this is feasible, though.

Interesting. I’m assuming I would need to copy from one of the following default fields, rename it, and save to site/fields/newfield/? And I’d add some custom html and loops in there as well?


Yes, exactly. You would copy the checkboxes field to site/fields/menuselect and use the following class definition:

class MenuselectField extends CheckboxesField {

You would then probably override the options and content methods. Your options would get a grouped list of menu items and return it as array, content would take that and output the groups using headlines etc.

Thanks Lukas. Now I’m testing with a hard coded, multidimensional array of menu items as the return from the options method. One issue I’m having is it won’t save. I keep getting the “Please fill in all fields correctly” warning. If I return a one-dimensional array of pages and their titles from the options method, it saves just fine.

I’ve narrowed it down to some checking feature that compares the array keys. It will save a checkbox selection if the secondary array key is the same as the primary array key. In the following example, menus/fries will not save, but if I set the fries key to menus/starters, it will save.

// site/fields/menuselect/menuselect.php
class MenuselectField extends CheckboxesField {

    public function options() {
	$menu_categories = array (
	    'menus/starters' => array(
	    	// 'menus/fries' => 'Fries ', // will NOT save
	    	'menus/starters' => 'Fries ', // will save
	    	'menus/chips-dip' => 'Chips & Dip',

        return $menu_categories;

    public function content() {
        foreach($this->options() as $menu_page => $menu_items) {
            $html .= "<h1>$menu_page</h1>";
            foreach($menu_items as $menu_item => $value) {
	        $html .= '<li class="input-list-item field-grid-item' . $width . '">';
	        $html .= $this->item($menu_item, $value);
	        $html .= '</li>';
1 Like

Yes, I forgot about that. options() must normally return a one-dimensional array.

You could however also override the validate() method from the InputListField. It is responsible for the error message and if you modify it to work with your multi-dimensional options array, it should work just fine.

1 Like

The custom validation works great!

And to build the menu array dynamically in the options method, could I do something like this? It doesn’t seem to like me using $pages in this site/fields/menuselect/menuselect.php file…

	foreach($pages->find('menus')->children()->visible() as $page) {
		$menu_categories[$page->uid()] = $page->title();

Have you tried kirby()->site()->find() ...?

page('menus')->children()->visible() should also work and is shorter.

Thank you both! It’s kind of hard to output headlines as everything in this custom field comes out under the <div class="field field-grid-item">. But it still works!