tuna-cms/tuna-bundle
Tuna CMS Bundle packages the Tuna CMS core into a reusable PHP bundle, making it easier to add structured content management to applications. It centralizes CMS services and configuration for quicker, cleaner integration.
Before you run composer require, confirm two things: this is a Symfony bundle, not a Laravel package (it will not work with artisan or service providers), and the repo has essentially no ecosystem around it (0 dependents). Treat it as a research spike, not a drop-in dependency.
Minimal path:
composer.json in the repo and compare symfony/* and php constraints against your project. If it pins an older Symfony (e.g. 3.x/4.x), stop here — adapting it is usually more work than writing your own content model.composer require tuna-cms/tuna-bundle.config/bundles.php (or AppKernel::registerBundles() on older versions) alongside its dependencies — expect transitive deps like Doctrine ORM, a form component, and likely an admin generator (Sonata-style) or its own admin controllers.@TunaBundle/Resources/config/routing.xml or similar) under a prefix such as /admin and /.Resources/config into config/packages/tuna.yaml; set at minimum the entity classes, upload directories, and security role required for the admin.bin/console doctrine:schema:validate then doctrine:schema:update --dump-sql (or the bundle’s migrations if shipped) and inspect the SQL before applying.access_control entry protecting the admin path, otherwise you have an unauthenticated CMS backend.First use case: stand up a single static "page" with a text block, render it through a Twig template you control, and only then explore menus/media. That validates install → schema → admin → frontend routing end to end with the smallest surface area.
Where to look first, in order: README.md → composer.json (version/dep reality check) → DependencyInjection/Configuration.php (the real config contract, better than any docs) → Entity/ (your content model) → Controller/ (routing and template names).
Content model as Doctrine entities. Expect Page, Block/ContentBlock, Menu, and media entities. The practical pattern is to extend them in your app (class Page extends BasePage) and point the bundle config at your subclass, so you can add fields without forking. Do this before writing any templates — retrofitting an entity swap later means a migration.
Admin as a CRUD surface, frontend as thin controllers. Keep the bundle handling persistence and admin UI; keep all rendering in your own templates/ directory. Override templates by placing files at the same relative path in templates/bundles/TunaBundle/... (Symfony’s bundle override convention) rather than editing vendor files.
Blocks as composable partials. A CMS of this shape usually maps block types to Twig partials. Build a small dispatcher in your app — a Twig function or a {% include 'blocks/' ~ block.type ~ '.html.twig' %} with a default fallback — so unknown block types degrade gracefully instead of throwing.
Route-everything-through-a-slug controller. Typical integration: a catch-all route (/{slug}) that queries Page by slug and 404s cleanly. Register it last in your routing so it doesn’t swallow admin and asset routes.
Config-as-code for structure, admin for content. Seed menus and page skeletons in a Doctrine fixture or a console command so environments are reproducible; let editors own the text. This keeps dev/staging/prod in sync and avoids "works on my database" bugs.
Service decoration for behaviour. If you need to change how pages resolve or how media URLs are generated, decorate the bundle’s service (decorates: tuna.some_service) instead of subclassing controllers. Verify the actual service IDs with bin/console debug:container | grep -i tuna.
Multi-locale, if supported. Check for a locale/translations field on entities. If present, wire your frontend controller to read the request locale and fall back explicitly — CMS locale fallbacks are a common source of blank pages.
config/*.php returning arrays in the Laravel sense. Config is YAML under a bundle-specific root key; "publishing" assets/config is bin/console assets:install / manual copy, not vendor:publish.composer.json so a surprise push doesn’t break your build.There is no extension able to load the configuration for "tuna", your bundle isn’t registered or the config key doesn’t match the extension alias — read DependencyInjection/*Extension.php for the true alias.auto_mapping. Use doctrine.orm.mappings with type: annotation|xml and prefix: set explicitly, and run debug:doctrine equivalents (doctrine:mapping:info) when entities "exist" but aren’t found.schema:update can silently drop columns added by other bundles. Always --dump-sql first, and prefer generating a real migration you can review.access_control: { path: ^/admin, roles: ROLE_ADMIN } and confirm with an incognito window. Don’t rely on "it wasn’t documented, so it must be locked".bin/console debug:twig lists the resolved paths; if your override isn’t in the list, the name is off.web/-relative assets. If you’re on Webpack Encore or Vite, the admin CSS may 404 — check the browser network tab before debugging PHP.bin/console cache:clear. A stale container will report services and routes that no longer exist, sending you down false trails.PageRepository::findBySlug) so a future swap to a maintained CMS bundle or a hand-rolled content model touches one class, not your templates.How can I help you explore Laravel packages today?