Humblee

A humble PHP framework & CMS

Creating Pages

In Humblee, a "page" is the combination of three things: a view file that defines the HTML structure, a template record in the CMS that connects a URL to that view and declares which content blocks are editable, and the content that editors fill in through the admin interface. You define the structure in code, the CMS handles the content.


Step 1: Create a view file

Create a PHP file in application/views/. This is the HTML template for the page. It has access to a $content variable — an associative array of content block objects, keyed by their objectkey. Use Draw::content() to output each block wherever it belongs in the layout.

<?php
declare(strict_types=1);

use Humblee\Foundation\Draw;
?>

<article class="content">
    <h1><?php Draw::content($content, 'page_title') ?></h1>
    <div class="intro">
        <?php Draw::content($content, 'intro_text') ?>
    </div>
    <div class="body-copy">
        <?php Draw::content($content, 'pagebody') ?>
    </div>
</article>

The string you pass as the second argument — 'page_title', 'pagebody', etc. — is the objectkey. This must exactly match the objectkey of a content block type defined in the CMS. If the key does not match, Draw::content() outputs nothing silently.

Available Draw helpers

Helper Purpose
Draw::content($content, 'objectkey') Outputs the live content for a named block
Draw::metaTags($content) Outputs <title> and <meta> tags from the built-in meta_tags block

Draw::metaTags() is already called in application/views/templates/template.php for every page, so you do not need to include it in your individual view files.

When an admin is logged in

When a user with the content, publish, or admin role views the page, Draw::content() wraps each block in a <div class="cms_block"> with data attributes that the CMS toolbar uses to enable inline editing. This is automatic — your view requires no changes to support it.


Step 2: Register the template in the CMS

Navigate to Admin > Templates. Create a new template record. The two most important fields are the page type and the content blocks.

Page type: view

Use this for the majority of pages. The CMS will render your view file directly, with the page's content already loaded and passed in as $content.

Set the path field to the path of your view file relative to application/views/, without the .php extension. For example, if you created application/views/about.php, enter:

about

For a file in a subdirectory like application/views/marketing/landing.php, enter:

marketing/landing

The framework resolves this as: application/views/{path}.php.

Page type: controller

Use this when the page needs to load its own data before rendering — for example, fetching a list of records from the database, checking a condition, or preparing variables that the view depends on.

You provide a controller class name and a method name. The framework will instantiate App\Controller\{ClassName} and call the specified method, passing it an array of the current page state (the resolved $page, $content, and $template objects). The method returns an HTML string which becomes the rendered page body.

In the CMS, set the controller name and action (method) in the template's path fields.

In code, create application/Controller/YourController.php:

<?php
declare(strict_types=1);

namespace App\Controller;

use Humblee\Foundation\Core;

class YourController
{
    public function index(array $templateData): string
    {
        $content  = $templateData['content'];
        $page     = $templateData['page'];

        // Load any additional data your view needs
        $items = \ORM::for_table('your_table')->find_many();

        return Core::view(
            _app_server_path . 'application/views/your-view.php',
            ['content' => $content, 'page' => $page, 'items' => $items]
        );
    }
}

The view file receives whatever variables you pass to Core::view(). The $content array is still available for Draw::content() calls — just pass it through as shown above.

When to use each: if the page is purely content-driven (text, images, blocks an editor updates), use view. If the page needs to query the database or run application logic before rendering, use controller.

Selecting content blocks

This is the most consequential field in the template form. The blocks selection defines which content block types are available for editing on pages that use this template.

There are two reasons this matters:

  1. The editor UI. When an admin opens a page's content editor, they see only the blocks listed in the template. If a block is not in the list, there is no way to add content to it through the admin interface.

  2. Your view. Draw::content() can only output a block if that block has content saved in the database. A block that was never shown in the editor will never have content, and Draw::content() will output nothing for it.

The rule is: every objectkey you call in your view file must have a corresponding block selected in the template. If your view calls Draw::content($content, 'sidebar'), then the sidebar block type must be in the template's block list, or editors will have no way to populate it.


Step 3: Create a page

Navigate to Admin > Pages. Add a new page, give it a title and a URL slug, and assign the template you just created. The page is now routable — visiting its URL will render the view — but all content blocks will be empty until an editor fills them in.


Step 4: Edit the content

There are two ways to reach the content editor:

Via the admin dashboard: Go to Admin > Pages, find the page, and click into any of its content blocks. This opens the full editor for that block.

Via inline editing: When an admin is logged in and viewing any page on the site, the CMS toolbar appears at the top of the browser. Clicking on any content area on the page opens that block's editor in a panel. The editable regions are highlighted automatically — this is the cms_block wrapping that Draw::content() adds for authenticated users.

Drafts and publishing

Saving content creates a new revision but does not change what visitors see. To make a revision live, use the Publish action in the editor. The CMS stores unlimited revisions and any previous revision can be restored at any time. Only users with the publish role (or developer or admin) can move a draft to live.

Content block types

The input type of a block — plain text, rich text (WYSIWYG), Markdown, or a custom structured form — is defined per block type in Admin > Blocks. Your view does not need to know the input type; Draw::content() always outputs the rendered HTML. Markdown blocks are automatically converted by Parsedown before output.