agence-adeliom/easy-block-bundle
Symfony bundle adding a basic block component for EasyAdmin: manage blocks via a CRUD interface and render them in Twig. Supports Symfony 6.4/7.x (PHP 8.2+), with older branches for Symfony 5.4/6.x and 4.4/5.x.
Installation:
composer require agence-adeliom/easy-block-bundle
Ensure your composer.json includes the GitHub recipes endpoint for Symfony Flex:
"extra": {
"symfony": {
"endpoint": [
"https://api.github.com/repos/agence-adeliom/symfony-recipes/contents/index.json?ref=flex/main",
"flex://defaults"
],
"allow-contrib": true
}
}
Database Migration: Run Doctrine migrations to create the required tables:
php bin/console doctrine:migration:diff
php bin/console doctrine:migration:migrate
Register the Bundle:
Add AgenceAdeliom\EasyBlockBundle\EasyBlockBundle to your bundles.php (Symfony 5+).
First Use Case: Create a block via EasyAdmin CRUD:
/admin/block (or your configured EasyAdmin route).{{ render_block('block-slug') }}
Block Management via EasyAdmin:
Block CRUD to customize fields or validation:
# config/easyadmin.yaml
easy_admin:
entities:
AgenceAdeliom\EasyBlockBundle\Entity\Block:
class: AgenceAdeliom\EasyBlockBundle\Entity\Block
list:
fields: ['title', 'slug', 'isPublished', 'createdAt']
form:
fields: ['title', 'slug', 'content', 'isPublished']
BlockRepository to fetch blocks programmatically:
$block = $blockRepository->findOneBy(['slug' => 'hero-banner']);
Rendering Blocks in Twig:
{{ render_block('hero-banner') }}
{% if block is defined %}
{{ render_block(block.slug) }}
{% endif %}
{{ render_block('dynamic-block', {'user': app.user}) }}
Dynamic Block Assignment:
Page entity):
// src/Entity/Page.php
#[ORM\Column]
private ?string $heroBlockSlug;
{% if page.heroBlockSlug %}
{{ render_block(page.heroBlockSlug) }}
{% endif %}
Integration with EasyAdmin Dashboard:
{# templates/easyadmin/layout.html.twig #}
<div class="dashboard-block">
{{ render_block('admin-dashboard-hero') }}
</div>
Block Types:
Extend the Block entity to support custom types (e.g., ImageBlock, VideoBlock):
// src/Entity/CustomBlock.php
class CustomBlock extends Block
{
#[ORM\Column]
private ?string $customField;
// Add getters/setters
}
Register the new entity in EasyAdmin.
Caching: Cache rendered blocks for performance:
$renderedBlock = $this->get('easy_block.renderer')->render('block-slug');
$this->get('cache')->save('block:block-slug', $renderedBlock, 'blocks', 3600);
Event Listeners:
Hook into block lifecycle events (e.g., BlockEvents::PRE_RENDER):
// src/EventListener/BlockListener.php
class BlockListener
{
public function onPreRender(PreRenderEvent $event): void
{
$event->setContent($this->modifyContent($event->getContent()));
}
}
Register the listener in services.yaml:
services:
App\EventListener\BlockListener:
tags:
- { name: kernel.event_listener, event: easy_block.pre_render }
Slug Uniqueness:
Block entity validation if needed:
#[Assert\Unique(entityClass: Block::class, message: 'Slug already exists')]
private ?string $slug;
Twig Function Not Found:
config/packages/easy_block.yaml for:
twig:
twig:
extensions:
- AgenceAdeliom\EasyBlockBundle\Twig\EasyBlockExtension
Permission Issues:
# config/easyadmin.yaml
AgenceAdeliom\EasyBlockBundle\Entity\Block:
permissions: ['ROLE_ADMIN']
Content Security:
{{ render_block('user-generated', {'sanitize': true}) }}
Check Block Existence:
$block = $blockRepository->findOneBy(['slug' => 'missing-slug']);
if (!$block) {
throw new \RuntimeException("Block 'missing-slug' not found");
}
Enable Debug Mode:
Set EASY_BLOCK_DEBUG: true in .env to log Twig rendering errors.
Clear Cache: After extending the bundle, clear the cache:
php bin/console cache:clear
Custom Renderers:
Override the default renderer by implementing BlockRendererInterface:
class CustomRenderer implements BlockRendererInterface
{
public function render(Block $block, array $context = []): string
{
// Custom logic
return $this->twig->render('custom_block.html.twig', [
'block' => $block,
'context' => $context,
]);
}
}
Register it in services.yaml:
services:
App\Renderer\CustomRenderer:
tags:
- { name: easy_block.renderer }
Block Storage:
Extend the BlockStorageInterface to support non-Doctrine storage (e.g., Redis):
class RedisBlockStorage implements BlockStorageInterface
{
public function find(string $slug): ?Block
{
// Custom Redis logic
}
}
Bind it in services.yaml:
services:
AgenceAdeliom\EasyBlockBundle\Storage\BlockStorageInterface: '@App\Storage\RedisBlockStorage'
Twig Filters: Add custom Twig filters for block content:
// src/Twig/BlockFilterExtension.php
class BlockFilterExtension extends \Twig\Extension\AbstractExtension
{
public function getFilters(): array
{
return [
new \Twig\TwigFilter('markdown', [$this, 'convertToMarkdown']),
];
}
public function convertToMarkdown(string $content): string
{
return (new Parser())->parse($content);
}
}
Register the extension in services.yaml:
services:
App\Twig\BlockFilterExtension:
tags: ['twig.extension']
How can I help you explore Laravel packages today?