---
title: "DI and actions — Advertisements for Laravel | Roundly"
description: "Inject AdvertisementManager for the same API without static calls, or call the single-purpose action behind each facade method."
url: https://roundly-consulting.com/open-source/docs/advertisements-for-laravel/without-facade
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Advertisements for Laravel](https://roundly-consulting.com/open-source/docs/advertisements-for-laravel.md)

# 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

```php
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:

```php
use RoundlyConsulting\Advertisements\Actions\PublishAdvertisement;

// The raw use case:
app(PublishAdvertisement::class)->execute($ad, now()->addDay());
```

```php
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):

```php
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 crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/advertisements-for-laravel.md)
