pasinter/admanager-bundle
Symfony bundle for managing banner advertisements, campaigns, and ad zones. Define banners, assign them to zones, track impressions and clicks, and integrate ad delivery into Twig templates with Doctrine-backed entities and simple configuration.
A word of framing first: despite this being listed for a "Laravel developer" assessment, pasinter/admanager-bundle is a Symfony bundle, not a Composer package that drops into Laravel. The primary integration path is Symfony (Flex, config/bundles.php, Doctrine). In a Laravel app you would either run a Symfony microservice alongside it, or treat the bundle as reference code and port the entities/services you need. Plan accordingly before you composer require it.
Minimal steps in a Symfony app:
composer require pasinter/admanager-bundle, then register it in config/bundles.php (or AppKernel on older Symfony). Check composer.json for the supported Symfony version range before committing — this is an 11-star package with zero dependents, so version compatibility is the first real gate.DependencyInjection/Configuration.php first, not the README. That file tells you the actual config tree, service IDs, and defaults. Next look at Resources/config/ for the service definitions and Resources/views/ for the Twig templates, then the entity/mapping directory. That's your real documentation.config/packages/ad_manager.yaml (or config.yml), enabling whatever slot/zone/banner settings the extension exposes.bin/console doctrine:schema:validate and then a migration (doctrine:migrations:diff → migrate). Do not rely on schema:update --force in anything you plan to keep.If the above compiles and renders, you're integrated. Everything else is campaign management, scheduling, and tracking.
Slot/zone rendering in templates. The bundle's day-to-day value is rendering an ad for a named slot without leaking ad logic into every controller. Keep slot names in a config map (or a small constants class) so templates don't hardcode strings:
{{ ad_slot('sidebar_top') }} (whatever the bundle's extension exposes — check Resources/views/ and the Twig/ directory).Always wrap rendering in a fallback so a missing or exhausted ad doesn't break the page:
{{ ad_slot('sidebar_top')|default('') }}
Campaign scheduling via the database, not code. The natural pattern is: campaigns have startAt/endAt, banners belong to campaigns, and a slot picks among active banners. Drive scheduling through the entity's date fields and let a repository query filter by "now", rather than adding if date > ... in Twig. This keeps the "why is no ad showing?" question answerable in one SQL query.
Service injection over global helpers in non-template code. For anything beyond rendering — reporting, API endpoints, admin CRUD — inject the ad manager service into your controller or a thin application service. This lets you swap in a decorated service for testing or add business rules (frequency capping, user-segment targeting) without forking the bundle.
Admin integration. If the bundle ships form types, reuse them inside your own EasyAdmin/Sonata/custom admin controller. The bundle almost certainly does not ship a full admin UI — expect to build CRUD screens yourself around its entities. Budget for that; it's usually the largest chunk of work.
Tracking impressions and clicks. The typical pattern is a controller action (or small event subscriber) that records an impression when a banner renders and redirects on click. Wire the impression record to a kernel.response listener or a Twig render call so it's automatic, and make the write asynchronous or batched if traffic is meaningful — synchronous inserts on every page render will become your bottleneck.
Caching rendered ads. Cache the rendered slot HTML keyed by slot name + campaign window + user segment, with a TTL shorter than the shortest campaign boundary. Invalidate on campaign save. This is where most real-world implementations spend their tuning effort.
Headless / API usage. If you need ads in a SPA, expose a small JSON endpoint (GET /api/ads/{slot}) that returns active creatives for a slot, and let the frontend render. You keep all scheduling logic server-side and the bundle stays a library.
composer.json, read the code before trusting it, and be prepared to vendor/fork it. Run composer outdated regularly and decide explicitly whether to keep it.composer require.AppKernel, services.yml under Resources/config, and deprecated Doctrine APIs. If you're on Symfony 6/7, expect to patch service definitions and possibly entity mappings.DependencyInjection/Configuration.php. When a setting is silently ignored, it's almost always a typo'd key or a wrong nesting level — the bundle will not always complain.|raw or the bundle's own safe wrapper). If your banners contain <script> tags, verify what the bundle does with them; if it escapes by default, ads render as text — a classic "why is the page showing HTML source?" bug.decorates: in your service config) and overriding templates under templates/bundles/AdManagerBundle/ over editing vendor code. Both survive a composer update; vendor edits do not.AdRenderer) around it in your own code. When you eventually replace the bundle, you swap one class instead of touching every template.How can I help you explore Laravel packages today?