Skip to main content
Version: 2026.2

Activities

Customer activities are a key part of CMF. An activity can be almost anything a customer does, from something as simple as a login to something as complex as an order or a booking. Use CMF to store customer-related activity data and reuse it, for example for segment building or as information for the customer service team.

CMF stores activities together in a generic JSON store called the ActivityStore, backed by default by a MariaDB table (plugin_cmf_activities).

Using Pimcore Data Objects and Other Data Entities as Activities

Besides the generic activity store, you can also use Pimcore data objects (or other data entities and sources) as activities. They only need to implement ActivityInterface (Pimcore data objects extend AbstractObjectActivity).

These activities keep their own data persistence, but CMF also stores them in the generic ActivityStore, which saves every activity in a standardized way and represents a full history of customer activities.

For example, if an order is later cancelled, the cancellation can become a new activity in the ActivityStore, giving you both the original order activity and a separate cancellation activity. Whether you need that level of detail depends on your use case. You can also delete or update the original activity in the ActivityStore if necessary.

Generic Activities

As mentioned above, activities can use their own persistence in addition to the ActivityStore, but they do not have to. You can also save activities only in the ActivityStore. The framework includes a generic activity implementation for this case: CustomerManagementFrameworkBundle\Model\Activity\GenericActivity.

The REST API uses this activity as the default when no implementationClass is specified. It handles a nested, associative array of data that is stored only in the ActivityStore.

That said, GenericActivity is not the only option for activities without their own persistence. Implementing a dedicated activity class, for example to track logins, is often worthwhile. See implementations like MailchimpStatusChangeActivity or TrackedUrlActivity for reference.

ActivityStore

The ActivityStore saves and reads activity data to and from the database. By default, it uses a MariaDB implementation. Activity attributes only need to form a valid JSON object; there is no limit on the number of attributes or their nesting depth. The MariaDB implementation stores the table using dynamic columns, which allows SQL queries against the attribute fields even though they are all stored together in a blob field. See the MariaDB dynamic columns documentation for details.

Implement ActivityStoreInterface to create your own activity storage backend, for example on MongoDB.

ActivityManager

The ActivityManager sits on top of the ActivityStore and handles activities within CMF. It is a relatively lightweight component that manages the process of tracking activities; most of the work happens in the ActivityStore itself. The ActivityManager just coordinates that process independently of the concrete ActivityStore implementation.

Tracking an Activity

<?php
// $activity needs to implement ActivityInterface
\Pimcore::getContainer()->get('cmf.activity_manager')->trackActivity($activity);

ActivityView

CMF includes a list view for displaying activities, added as a tab on the customer object.

ActivityViewActivityViewActivityView

Control How Activities Are Displayed in the ActivityView

Implement these three methods of ActivityInterface to control how activities appear in the ActivityView:

<?php
/**
* Returns an associative array with data to show, in addition to the type and activity date, in the
* ActivityView overview list.
*
* @param ActivityStoreEntryInterface $entry
*
* @return array
*/
public static function cmfGetOverviewData(ActivityStoreEntryInterface $entry);

/**
* Returns an associative array with data to show on the ActivityView detail page.
*
* @param ActivityStoreEntryInterface $entry
*
* @return array
*/
public static function cmfGetDetailviewData(ActivityStoreEntryInterface $entry);

/**
* Optional: returns a template file to use for the ActivityView detail page, so you can implement a fully
* custom detail page per activity type.
*
* @param ActivityStoreEntryInterface $entry
*
* @return string|bool
*/
public static function cmfGetDetailviewTemplate(ActivityStoreEntryInterface $entry);
<?php

public static function cmfGetDetailviewData(ActivityStoreEntryInterface $entry)
{
/**
* @var ActivityViewInterface $activityView
*/
$activityView = \Pimcore::getContainer()->get('cmf.activity_view');

return ['data object' => $activityView->createPimcoreElementLink(3, 'object')];
}
<?php

public static function cmfGetOverviewData(ActivityStoreEntryInterface $entry)
{
/**
* @var ActivityViewInterface $activityView
*/
$activityView = \Pimcore::getContainer()->get('cmf.activity_view');

return ['asset' => $activityView->createPimcoreElementLink(3, 'asset', 'btn btn-xs btn-success', 'open asset')];
}

ActivityStore Entry Metadata

You can store metadata items on activity store entries. This is useful, for example, for a segment builder that needs to record whether it already processed an activity.

Examples

<?php

$entry = $activityStore->getEntryById(465374);

// get all metadata items
$data = $entry->getMetadata();

// get a single metadata item
$data = $entry->getMetadataItem('BookingSegmentBuilder state');

// overwrite a single metadata item
$entry->setMetadataItem('BookingSegmentBuilder state', 'processed');
$entry->save();

// overwrite all metadata items (drops all other metadata keys)
$entry->setMetadata([
'BookingSegmentBuilder state' => 'processed',
]);
$entry->save();