ibexa/fieldtype-matrix
Matrix Field Type for Ibexa DXP and Ibexa Open Source adds configurable matrix fields to content types, letting editors define rows and columns of data. Licensed under Ibexa BUL or GPLv2.
Prerequisites: Ibexa DXP (or Ibexa OSS) 4.x with the admin UI, Composer, and an ibexa/* version constraint already pinned. The package tracks Ibexa's release train (the latest release date matches DXP releases), so match it to your platform version rather than pulling dev-master.
composer require ibexa/fieldtype-matrix
Register the bundle. Locate the bundle class under src/bundle/ and add it to config/bundles.php (or app/AppKernel.php on older skeletons). Ibexa's own bundles usually auto-register through the Flex recipe — if the Matrix field type does not appear in the Content Types UI, bundle registration is the first thing to verify.
Where to look first. The README contains only license text, so read the source in this order:
src/lib/FieldType/Matrix.php (or src/FieldType/) — the FieldType implementation: getFieldTypeIdentifier(), validate(), isEmpty(), toHash()/fromHash(), toPersistenceValue()/fromPersistenceValue().Value object under FieldType/Matrix/Value.php — the class you actually touch from PHP and Twig.Type class and its settings schema — content-type-level configuration (columns/labels). This is where the real design decisions live.src/bundle/Resources/config/*.yml — service ids, the ibexa.field_type tag, and the field type identifier string you pass to FieldTypeService.src/bundle/Resources/views/ — reference Twig rendering.tests/ — the most reliable documentation available for the value hash shape and settings arrays.First use case. In admin, go to Content Types → your type → Add field definition → Matrix. Configure the columns/labels in the field definition settings, save, then edit a content item and fill in the cells. Render it with a nested loop. That round trip — define, edit, render — validates the install; everything after that is integration work.
Defining the field in code (fixtures, install scripts, migrations) is a normal field definition plus a settings array. There is no published schema, so create the field once in admin and dump the resulting settings as your template:
$contentType->addFieldDefinition(
$contentTypeService->newFieldDefinitionCreateStruct('specs', 'ibexa_matrix') // confirm identifier in bundle config
);
$fieldDefinition->fieldSettings = [/* copy from admin export or Type::getSettingsSchema() */];
Reading in PHP — always go through the value object, never the raw attribute value:
$value = $content->getFieldValue('specs');
if (!$value->isEmpty()) {
foreach ($value->getMatrix() as $rowKey => $row) {
// $row is typically array<string, mixed> keyed by column identifier
}
}
Check the shipped Value class for the exact accessor; toHash() gives you the serialized form for exports and imports.
Rendering in Twig — treat it as a plain 2D structure and guard for empty:
{% set matrix = ez_field_value(content, 'specs') %}
{% if not matrix.isEmpty() %}
<table>
{% for rowKey, row in matrix.matrix %}
<tr>
{% for columnKey, cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
Writing and updating — always via a draft, then publish. Matrix edits are content changes and go through normal versioning:
$draft = $contentService->createContentDraft($contentInfo);
$draft->getVersionInfo()->getContent()->setFieldValue('specs', $newValue);
$contentService->publishVersion($draft->getVersionInfo());
For bulk import, build the value with the field type's hash form (fromHash()) instead of hand-constructing the value object — the hash is the stable contract between persistence and the public API.
Integration tips
Value shape is an implementation detail of a low-adoption package; isolating it means a version bump touches one class instead of every template.Nameable if you want content names derived from matrix cells.Indexable and decide fulltext vs. identifier. After changing field settings, reindex with bin/console ibexa:reindex.NOASSERTION. GPL-2 is widely treated as incompatible with distribution inside closed-source products, and the BUL branch requires an active DXP subscription. Clear this before the field type becomes load-bearing — license scanners will flag it and some CI policies will fail the build.tests/ is your API reference. When debugging, start from the FieldType test fixtures — they show the exact settings and value arrays the code expects.Resources/public build (or encore/yarn output) matches your admin UI version — version skew between admin UI and field type is a common failure.isEmpty() plus validator configuration decide whether publishing is blocked. If you get a confusing publish error, look at the validator constraints in Resources/config — they are declared there, not in PHP.bin/console debug:container --tag=ibexa.field_type | grep -i matrix to confirm registration; debug:container on the field type service to confirm it compiles; clear cache after config changes; watch var/log for exceptions thrown inside toPersistenceValue.Type, decorate the storage gateway, or swap the form mapper — but you inherit the maintenance burden of a single-vendor package with no downstream ecosystem. Prefer composition (wrap, don't extend) at your application boundary.getFieldValue() in a non-main language before designing a multilingual grid around it.How can I help you explore Laravel packages today?