PageTree demo by

Developers

Blocks in your own models

The PageTree block editor is also a form widget, pagecomponents. You can add it to any model of your plugins (product, article, listing…) and render its blocks in your theme.

How it works

  • PageTree registers the widget under the pagecomponents code. It is the widget behind the blocks field of PageTree pages.
  • It offers the same block library, the same forms and the same preview.
  • It stores the blocks as JSON in a column of your table.
  • In the theme, the {% pagecomponent %} Twig tag renders a block.

1. The database column

Add a text column large enough for the blocks JSON. PageTree itself uses a longText column for its pages.

// plugins/acme/shop/updates/add_content_blocks_to_products.php
use Schema;
use October\Rain\Database\Updates\Migration;

return new class extends Migration
{
    public function up()
    {
        Schema::table('acme_shop_products', function ($table) {
            $table->longText('content_blocks')->nullable();
        });
    }

    public function down()
    {
        Schema::table('acme_shop_products', function ($table) {
            $table->dropColumn('content_blocks');
        });
    }
};

Register the migration in the updates/version.yaml file of your plugin, like any October CMS migration.

2. The model

Declare the column as jsonable. This is required: the widget returns a decoded object, which the model must encode back to JSON when saving.

class Product extends Model
{
    protected $jsonable = ['content_blocks'];
}

3. The form

Declare the field in the model fields.yaml. This example mirrors the PageTree page form: a stretched tab, with no inner padding, that fills the full height.

fields:
    name:
        label: Name
        type: text

tabs:
    stretch: true
    paneCssClass:
        Content: p-0
    fields:
        content_blocks:
            tab: Content
            type: pagecomponents
            span: full
            stretch: true
OptionEffect
type: pagecomponentsRenders the block editor.
layoutFieldName of a field of the same form that holds the preview layout (see below). Optional.
span, stretch, tabStandard October CMS form field options.

The preview

  • Without layoutField: the widget toolbar shows Edit Configuration. Its Preview Mode lets you pick a layout of the active theme, or the Manual CSS/JS configuration. The choice is saved with the blocks.
  • With layoutField: the preview uses the value of that field when the form loads. In your own models, a layout change is picked up after saving and reloading the form.
  • The layout preview goes through the PageTree back-office, so the administrator needs the Manage block pages permission.

4. Rendering in the theme

The blocks live under the components key of the saved value. Loop over them and render each one with {% pagecomponent %}, for example in a CMS page:

title = "Product"
url = "/products/:slug"
layout = "default"
==
function onStart()
{
    $this['product'] = \Acme\Shop\Models\Product::where('slug', $this->param('slug'))->first();

    if (!$this['product']) {
        return $this->controller->run('404');
    }
}
==
<h1>{{ product.name }}</h1>

{% for block in product.content_blocks.components|default([]) %}
    {% pagecomponent block %}
{% endfor %}

The tag also works in a partial or in a component partial.

  • Hidden blocks are not rendered.
  • The stylesheets of the built-in blocks are output with their HTML: there is nothing to load in the layout.
  • A block whose type no longer exists is skipped and logged.

Limitations

  • CMS component blocks render nothing outside a PageTree page: {% pagecomponent %} does not declare the component on the page. Place the component directly in your page or layout instead.
  • Version history, publishing and SEO belong to PageTree pages. For your model, handle them yourself (status and date fields, meta tags).

To render the blocks of an existing PageTree page inside a CMS page, see Theme integration.