usamamuneerchaudhary/adment
Adment is a Laravel admin panel package that streamlines building back-office dashboards and internal tools, offering CRUD resources, authentication, roles and permissions through a clean, configurable interface so you can focus on your application's core logic.
1. Confirm the constraint before anything else. Adment targets Filament v5. If your panel is on Filament v3/v4, stop here — you'd be downgrading your whole admin stack to adopt a 10-star ad manager. Check the package's composer.json for the exact filament/filament and laravel/framework ranges; also confirm PHP version and that livewire/livewire v4 (the v5 companion) is what you're on.
composer require usamamuneerchaudhary/adment
php artisan vendor:publish --tag="adment-config"
php artisan vendor:publish --tag="adment-migrations"
php artisan migrate
2. Register the plugin in your panel provider. This is the step people forget; without it you get no admin UI and assume the package is broken.
use Usamamuneerchaudhary\Adment\AdmentPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugin(AdmentPlugin::make());
}
3. Look at these files first, in this order:
| Path | Why |
|---|---|
config/adment.php |
Caching, tracking toggles, AdSense client ID, route prefix — everything you'll tune |
database/migrations/* |
The real schema: placements, creatives, and likely impressions/clicks. Read this before designing anything |
src/Models/* |
Relationship names and scopes; this is your actual API |
src/Http/ or routes/ |
The public API and click-tracking endpoint shapes |
resources/views/ |
The Blade components you'll drop into layouts |
4. First use case — the 20-minute win. Create a placement in the admin (a named slot: header-banner, sidebar-300x250, in-article), attach one or more creatives to it, then render the placement in your Blade layout by key. That placement-key indirection is the package's core value: your templates reference header-banner, not an ad unit ID, so swapping AdSense ↔ direct-sold creative never touches a view.
{{-- indicative shape — confirm the component/helper name in resources/views --}}
<x-adment::placement name="header-banner" />
{{-- or --}}
@adment('header-banner')
Ship one placement, verify an impression lands in the DB, then roll out.
Placement-first, always. Treat placements as the contract between marketing and code, and creatives as content. Never hardcode a creative ID in a Blade file. When you need a new ad slot, the workflow is: dev adds @adment('new-slot') → marketer creates the placement with that key → done. Budget one PR per slot, not per campaign.
AdSense mode vs. self-hosted creatives. Typically a placement is either backed by an AdSense unit (script + <ins> tag, client ID from config) or by rotating local creatives. Keep this decision in the placement record, not in code, so you can switch a slot to a house ad during a slow sales month without a deploy. Store the AdSense slot ID on the placement/creative and the publisher ID in config/env — never commit the publisher ID.
Responsive creatives. Do the responsive work with srcset/sizes and an explicit aspect ratio on the wrapper, not with JavaScript measurement. Two practical patterns:
Either way, always reserve space so the ad never causes CLS.
Click tracking without write amplification. If clicks go through a redirect route, expect a row-insert per click. On any page with real traffic that's a hot table. Mitigations, in order of preference:
ShouldQueue, batched) and keep the redirect synchronous and fast.model:prune on the click model with 30–90 days retention.// AppServiceProvider::boot() — pattern, adjust to the package's model names
AdmentClicks::prune(); // or a scheduled command
Also decide where the impression is counted. If your pages are full-page cached (Cloudflare, spatie/laravel-responsecache), a server-rendered impression counter will undercount badly. Fire impressions from a tiny JS beacon against the public API instead, and skip counting for bots/referrer-less requests.
Public API + caching. The package exposes a JSON API for placements — this is the piece to lean on for SPAs, Inertia pages, and edge-cached fragments. Cache the placement payload (not the HTML) in Redis with a key derived from the placement key, and bust it from the placement/creative model's saved/deleted events:
// pseudo-hook
Placement::saved(fn ($p) => Cache::forget("adment:placement:{$p->key}"));
Then let the client render. This keeps the API cheap enough that you can call it on every page load.
Scheduling and rotation. Use whatever start/end/weight fields exist on creatives for campaign flights rather than doing it manually at deploy time. Add a scope like ->active() in your own code if the package doesn't ship one — you'll want it in every query.
Multi-panel / multi-tenant. Add a tenant_id/panel_id column via your own migration and a global scope if you run more than one panel. Don't fork the package for this; a global scope is enough for the admin queries and a where clause on the public API.
"usamamuneerchaudhary/adment": "1.2.3") and read src/ before trusting any README. Vendoring a copy is a legitimate choice for something this small.script-src for pagead2.googlesyndication.com / *.googlesyndication.com / *.google.com, frame-src for googleads.g.doubleclick.net, and img-src for the ad CDN. Symptom is a blank slot with only a console CSP violation — easy to misdiagnose as a package bug.<ins> element with CSS after render is a good way to get an account flagged. Style the container, not the ad.min-height on the wrapper from the placement's configured size.php artisan config:cache before publishing config gives you null for every adment setting. Publish, verify, then re-cache.php artisan filament:assets after install/upgrade, or the admin resource renders unstyled and you think the plugin registration failed.auth middleware or CSRF assumptions. Whitelist it explicitly and exclude it from session/cookie middleware if you're on a strict stack — otherwise every click writes a session row.active and within flight dates? (2) does a creative exist and is it enabled, (3) is the config ID set, (4) is the Blade component actually rendering (view-source, not devtools — JS may just be blocked), (5) check the impressions table for the placement ID.App\Support\Ads facade from day one makes that swap a one-file change — strongly recommended for a dependency at this maturity level.How can I help you explore Laravel packages today?