From f708502b581773d4f226926f5d693971a406124c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Noco=C5=84?= Date: Thu, 12 Feb 2026 16:59:36 +0100 Subject: [PATCH 1/3] Documented adding custom fields to CDP export --- .../User/DateOfBirthUserItemProcessor.php | 57 +++++++++++++++++++ composer.json | 3 +- docs/cdp/cdp_activation/cdp_data_export.md | 6 ++ docs/cdp/cdp_data_customization.md | 51 +++++++++++++++-- 4 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php diff --git a/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php b/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php new file mode 100644 index 0000000000..3b2dbf224c --- /dev/null +++ b/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php @@ -0,0 +1,57 @@ +dateOfBirthFieldIdentifier = $dateOfBirthFieldIdentifier; + + parent::__construct($userFieldTypeIdentifier); + } + + protected function doProcess(array $processedItemData, Content $userContent): array + { + $userField = $this->getUserField($userContent); + + if (null === $userField) { + throw new InvalidArgumentException('$userContent', 'User content does not contain user field'); + } + + $dateOfBirthField = $userContent->getField($this->dateOfBirthFieldIdentifier); + + if (null === $dateOfBirthField || !$dateOfBirthField->value instanceof DateValue) { + return $processedItemData; + } + + /** @var \Ibexa\Core\FieldType\Date\Value $dateValue */ + $dateValue = $dateOfBirthField->value; + + if (null === $dateValue->date) { + return $processedItemData; + } + + return array_merge( + $processedItemData, + [ + 'date_of_birth' => $dateValue->date->format('Y-m-d'), + ] + ); + } +} diff --git a/composer.json b/composer.json index 07fc0600c9..690d297d96 100644 --- a/composer.json +++ b/composer.json @@ -75,7 +75,8 @@ "ibexa/messenger": "~4.6.x-dev", "ibexa/collaboration": "~4.6.x-dev", "ibexa/share": "~4.6.x-dev", - "ibexa/phpstan": "~4.6.-dev" + "ibexa/phpstan": "~4.6.-dev", + "ibexa/cdp": "~4.6.x-dev" }, "scripts": { "fix-cs": "php-cs-fixer fix --config=.php-cs-fixer.php -v --show-progress=dots", diff --git a/docs/cdp/cdp_activation/cdp_data_export.md b/docs/cdp/cdp_activation/cdp_data_export.md index 6b4b417e53..d2b13ba2a1 100644 --- a/docs/cdp/cdp_activation/cdp_data_export.md +++ b/docs/cdp/cdp_activation/cdp_data_export.md @@ -45,6 +45,9 @@ There are two versions of this command `--draft/--no-draft`. The first one is used to send the test user data to the Data Manager. If it passes a validation test in the **Activation** section, use the latter one to send a full version. +You can extend exported user data with custom fields from your user content, such as date of birth, preferences, or other profile information. +For more information, see [Data customization](../cdp_data_customization.md#export-additional-user-data). + Next, go back to [[= product_name_cdp =]] and select **Validate & download**. If the file passes, you can see a confirmation message. Now, you can go to the **File mapping** section. @@ -63,6 +66,9 @@ If you make any alterations, select the **Parse File** to generate columns with In the **Transform & Map** section you transform data and map it to a schema. At this point, you can map **email** to **email** and **id** to **integer** fields to get custom columns. +If you have [extended user data export with custom fields](cdp_data_customization.md#export-additional-user-data), those fields appear as additional columns in this section. +Make sure to add them to your schema in Raptor so they can be used for segmentation and personalization. + Next, select **Create schema based on the downloaded columns**. It moves you to Schema Creator. There, choose **PersonalData** as a parent and name the schema. diff --git a/docs/cdp/cdp_data_customization.md b/docs/cdp/cdp_data_customization.md index e2f8259135..fc6473395a 100644 --- a/docs/cdp/cdp_data_customization.md +++ b/docs/cdp/cdp_data_customization.md @@ -9,10 +9,51 @@ You can customize content and product data exported to CDP and you can control w By default, custom field types have basic export functionality. It casts their `Value` object to string, thanks to `\Stringable` implementation. ​ +## Export additional user data + +You can extend user data exported to CDP by attaching custom information from user content fields, such as date of birth, preferences, or custom profile data. +Use it for for advanced customer segmentation and personalization in marketing campaigns. + +To add custom data to user exports, create a class that extends [`\Ibexa\Contracts\Cdp\Export\User\AbstractUserItemProcessor`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Cdp-Export-User-AbstractUserItemProcessor.html) and implement the `doProcess()` method. +The base class handles user field validation and provides helper methods for working with user content. + +The following example adds a custom date of birth field to the exported data: + +``` php +[[= include_file('code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php') =]] +``` + +Register your processor as a Symfony service and tag it with `ibexa.cdp.export.user.item_processor`: + +``` yaml + App\Export\User\DateOfBirthUserItemProcessor: + parent: Ibexa\Contracts\Cdp\Export\User\AbstractUserItemProcessor + arguments: + $dateOfBirthFieldIdentifier: 'date_of_birth' + tags: + - { name: 'ibexa.cdp.export.user.item_processor', priority: 100 } +``` + +The `priority` parameter controls the order of execution when multiple processors are registered. +Higher priority values run first. +Your custom processor merges data with `$processedItemData` from previous processors, allowing you to chain multiple processors together. + +The exported user data includes your custom fields: + +```json +{ + "date_of_birth": "2000-01-01", + "id": 1, + "login": "example", + "email": "example@example.org", + "name": "John Doe", +} +``` + ## Export field types ​ Field types are exported with metadata, for example, ID, field definition name, type, or value. -You can also provide your own `\Ibexa\Contracts\Cdp\Export\Content\FieldProcessorInterface` instance to extend metadata. +You can also provide your own [`\Ibexa\Contracts\Cdp\Export\Content\FieldProcessorInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Cdp-Export-Content-FieldProcessorInterface.html) instance to extend metadata. The provided implementation has to be defined as a service and tagged with `ibexa.cdp.export.content.field_processor`. Additionally, you can specify `priority` to override the default behavior. All system Field Processors use `-100` priority, and any higher priority value overrides them. @@ -47,7 +88,7 @@ You can only use scalar values. ​ You can provide your own CDP export functionality by using one of the system Field Processors: -#### `\Ibexa\Cdp\Export\Content\FieldProcessor\SkippingFieldProcessor`. +#### `\Ibexa\Cdp\Export\Content\FieldProcessor\SkippingFieldProcessor` ​ It results in the field type being excluded from the exported payload. To avoid adding the field type data to the payload, register a new service as follows: @@ -64,7 +105,7 @@ custom_fieldtype.cdp.export.field_processor: ​ ## Export field type values ​ -To customize export of field type values, provide your own `\Ibexa\Contracts\Cdp\Export\Content\FieldValueProcessorInterface` instance. +To customize export of field type values, provide your own [`\Ibexa\Contracts\Cdp\Export\Content\FieldValueProcessorInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Cdp-Export-Content-FieldValueProcessorInterface.html) instance. New implementation has to be registered as a service manually or by using autoconfiguration. The service has to use the tag `ibexa.cdp.export.content.field_value_processor`. You can also provide `priority` property to override other Field Value Processors. @@ -85,7 +126,7 @@ This Processor is a default one, as long as no other Processor with higher prior ​ #### `\Ibexa\Cdp\Export\Content\FieldValueProcessor\JsonHashFieldValueProcessor` ​ -This Processor generates JSON data from hash representation of the field type (it uses `\Ibexa\Contracts\Core\FieldType\FieldType::toHash` method). +This Processor generates JSON data from hash representation of the field type (it uses [`\Ibexa\Contracts\Core\FieldType\FieldType::toHash`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-FieldType-FieldType.html#method_toHash) method). !!! caution @@ -101,4 +142,4 @@ custom_fieldtype.cdp.export.field_processor: $fieldTypeIdentifier: custom_fieldtype tags: - { name: 'ibexa.cdp.export.content.field_value_processor', priority: 0 } -``` \ No newline at end of file +``` From d32ded9b3f797b75172ac64c8a83c7b4edd85ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Noco=C5=84?= Date: Fri, 13 Feb 2026 09:46:06 +0100 Subject: [PATCH 2/3] Reworded a bit --- docs/cdp/cdp_data_customization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cdp/cdp_data_customization.md b/docs/cdp/cdp_data_customization.md index fc6473395a..fba57b1ae5 100644 --- a/docs/cdp/cdp_data_customization.md +++ b/docs/cdp/cdp_data_customization.md @@ -5,14 +5,14 @@ edition: experience # Data customization ​ -You can customize content and product data exported to CDP and you can control what field type information you want to export. +You can customize user, content, and product data exported to CDP and you can control what field type information you want to export. By default, custom field types have basic export functionality. It casts their `Value` object to string, thanks to `\Stringable` implementation. ​ ## Export additional user data -You can extend user data exported to CDP by attaching custom information from user content fields, such as date of birth, preferences, or custom profile data. -Use it for for advanced customer segmentation and personalization in marketing campaigns. +You can extend user data exported to CDP by attaching custom information, for example user content fields or user preferences. +Use it for advanced customer segmentation and personalization in marketing campaigns. To add custom data to user exports, create a class that extends [`\Ibexa\Contracts\Cdp\Export\User\AbstractUserItemProcessor`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Cdp-Export-User-AbstractUserItemProcessor.html) and implement the `doProcess()` method. The base class handles user field validation and provides helper methods for working with user content. From fa3d2681854ec497cc3ba4352a369ce3a2a0c21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Noco=C5=84?= Date: Fri, 13 Feb 2026 14:32:39 +0100 Subject: [PATCH 3/3] Always include the field --- .../User/DateOfBirthUserItemProcessor.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php b/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php index 3b2dbf224c..6d48cfd4cd 100644 --- a/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php +++ b/code_samples/cdp/date_of_birth_export/src/Export/User/DateOfBirthUserItemProcessor.php @@ -34,23 +34,20 @@ protected function doProcess(array $processedItemData, Content $userContent): ar throw new InvalidArgumentException('$userContent', 'User content does not contain user field'); } + $dateOfBirth = ''; $dateOfBirthField = $userContent->getField($this->dateOfBirthFieldIdentifier); - if (null === $dateOfBirthField || !$dateOfBirthField->value instanceof DateValue) { - return $processedItemData; - } - - /** @var \Ibexa\Core\FieldType\Date\Value $dateValue */ - $dateValue = $dateOfBirthField->value; - - if (null === $dateValue->date) { - return $processedItemData; + if ($dateOfBirthField !== null + && $dateOfBirthField->value instanceof DateValue + && $dateOfBirthField->value->date !== null + ) { + $dateOfBirth = $dateOfBirthField->value->date->format('Y-m-d'); } return array_merge( $processedItemData, [ - 'date_of_birth' => $dateValue->date->format('Y-m-d'), + 'date_of_birth' => $dateOfBirth, ] ); }