Programmatically Creating Express Objects

Note: This requires Concrete CMS 8.1.0 or greater.

Until now, when working with Express objects we've focused on creating them through the Dashboard, and working with them once the data objects themselves and their entries were in place. This is a popular use case, since one of Express's biggest features is that its easy to create objects without knowing how to code. However, if you're creating a Concrete package and would like to add an Express Object as part of that package, you're going to want to create one programmatically. Here's how that's possible.

Package Prerequisite

Note: this documentation assumes you'll be adding an Express Object via the install() method of a Package. If you aren't familiar with creating Concrete packages you should check out that documentation first.

The Express Object Builder

First, you're going to retrieve an instance of the Express Object Builder. This can be done by importing Express facade into your package's controller.php file:

use Express;

Then, call Express::buildObject() with the proper parameters:

  1. Object Handle
  2. Object Plural Handle
  3. Object Name
  4. The package object.

Let's say we're going to create our Marina object from a previous example programmatically. Our install() method will look like this:

public function install()
{
    $pkg = parent::install();
    $object = Express::buildObject('marina', 'marinas', 'Marina', $pkg);
}

At this point, $object is an instance of the Concrete\Core\Express\ObjectBuilder object. It is not yet saved. You'll want to add attributes and associations to it before you save it.

Adding Attributes to the Object

Let's add a name and an address attribute to our marina object:

$object->addAttribute('text', 'Name', 'marina_name');
$object->addAttribute('address', 'Address', 'marina_address');

In this example, we're adding two custom attributes – name and address. The attribute type is the first parameter, the name is the second parameter, and the handle is the third. An optional fourth parameter is a settings object corresponding to the Concrete\Core\Entity\Attribute\Key\Settings\Settings object for that type. This lets you customize certain aspects of certain attribute types. For example, let's say we want our address attribute to only support "United States" and the "United Kingdom" as countries (because those are the only places our marinas operate.) Here's how we can handle that:

$settings = new \Concrete\Core\Entity\Attribute\Key\Settings\AddressSettings();
$settings->setCustomCountries(array("US","UK"));
$settings->setHasCustomCountries(true);
$settings->setDefaultCountry("UK");
$object->addAttribute('address', 'Address', 'marina_address', $settings);

That's it! Now we've got a custom attribute added with custom settings.

Saving the Object

Once we've built the object and have added the attributes we'd like to add, it's a simple matter to save the object.

$object = $object->save();

Saving the object builder results in the final Concrete\Core\Entity\Express\Entity object being returned. All attributes are also created.

Adding Associations to the Object

You can add associations in a similar way. Let's associate a marina object to a boat object. The marina object has many boats:

$marina = Express::buildObject('marina', 'marinas', 'Marina', $pkg);
$boat = Express::buildObject('boat', 'boats', 'Boat', $pkg);

$builder = $marina->buildAssociation();
$builder->addOneToMany($boat);
$boat = $builder->save();
$marina = $marina->getEntity();

Now you have the $boat and $marina express entity objects, with the Marina object containing a One-To-Many association to the Boat object, and the Boat object containing a Many-To-One association to the Marina. Since you haven't provided a custom name for the properties, the One-To-Many association is named 'boats' and the Many-to-One is named 'marina'.

You can add more exotic associations as well. In this example, we're creating skill, project and developer express objects and relating them in some exciting ways:

$project = Express::buildObject('project', 'projects', 'Project');
$skill = Express::buildObject('skill', 'skills', 'Skill');
$developer = Express::buildObject('developer', 'developers', 'Developer');

$project->buildAssociation()->addManyToMany($skill)->save();
$skill->buildAssociation()->addOneToOne($developer, 'best_developer')->save();

The projects and skills are related via a Many-To-Many, with an additional One-To-One association from the skill Object to the Developer object.

Creating an Express Object Form

It's easy to programmatically create an Express form for your objects as well. Here we'll create an Express object, some attributes, and create a form and field set to go along with them:

$student = Express::buildObject('student', 'students', 'Student', $pkg);
$student->addAttribute('text', 'First Name', 'first_name');
$student->addAttribute('text', 'Last Name', 'last_name');
$student->addAttribute('textarea', 'Bio', 'bio');
$studentEntity = $student->save();

$form = $student->buildForm('Form');
$form->addFieldset('Basics')
    ->addAttributeKeyControl('first_name')
    ->addAttributeKeyControl('last_name')
    ->addTextControl('', 'This is just some basic explanatory text.')
    ->addAttributeKeyControl('bio');
$form = $form->save();

Now that you have the form, you'll want to ensure that your Express object uses it as a default form for viewing and editing – otherwise you'll get an error.

$entityManager = $student->getEntityManager();
$studentEntity->setDefaultViewForm($form);
$studentEntity->setDefaultEditForm($form);
$entityManager->persist($studentEntity);
$entityManager->flush();

(Note: in Concrete 8.3.1 and later, this is not necessary - the default view/edit form will be set automatically when building your first form through the form builder.)