NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Contacts for Laravel

DI and actions

The facade is sugar — there are three equivalent entry points, and the choice is yours:

  • The Contacts facade — the shortest, and the recommended default.
  • The manager, injected through the constructor — the same API as an explicit dependency, with no static calls. RoundlyConsulting\Contacts\ContactsManager is the facade root, bound as a singleton.
  • Actions — single-purpose classes with an execute() method, for composing into your own actions, jobs and commands.

Injecting the manager

Contacts::fake() swaps the manager binding, so constructor-injected code is recorded too:

use App\Models\User;
use RoundlyConsulting\Contacts\ContactsManager;
use RoundlyConsulting\Contacts\Models\Contact;

final readonly class AddWorkEmail
{
    public function __construct(private ContactsManager $contacts) {}

    public function __invoke(User $user, string $email): Contact
    {
        $contact = $this->contacts->for($user)->email($email)->label('Work')->primary()->add();

        $this->contacts->verification()->request($contact);

        return $contact;
    }
}

Running an action

Every write on the facade is one action, resolved from the container:

Facade callAction
for($owner)->…->add(), for($owner)->add($data)AddContactAction::execute($owner, $data)
for($owner)->sync($type, $items)SyncContactsAction::execute($owner, $type, $items)
update($contact, $data)UpdateContactAction::execute($contact, $data)
delete($contact)DeleteContactAction::execute($contact)
setPrimary($contact)SetPrimaryContactAction::execute($contact)
verification()->request($contact)RequestContactVerificationAction::execute($contact)
verification()->confirm($contact, $token)ConfirmContactVerificationAction::execute($contact, $token)
verification()->markVerified($contact, $at)VerifyContactAction::execute($contact, $at)
use RoundlyConsulting\Contacts\Actions\AddContactAction;
use RoundlyConsulting\Contacts\Actions\RequestContactVerificationAction;
use RoundlyConsulting\Contacts\DataTransferObjects\ContactData;
use RoundlyConsulting\Contacts\Enums\ContactType;

$contact = app(AddContactAction::class)->execute($user, new ContactData(ContactType::Email, '[email protected]'));
$token = app(RequestContactVerificationAction::class)->execute($contact);

Each action takes the owner or contact and a ContactData DTO where it needs one, and applies the same normalization, validation, primary handling and events as the facade. An action you call directly runs for real — Contacts::fake() records calls that go through the manager.

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

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