enhavo/block-bundle
Enhavo Block Bundle is a subtree split of the open-source Enhavo CMS, built on the Symfony PHP framework. It provides flexible block components for managing complex data structures through a clean, usable interface. See the main repository for details.
This package is a read-only subtree split of the enhavo/enhavo monorepo. Treat the GitHub README as a pointer, not documentation: almost everything you need is in the source of the main repo and in the bundle's src/.
enhavo/enhavo (or the relevant Enhavo meta-package). Requiring enhavo/block-bundle in isolation is only useful if you are deliberately assembling a subset of the CMS; otherwise you will fight missing sibling bundles (form, routing, asset, template bundles) all day.config/bundles.php if it is not already registered:
Enhavo\Bundle\BlockBundle\EnhavoBlockBundle::class => ['all' => true],
src/EnhavoBlockBundle.php – what services/compiler passes it wires.src/Resources/config/ – service definitions, tags, and any Configuration.php for the enhavo_block tree. This is the real "API surface" for enabling/disabling block types.src/Form/Type/ – block types are Symfony form types; each one tells you the block's fields and, usually, its template/component name.src/Entity/ (or src/Model/) – the block entities, the abstract base block, and how blocks are associated to pages/content nodes.src/Resources/views/ – the render templates and their naming convention.Because the README is essentially a stub, budget an hour for source reading before your first commit. There are no published API docs for this split; the main repository's docs and test suite are your reference.
In Enhavo, a block is almost always a triad:
When adding a custom block, add all three and register the form type as a service. Copy an existing, simple block (text or picture) end-to-end rather than starting from scratch — the base classes carry required methods you will otherwise miss.
// src/Entity/PromoBlock.php
class PromoBlock extends AbstractBlock
{
private ?string $headline = null;
private ?string $ctaUrl = null;
// getters/setters
}
// src/Form/Type/PromoBlockType.php
class PromoBlockType extends AbstractBlockType
{
public function getComponent(): string { return 'promo-block'; }
public function getTemplate(): string { return '@App/blocks/promo.html.twig'; }
public function getLabel(): string { return 'Promo block'; }
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('headline', TextType::class)
->add('ctaUrl', UrlType::class);
}
}
Register the type and, if the bundle uses a tagged locator or a config map for blocks, add it there too (check Configuration.php — some Enhavo versions enumerate block types in enhavo_block.blocks). A block type that is registered as a service but absent from the config map will simply not appear in the admin picker.
Pages and other "blockable" entities in Enhavo typically implement a block-node interface and use a trait for getBlocks() / addBlock() with an ordered, one-to-many relation. If you are creating your own block-holding entity, reuse that interface and trait rather than inventing your own collection — the admin form types and renderer expect it.
Rendering usually goes through the bundle's renderer service or a Twig helper, resolving the block to its template:
{% for block in page.blocks %}
{{ block_render(block) }}
{% endfor %}
Exact helper naming varies by version; check the Twig extension in src/Twig/ before assuming. For one-off layouts, including the template directly ({% include block.template %}) works but bypasses the renderer's resolution, caching, and nested-block handling — prefer the renderer unless you have a reason not to.
Two non-invasive extension points cover most day-to-day needs:
templates/bundles/EnhavoBlockBundle/...) to restyle an existing block without touching vendor code.FormTypeExtension to add a field to an existing block type instead of forking the entity.EnhavoBlockBundle; changes belong in enhavo/enhavo. The split repo history is regenerated, so local patches there will be lost.enhavo/block-bundle and the sibling bundles manifests as missing services or "class not found" in the DI container, not as an obvious version error.getTemplate() is the usual culprit. Use bin/console debug:twig and enable Twig's template hints in the profiler to see which path was actually attempted.enhavo_block configuration. When a block "doesn't exist", check both registration mechanisms.getComponent() value must correspond to a registered frontend component. A block that works in the frontend can still be broken in the editor if the component name is wrong.bin/console debug:container --tag=<block tag> # is my block type registered?
bin/console config:dump-reference enhavo_block # what options exist?
bin/console doctrine:mapping:info # discriminator sanity check
How can I help you explore Laravel packages today?