tomatophp/circle-contacts-module
Circle Contacts is a TomatoPHP module that adds contact management to your Laravel app, letting you store, organize, and browse people and their details through a ready-made admin interface, with a simple structure you can extend for your own CRM or directory needs.
This is the first tagged release (v1.0.0) of tomatophp/circle-contacts-module, a contacts sub-module for the TomatoPHP Circle CRM stack. Release notes contain only a changelog link, so treat the source as the documentation.
Minimal steps:
composer require tomatophp/circle-contacts-module
php artisan vendor:publish --provider="TomatoPHP\CircleContacts\CircleContactsServiceProvider"
php artisan migrate
Before writing any code, read these in order:
composer.json — confirms the required Laravel, PHP, tomatophp/circle and Filament/Tomato panel versions. This dictates everything else; a Filament major-version mismatch is the most common install failure.src/Providers/*ServiceProvider.php — shows what is auto-discovered, what is publishable (config, migrations, lang, views) and whether the Filament plugin/resource registers itself or must be added to a panel provider.config/circle-contacts.php (after publishing) — usually exposes table names, model class bindings and feature flags. This is your primary extension surface.database/migrations/ — check table names and foreign keys against your existing schema before running.src/Models/ and src/Filament/ — the actual contact entity and its admin UI.First use case: if the panel plugin auto-registers, you get a Contacts resource in the Circle admin immediately — create a contact, attach it to a customer/lead, confirm the relation shows on the parent record. If it does not auto-register, add the plugin to your panel provider (->plugin(CircleContactsPlugin::make())) and verify the navigation group appears. If you are headless (API only), skip the UI and exercise the model + service layer directly in tinker.
Treat the package models as base classes, not as your domain. The standard TomatoPHP pattern is a config-driven model swap: publish the config, point the model key at App\Models\Contact extends TomatoPHP\CircleContacts\Models\Contact, and add your own casts, scopes, accessors and relationships there. This survives package upgrades far better than editing vendor files. Verify the config key exists in v1.0.0 before relying on it; if it does not, bind your subclass in a service provider with $this->app->bind().
Relation managers on the parent resource. Contacts almost always hang off a customer/lead/company. Register the package's relation manager (or your own wrapping its table) on the parent Filament resource rather than building a second CRUD screen. This keeps one source of truth for contact editing and reuses the package's form schema.
Events over direct coupling. If you need to sync contacts to an external CRM, mail provider or audit log, listen on Eloquent model events (created, updated, deleted) or a package event class if one is dispatched. Observers registered in your own provider keep the integration out of the vendor namespace and are trivially testable.
API surface. For a JSON API, do not serialize the package model directly — wrap it in your own ContactResource. The package's attribute set will change between minor releases in a 1.x line, and leaking it into your public contract creates avoidable breaking changes.
Multi-tenancy / ownership. If contacts are team- or tenant-scoped, add a global scope in your subclass and confirm the package's Filament resource respects it. Filament resources that query the model directly will honour global scopes; ones using raw queries may not — check getQuery() / getTableQuery() in the resource.
Testing. Publish and use the package factories if present; otherwise define your own. Refresh the database per test and assert on your subclass, not the vendor class, so a future model swap is caught by the suite.
Feature flags in config. Anything you are unsure about — soft deletes, activity logging, duplicate detection — gate behind your own config entry so you can toggle behaviour per environment without a deploy.
^1.0 and read the diff of each release rather than assuming semver discipline.database/migrations permanently; running package migrations keeps them updateable. Pick one strategy and be consistent — mixing both causes duplicate-table errors. Also check the contacts table name against any existing contacts table in your app.php artisan config:clear and php artisan filament:optimize-clear (or optimize:clear) after publishing config or registering a plugin. Stale cached config is the usual cause of "my model swap is ignored".composer.json matches your panel before installing.php artisan about and php artisan filament:about narrow this quickly.QueryException on contacts.email and decide explicitly between upsert, skip, or error — do not let the framework surface a 500.app()->environment('local') checks.How can I help you explore Laravel packages today?