tilleuls/ckeditor-sonata-media-bundle
Symfony bundle that integrates CKEditor with Sonata Media, letting editors browse and insert images and files from the Sonata Media library directly through a CKEditor toolbar button, with configurable Sonata admin integration for seamless media embedding.
This bundle is thin glue: it exposes SonataMediaBundle's media library and upload pipeline to CKEditor 4's "file browser" popup. It does not replace SonataMediaBundle, and it does not provide an editor itself — you still need SonataMediaBundle (with at least one context and provider configured) and a CKEditor bundle such as egeloen/ivory-ckeditor-bundle or its successor friendsofsymfony/ckeditor-bundle.
Prerequisites checklist
sonata-project/media-bundle installed, configured with a filesystem/CDN, at least one provider (sonata.media.provider.image), and at least one context.sonata_media formats defined (e.g. small, medium, reference), because the browse popup renders thumbnails using a named format.ckeditor form types.Install and wire up
composer require tilleuls/ckeditor-sonata-media-bundle
Register the bundle (manually in config/bundles.php on Symfony 4+, or AppKernel on older apps):
// config/bundles.php
return [
// ...
CoopTilleuls\Bundle\CKEditorSonataMediaBundle\CoopTilleulsCKEditorSonataMediaBundle::class => ['all' => true],
];
Import the bundle routes — this is the step people most often forget:
# config/routes/coop_tilleuls_ckeditor_sonata_media.yaml
coop_tilleuls_ckeditor_sonata_media:
resource: "@CoopTilleulsCKEditorSonataMediaBundle/Resources/config/routing.xml"
prefix: /ckeditor
Point your CKEditor config at the two routes it provides (..._browse and ..._upload). With IvoryCKEditorBundle:
# config/packages/ivory_ck_editor.yaml
ivory_ck_editor:
configs:
default:
filebrowserBrowseRoute: coop_tilleuls_ckeditor_sonata_media_browse
filebrowserBrowseRouteParameters: { context: default, format: small }
filebrowserUploadRoute: coop_tilleuls_ckeditor_sonata_media_upload
filebrowserUploadRouteParameters: { context: default }
With FOSCKEditorBundle the keys are the same but under fos_ck_editor. After that, clear the cache, publish assets (bin/console assets:install --symlink), and click the "Browse Server" button in CKEditor's Image dialog.
Where to look first
Resources/config/routing.xml inside the bundle — confirms the exact route names and URL parameters (context, format) the controller expects.Controller/ directory — shows how browse and upload select a context and build the media query.Resources/views/ templates — the browse grid is a Twig template you can override to match your admin theme.First use case: give content editors an "Insert image from media library" workflow — open the image dialog, browse existing Sonata media filtered to the default context, or upload a new file without leaving the editor.
One CKEditor config per content context. The bundle's real value is filtering media by Sonata context. Create a config per editorial area so editors only see relevant assets:
ivory_ck_editor:
configs:
article:
filebrowserBrowseRoute: coop_tilleuls_ckeditor_sonata_media_browse
filebrowserBrowseRouteParameters: { context: article, format: medium }
filebrowserUploadRoute: coop_tilleuls_ckeditor_sonata_media_upload
filebrowserUploadRouteParameters: { context: article }
homepage:
filebrowserBrowseRoute: coop_tilleuls_ckeditor_sonata_media_browse
filebrowserBrowseRouteParameters: { context: homepage, format: wide }
Then reference the config in your form type ('config_name' => 'article').
Control the thumbnail size via format. format is a Sonata media format name, not a pixel value. Define the formats you want in sonata_media and reuse them in filebrowserBrowseRouteParameters. If the format does not exist, the grid renders without thumbnails or throws when generating the cache.
Uploads inherit the context from the route parameters. The upload action creates media in the context passed in the URL. Use the same context for browse and upload, otherwise newly uploaded files won't appear in the browser you opened.
Override the browse template to align with your admin skin. Copy the bundle template into templates/bundles/CoopTilleulsCKEditorSonataMediaBundle/... (Symfony 4+) or app/Resources/CoopTilleulsCKEditorSonataMediaBundle/views/... (Symfony 3) and adjust the markup. Keep the JavaScript that calls window.opener.CKEDITOR.tools.callFunction(...) intact — that's the handshake with the editor.
Add access control and ACL scoping in a wrapper controller. The bundle's browse action queries Sonata media directly; it does not apply SonataAdmin ACL rules or per-user restrictions. For anything multi-tenant, override the browse route to point at your own controller that filters by owner/context and reuses the bundle's template:
# config/routes/coop_tilleuls_ckeditor_sonata_media.yaml
coop_tilleuls_ckeditor_sonata_media_browse:
path: /ckeditor/browse
controller: App\Controller\CkEditorMediaController::browse
methods: [GET]
Lock down the routes. They are normal Symfony routes and need explicit protection. Add an access_control entry requiring an authenticated admin role for your /ckeditor prefix:
security:
access_control:
- { path: ^/ckeditor, roles: ROLE_ADMIN, requires_channel: https }
Use it inside Sonata Admin forms. Combine with the Ivory/FOS CKEditor form type in an admin class; editors then get the same media browser inside the admin panel. Keep the CKEditor config name consistent between the admin form and the front-end forms so behavior doesn't diverge.
Handle upload responses in the popup. The upload action returns an HTML page that runs JavaScript to insert the selected/uploaded media into the parent editor. If you customize the upload template, preserve the CKEditorFuncNum handling from the original.
filebrowserBrowseUrl, CKEditorFuncNum). CKEditor 5 dropped that model; you need a custom build with a media-library plugin instead.bin/console debug:router | grep ckeditor and verify both browse and upload routes exist with your prefix. A missing routing import produces a silent "file browser not available" error in the popup.context: article is passed but no media exists in that Sonata context (or the context isn't declared in sonata_media.contexts), the browser shows an empty grid. Verify the context in config/packages/sonata_media.yaml.format parameter must match a key under sonata_media.formats. Otherwise thumbnail generation fails or returns a broken image; check var/log and the media cache directory.CKEditor, CKEditorFuncNum, langCode) to the browse URL. Use filebrowserBrowseRouteParameters / filebrowserUploadRouteParameters rather than putting ?context=... in a URL.SameSite=Strict or your firewall redirects to login inside the popup, the editor appears to hang. Test browse URLs directly in a browser tab while logged in.upload_max_filesize/post_max_size and Sonata Media's per-provider max_size/allowed extensions. A rejected upload often returns a valid-looking page with no image — inspect the popup's HTML and the server log.sonata.media.security.* strategies. In multi-user setups, wrap or replace the controller; otherwise every editor can see every asset in the context.fos_ck_editor) — same route options, but package names and bundle registration differ. Don't mix config roots.composer.json and run the browse/upload flow after every upgrade. Check the bundle's own composer.json constraints before assuming Symfony 6/7 support.templates/bundles/..., run bin/console cache:clear; Twig's bundle override resolution won't pick it up otherwise./ckeditor/browse?context=default&format=small), inspect the popup for JS errors, check debug:router, confirm the media manager returns items in a small command or test, then verify the CKEditor filebrowser* keys actually land in the generated editor config (view source).How can I help you explore Laravel packages today?