Installation:
composer require app-verk/block-bundle
Enable the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):
AppVerk\BlockBundle\BlockBundle::class => ['all' => true],
First Block Creation:
Create a block class extending AbstractBlock:
// src/Block/FirstBlock.php
namespace App\Block;
use AppVerk\BlockBundle\Block\AbstractBlock;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FirstBlock extends AbstractBlock {
protected function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'title' => 'Default Title',
'template' => '@App/Block/first.html.twig'
]);
}
}
Register as Service:
# config/services.yaml
services:
App\Block\FirstBlock:
public: true
tags: ['block']
First Render:
{{ render_block('App\\Block\\FirstBlock', {
'title': 'Custom Title'
}) }}
Create a block for a reusable sidebar component:
// src/Block/SidebarBlock.php
class SidebarBlock extends AbstractBlock {
protected function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'items' => [],
'template' => '@App/Block/sidebar.html.twig'
]);
}
}
Render with:
{{ render_block('App\\Block\\SidebarBlock', {
'items': [{'title': 'Item 1'}, {'title': 'Item 2'}]
}) }}
Block Development:
AbstractBlock for all custom blocks.configureOptions() to define default settings.execute() for complex logic (e.g., DB queries, API calls).Twig Integration:
{{ render_block('Fully\\Qualified\\Block\\Class') }} for dynamic rendering.{'key': 'value'}).Dependency Injection:
EntityManager, Twig) via constructor.public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
Block Inheritance: Create base blocks for shared functionality:
class BaseContentBlock extends AbstractBlock {
protected function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'title' => '',
'content' => '',
'template' => '@App/Block/base_content.html.twig'
]);
}
}
Dynamic Template Resolution:
Override getTemplate() to resolve templates dynamically:
public function getTemplate() {
return $this->getSetting('template') ?: '@App/Block/default.html.twig';
}
Block Collections: Group blocks in a parent block for modularity:
class PageBlock extends AbstractBlock {
public function execute() {
$blocks = [
'header' => $this->renderBlock('HeaderBlock'),
'content' => $this->renderBlock('ContentBlock'),
];
return $this->renderResponse('@App/Block/page.html.twig', ['blocks' => $blocks]);
}
}
Caching:
Implement isCacheable() and getCacheKey() for static blocks:
public function isCacheable() {
return true;
}
public function getCacheKey() {
return md5($this->getSetting('slug'));
}
Symfony Forms: Use blocks to render forms dynamically:
class LoginBlock extends AbstractBlock {
public function execute() {
$form = $this->createForm(LoginType::class);
return $this->renderResponse('@App/Block/login.html.twig', ['form' => $form->createView()]);
}
}
API Integration:
Fetch remote data in execute():
public function execute() {
$data = json_decode(file_get_contents('https://api.example.com/data'), true);
return $this->renderResponse('@App/Block/api.html.twig', ['data' => $data]);
}
Event Listeners: Trigger events in blocks for cross-cutting concerns:
public function execute() {
$this->dispatchEvent('block.pre_render', $this);
// ...
}
Asset Management: Use blocks to manage CSS/JS:
class AssetsBlock extends AbstractBlock {
public function execute() {
return $this->renderResponse('@App/Block/assets.html.twig', [
'stylesheets' => $this->getSetting('stylesheets', []),
'javascripts' => $this->getSetting('javascripts', [])
]);
}
}
Service Visibility:
public: true for block services will cause render_block() to fail.services.yaml.Template Paths:
@Bundle/Controller/action syntax or full paths.{{ dump(render_block('BlockClass').template) }}.Circular Dependencies:
execute().Options Resolution:
configureOptions() without calling parent::configureOptions() breaks default settings.protected function configureOptions(OptionsResolver $resolver) {
parent::configureOptions($resolver);
// Custom options
}
EntityManager Injection:
EntityManagerInterface in blocks that query the database.Block Output: Dump block settings in Twig for debugging:
{{ dump(render_block('BlockClass').settings) }}
Service Existence: Check if a block is registered as a service:
php bin/console debug:container App\\Block\\BlockClass
Template Errors:
Use {{ render_block('BlockClass', {'template': 'custom_path'}) }} to test template paths.
Event Debugging: Listen for block events to inspect execution flow:
services:
App\EventListener\BlockListener:
tags:
- { name: kernel.event_listener, event: block.pre_render, method: onBlockPreRender }
Default Template:
The bundle does not enforce a default template. Always specify one in configureOptions():
$resolver->setDefaults(['template' => '@App/Block/default.html.twig']);
Options Merging:
Options passed to render_block() override defaults but do not merge by default. Use OptionsResolver to handle merging:
$resolver->setNormalizer('items', function($items, $options) {
return array_merge($options['default_items'] ?? [], $items);
});
Block Aliases: The bundle does not support aliasing block classes out of the box. Use Symfony’s service aliases if needed:
services:
block.hello:
alias: App\Block\HelloBlock
public: true
Custom Block Types:
Extend AbstractBlock to create domain-specific block types (e.g., DatabaseBlock, ApiBlock).
Block Storage:
Persist block configurations to a database by implementing BlockInterface and adding a BlockRepository.
Block Events: Dispatch custom events for pre/post-render hooks:
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class MyBlock extends AbstractBlock {
public function __construct(EventDispatcherInterface $dispatcher) {
$this->dispatcher = $dispatcher;
}
public function execute() {
$this->dispatcher->dispatch(new BlockEvent($this), 'block.my_event');
// ...
}
}
Block Security:
Add security checks in execute():
public function execute() {
if (!$this->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
// ...
How can I help you explore Laravel packages today?