The HasContacts trait
Add the HasContacts trait to any Eloquent model — a user, a company, an order. No schema change per model:
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Contacts\Concerns\HasContacts;
class User extends Model
{
use HasContacts;
}Adding contacts
The trait is sugar over the same manager the Contacts facade uses — every helper goes through it, so values are normalized and validated, auto-primary applies, events fire and Contacts::fake() records the call:
use RoundlyConsulting\Contacts\DataTransferObjects\ContactData;
use RoundlyConsulting\Contacts\Enums\ContactType;
// Add — normalized + validated, primary guaranteed unique per kind.
$user->addEmail('[email protected] ', label: 'Work', primary: true);
$user->addPhone('+421 900 000 000', label: 'Mobile');
$user->addUrl('example.com');
$user->addAddressContact('12 Main St');
// Any kind, from a DTO
$user->addContact(new ContactData(ContactType::Social, '@janedoe', label: 'Mastodon'));- addEmail(), addPhone(), addUrl(), addAddressContact() — each takes (string $value, ?string $label = null, bool $primary = false).
- addContact(ContactData $data) — any kind, including registered custom kinds.
- addStructuredAddress(AddressData $data, ?string $label = null, bool $primary = false) — see Structured addresses.
- addAddress() — deprecated alias of addAddressContact(), removed in the next major release.
Alongside HasAddresses
The deprecated addAddress() collides with HasAddresses::addAddress(AddressData) from addresses-for-laravel, so a model using both traits is a PHP fatal error at class load until you resolve it. Keep the addresses method and reach the contacts one by its own name:
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Addresses\Contracts\Addressable;
use RoundlyConsulting\Addresses\DataTransferObjects\AddressData;
use RoundlyConsulting\Addresses\Traits\HasAddresses;
use RoundlyConsulting\Contacts\Concerns\HasContacts;
class Company extends Model implements Addressable
{
use HasAddresses, HasContacts {
HasAddresses::addAddress insteadof HasContacts;
}
}
$company->addAddress(AddressData::make( // addresses: a structured Address
city: 'Bratislava', street: 'Hlavna 1', postalCode: '81101', countryIso: 'SK',
));
$company->addAddressContact('12 Main St'); // contacts: an address contactReading contacts
$user->primaryEmail()?->value; // '[email protected]'
$user->primaryPhone()?->value; // '+421900000000'
$user->primaryContact(ContactType::Url); // ?Contact — enum or string kind
$user->contactsOfType('phone'); // ordered EloquentCollection<Contact>
$user->contactBook(); // the ContactBook — same as Contacts::for($user)
$user->contacts; // Collection — every contact
$user->contacts()->primary()->verified()->get(); // scopes chain on the relation- contacts() — the MorphMany relation; every Contact scope chains on it.
- contactsOfType(ContactType|string) — one kind, ordered by position then id.
- primaryContact(ContactType|string) — the primary of a kind, or null.
- primaryEmail() / primaryPhone() — shortcuts for the two most common kinds.
- contactBook() — the owner’s ContactBook, the same object Contacts::for($user) returns.
The Contact model
Each Contact exposes its kind, value, flags and metadata:
$contact->type; // ContactType::Email
$contact->kind; // 'email' — the raw stored kind
$contact->value; // '[email protected]'
$contact->label; // 'Work' — free-text label for this one contact
$contact->name; // display name ('' when not set)
$contact->category; // ?string
$contact->is_primary; // bool
$contact->position; // int — order within the kind
$contact->verified_at; // ?Carbon
$contact->meta; // Collection (JSON column)
$contact->owner; // the owning model (MorphTo), or null
$contact->isVerified(); // bool
$contact->kindLabel(); // 'Email'
$contact->kindIcon(); // 'envelope'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.