DI and actions
The facade is the recommended default, not the only way in. There are three equivalent entry points:
- The Advertisements facade — shortest, and what the rest of these docs use.
- The manager, RoundlyConsulting\Advertisements\AdvertisementManager — the facade root. Inject it through the constructor for the same API with an explicit dependency and no static calls.
- Actions — single-purpose classes in RoundlyConsulting\Advertisements\Actions with one execute() method, for composing into your own actions, jobs and commands.
Injecting the manager
use RoundlyConsulting\Advertisements\AdvertisementManager;
use RoundlyConsulting\Advertisements\Models\Advertisement;
final class LaunchCampaign
{
public function __construct(private AdvertisementManager $ads) {}
public function __invoke(Advertisement $ad): void
{
$this->ads->for($ad)->placements()->sync(['sidebar', 'header']);
$this->ads->publish($ad);
}
}AdvertisementManager is deliberately not final: Advertisements::fake() swaps in AdvertisementsFake, a subtype, behind the facade and in the container — so constructor-injected managers get the fake too.
Calling an action
Resolve an action from the container, or type-hint it in a controller, job or action of your own:
use RoundlyConsulting\Advertisements\Actions\PublishAdvertisement;
// The raw use case:
app(PublishAdvertisement::class)->execute($ad, now()->addDay());use Illuminate\Http\RedirectResponse;
use RoundlyConsulting\Advertisements\Actions\PublishAdvertisement;
use RoundlyConsulting\Advertisements\Models\Advertisement;
final class ScheduleAdvertisementController
{
public function __invoke(Advertisement $advertisement, PublishAdvertisement $publish): RedirectResponse
{
$publish->execute($advertisement, now()->addDay()); // scheduled for tomorrow
return back();
}
}The manager resolves each action from the container per call, so binding your own implementation changes the facade’s behaviour as well. The fake replaces the manager, not the actions — an action you call directly still writes rows.
Facade method → action
| Facade method | Action | Signature |
|---|---|---|
create() | CreateAdvertisement | execute(AdvertisementData $data): Advertisement |
update() | UpdateAdvertisement | execute(Advertisement $ad, AdvertisementData $data): Advertisement |
publish() | PublishAdvertisement | execute(Advertisement $ad, ?CarbonInterface $at = null): Advertisement |
unpublish() | UnpublishAdvertisement | execute(Advertisement $ad): Advertisement |
expire() | ExpireAdvertisement | execute(Advertisement $ad, ?CarbonInterface $at = null): Advertisement |
archive() | ArchiveAdvertisement | execute(Advertisement $ad): Advertisement |
delete() | DeleteAdvertisement | execute(Advertisement $ad): bool |
for($ad)->placements()->attach() | AttachPlacements | execute(Advertisement $ad, iterable $placements): Advertisement |
for($ad)->placements()->detach() | DetachPlacements | execute(Advertisement $ad, iterable $placements): Advertisement |
for($ad)->placements()->sync() | SyncPlacements | execute(Advertisement $ad, iterable $placements): Advertisement |
for($ad)->track()->impression() | RecordImpression | execute(Advertisement $ad, $placement = null, ?ImpressionData $data = null): ?AdvertisementEvent |
for($ad)->track()->click() | RecordClick | execute(Advertisement $ad, $placement = null, ?ImpressionData $data = null): ?AdvertisementEvent |
create() and update() take an AdvertisementData; the tracking actions take an optional ImpressionData (see Creating & updating ads and Impression & click tracking). The raw tracking actions record what they are given — the live-ad and placement checks live in for($ad)->track($placement):
use RoundlyConsulting\Advertisements\Actions\RecordImpression;
use RoundlyConsulting\Advertisements\DataTransferObjects\ImpressionData;
// Records what it is given — the live-ad and placement checks live in for($ad)->track($placement).
app(RecordImpression::class)->execute($ad, 'sidebar', new ImpressionData(ip: $request->ip()));RecordAdvertisementEvent is @internal — the single code path behind RecordImpression, RecordClick and the buffered job. Record through the facade or those two actions instead.
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.