kpeu3u/symfony-cookie-consent-bundle
A Symfony bundle for cookie consent management: display a customizable banner, let visitors accept or reject cookie categories, and gate scripts until consent is recorded. Simple configuration for GDPR-style consent flows.
First, a reality check: kpeu3u/symfony-cookie-consent-bundle is a Symfony bundle, not a Laravel package. It ships no service provider, no config/cookie-consent.php, and no Blade views. In a pure Laravel app you cannot composer require it and expect a banner to appear. The realistic day-to-day paths are:
If you are on the Symfony side, the minimum steps are:
composer require kpeu3u/symfony-cookie-consent-bundle:^2.0 -W
php bin/console assets:install public
php bin/console cache:clear
Then register the bundle in config/bundles.php, add a config/packages/*cookie_consent* file with your categories and vendors, and render the banner from your base layout template.
Where to look first: docs/upgrading.md (mandatory if you are on 1.x), then the README's configuration reference, then the bundle's own templates under its Resources/views/templates directory — those are your override surface.
First use case: define two categories (e.g. analytics, marketing), one vendor each, render the banner once in the base layout inside the <body>, and guard a single optional integration (Google Analytics) behind a consent check. Ship that end-to-end before touching themes, logging or translations.
Requirements to pin in CI: PHP 8.3+ (PHP 8.4+ if you run Symfony 8), Symfony 7.4+.
Config-driven catalogue, code-driven enforcement. Categories, vendors, labels and layout/theme all live in bundle config. But the bundle explicitly does not block third-party scripts and does not delete cookies. Every optional integration needs an explicit guard. The pattern that scales best is a single "consent state" accessor plus per-integration checks at the exact injection point.
Guard at the edge, not in the layout. Rather than conditionally rendering a <script> block in Twig/Blade, emit all optional scripts as inert type="text/plain" (or data-consent-category) tags and activate them from a small listener bound to the consent-changed event. This survives SPA-style navigation and avoids cache-key explosions from per-visitor HTML.
<script type="text/plain" data-consent="analytics" data-src="https://www.googletagmanager.com/gtag/js?id=..."></script>
Treat the stored decision as the only source of truth. In 2.0, optional services are preselected in the settings form for visitors with no saved preferences. That is a UI default, not consent — consent is granted only on submission. So your guard must read the persisted cookie/record and default to deny when absent.
Server-side guards too. For anything rendered server-side (a tracking pixel, an embedded iframe, a personalisation block), inject the current consent decision into the render context and branch there. Keep the category names in one constant/class so config and guards cannot drift apart.
Laravel-side mirroring. If a Laravel app shares a domain with the Symfony frontend, define the consent cookie name and payload shape once (a small shared doc or a config value), then read it in a Laravel middleware and expose it as a request attribute or a Blade directive. Do not let Laravel re-implement its own banner on the same domain — two banners writing the same cookie name is a guaranteed support ticket.
Single-vendor categories. 2.0 collapses categories with one vendor into a simplified toggle. Model your config accordingly — if a category will only ever have one vendor, embrace it; if you expect to add vendors later, ship it with two so the UI doesn't silently change shape on a future release.
Doctrine logging. If you enable database logging, it is opt-in and requires the bundle's Doctrine mapping plus an up-to-date schema. Keep it on a separate connection or table prefix if you have GDPR-driven retention policies; log rows are personal data.
Testing. Functional-test the round trip: clear cookies, request a page, assert the banner renders and nothing optional loads; submit the form with the CSRF token; assert the cookie is set and the optional script now renders. Add a test that an unsubmitted settings form (preselected boxes) still blocks optional scripts.
docs/upgrading.md before touching composer.json, and expect custom form markup to break — CSRF protection and form handling were reworked.php bin/console assets:install public + cache:clear. Skipping this is the classic "I upgraded and the banner still looks like 1.x" report. If you use AssetMapper/Encore, confirm whether the bundle assets are copied or symlinked, and clear the built asset cache too.symfony/* to 7.4 or your matrix will fail for reasons unrelated to your code.light/dark/automatic and top/bottom banner vs dialog should be handled via config, not by forking templates. automatic follows prefers-color-scheme; override CSS custom properties rather than editing bundle markup so upgrades stay cheap.translations/ directory. Mixing raw English strings into config is the thing you'll regret when the second locale lands.templates/bundles/... → service decoration. Avoid patching vendor files; 2.0 changed enough that a fork will be painful to carry.How can I help you explore Laravel packages today?