Advanced: Creating a Custom Attribute Category

We've gone through creating an attribute key and a custom attribute type, and this is likely sufficient for most developers. However, if you have a completely custom object and would like to add support for attributes to this object, you'll have to completely an attribute category for this object, some associated Doctrine ORM entities, and add getAttribute() and setAttribute() support to your object. Let's walk through how that is done.

Package

It's recommended that you create your custom attribute category as part of a package, so that it can be installed and uninstalled easily. If you haven't done much work with packages, you'll want to read the extensive package documentation.

Let's say you've created a package named "Calendar" that is found at packages/calendar, and it contains a custom object named "MyCal\Entity\Calendar\Event" that you'd like to add attributes to. This Event class is also a Doctrine ORM entity. Let's walk through how that's done.

Version Requirements

Note: you must ensure that your package controller sets the minimum concrete5 version to 8.0 or greater.

Setup the Entities

First, you'll want to create an event Key and Value entity, Concrete\Package\Calendar\Entity\Attribute\Key\EventKey and Concrete\Package\Calendar\Entity\Attribute\Value\EventValue. These should be found here

(Note: In this example, the MyCal\Entity\Calendar\Event object's primary key is eventID, which is also a column on this table as well.)

Here we are creating CalendarEventAttributeValues database table that will have a column for eventID, and all the base attribute value fields (like akID for attribute key and avID for the attribute generic value.) If we pass our custom MyCal\Entity\Calendar\Event object to setEvent() Doctrine will automatically translate it to eventID, and vice versa when retrieving the data from the database.

These two entities will store everything about our custom attributes. Any special calendar event keys added will be stored in the CalendarEventAttributeKeys database table, and any attribute values set against the calendar event object will be stored in the CalendarEventAttributeValues table.

Create the Category Controller Class

Now we need to create the category controller class that handles listing and assigning attribute keys and values. The namespace for this class should be Concrete\Package\Calendar\Attribute\Category\EventCategory, and it should be found at packages/calendar/src/Concrete/Attribute/Category/EventCategory.

This class must implement the Concrete\Core\Attribute\Category\CategoryInterface. The easiest way to accomplish this is to extend the Concrete\Core\Attribute\Category\AbstractStandardCategory, and implement the category-specific methods. Here's an example of a finished category.

Let's walk through this. First, we define our createAttributeKey method to return an instance of the attribute key entity we created earlier. Next, we tell the core search indexer what database table we're going to use for our indexing. The next two methods inform the standard search indexer how to get the primary key for each row from the passed object (in this case, an instance of our MyCal\Entity\Calendar\Event object), and how to set up that field. Next, we define the key and value repositories for the entities we created earlier. Finally, we tell our category how to get all attribute values for a given event object, and how to retrieve a specific attribute value object for a given key/event pair.

Register the Attribute Category Driver

Now that you've created your entities and the category controller class, you need to register you attribute category with concrete5. This should happen from within the on_start() method of your package's controller.

First, determine where you want the page to live. In our package, we're creating our page at /dashboard/calendar/attributes. That means creating the controller at Concrete\Package\Calendar\Controller\SinglePage\Dashboard\Calenar\Attributes. It should look like this:

By extending the core DashboardAttributesPageController we don't have to do much at all. We simply need to inform the page as to how to retrieve our various attribute keys, and what generic category object we're using. Since we have given our category the 'event' handle, that's what we use here.

The view layer is similarly simple. In packages/calendar/single_pages/dashboard/calendar/attributes.php we place these contents:

That's it! Now setAttribute() and getAttribute() will work on your custom event object.

Install the Package and Dashboard Pages.

Now it's time to actually install the attribute category and its Dashboard pages. The easiest way to do this with the concrete5 content importer. From within your package's install() method, run this command:

Notice, we make sure to include the on_start() method in our install() method. It does not get run by default during installation, and we'll need to have those categories registered for everything to run smoothly. Also, make sure you have a content.xml file in the root of your package directory. Within it, describe the dashboard pages and attribute category in the CIF format:

Conclusion

Now you should have a fully functional attribute key category that is tied to your custom object, along with the dashboard interfaces to support it.

Advanced: Adding Extra Data to Attribute Category

You can add additional parameters to a particular category of attribute keys. Just add these extra settings to the attribute key entity for the particular category, provide the additional fields in a custom element, and make sure to save the data in the attribute category key create/update requests. For example, concrete5's user attributes provide the following additional fields when creating user keys:

Ensure that the Key Entity has Extra Fields.

The Concrete\Core\Entity\Attribute\Key\UserKey field has extra fields for the storage of this extra data. Here's what they look like in the entity.

The $key object is an instance of Concrete\Core\Entity\Attribute\Key\UserKey. It may be null (if the attribute is being added). (Note: additional settings have been removed to make this easier to read.)

Save the Additional Settings

When creating or updating user keys, we need to save these additional settings. Fortunately, subclassing addFromRequest and updateFromRequest in the Concrete\Core\Attribute\Category\UserCategory object is all that we have to do. Here's how we do it:

That's it! This is an example of a core category, but categories defined in packages work exactly the same way – just make sure to create your element at packages/your_package/elements/attribute/categories/your_category_handle.php.

Installing the Category Through PHP Code

The custom attribute key category is installed when running ContentImporter::importContentFile above, provided that this snippet exists within the content.xml file: