---
title: "DI and actions — Coupons for Laravel | Roundly"
description: "Inject CouponManager instead of calling the facade, or run the single-purpose actions yourself — plus the full facade method to action map."
url: https://roundly-consulting.com/open-source/docs/coupons-for-laravel/without-facade
language: en
---

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

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

# DI and actions

The facade is the recommended default, not the only way in. There are three equivalent entry points, and all of them run the same code:

- The Coupons facade — the shortest form, recommended for everyday calls.
- The manager, RoundlyConsulting\\Coupons\\CouponManager — the facade root. Inject it through the constructor for the same API as an explicit dependency, with no static calls.
- Actions — single-purpose classes in RoundlyConsulting\\Coupons\\Actions with one execute() method, for composing into your own actions, jobs and commands.

## Injecting the manager

```php
use RoundlyConsulting\Coupons\CouponManager;
use RoundlyConsulting\Coupons\DataTransferObjects\RedemptionResult;
use RoundlyConsulting\Money\Money;

final class ApplyCoupon
{
    public function __construct(private CouponManager $coupons) {}

    public function __invoke(string $code, Money $cart, User $user): RedemptionResult
    {
        return $this->coupons->code($code)->redeem($cart, $user);
    }
}
```

Coupons::fake() swaps the manager for the facade and for every injected CouponManager — the manager isn’t final so the fake can subtype it — so a class that injects it is tested exactly like one that calls the facade.

## Running an action

Each operation is also a plain action class you can resolve and run yourself. Most take plain arguments; CreateCouponAction takes a CreateCouponData DTO and RedeemCouponAction a RedeemCouponData DTO:

```php
use RoundlyConsulting\Coupons\Actions\{CheckCouponAction, CreateCouponAction, ExpireCouponsAction,
    PruneCouponsAction, RedeemCouponAction, RevokeCouponAction};
use RoundlyConsulting\Coupons\DataTransferObjects\RedeemCouponData;

app(CreateCouponAction::class)->execute($data);                  // Coupon; quiet: true skips CouponCreated
app(CheckCouponAction::class)->execute('SUMMER', $cart, $user);   // ?RedemptionFailureReason
app(RedeemCouponAction::class)->execute(new RedeemCouponData(coupon: 'SUMMER', price: $cart, redeemer: $user));
app(RevokeCouponAction::class)->execute($coupon);
app(ExpireCouponsAction::class)->execute();                       // int
app(PruneCouponsAction::class)->execute(days: 30, force: false);  // int
```

The manager resolves each action from the container when a verb is called. An action you run yourself bypasses the manager, so Coupons::fake() doesn’t record it — in tests, assert on the database and events instead. The Coupon model, the HasCoupons trait, the Redeemable rule and both Artisan commands all go through the manager.

## Facade method → action

| Facade method | Action | Notes |
| --- | --- | --- |
| `generate(), create()` | `CreateCouponAction` | Takes a CreateCouponData DTO; generate() builds it for you. |
| `createQuietly()` | `CreateCouponAction` | execute($data, quiet: true) — no CouponCreated. |
| `check()` | `CheckCouponAction` | execute($coupon, $price, $redeemer) — never throws, locks or writes. |
| `redeem()` | `RedeemCouponAction` | Takes a RedeemCouponData DTO (coupon, price, redeemer). |
| `revoke()` | `RevokeCouponAction` | Takes a Coupon model; the manager resolves a code first. |
| `expireAll()` | `ExpireCouponsAction` | execute(?string $code = null): int |
| `prune()` | `PruneCouponsAction` | execute(int $days = 30, bool $force = false): int |
| `preview()` | — | Coupon::previewDiscount() on the resolved model. |
| `find(), findOrFail(), exists(), redeemable()` | — | A query on the model configured in coupons.model. |
| `code()` | — | A CouponCode handle over check(), preview(), redeem() and revoke(). |

## 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/coupons-for-laravel.md)
