Kirby CMS

Everything interesting about Kirby

Kirby is a flat-file CMS which is based on text files and assets organized in nested folders. That makes it really easy to work with.

They made it easy to connect to any data source like a SQL database, REST API or spreadsheet and create virtual pages on the frontend.

Blueprint Gotcha

If your fields or section don't render or return weird errors, check if the indentation in the YAML file is consistent. Don't mix tabs and spaces!

Fields

Block Editor

Layout blocks, similar to Tumblr or Gutenberg https://github.com/getkirby/editor/wiki/Templates

Virtual Pages with an Existing Database

In Kirby, it's easy to consume external data on the frontend. It's also possible to set up a database backend with Kirby's panels.

The basics of reading and writing to a database are described in the guide: Content from a database

What Kirby offers you here is extending and overwriting the Kirby Page object to read and write from the database instead of the file system.

The downside of this is, that it's realized in a very opinionated way. Kirby Pages expect a certain data structure that you have to simulate with your database model, e.g. the reliance on the slug field1 as identifier or the status has to be in a certain way. A title has to exist in some way.

If you have tabular data, out of the box, Kirby lists your "virtual pages" just with the title field and an info field.

To create a sortable table, the Kirby Pagetable Plugin solves this quite nicely. But there is a downside as well. Want to admin a table with, 10000 records? As far as I can tell, pagination is done in the frontend, if the 10000 records make it through the wire without a timeout.

My conclusion for the first few tests is: for complex relational database interfaces (customers and orders etc. in an online-shop) use a dedicated system for that. I'm sure it's possible to build a complex database admin interface on top of Kirby's panels, it's just PHP, but I'm not sure if it's worth it.

It's definitely worth for a single table with a limited amount of data.

Dealing with large virtual collections

I think the easiest way to work with large collections currently is to avoid the normal panel sections and write your own. Also, “virtual pages” aren’t meant to be lazy loaded, so you probably want to avoid those in general.

There’s a feedback ticket here: Lazy loading virtual pages · Kirby Feedback 3, but I don’t think it will ever be implemented, goes a bit too much against how Kirby 3 works

https://forum.getkirby.com/t/lazy-loading-possible-when-using-pages-factory/27032/2

Virtual Files

Convert external links to assets to file objects etc.

Virtual files | Kirby CMS

Query Language

Kirby comes with a powerful query language, that is mainly used in blueprints to build dynamic form fields, like select boxes with child pages or things like that. It can be extended by the filter syntax as described in the filtering compendium

Its simple dot notation gets translated to Kirby's PHP API, where

page.children.filterBy("featured", true)

is equivalent to

$page->children()->filterBy('featured', true)

As a developer, there are certainly moments where you wish you could write a query like that in a content field, instead of creating a complex query builder with form fields and then just parse that string and turning it to a native PHP Kirby query.

This is possible with the Kirby\Toolkit\Query class that "can be used to query arrays and objects, including their methods, with a very simple string-based syntax."

The following example queries Kirby's internal objects.

$siblings = new Kirby\Toolkit\Query('page.siblings(false)',[
    //'kirby' => $kirby,
    //'site' => $site,
    //'pages' => $pages,
    'page' => page()
]);

dump($siblings->result()->toArray());

But it's also possible to query a custom array or collection.

Convert an array into a Kirby\Toolkit\Collection to have all filter methods like findBy or count or filterBy available.

$collection = new Kirby\Toolkit\Collection([
    ['id' => 1, 'title' => 'Title 1'],
    ['id' => 2, 'title' => 'Title 2'],
    ['id' => 3, 'title' => 'Title 3']
]);

$output = new Kirby\Toolkit\Query('collection.shuffle.findBy("id",2)', ['collection' => $collection]);

dump($output->result());

Result:

Array
(
    [id] => 2
    [title] => Title 2
)

or

$output = new Kirby\Toolkit\Query('collection.shuffle.filterBy("id",">",1).data', ['collection' => $collection]);

dump($output->result());

Result:

Array
(
    [1] => Array
        (
            [id] => 2
            [title] => Title 2
        )

    [2] => Array
        (
            [id] => 3
            [title] => Title 3
        )

)

This is amazing and gives a lot of flexibility to developers who know what they are doing!

It's also worth to look into Kirby\Form\Options::query() that is mainly used for dropdown fields, but can provide some additional help, as discussed here.

Debug

var_dump(db::trace()); should give you a complete trace of what’s happening.

var_dump(db::lastQuery()) shows the last query

var_dump(db::lastError()) shows the last error

source

Whoops

Kirby CMS is using https://github.com/filp/whoops to show detailed error pages, but is missing the Arguments section with a dump of vars in the function call (or so).

To fix that you have to install Symfony's varDumper which itself redeclares the Kirby dump() function and breaks Kirby. Unless you turn Kirby's dump() off in the index.php:


<?php
define('KIRBY_HELPER_DUMP', false);
require __DIR__ .'/kirby/bootstrap.php';
echo (new Kirby)->render();

Page Models

