Create the Empty Block Type Code

Concrete CMS block types installed by our Dreamrs package are placed in the packages/dreamrs/blocks directory. Let’s create that directory:

cd public/packages/dreamrs  
mkdir blocks  
cd blocks  
mkdir page_header

We’re choosing page_header as our block type handle, because it matches the name that we want for this block. Like other things in Concrete, a block type has a handle. This is a lowercase-only, non-spaced string that describes the block type in the filesystem. Examples include "content", "youtube", "page_title", etc...

Icon

Next, we need to give the block type an icon, with a PNG file named icon.png. The dimensions for this file should be 50x50 pixels. You can choose any image here, but I’m going to actually generate my image from Font Awesome icons using the excellent FA2PNG Tool. Since all the Concrete core icons use a specific shade of light blue (#2aa9dd), I’m going to choose a different color for the custom block types in this package. Let’s use #ff3334, which is derived from the Dreamrs logo, and base it on the Header icon. Here’s my generated icon:

Name this file icon.png, and place it within the page_header directory.

Controller

Next, we need to create the controller for the block type. Block type’s have controllers, just as packages do. The controller for a block type contains all logic that runs behind the scenes whenever a block is added, edited, viewed or saved/updated. Controllers also pass data into the add, edit and view templates for a block type.

Create a file named controller.php in the page_header directory.

cd page_header  
touch controller.php

Edit this file, and first give it the required namespace.

<?php  
namespace Concrete\Package\Dreamrs\Block\PageHeader

Just like the namespace of the PageTheme class we created earlier, the namespace of the block type class starts with the package namespace we used before, adds Block and the PascalCase’d version of the block type’s handle.

Next, let’s add this line to halt execution of the page if the script is run directly.

defined('C5_EXECUTE') or die('Access Denied.');

Now let’s import the base block type class that our block type controller must extend.

use Concrete\Core\Block\BlockController;

We’ll be extending this class. Now we will create the first version of our controller:

class Controller extends BlockController  
{

    protected $btInterfaceWidth = 470;
    protected $btInterfaceHeight = 300;
    protected $btCacheBlockOutput = true;
    protected $btCacheBlockOutputOnPost = true;
    protected $btCacheBlockOutputForRegisteredUsers = true;
    protected $btTable = 'btDreamrsPageHeader';
    protected $btDefaultSet = 'basic';

    public function getBlockTypeDescription()
    {
        return t('Shows a Header at the top of a page.');
    }

    public function getBlockTypeName()
    {
        return t('Page Header');
    }

    public function add()
    {

    }

    public function edit()
    {

    }

    public function view()
    {

    }
}

Let’s walk through this. Controller is the required name for this file, just like for a package controller. $btInterfaceWidth and $btInterfaceHeight control the width and the height of the block type’s add and edit interface window. This is the window displayed when adding or updating a block type through the CMS.

Next we have some caching settings for our block type. $btCacheBlockOutput, $btCacheBlockOutputOnPost and $btCacheBlockOutputForRegisteredUsers control whether to cache block output, and if so, whether the block type output can be safely cached on POST requests, or even for registered users. If you aren’t sure about whether your block output can be cached, you can omit these settings, but in order to get good performance out of our site, I’m going to turn these on for block types I know can be cached (including this one.)

Next we have our $btTable property. $btTable maps to the database table we’re going to be using to save this block type’s data. We’re going to create a database table named btDreamrsPageHeader to save this block type’s custom fields. More on this soon.

Next, we have the $btDefaultSet property. This property determines what block type set the block type should go into. If you omit this, the block type will be listed at the end of block types. I’m choosing the Basic block type set, because this is a pretty common block type in our site. If you want a full list of block type sets, you can find them in the BlockTypeSets database table.

After this, you you can see five methods. The name and description methods should be pretty self-explanatory: we’re returning the name and description of our block type for use in various Concrete editing interfaces. And finally we have view(), add(), and edit() methods. These are run when a block is viewed, added or edited (respectively). We will fill out these methods soon.

view.php, add.php and edit.php

Now, we need to add our various templates to the block type folder. A block must have three templates -- view.php which is rendered when a block is viewed in a site, add.php which is rendered when its added, and edit.php for when it is edited. Let’s create these empty files:

touch add.php  
touch edit.php  
touch view.php

Let’s just add some placeholder text into these files os we can verify they’re loading properly. In view.php and edit.php, let’s add this text:

<?php  
defined('C5_EXECUTE') or die('Access Denied.');  
?>  
Hello World.

We’re going to do something a little different in add.php. In most cases, the add and edit templates of block types are the same -- it’s just that edit.php also contains data loaded from the block instance. So let’s not duplicate the template, because then we’re going to have to keep it in sync across both add.php and edit.php as we add features. Instead, let’s include the edit.php template within the add.php template. Add this to add.php:

<?php  
defined('C5_EXECUTE') or die('Access Denied.');  
$view->inc(‘edit.php’);

Our experience with the inc() method in our theme helps us here.

db.xml

At this point, let’s think a little about what type of data our block is going to store. Every block type that has specific data set when added or updated (which is the vast, vast majority of them) needs to have a custom database table, with its fields set up as columns in the table. We already have a name for our database table -- btDreamrsPageHeader (we set it as the $btTable property in our class. So what columns are we going to place in this table?

We want to allow the use of an optional background image, an optional title, as well as a checkbox to control whether the current page’s title is used (vs. the custom title.)

  • Image

  • Override Page Name?

  • Custom Page Header Title

What are the data types for these fields? For image, you might think it would be a string (a path to an image file) but in Concrete, whenever you’re working with an uploaded file/image, or a link to a page, or a user object, you’re going to want to user an integer to represent the item, because the primary key for these objects is an integer. Override Page Name is pretty clearly a boolean true or false, and custom Page Title is a string. Given this, I believe our database table should have the following columns.

  • fID - integer
  • overridePageName -- boolean
  • customPageHeaderTitle - string

With that decided, we’re going to create a file name db.xml that describes this database table, in the Doctrine XML format. It is based off of Doctrine DBAL, and allows for cross-platform descriptions of types and nomenclatures. Create a file named db.xml in the block directory:

touch db.xml

and place the following inside it:

<?xml version="1.0" encoding="UTF-8"?>
<schema
  xmlns="http://www.concrete5.org/doctrine-xml/0.5"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.concrete5.org/doctrine-xml/0.5 http://concrete5.github.io/doctrine-xml/doctrine-xml-0.5.xsd">
  <table name="btDreamrsPageHeader">
    <field name="bID" type="integer" size="10">
      <unsigned/>
      <key/>
    </field>
    <field name="fID" type="integer" size="10">
      <unsigned/>
      </field>
    <field name="customPageHeaderTitle" type="string" size="255">
      <notnull/>
    </field>
    <field name="overridePageName" type="boolean">
      <default value="0"/>
      <notnull/>
    </field>
  </table>
</schema>

fID maps to file ID, which is an integer. bID stands for block ID -- any block type database table should have an unsigned integer named bID in order to automatically save data when blocks are added, updated or deleted. The other columns match their original column names pretty close.

When the block type is installed, the database table will automatically be created. If at any point you add fields to this database table in the XML, an update to the package will automatically modify the schema of the database table.

We now have everything we need in our folder, so let’s install our block type. (Note: curious about what else a block type might contain? Check out (Anatomy of a Block Type)[https://documentation.concretecms.org/developers/working-with-blocks/creating-a-new-block-type/getting-started/anatomy-of-a-block].