Open source
Contacts for Laravel
composer require roundly-consulting/contacts-for-laravelOverview
Typed, validated, primary-aware contacts for any Laravel model. Attach emails, phones, addresses, URLs, social handles and your own custom kinds to a user, a company, an order or anything else — all in one polymorphic contacts table, normalized on the way in, with one primary per kind, ordering, a channel-agnostic verification flow, events, a fluent facade and native vCard 3.0 export. MIT-licensed and dependency-light: Laravel plus a few small Roundly foundation packages — no third-party libraries.
What you get
Typed, normalized contacts
Emails, phones, addresses, URLs, social handles and custom kinds on any model — lower-cased, E.164-ish and scheme-complete on the way in.
One primary per kind
The first contact of a kind becomes primary automatically, promoting another demotes the old one, and when the primary leaves, the next takes over.
Verification without sending
Numeric codes or random tokens, stored only as a hash with an expiry and a wrong-guess limit. An event hands you the plaintext — you deliver it your way.
Reusable validation
ValidContactValue, ContactType::rules() and ready contacts.* array rules drop straight into your FormRequests.
vCard 3.0 & notification routing
Export an owner’s contacts as a valid vCard, and route mail, Vonage and Twilio notifications to its primary contacts.
Addresses & relationships
Structured postal addresses on a contact and a typed relationship graph — works at, spouse of — with invitations.
Built for tests
A recording Contacts::fake() that writes nothing — injected managers included — plus factory states and Pest matchers.
Documentation
Installation
Install via Composer, publish the contacts migration and the two companion migrations, then optionally publish config and translations.
Configuration
Every config key and its default — model, table, key type, auto-primary, country code, verification, custom kinds and relationship kinds.
Database schema & key types
The contacts table column by column, its indexes, soft deletes, and how key_type types owner_id for UUID or ULID owner models.
The HasContacts trait
Add HasContacts to any model for one-line addEmail/addPhone helpers, primary lookups, ordered per-kind collections and the contacts relation.
The Contacts facade
Contacts::for($owner) for an owner’s contact book, verification() for the token flow, flat calls for one contact — plus the builder and ContactData.
DI and actions
Inject ContactsManager for the facade’s API without static calls, or run one of the eight single-purpose contact actions directly.
Contact kinds & normalization
The six built-in ContactType kinds, how each value is normalized and validated, their labels and icons, and the enum helpers for selects.
Custom kinds
Register kinds such as whatsapp in config with their own label, icon and validation rules — each keeps its own primary and query scope.
Primary contacts & ordering
One primary per owner and kind — auto-assigned, swapped in a transaction, handed to the next contact when it leaves — plus per-kind ordering.
Syncing contacts
Reconcile an owner’s contacts of one kind to an exact list — update matches in place, create new ones, delete the rest, order by input.
Verification
Issue a code or token stored only as a hash with an expiry and an attempt limit, deliver it from an event, then confirm it to verify the contact.
Validation
Reuse the package’s validation in your forms: the ValidContactValue rule, ContactType::rules() and ready rules for repeatable contact arrays.
Query scopes
Composable Contact scopes — by owner, category, kind, primary, verification status, free-text search and position order.
Events
Six events for adds, updates, deletes, verification requests, verification and primary changes — react without forking.
Notification routing
Opt a model into routing mail, Vonage and Twilio notifications to its primary email and phone with RoutesNotificationsViaContacts.
vCard export
Export an owner’s contacts or a single contact as a valid vCard 3.0 card that Apple, Google and other address books read.
Structured addresses
Give a contact validated billing, home or work addresses via addresses-for-laravel, with a one-line render that falls back to the value.
Affiliations & relationships
Link contacts to each other and to owners — works at, spouse of — with invitations, expiry and an optional allow-list of relationship kinds.
Exceptions
Every exception the package throws, when it is thrown, and how to catch them all through the ContactException base class.
Testing
Swap in the recording Contacts::fake() and assert every write, build fixtures with per-kind factory states, and assert owners with Pest matchers.
Requirements
PHP 8.4+, Laravel 12 or 13, and five small Roundly foundation packages that Composer installs for you.
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.