Programmatically Creating Composer Forms and Controls

Background

If you want to create a page type for your package, it is common to want to give the users a default layout, and an easy to edit composer page creation form. This ensures that the page type can be used easily, gives an excellent user experience, and allows you to preset the page types layout to have the elements work together so the end user doesn't have to worry about which blocks go where.

Example Specification

For this example, a Composer form will be created to let a user add a picture, a description, and some page attributes. It will be assumed that the attributes have already been added to the Page Type (see Adding Attribute Sets and Keys Programmatically and Adding Page Types Programmatically for more info). The Page Type will be for a Country listing, and users will provide a map, name, population, capital city, and description.

Prerequisites

If this package is not programmatically creating the Page Types, and the Attributes, the classes required for managing them will need to be included in the head of the controller.

use PageType;
use \Concrete\Core\Attribute\Key\CollectionKey as CollectionKey;

Layout Sets

Layout sets group the information on the Composer form. This can be used to logically group information for the end user to fill in. For this example, there is only one set required: Country Info.

First, we need to bring in the prerequisite classes in the head of our controller.

use \Concrete\Core\Page\Type\Composer\LayoutSet as LayoutSet;

Then we need to get the specific page type that we are using, in this case, the "Country" Page Type, in our installation/update function.

$pt = PageType::getByHandle('country);

And then we can simply add a set by calling an add_set method of the Page Type, and set it to a variable to be used later:

$countrySet = $pt->addPageTypeComposerFormLayoutSet('Country Info', 'Country Info');

Form Controls

And now we add controls to the Composer Form. These can be Blocks, Built-In Properties, and Custom Attributes.

Built-in Attributes

Since the form should be in some logical order, the first thing that should be added is the Country Name, which for this purpose will also be the page name. For that, we will use the Built-in Page Name Property of the Page itself. Unlike Block and Attribute Controls, for this one, you have to know the actual handle of the property, and pull in that specific class. The properties are DateTime, Description, Name, PageTemplate, PublishTarget, UrlSlug, and User. Since we are looking for the name, the class we need to bring in at the top of the controller is:

use \Concrete\Core\Page\Type\Composer\Control\CorePageProperty\NameCorePageProperty as NameControl;

And then we generate a new instance of that class:

$countryName = new NameControl();

And then we add it to the set:

$countryName->addToPageTypeComposerFormLayoutSet($countrySet);

Now, if you want to change the description or label in the composer form, you have to do it after it has been added to the set (there is a patch to allow this during the creation for future versions). To to that, we would add it to the set a little differently, to get the new control object, and then set them there:

$countryControl = $countryName->addToPageTypeComposerFormLayoutSet($countrySet);
$countryControl->updateFormLayoutSetControlCustomLabel('Country Name');
$countryControl->updateFormLayoutSetControlDescription('Enter a Country Name for the Page Name');

Block Controls

Since we want to add a map, and a description, we will need some built in blocks. And as usual, there are classes we will need to call at the top of the controller: the Control class itself, and the BlockType class for getting the Block Type ID.

use BlockType;
use \Concrete\Core\Page\Type\Composer\Control\BlockControl as BlockControl;

We initiate a new Block Control, find the correct ID for that Block Type, plug that into the Control, and then add that into the set.

$countryMap = new BlockControl();
$imageBlock = BlockType::getByHandle('image');
$countryMap->setBlockTypeID($imageBlock->getBlockTypeID());
$countryMap->addToPageTypeComposerFormLayoutSet($countrySet);

And then we do the same for the description.

$countryDesc = new BlockControl();
$descBlockID = BlockType::getByHandle('content')->getBlockTypeID();
$countryDesc->setBlockTypeID($descBlockID);
$countryDesc->addToPageTypeComposerFormLayoutSet($countrySet);

Custom Attributes

For the population and capital city, we will need to have added attributes to the page type for these. Assuming we have done so, we can add them through the AttributeKeyControl. As with the other options, there are classes we need to call first. In this case, we have to call the Control itself, plus the specific Attribute Key Category we are calling from (in this case, since it's a page type, it's most often going to be a CollectionKey, or a custom category):

use \Concrete\Core\Attribute\Key\CollectionKey as CollectionKey;
use \Concrete\Core\Page\Type\Composer\Control\CollectionAttributeControl as AttributeControl;

The structure is very similar to the Block Control. We get the ID of the Attribute Key, add it into the instantiated Control, and then add that to the set:

$countryCapital = new AttributeControl();
$capitalID = CollectionKey::getByHandle('capital')->getAttributeKeyID();
$countryCapital->setAttributeKeyID($capitalID);