Installation
Run composer require comur/content-admin-bundle in your Symfony project. Ensure ComurImageBundle is also installed (composer require comur/image-bundle) for image editing support.
Enable the Bundle
Add Comur\ContentAdminBundle\ComurContentAdminBundle::class to config/bundles.php.
First Use Case: Inline Template Editing
templates/base.html.twig) with a placeholder for dynamic content:
<div class="editable-content" data-content-id="homepage_hero">
{{ content_placeholder('homepage_hero') }}
</div>
Generate Admin CRUD
Use Symfony’s make:crud or manually create an admin entity (e.g., ContentBlock) with fields like id, name, and content (text/HTML).
Define Editable Areas
Use {{ content_placeholder('unique_key') }} in Twig templates to mark editable regions. The unique_key must match your admin entity’s field or a custom mapping.
Admin Configuration
Extend the bundle’s configuration in config/packages/comur_content_admin.yaml:
comur_content_admin:
editable_areas:
homepage_hero:
entity: App\Entity\ContentBlock
field: content
ckeditor_config: ~
entity: The Symfony entity managing the content.field: The entity field storing the HTML/content (e.g., content).ckeditor_config: Optional CKEditor settings (e.g., toolbars, plugins).Integrate with Admin Panels
Use SonataAdminBundle or EasyAdminBundle to expose your ContentBlock entity:
// src/Admin/ContentBlockAdmin.php
namespace App\Admin;
use Comur\ContentAdminBundle\Admin\Type\CKEditorType;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
class ContentBlockAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('content', CKEditorType::class, [
'config' => ['toolbar' => ['Bold', 'Italic', 'Image']],
]);
}
}
Frontend Rendering
Ensure the bundle’s JavaScript is loaded (auto-injected via Twig). The editable-content divs will render as inline CKEditor instances when accessed via the admin panel.
Dynamic Entity Mapping For reusable content blocks, map multiple Twig placeholders to a single entity field:
comur_content_admin:
editable_areas:
homepage_hero: { entity: App\Entity\ContentBlock, field: hero_content }
footer_links: { entity: App\Entity\ContentBlock, field: footer_content }
Custom CKEditor Configs Override default CKEditor settings per editable area:
ckeditor_config:
homepage_hero:
toolbar: ['Bold', 'Italic', 'Image', 'CodeSnippet']
extraPlugins: 'codesnippet'
Image Handling
Leverage ComurImageBundle for drag-and-drop image uploads in CKEditor:
ComurImageBundle is configured in config/packages/comur_image.yaml.Image button to your CKEditor toolbar (see above).Versioning
Combine with DoctrineExtensions’ Sluggable or Timestampable behaviors to track content changes:
use Gedmo\Mapping\Annotation as Gedmo;
class ContentBlock
{
// ...
/**
* @Gedmo\Timestampable(on="update")
*/
protected $updatedAt;
}
Multi-Language Support
Use Symfony’s translation system with trans filters in Twig:
{{ content_placeholder('welcome_message')|trans }}
Store translations in your ContentBlock entity (e.g., content_en, content_fr).
Placeholder Mismatches
data-content-id) must exactly match the YAML keys under editable_areas.comur_content_admin.yaml against your template’s data-content-id attributes.CKEditor Not Loading
editable-content class is missing.yarn encore dev (or webpack if using Webpack Encore).config/packages/twig.yaml:
twig:
globals:
comur_content_admin: '@comur_content_admin.twig.content_admin_extension'
Image Upload Permissions
ComurImageBundle uploads fail due to missing permissions on the upload directory.uploads/ directory (or custom path) is writable:
chmod -R 775 var/uploads
Entity Field Type Conflicts
content field must be of type text (not string or json) to store HTML./**
* @ORM\Column(type="text")
*/
private $content;
Caching Headaches
config/packages/framework.yaml:
framework:
http_cache:
invalidation:
paths: ['/admin/*']
{{ parent() }} in Twig to bypass cached fragments.Check Console Logs
Enable Symfony’s profiler (_profiler) to inspect:
comur.content-admin errors).Verify Bundle Activation
Run php bin/console debug:container comur_content_admin to confirm the bundle is loaded.
CKEditor Debugging
F12) and check the Console tab for CKEditor errors.Database Dumps
For content discrepancies, dump your ContentBlock entity:
php bin/console doctrine:query:sql "SELECT * FROM content_block"
Custom Editors Override the default CKEditor by extending the bundle’s type:
// src/Admin/Type/CustomCKEditorType.php
namespace App\Admin\Type;
use Comur\ContentAdminBundle\Admin\Type\CKEditorType;
class CustomCKEditorType extends CKEditorType
{
public function getParent()
{
return 'ckeditor';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'config' => [
'toolbar' => ['CustomButton'],
'extraPlugins' => 'customplugin',
],
]);
}
}
Event Listeners Hook into content updates via Symfony events:
// src/EventListener/ContentUpdateListener.php
namespace App\EventListener;
use Comur\ContentAdminBundle\Event\ContentUpdateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ContentUpdateListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
ContentUpdateEvent::NAME => 'onContentUpdate',
];
}
public function onContentUpdate(ContentUpdateEvent $event)
{
$content = $event->getContent();
// Log, validate, or transform content here
}
}
Twig Extensions Extend the bundle’s Twig functions to add custom placeholders or filters:
// src/Twig/ContentAdminExtension.php
namespace App\Twig;
use Comur\ContentAdminBundle\Twig\ContentAdminExtension as BaseExtension;
class ContentAdminExtension extends BaseExtension
{
public function getFunctions()
{
return array_merge(
parent::getFunctions(),
[
new \Twig\TwigFunction('custom_placeholder', [$this, 'renderCustomPlaceholder']),
]
);
}
public function renderCustomPlaceholder(string $key, array $options = [])
How can I help you explore Laravel packages today?