misaf/vendra-user-profile
misaf/vendra-user-profile is a Laravel package for adding user profile management to Vendra applications. It helps organize profile data and integrate user profile features with minimal setup, giving developers a convenient foundation for profile-related workflows.
Architecture fit. This is not a general-purpose profile package; it is a module of the Vendra platform, published as a read-only subtree split of misaf/vendra. Its real architectural value is as a first-party extension point, not as reusable CRUD. The evidence in the skill definition is decisive: relations are registered via User::resolveRelationUsing(...) in the service provider rather than a trait on App\Models\User, tenant awareness is derived implicitly from the bound TenantResolver in misaf/vendra-support with no config toggle, UI is delivered as a Filament v5 plugin, and feature gating runs through Pennant (Features\ModuleEnabled). Two adoption paths therefore exist:
UserProfileRelationManagers registry for optional modules (Address, Phone, Document, Verification), en/de/fa translations, and admin UI placement beside Users — all without reverse dependencies. This is the intended integration and the cheapest.userProfiles table, a hasOne, and avatar handling. A 60-line migration and a form request would be cheaper to own.Integration feasibility. Feasible but documentation-led integration will mislead. The supplied Laravel assessment is hedged and generic ("check the README for exact tags, typically…"), which suggests it was reconstructed rather than written by the maintainer. Worse, the two doc sets contradict each other on the public contract: one describes a profiles table, a HasProfile trait, and $user->profile; the other specifies user_profiles, no trait, and relations registered as profiles and userProfiles via resolveRelationUsing. That is not a detail — it determines your migrations, your model, your API resources, and your tests. The first integration task is source verification against composer.json, the service provider, and database/migrations, not documentation reading.
Technical risk.
| Risk | Severity | Rationale |
|---|---|---|
| Bus factor / adoption | High | 1 star, single maintainer, read-only split — no issue tracker or PR flow on this repo; all fixes go upstream to misaf/vendra. |
| Contract ambiguity | High | Conflicting table/relation/trait documentation (above). |
| Implicit tenancy coupling | Medium-High | Tenancy is inferred from the bound TenantResolver; the module must run with tenancy off, and pre-tenancy installs require vendra-tenant:enable {tenant} to backfill tenant_id. |
| Version graph alignment | Medium | self.version alignment across the Vendra package graph; the split's version is not independently meaningful. v1.12.3 is a patch off v1.11.0 but release notes only link a compare view. |
| Invariant enforcement | Medium | Exactly one is_default profile per user, enforced by an observer plus a default_user_guard unique index — concurrent writes can surface as exceptions rather than validation errors. |
| Translatable scope | Medium | Any field in $translatable must be a JSON column, and the choice propagates to casts, factories, validation, Filament locale UI, API serialization, and tests. Retrofitting this later is a migration, not a config change. |
Key questions for the maintainer or your spike.
profiles or user_profiles, profile or userProfiles — and is a trait still required on User?User model binding point exist for substituting a custom profile model, and is UserProfileRelationManagers a stable public contract?meta), and does the package expose it?Stack fit. Assume a Laravel 12-era application with Filament v5 for admin surfaces, Pennant for feature flags, Spatie Translatable conventions, and misaf/vendra-support for tenancy. If your admin is not Filament, roughly half the package (resources, clusters, navigation priorities, permission enums) is inert, and you are paying maintenance for UI you do not render. If you are not multi-tenant, the TenantAwareness/BelongsToTenant machinery is harmless but is still a dependency edge you inherit.
Migration path (brownfield). Expect three distinct workstreams, not one:
users or a bespoke table. Decide the backfill: existing rows into user_profiles, and how to elect the single is_default row per user without violating default_user_guard. If the host was migrated before tenancy was enabled, schedule the vendra-tenant:enable backfill as an explicit, monitored step — not an implicit migration.php artisan route:list.Plugin/ServiceProvider rather than hand-wiring resources into unrelated panel providers, and expose profiles over APIs through a UserResource with conditional loading ($user->load('profile')).Compatibility. Pin exact versions and treat the split's tag as metadata only; the meaningful version is the monorepo release. Because the split is read-only and MIT, plan for a fork contingency — but a fork breaks self.version alignment across the Vendra graph, so it must be a deliberate, documented decision with an owner.
Sequencing.
is_default election; contract tests around the unique index.User relation wiring and authorization/policies.Maintenance. Treat the package as owned code. Published config, migrations, and views must live in version control and be diffed after every upgrade so local overrides do not drift from vendor changes; clear cached config (config:clear) after any config change. Every upgrade means reading the monorepo compare view, running the full suite, and checking for migration or config changes — the split's release notes ("Full Changelog" link only) provide no upgrade guidance. Budget a named internal owner; this is not a package you install and forget.
Support. There is no vendor SLA, no meaningful community (1 star), and no issue tracker on the read-only split. Effectively you are supporting it. The mitigating asset is the skills/vendra-user-profile-development documentation, which codifies module boundaries, strict-typing and PHPDoc conventions, test commands (php artisan test --compact --testsuite=vendra-user-profile, composer stan), and translation-parity expectations. That is unusually strong for a package of this size and is the main argument for adoption — but it is a convention set for contributors to the monorepo, not a support contract for integrators.
Scaling. Three concrete pressure points:
$user->load('profile')) and withCount/eager loading in Filament tables and relation managers.default_user_guard; high-concurrency profile changes will surface QueryException and must be handled gracefully, not swallowed.storage:link, and validation rules; use public visibility only when public access is genuinely required, and route through a CDN before volume grows.Failure modes to instrument. Silent profile-update failures ($fillable, validation, route model binding, policy denial) are the most common and least visible — log at the update boundary with query logging or Telescope during rollout. Others: duplicate profile creation where updateOrCreate was not used; multiple defaults under race; tenant_id leakage or null ownership when tenancy is toggled; published-route conflicts after upgrades; stale config cache; missing translation keys after adding locales.
Ramp-up. A Laravel-competent engineer reaches productivity on the profile domain in roughly 3–5 days, but only if they already understand the host platform: TenantResolver semantics, TenantTableRegistry, Filament v5 namespaces (Filament\Forms\Components vs Filament\Schemas\Components), plugin discovery, and Pennant's before() global-switch ordering. Absent that platform fluency, add one to two weeks. The skills documents are a reasonable onboarding path — assign them as required reading and have the first task be the source-verification spike described above, so the team resolves the documentation contradictions once, centrally, rather than in each feature branch.
How can I help you explore Laravel packages today?