The $page object is the heart and soul of Kirby. It is used to construct pages and all their dependencies like children, files, content, etc.

Access Methods From Another Page Model


class ProductPage extends Kirby\Cms\Page
{

    public function getSomeData(){
        return $request = Remote::get($url)->json();
    }

}

Just query the page and use it's methods.


class DataPage extends Kirby\Cms\Page
{
    public function children(){
        $data = site()->find('product/data')->getSomeData();
        $pages = [];
        return Pages::factory($pages, $this);
    }
}

// Assuming you have a ProjectPage model
use App\Models\ProjectPage;

// Get the id of the page you want to work with
$pageId = 'some-page-id';

// Fetch the page data
$pageData = kirby()->page($pageId)->toArray();

// Create a new instance of the ProjectPage model with the page data
$projectPage = new ProjectPage($pageData);

// Now you can call the methods defined in the ProjectPage model
echo $projectPage->cover();

https://www.phind.com/search?cache=ysgt2k0fj9zlyrrc1n59utnt&source=sidebar

Controllers

Add additional fields to $page->content


<?php
return function ($page, $kirby, $site) {

    $merged = array_merge(
        $page->content()->toArray(),
        [
            'foo' => 'bar',
            'abc' => 'xyz'
        ]
    );

    $page->content()->update($merged);

    return $page;

    // or access it with  $products in the template
    return [
        'products' => [
            'foo' => 'bar',
            'abc' => 'xyz'
        ]
    ];
};


{{  $page->foo()->upper() }}

<!-- alternative -->

{{  $products->foo()->upper() }}

Debug with Clockwork

https://github.com/itsgoingd/clockwork

Installation Guide

Install Clockwork as extension in your browser Chrome or Firefox

Install Clockwork in your project

$ composer require itsgoingd/clockwork

Open Kirby's index.php and initialize Clockwork right in the beginning:


<?php

require __DIR__.'/kirby/bootstrap.php';

$clockwork = Clockwork\Support\Vanilla\Clockwork::init(
    [
        'storage'            => 'sql',
        // sqlite database will be saved directly in the vendor folder
        'storage_database'   => 'sqlite:'.__DIR__.'/clockwork.sqlite',

        'register_helpers'   => true,
        'enable'             => true,
        'storage_expiration' => (24 * 60),

        'requests'           => [
            'except' => [
                // '/api/*/lock',
                // '/api/*/unlock',
            ],
        ],

        'filter_uris'        => [
            '.*/lock',
            '.*/unlock',
            '/api/auth',

        ],
    ]
);

All options are described here: https://github.com/itsgoingd/clockwork/blob/master/Clockwork/Support/Vanilla/config.php

At the end of the index file, where you render the CMS, write the output in a variable, end the Clockwork process and then echo the Kirby output.


// echo (new Kirby)->render();
$output = (new Kirby)->render();
clock()->requestProcessed();

echo $output;

Next, you go into your Kirby config.php file to set up a route for Clockworks API:



return [
    'debug'     => true,

    'routes'    => [

        [
            'pattern' => '__clockwork/(:any)',
            'action'  => function ($all) {
                clock()->returnMetadata($all);
                exit;
            },
        ],
    ],

    // this is just a neat way to see what hooks are called, in which order.
    'hooks'     => [
        '*' => function ($event) {
            clock()->debug($event->name());
        },
    ],
];

Now your PHP scripts you can measure processing time https://underground.works/clockwork/#docs-timeline:

clock()->startEvent('twitter-api-call', "Loading user's latest tweets via Twitter API")

// some long running process

clock()->endEvent('twitter-api-call')

and get some fancy bar graphs

or log variables https://underground.works/clockwork/#docs-logging:

clock()->debug('writeContent', [$data,$this->getTableColumns()]);

and get insight in your code.

Database

Use Laravel Eloquent in Kirby https://github.com/beebmx/kirby-db

Laravel

If Laravel is part of your project, Kirby makes it easy to bridge the gap.

Laravel's Query Builder and Eloquent ORM is pretty nice so the beebmx/kirby-db got you covered here - especially if you have a database that is administrated by a Laravel app and happens to be multilingual using spatie/laravel-translatable. To get this data out and in your Kirby site, both packages are working together quite nicely.

And then, if you like template inheritance like in Twig or Laravel Blade templates, even that is possible, with afbora/kirby-blade.

Make sure you install it in the following order. At this time (2020-10-04) the blade templates are based on packages for Laravel 7 and translatable are pulling in packages for Laravel 8 if they are not restrained.

  1. afbora/kirby-blade: Enable Laravel Blade Template Engine for Kirby 3
  2. beebmx/kirby-db: Enable Kirby 3 database support for Illuminate\Database
  3. spatie/laravel-translatable: Making Eloquent models translatable

Blade Templates

They are easy to use and to extend, and you can mix it with Kirby's snippets and templates.

The argument to use Blade is not anymore to have a master layout and inheritance, because Kirby has getkirby/layouts and snippets can have slots for components (since 3.9) which is probably more performant than Blade components2 3

kirby3-plugin