Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Contacts Laravel Package

rinvex/laravel-contacts

Rinvex Contacts is a polymorphic contact management package for Laravel. Attach multiple emails, phone numbers, addresses and social profiles to any model through a clean, extensible Eloquent API, featuring built-in validation, caching support, configurable tables and translations.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is archived, so treat the first step as a compatibility check rather than a normal composer require:

composer require rinvex/laravel-contacts

If Composer refuses because of illuminate/* version constraints, check the package's composer.json first. On Laravel 10/11 you will almost certainly need to fork it, loosen the constraints, and require your fork via a VCS repository. Do that before writing any application code — do not build features on top of a package you can only install on a legacy branch.

Then publish and migrate:

php artisan vendor:publish --provider="Rinvex\Contacts\Providers\ContactsServiceProvider"
php artisan migrate

Run php artisan vendor:publish with no flags first if the provider form publishes nothing; Rinvex packages vary in whether they register tags. Inspect the published config/contacts.php and the migration in database/migrations — the migration is the authoritative source for column names (name, email, phone, birthday, notes, contactable_id, contactable_type in the upstream version), so read it rather than trusting docs.

Where to look first: the Contactable trait in the package source (that is the only integration surface you need), then the Contact model and its $fillable, then config/contacts.php for the model-swap key.

First use case: attaching free-form contact records to an existing model — a Customer with several people associated with it.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Rinvex\Contacts\Traits\Contactable;

class Customer extends Model
{
    use Contactable;
}
$customer->contacts()->create([
    'name'  => 'Jane Doe',
    'email' => '[email protected]',
    'phone' => '+1 555 0100',
]);

Verify the trait's namespace and method names against the installed source before committing — the README and the code have drifted in Rinvex packages before, and there is no maintainer left to reconcile them.

Implementation Patterns

Attach from a form request. The package does no validation, so validate in a FormRequest and pass a clean array:

$customer->contacts()->create($request->validated('contact'));

Keep the contact payload as a single nested array (contact[name], etc.) so the same request class works for every contactable model.

Treat the relation as the unit of sync. For a "contacts" tab in an admin panel, replace-all is usually correct and far simpler than diffing:

DB::transaction(function () use ($customer, $rows) {
    $customer->contacts()->delete();
    $customer->contacts()->createMany($rows);
});

If you need stable IDs for audit references, diff on a natural key (email) instead and update in place.

Reverse lookups. The Contact model carries a contactable morph-to, so cross-model search is a normal Eloquent query:

$contacts = Contact::query()
    ->where('email', $email)
    ->with('contactable')
    ->get();

This is the pattern that justifies a polymorphic table over per-model contact tables — use it for "find every record that has this phone number."

Enforce a morph map. Do this on day one, before the table has data:

// AppServiceProvider::boot()
Relation::enforceMorphMap([
    'customer' => \App\Models\Customer::class,
    'vendor'   => \App\Models\Vendor::class,
]);

Otherwise contactable_type stores App\Models\Customer verbatim and every namespace refactor becomes a data migration.

Custom model via config. Extend the base model and swap it in config/contacts.php rather than editing vendor code:

namespace App\Models;

class Contact extends \Rinvex\Contacts\Models\Contact
{
    protected $casts = ['birthday' => 'date'];
}
// config/contacts.php
'models' => ['contact' => \App\Models\Contact::class],

Check that the trait resolves the model through the config repository — if it hardcodes the class anywhere, subclassing gives you a split-brain bug where create() writes one model and queries read another. Grep the package for Contact::class to confirm.

Eager load. Contact lists are almost always rendered in a table or API resource alongside their parents: Customer::with('contacts')->paginate(25). Without it you get one query per row.

Import path. For CSV/bulk loads, bypass the relation and insert through the model with chunked createMany, keyed on contactable_type/contactable_id you resolve yourself.

Gotchas and Tips

It is abandoned. There will be no Laravel 10/11 support, no PHP 8.2 fixes, no security patches. Budget for a fork in your dependency list, or plan a replacement. The schema is trivial (contactable_id, contactable_type, plus contact columns) — migrating off it later is an afternoon's work, provided you don't scatter Rinvex\Contacts\Models\Contact imports across the codebase. Funnel everything through your own repository or service class so an exit stays cheap.

Migration and provider wiring. Rinvex packages pull in rinvex/laravel-support, which brings its own traits (slugging, caching) that the base model may or may not use. If php artisan migrate silently creates nothing, the provider's loadMigrationsFrom path is likely wrong for your Laravel version — publish the migration into your app instead.

No uniqueness constraints. Nothing stops two identical emails on the same contactable, or the same contact attached twice. Add your own validation rule (Rule::unique('contacts')->where(...)) if duplicates matter.

Caching behavior. Rinvex base models commonly implement cacheable traits that store query results under package-defined keys. If edits don't appear immediately, look for a cache flush requirement — and disable or flush that cache in your test suite, or you will chase phantom failures across test methods.

Testing. Use RefreshDatabase; the contacts table comes from the package migration, so don't hand-roll it in a test. Assert through the relation ($customer->contacts()->count()) rather than against the raw table so a future model swap doesn't break assertions.

Soft deletes. The upstream migration includes deleted_at. $customer->contacts()->delete() soft-deletes rather than removes, so a naive "replace all" sync accumulates rows. Use forceDelete() or add a purge job if the table grows.

Extension points, ranked. Config model swap is the sanctioned one; a Contact subclass plus a local migration for index/unique tweaks is the pragmatic one; overriding the vendor model in place is not — you lose it on the next (hypothetical) update and it makes the eventual removal harder.

Do not upgrade blindly. Pin the exact version in composer.json and vendor the fork in-repo or via a tagged private repository; a composer update on a floating constraint against an archived package is how a Saturday disappears.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log