---
title: "DI and actions — Attributes for Laravel | Roundly"
description: "Skip the facade: inject AttributesManager for the same API without static calls, or call a single-purpose action directly."
url: https://roundly-consulting.com/open-source/docs/attributes-for-laravel/without-facade
language: en
---

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

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

# DI and actions

The facade is the recommended default, not a requirement. There are three equivalent entry points, and all three run the same code:

- The Attributes facade — shortest; recommended for most code.
- The manager, RoundlyConsulting\\Attributes\\AttributesManager, injected through the constructor — it’s the facade root, a container singleton with exactly the same API, as an explicit dependency with no static calls.
- Actions — single-purpose final readonly classes with one execute() method, for composing into your own actions, jobs and commands.

## Inject the manager

```php
use RoundlyConsulting\Attributes\AttributesManager;

final class ImportColor
{
    public function __construct(private AttributesManager $attributes) {}

    public function __invoke(Product $product, string $color): void
    {
        $this->attributes->for($product)->set('color', $color);
    }
}
```

Attributes::fake() swaps the container binding as well as the facade, so an injected AttributesManager records too.

## Call an action

```php
use RoundlyConsulting\Attributes\Actions\AttachAttributeAction;
use RoundlyConsulting\Attributes\Actions\SyncAttributesAction;
use RoundlyConsulting\Attributes\DataTransferObjects\AttributeData;

// The raw actions — one execute() each
app(AttachAttributeAction::class)->execute($product, new AttributeData('color', 'red'));

$attributes = app(SyncAttributesAction::class)->execute($product, [
    new AttributeData('color', 'red', collect(['hex' => '#f00'])),
    new AttributeData('size', 'L'),
]);                                                     // Collection<int, Attribute>
```

Actions take DTOs from RoundlyConsulting\\Attributes\\DataTransferObjects: AttributeData(name, value = null, ?Collection meta = null) for a value, AttributeDefinitionData for a definition. An action called directly bypasses the manager, so the fake doesn’t record it.

## Facade method → action

| Facade path | Action | execute() |
| --- | --- | --- |
| `for($o)->set()` | `AttachAttributeAction` | `(Model $owner, AttributeData $data): Attribute` |
| `for($o)->setMany(), for($o)->stage()->save()` | `AttachAttributesAction` | `(Model $owner, AttributeData ...$data): Collection<int, Attribute>` |
| `for($o)->sync(), for($o)->stage()->sync()` | `SyncAttributesAction` | `(Model $owner, list<AttributeData> $data, bool $forceDelete = false): Collection<int, Attribute>` |
| `for($o)->forget()` | `DetachAttributesAction` | `(Model $owner, list<string> $names, bool $forceDelete = false): int` |
| `for($o)->forgetExcept()` | `DetachAttributesExceptAction` | `(Model $owner, list<string> $keep, bool $forceDelete = false): list<string>` |
| `for($o)->meta()` | `SyncAttributeMetaAction` | `(Model $owner, string $name, ?Collection $meta): Attribute` |
| `prune()` | `PruneAttributesAction` | `(?int $days = null): int` |

WriteAttributeAction (the raw upsert behind every attach, sync and meta write) and RecordAttributeRevisionAction are @internal — the actions above call them, so don’t call them yourself. The attach, sync and meta actions validate before they write, and every write action runs in one transaction — a savepoint when one is already open. The definition and validation methods have no action; they live on the AttributeRegistry.

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