Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ads Manager Laravel Package

juzaweb/ads-manager

A Laravel advertising manager for creating and organizing ad campaigns, banners, and placements. Easily manage ad zones, track impressions and clicks, and integrate ad delivery into your Juzaweb or Laravel application with a simple, flexible admin interface.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and boot

composer require juzaweb/ads-manager
composer dump-autoload -o   # required as of 2.0.2 (helper loading moved to composer.json)
php artisan migrate

If you are on Juzaweb CMS, enable the module in Admin → Modules (or php artisan module:list / module:enable AdsManager). On standalone Laravel, confirm the service provider is registered and publish any module config/views if the package ships them (php artisan vendor:publish --tag=ads-manager or the module equivalent).

Where to look first

  1. modules/AdsManager/ (or vendor/juzaweb/ads-manager/) — read composer.json first: it tells you the helper file, the namespace, and the Laravel version constraint you are actually running against.
  2. config/ and src/Providers/ inside the module — this is where positions/zones, ad types, and the Blade directive (if any) are registered.
  3. resources/views/ — the ad rendering partial is the thing you will override most often, and it shows you exactly which variable names the renderer expects.
  4. Migrations — ads, ad_positions/zones, and an impressions/clicks table. Knowing the schema saves you guessing at relationship names.

First use case: a header banner

  1. In the admin, create a position with slug header.banner.
  2. Create an ad: type = HTML or image, paste markup or upload the creative, assign it to header.banner, set start/end dates, mark active.
  3. In resources/views/layouts/app.blade.php (or a partials/ads/header-banner.blade.php), render the position using the package's directive/helper — typically something like {!! ads('header.banner') !!} or @ad('header.banner'). Verify the exact signature against the module's helper file; that file is the authoritative API.
  4. Load the page with the ad inactive and active, and confirm the slot disappears cleanly rather than throwing.

That is the whole loop: position slug → ad record → one render call in a layout partial. Everything else is campaign management in the admin UI.

Implementation Patterns

Treat position slugs as a public contract. Slugs like home.top, post.sidebar, article.inline are referenced from Blade and from the admin UI. Define them once — a config/ads.php array or a small enum class — and reference the constant in views rather than retyping strings. When marketing asks for a new slot, the change should be one constant plus one Blade include.

Centralise rendering in partials, not layouts. Wrap every slot in its own partial (partials/ads/sidebar.blade.php) that handles the empty case:

@php $html = ads('post.sidebar'); @endphp
@if ($html)
    <aside class="ad-slot ad-slot--sidebar">{!! $html !!}</aside>
@endif

This gives you one place to add wrappers, loading="lazy", reserved height, and analytics attributes — and one place to change when the package's render signature moves.

Reserve space to avoid CLS. Because ads load server-side, add explicit min-height/aspect-ratio to .ad-slot. A late-injected banner is the most common cause of layout shift complaints from a new ad integration.

Schedule campaigns instead of toggling. Use start/end dates plus the active flag rather than editing views. For a launch campaign, create the ad with a future start date and let the package flip it — no deploy required at go-live. Keep a permanent low-priority "house ad" per position as a fallback, so empty inventory never leaves a hole.

Cache deliberately, and briefly. Rendered ad HTML is a good cache candidate (one query per position per request is wasteful on a high-traffic layout). Cache per position slug with a short TTL — 5 to 15 minutes — and key it on the slug only, not the user. Critical: TTL must be shorter than your nearest schedule boundary, otherwise an ad stays up past its end date. If you do full-page caching, this constraint is the one that bites.

Keep click tracking off the cache path. If the package exposes a click-redirect route, make sure it is excluded from full-page cache, CDN caching, and any middleware that expects a session. Click handlers must reach PHP every time.

Seed realistic ads for local dev. Add a DevelopmentAdsSeeder that creates the position slugs your views reference plus one dummy HTML ad each. New developers then see populated slots on php artisan migrate:fresh --seed instead of wondering whether the integration is broken.

Write two tests per slot. One asserts the slot renders when an active ad exists; one asserts the page renders 200 with no ad (the fallback case). These catch the two regressions that actually happen: broken directive and unguarded null.

Respect the ad type boundary. HTML/script ads are for creatives you control. If any non-admin role can create ads, either restrict the ad-editor permission to trusted staff or run the rendered output through a sanitiser. This is the single highest-risk surface in the package.

Gotchas and Tips

The 2.0.2 autoload move is a deploy-pipeline trap. Helper loading shifted from module.json to Composer's autoload.files. Any environment that installs from a prebuilt artifact, a committed vendor/, or a Docker layer cache without re-running composer dump-autoload boots with the helpers missing — a fatal Call to undefined function at boot. Add the dump step to your deploy runbook and note it in the upgrade PR. If you see a redeclaration fatal instead, the module.json helper entry probably was not fully removed at your version — check both files.

Helpers are global functions. That means no namespace, no IDE autocomplete, and collision risk in a multi-module app. Do not wrap calls in function_exists() to paper over it; find the collision and namespace your own helper. Also note eager autoload.files runs on every request including queue workers and health checks — negligible for a small helper set, visible if it grows.

Nothing renders — check in this order: (1) module enabled; (2) position slug matches exactly, including case and dots; (3) ad is active and inside its date window; (4) ad is assigned to that position, not just created; (5) php artisan view:clear and config:clear after publishing views/config; (6) the compiled view is not a stale copy from a previous package version.

Timezone on scheduling. Start/end dates are the classic off-by-a-day bug. Confirm whether the comparison happens in config('app.timezone') or UTC, and test an ad that ends "today" in a non-UTC timezone. If the package stores naive datetimes, pick one convention and never mix.

Ad blockers and bots corrupt your stats. Impressions counted server-side on render will be inflated by crawlers and will not match client-side analytics; impressions counted client-side will be suppressed by ad blockers. Pick one, document which, and never present both numbers in the same report. If you need defensible figures, filter known bot user agents on the impression endpoint.

Debugging a suspiciously empty slot. Temporarily dump the position query result in the partial (or dd(ads('slug'))) rather than guessing at the admin state — the returned string is empty for any of the five failure reasons above, and inspecting the record distinguishes them in one step.

Extension points worth knowing. Custom ad types and view overrides are the intended customisation surface: publish the module views, copy the render partial into resources/views/vendor/..., and adjust there rather than editing vendor/. If you need a new creative format, implement it as a type in your own module and delegate the position/rotation/scheduling logic to ads-manager rather than reimplementing it.

Pin the version in composer.json. The package is small and low-star; a patch-level load-path change already shipped once. Pin to ~2.0.2 and read release notes before bumping, particularly any release whose scope is build: or that touches composer.json.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console