oki-technologies/people-laravel
A Laravel package from Oki Technologies for managing people records—contacts, profiles, and related data—with migrations, Eloquent models, and configuration to drop into any Laravel app. Framework-native conventions keep setup minimal.
oki-technologies/people-laravel is a patch release (v1.0.1) on the 1.0 line — the release notes carry only a changelog link, so treat this as a bug-fix drop with no advertised API changes. Pin to ^1.0 and read the commit log before bumping if you were working around a defect.
Minimal setup:
composer require oki-technologies/people-laravel:^1.0
php artisan vendor:publish --provider="OkiTechnologies\People\PeopleServiceProvider"
php artisan migrate
Publish tags are usually split (people-config, people-migrations, people-views); publish config and migrations separately so you can re-publish config on upgrade without clobbering schema.
Where to look first, in order:
config/people.php — model bindings, table names, route prefix/middleware, feature flags.src/PeopleServiceProvider.php — what is registered, what is deferred, which contracts are bound to which implementations.database/migrations/ — the canonical schema (people table plus the polymorphic pivot) before you write any query of your own.First use case: attach a person to an existing domain entity. Add the trait to the owning model, then create and associate:
use OkiTechnologies\People\Concerns\HasPeople;
class Organisation extends Model
{
use HasPeople;
}
$person = Organisation::find(1)->people()->create([
'first_name' => 'Ada',
'last_name' => 'Lovelace',
'email' => '[email protected]',
]);
If that snippet does not work verbatim, the trait or relationship name has shifted — check the trait before assuming a config problem.
Trait-first, not model-first. The package's value is the relationship layer. Consume HasPeople (and its inverse, e.g. HasOrganisations) rather than querying the people table directly; that keeps you insulated from schema changes in minor releases.
Resolve through the container, not new. Bind the package contract in your own service provider and inject it. Direct instantiation of package classes is the most common thing that breaks on upgrade.
public function __construct(private \OkiTechnologies\People\Contracts\PeopleManager $people) {}
Wrap multi-step writes in a transaction. Creating a person plus its associations plus any address/contact records is a single logical operation, and the package is unlikely to wrap it for you.
Use events for cross-cutting reactions. Listen for the created/updated/deleted person events and dispatch your own jobs (search indexing, CRM sync, notification fan-out) off those rather than overriding controllers or models.
Sync external sources via a queued job, not a controller. If you are importing from an HR system or an IdP, put the mapping in a dedicated job that calls the manager contract and upserts on a stable external identifier — never on email alone, which changes.
Keep the HTTP layer thin. If the package ships routes, either use them as-is behind your auth middleware or disable them ('routes' => false in config) and expose your own controllers calling the same contract. Mixing both leads to two divergent authorization paths.
Factories and test doubles. Publish or hand-roll a factory against the configured model class — read the class from config (config('people.models.person')) rather than hardcoding the FQCN, so tests survive a model swap.
people table, do not run the published migration blind. Diff the schema first and rename or skip; the publish step will happily produce a duplicate-table error in production.personable_type stores full class names and renaming a model silently orphans rows.config/people.php, run php artisan config:clear. A stale cached config is the number one cause of "my model binding is ignored."SoftDeletes; if it does, relationship counts and exists checks need withTrashed() where you expect to see historical records.v1.0.0...v1.0.1 before deploying. If you had a local workaround for a bug, verify it is still needed and remove it deliberately rather than leaving it to shadow the fix.logger()->debug(get_class(app(PeopleManager::class)))) — it answers "which binding am I actually hitting" faster than reading the provider.How can I help you explore Laravel packages today?