The Advertisements facade
RoundlyConsulting\Advertisements\Facades\Advertisements is the recommended entry point. Its root is RoundlyConsulting\Advertisements\AdvertisementManager, which resolves an action from the container for every operation — so a host binding for an action applies to the facade too. The lifecycle verbs are flat:
use RoundlyConsulting\Advertisements\DataTransferObjects\AdvertisementData;
use RoundlyConsulting\Advertisements\Facades\Advertisements;
use RoundlyConsulting\Money\Money;
$ad = Advertisements::create(new AdvertisementData(
name: 'Vintage road bike',
price: Money::ofMajor('250.00', 'EUR'),
category: 'bikes',
author: $user, // any Eloquent model (polymorphic author)
));
Advertisements::publish($ad); // live now → AdvertisementPublished
Advertisements::publish($ad, now()->addWeek()); // scheduled → AdvertisementPublished
Advertisements::unpublish($ad); // back to draft → AdvertisementUnpublished
Advertisements::expire($ad); // → AdvertisementExpired
Advertisements::archive($ad); // → AdvertisementArchived
Advertisements::delete($ad); // soft delete → AdvertisementDeletedServing is flat as well; everything about one ad — its placements and its tracking — hangs off the for($ad) handle:
// Serving
Advertisements::in('sidebar')->get(); // active ads in a placement
Advertisements::targetedIn('sidebar', request())->get(); // … that target the viewer
Advertisements::random('sidebar'); // ?Advertisement
echo Advertisements::render($ad, 'sidebar'); // the creative, or the text ad
// One ad's scoped API
Advertisements::for($ad)->placements()->sync(['sidebar', 'header']);
Advertisements::for($ad)->runsIn('sidebar'); // true
Advertisements::for($ad)->track('sidebar')->impression();Every method
| Method | Returns | What it does |
|---|---|---|
create($data) | Advertisement | Create an ad from an AdvertisementData. |
update($ad, $data) | Advertisement | Overwrite the ad from an AdvertisementData. |
publish($ad, ?$at = null) | Advertisement | Publish now, or schedule for a future instant. |
unpublish($ad) | Advertisement | Back to draft; clears published_at. |
expire($ad, ?$at = null) | Advertisement | Expire now, or set a future expiry. |
archive($ad) | Advertisement | Archive without deleting. |
delete($ad) | bool | Soft delete. |
query() | Builder | A fresh builder for the configured model. |
active() | Builder | query()->active(). |
in($placement) | Builder | Active ads in a placement (model, id or slug). |
targetedIn($placement, $viewer = null) | Builder | Active ads in a placement that match the viewer’s location. |
random($placement = null) | ?Advertisement | One random active ad, optionally per placement. |
render($ad, $placement, $attributes = []) | HtmlString | The creative for a placement, or the text ad. |
for($ad) | AdvertisementHandle | One ad’s scoped API — placements and tracking. |
for($ad)->placements()->attach / detach / sync($placements) | Advertisement | Add, remove or replace placements; the ad comes back with placements reloaded. |
for($ad)->runsIn($placement) | bool | Whether the ad is attached to the placement. |
for($ad)->track(?$placement)->impression(?$data) / ->click(?$data) | ?AdvertisementEvent | Record an impression or a click of a live ad; null when buffered. |
fake() | AdvertisementsFake | Swap in the recording fake for tests. |
- for($ad)->track() refuses an ad that isn’t live right now — draft, scheduled, expired or archived — with AdvertisementNotActive, and track($placement) refuses a placement the ad doesn’t run in with AdvertisementNotInPlacement. track() with no placement records without one.
- Recording returns the persisted AdvertisementEvent, or null when tracking.buffered queues it.
- render() goes through the bound CreativeRenderer — a responsive <img> when an image creative exists, the text ad otherwise.
- The model methods — $ad->publish(), unpublish(), expire(), archive(), delete() and renderCreative() — are sugar over the same manager, so they fire the same events and Advertisements::fake() records them.
The facade alias is registered globally unless register_facade_alias is false; the facade class itself always works.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.