ibexa/experience
Ibexa Experience is the Composer metapackage for the Experience edition of Ibexa DXP. It installs the Symfony-based platform and bundled components for content management, page building, personalization, and customer-facing digital experiences. Requires access to Ibexa's commercial package repositor...
ibexa/experience is a Composer metapackage, not an application. It is the Enterprise feature set of Ibexa DXP: requiring it pulls in ibexa/content, ibexa/admin-ui, ibexa/page-builder, ibexa/scheduler, ibexa/forms, ibexa/personalization, ibexa/customer-center and the related connectors. There is no src/ in this package worth reading — your code lives in your project, and your extension points live in the Ibexa\Contracts\* packages that ship alongside it.
Minimal steps
composer require this into an existing Symfony app. Start from the project skeleton so you get the directory layout, config/packages/ibexa.yaml, and the Symfony Flex recipes:
composer create-project ibexa/experience-skeleton my-dxp
(Or clone the Ibexa DXP project template and adjust composer.json to ibexa/experience for the Enterprise tier.)ibexa/* packages resolve from updates.ibexa.co, which requires the credentials from your Ibexa support portal in auth.json / COMPOSER_AUTH. Without them, composer install 401s and you will waste an hour blaming the network.docker-compose from the skeleton is the fastest path.composer.json → the exact ibexa/* set and version. This tells you which features exist at all.config/packages/ibexa.yaml → ibexa.system.<siteaccess> (languages, repositories, design/theme, var dirs) and ibexa.repositories.config/bundles.php → then read each bundle's DependencyInjection/Configuration.php in vendor/ibexa/<pkg>/src/bundle.vendor/ibexa/contracts/ → the interfaces you are supposed to implement. Code against these, never against Ibexa\Core\* internals.Feature-by-feature extension, not framework hacking. Almost every requirement maps to one of five extension points:
| Need | Pattern |
|---|---|
| New content modelling primitive | Custom Field Type (Contracts + storage converter + ibexa.field_type tag) |
| New editable page component | Page Builder Block (block definition class + template + YAML/attribute config) |
| New scheduled job | Scheduler EventType implementation registered with the scheduler tag |
| New form input | Form field mapper / ibexa_form extension |
| New API surface | GraphQL schema extension (.graphql/YAML mapping + resolver) or REST controller |
Custom block, the canonical example
// src/Block/MapBlock.php
use Ibexa\FieldTypePage\FieldType\Page\Block\Definition\BlockDefinition;
use Ibexa\Contracts\FieldTypePage\FieldType\Page\Block\Definition\BlockAttributeDefinition;
final class MapBlock extends BlockDefinition
{
public function getName(): string { return 'map_block'; }
public function getAttributes(): array
{
return [
'latitude' => new BlockAttributeDefinition('latitude', 'number'),
'longitude' => new BlockAttributeDefinition('longitude', 'number'),
'zoom' => new BlockAttributeDefinition('zoom', 'number'),
];
}
public function getViewTemplate(): string
{
return '@ibexadesign/blocks/map.html.twig';
}
}
Register it where your bundle loads the Page Builder block definitions, then place the Twig template under your theme/design directory — not a global templates/blocks/ path, which is the most common reason "the block renders but my template is ignored."
Template overrides via the design engine. Override vendor templates by matching the design/theme path in ibexa.system.<siteaccess>.design, or by adding your bundle's template dir with a higher-priority design. Never edit vendor/; you will lose it on the next composer update and the metapackage upgrades everything in lockstep.
Configuration over code. Semantic config is the cheapest customisation: content views, pagination, search, personalization scenarios, scheduler defaults, and admin UI menus all have YAML config. Prefer config/packages/*.yaml plus a bundle Extension class over decorating services.
Service decoration for behaviour. For anything not covered by config, decorate the Contract service:
services:
App\Decoration\MyContentService:
decorates: Ibexa\Contracts\Core\Repository\ContentService
arguments: ['@.inner']
This survives minor upgrades far better than subclassing core services.
Migrations, not manual content type clicking. Use the Ibexa Migrations bundle to version content types, roles, policies, and languages in YAML/PHP, then run them in CI:
php bin/console ibexa:migrations:generate --type=content_type
php bin/console ibexa:migrations:migrate
Permissions and limitations. Anything you build that reads content at scale should respect the Repository API's permission layer rather than raw SQL. Custom Limitation types (e.g. "own department") register a LimitationType and a policy provider — do this early; retrofitting permissions into an existing feature is painful.
Workflow and editorial. Editorial review goes through Symfony Workflow configured against the content draft states, not through custom "publish" buttons.
composer why ibexa/<pkg>) and open the issue there. Filing against ibexa/experience gets you nothing.ibexa/* package must share the same minor version. Pin the metapackage ("ibexa/experience": "4.6.*") rather than letting composer update walk the whole DXP forward. A partial upgrade produces cryptic container-compile errors from mismatched Contracts.cache:clear, then the HTTP cache pool, then reindex only if content shape changed. Blocks are cached inside the rendered page; if a block shows stale data, suspect the block cache TTL before your controller.Ibexa\Contracts\*. Autowiring against a Core service works until a minor release moves it. php bin/console debug:container --tag=ibexa.field_type (and the equivalent tags for blocks, form fields, scheduler events) is the fastest way to discover what is actually registered.debug:config ibexa is your friend. Siteaccess-aware semantic config resolves differently per siteaccess and per repository; what you see in YAML is not necessarily what the container ended up with.How can I help you explore Laravel packages today?