Installation Add the bundle to your project via Composer:
composer require anh/content-block-bundle
Enable it in config/bundles.php:
Anh\ContentBlockBundle\AnhContentBlockBundle::class => ['all' => true],
Database & Admin Setup Run migrations (if any) and clear cache:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
php bin/console cache:clear
Register the bundle in config/packages/anh_admin.yaml (if using anh/admin-bundle):
anh_admin:
resources:
AnhContentBlockBundle:
title: Content Blocks
First Use Case Create a content block type via the admin interface (if available) or manually via Doctrine:
use Anh\ContentBlockBundle\Entity\ContentBlock;
use Anh\ContentBlockBundle\Entity\ContentBlockType;
// Create a new block type
$blockType = new ContentBlockType();
$blockType->setName('Hero Section');
$blockType->setDescription('A hero section with title and image');
$blockType->setFields([
'title' => ['type' => 'text', 'label' => 'Title'],
'image' => ['type' => 'image', 'label' => 'Background Image'],
]);
$entityManager->persist($blockType);
$entityManager->flush();
// Create a block instance
$block = new ContentBlock();
$block->setType($blockType);
$block->setData(['title' => 'Welcome', 'image' => '/path/to/image.jpg']);
$entityManager->persist($block);
$entityManager->flush();
Dynamic Content Blocks Use the bundle to create reusable, configurable blocks (e.g., hero sections, testimonials, call-to-actions) that can be managed via the admin panel.
// Fetch blocks by type in a controller
$blocks = $entityManager->getRepository(ContentBlock::class)
->findBy(['type' => $heroBlockType]);
Embedding in Templates Render blocks in Twig using a custom loop:
{% for block in blocks %}
{% if block.type.name == 'Hero' %}
<section class="hero">
<h1>{{ block.data.title }}</h1>
<img src="{{ block.data.image }}" alt="{{ block.data.title }}">
</section>
{% endif %}
{% endfor %}
Field Validation & Serialization Validate block data before saving:
use Anh\ContentBlockBundle\Validator\Constraints as ContentBlockAssert;
$block->addValidatorConstraint(
new ContentBlockAssert\RequiredField('title')
);
Integration with Doctrine Resource Bundle
Extend anh/doctrine-resource-bundle to manage blocks as resources:
# config/packages/anh_doctrine_resource.yaml
anh_doctrine_resource:
resources:
Anh\ContentBlockBundle\Entity\ContentBlock:
title: Content Blocks
list: AnhContentBlockBundle:ContentBlock/list.html.twig
show: AnhContentBlockBundle:ContentBlock/show.html.twig
Anh\ContentBlockBundle\Field\FieldTypeInterface.$blocks = Cache::remember("blocks_{$typeId}", 3600, function () use ($typeId) {
return $entityManager->getRepository(ContentBlock::class)->findBy(['type' => $typeId]);
});
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$normalizer = new ObjectNormalizer();
$blockData = $normalizer->normalize($block, null, [
'groups' => ['block_data'],
]);
Missing Dependencies
Ensure anh/doctrine-resource-bundle and anh/admin-bundle are installed and configured. The bundle relies on these for CRUD operations and admin integration.
composer require anh/doctrine-resource-bundle anh/admin-bundle
Field Data Serialization
The data field in ContentBlock is stored as a JSON string. Ensure your field types can be serialized/deserialized properly. Use json_encode()/json_decode() or a library like symfony/serializer for complex data.
Admin Panel Limitations The admin panel (if available) may not support all field types out of the box. Customize the admin templates or override the field rendering logic in your theme.
Migration Issues
If you modify ContentBlockType fields, ensure you handle backward compatibility. Consider using a migration to update existing block data:
// Example: Adding a new field to existing blocks
$blocks = $entityManager->getRepository(ContentBlock::class)->findAll();
foreach ($blocks as $block) {
$block->setData(array_merge($block->getData(), ['new_field' => null]));
$entityManager->persist($block);
}
$entityManager->flush();
Validate Block Data
Use the ContentBlockAssert constraints to validate data before saving:
$validator = $this->get('validator');
$errors = $validator->validate($block);
if (count($errors) > 0) {
throw new \RuntimeException('Block validation failed: ' . (string) $errors);
}
Check Field Types
If a field isn’t rendering or saving correctly, verify its type in ContentBlockType:
$blockType->getFields(); // Debug the field definitions
Clear Cache After modifying bundle configurations or templates, clear the cache:
php bin/console cache:clear
Custom Field Types
Create a new field type by implementing FieldTypeInterface:
namespace App\ContentBlock\Field;
use Anh\ContentBlockBundle\Field\FieldTypeInterface;
class CustomFieldType implements FieldTypeInterface
{
public function getType(): string
{
return 'custom';
}
public function render($value, array $options = [])
{
// Custom rendering logic
}
}
Register it in your bundle’s services:
services:
App\ContentBlock\Field\CustomFieldType:
tags: { name: anh_content_block.field_type }
Override Templates
Override the default templates (e.g., list.html.twig, show.html.twig) in your theme to customize the admin interface:
templates/
AnhContentBlockBundle/
ContentBlock/
list.html.twig
show.html.twig
Event Listeners
Listen to block events (e.g., prePersist, preUpdate) to add custom logic:
use Anh\ContentBlockBundle\Event\ContentBlockEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ContentBlockSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
ContentBlockEvents::PRE_PERSIST => 'onPrePersist',
];
}
public function onPrePersist(ContentBlockEvent $event)
{
$block = $event->getBlock();
// Custom logic
}
}
Register the subscriber in services.yaml:
services:
App\EventSubscriber\ContentBlockSubscriber:
tags: { name: kernel.event_subscriber }
Query Builder Extensions
Extend the ContentBlockRepository to add custom query methods:
namespace App\Repository;
use Anh\ContentBlockBundle\Entity\ContentBlock;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
class ContentBlockRepository extends ServiceEntityRepository
{
public function findByTypeAndPublished(string $type, bool $published = true)
{
return $this->createQueryBuilder('cb')
->andWhere('cb.type = :type')
->andWhere('cb.published = :published')
->setParameter('type', $type)
->setParameter('published', $published)
->getQuery()
->getResult();
}
}
How can I help you explore Laravel packages today?