eloquent-works / persona
Laravel Persona gives your users elegant public profiles with slugs, bios, avatars, links, visibility, and profile pages.
Requires
- php: ^8.2
- illuminate/contracts: ^11.15|^12.0|^13.0
- illuminate/database: ^11.15|^12.0|^13.0
- illuminate/routing: ^11.15|^12.0|^13.0
- illuminate/support: ^11.15|^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.10
- laravel/pint: ^1.18
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.0
This package is not auto-updated.
Last update: 2026-08-11 13:53:07 UTC
README
Elegant, customizable public profiles for Laravel applications.
Laravel Persona gives an Eloquent user model profile pages, unique public usernames, display names, headlines, mottos, biographies, avatars, banners, social links, custom links, visibility controls, publishing, view tracking, profile comments, completeness scoring, badges, and convenient model helpers.
$profile = $user->createPersona([ 'display_name' => 'Nick', 'headline' => 'Laravel Package Builder', 'motto' => 'Build useful things.', 'bio' => 'Building useful Laravel packages.', 'location' => 'Kansas', 'is_public' => true, 'published_at' => now(), ]); $url = $user->personaUrl(); $score = $user->personaCompletenessScore();
📋 Supported Versions
| Package version | PHP | Laravel / Illuminate |
|---|---|---|
| Current | ^8.2 |
^12.0 || ^13.0 |
Composer resolves the compatible Illuminate packages for the consuming Laravel application.
🚀 Installation
Install Persona:
composer require eloquent-works/persona
Publish the configuration and migrations:
php artisan persona:install
Run the migrations:
php artisan migrate
Add HasPersona to the application user model:
<?php namespace App\Models; use EloquentWorks\Persona\Traits\HasPersona; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasPersona; }
See Installation for publishing options and setup guidance.
✨ Features
- Public or private user profiles
- Slug-based public usernames and route model binding
- Configurable username-change tokens
- Reserved-name, format, length, and uniqueness checks
- Display names, headlines, mottos, biographies, and locations
- Avatar and banner URLs through Laravel filesystems
- Website URLs, social links, and custom links
- Publishing and configurable visibility requirements
- Public, published, and visible query scopes
- Profile view counters
- Profile comments with replies, approval, pinning, editing, and deletion
- Profile completeness scoring
- Custom profile badge awarding
- Lifecycle events for creation, updates, publishing, unpublishing, and views
- Publishable views and customizable public routes
- Configurable models, tables, field limits, feature flags, and storage
- PHPUnit, PHPStan/Larastan, Laravel Pint, and Composer quality scripts
🚀 Quick Start
Create a profile
$profile = $user->createPersona([ 'display_name' => 'Nick', 'headline' => 'Laravel Package Builder', 'motto' => 'Build useful things.', 'bio' => 'Building useful Laravel packages.', 'location' => 'Kansas', 'website_url' => 'https://example.com', 'is_public' => true, 'published_at' => now(), ]);
Read or update the profile
$profile = $user->persona; $user->hasPersona(); $user->updatePersona([ 'headline' => 'Open-source Laravel Developer', ]);
Register the public profile route
Persona does not register public routes automatically.
use Illuminate\Support\Facades\Route; Route::persona();
The default route format is:
/@{persona}
Generate URLs
$profile->url(); $profile->avatarUrl(); $profile->bannerUrl(); $user->personaUrl();
👁️ Visibility and Publishing
$profile->isVisible(); $publicProfiles = Persona::public()->get(); $publishedProfiles = Persona::published()->get(); $visibleProfiles = Persona::visible()->get();
Visibility follows:
config('persona.visibility.require_published_at');
When require_published_at is disabled, a public profile may be visible without a publication timestamp. When enabled, the profile must be public and have a past published_at value.
Private profile responses
By default, Persona preserves its historical behavior and returns 404 for
profiles that are not publicly visible:
'visibility' => [ 'private_profile_response' => '404', ],
Applications may instead render a privacy-safe placeholder:
'visibility' => [ 'private_profile_response' => 'view', ],
The default private view displays:
This profile is private. This user has chosen to keep their profile private.
The private placeholder intentionally receives no Persona model and no user model. This prevents customized placeholder views from accidentally exposing private profile fields.
Customize the view with:
'views' => [ 'private' => 'profiles.private', ],
Authenticated profile owners may view their own private profile by default. Disable that behavior with:
'visibility' => [ 'owner_can_view_private' => false, ],
🏷️ Username Tokens
Persona uses the profile slug as its public username.
$profile->usernameTokens(); $profile->canChangeUsername(); $profile->nextUsernameTokenAt(); $profile->usernameIsAvailable('signal-nick'); $profile->changeUsername('signal-nick');
Use the helpers on the user model:
$user->personaUsernameTokens(); $user->canChangePersonaUsername(); $user->changePersonaUsername('signal-nick');
An administrative change can skip token spending:
$profile->changeUsername( 'signal-nick', spendToken: false, );
Persona normalizes the username and applies the configured length, regular expression, reserved-name, uniqueness, and token rules.
📊 Completeness
Refresh the profile's completeness score directly:
$score = $profile->refreshCompleteness();
Or through the user model:
$score = $user->personaCompletenessScore();
The user helper returns 0 when no Persona profile exists.
🏅 Badges
Award a badge directly through the profile:
$badge = $profile->awardBadge( 'package-builder', [ 'label' => 'Package Builder', 'description' => 'Published a Laravel package.', ], $user, );
Or use the user-model helper:
$badge = $user->awardPersonaBadge( 'package-builder', [ 'label' => 'Package Builder', ], );
The helper returns null when the user does not have a Persona profile.
💬 Profile Comments
$comment = $profile->addComment( $user, 'Great profile.', ); $reply = $comment->addReply( $otherUser, 'Thank you.', );
Moderate and edit comments:
$comment->approve(); $comment->unapprove(); $comment->pin(); $comment->unpin(); $comment->edit('Updated comment.'); $comment->delete();
Retrieve common comment groups:
$profile->approvedComments()->get(); $profile->pinnedComments()->get(); PersonaComment::topLevel()->approved()->get(); PersonaComment::repliesOnly()->get(); PersonaComment::pinned()->get();
Persona provides the model API. The consuming application remains responsible for routes, request validation, authorization, rate limiting, spam controls, and guest identity handling.
See Profile Comments.
📣 Events
Persona can dispatch:
PersonaCreatedPersonaUpdatedPersonaPublishedPersonaUnpublishedPersonaViewed
Disable lifecycle events globally:
'dispatch_events' => false,
See Events.
⚙️ Configuration
Publish the configuration file:
php artisan vendor:publish --tag=persona-config
Major configuration groups include:
return [ 'tables' => [], 'models' => [], 'routes' => [], 'usernames' => [], 'views' => [], 'storage' => [], 'slugs' => [], 'fields' => [], 'comments' => [], 'visibility' => [], 'links' => [], 'features' => [], 'dispatch_events' => true, ];
See Configuration for the complete reference.
✅ Quality Checks
Run all package checks:
composer quality
Or run them separately:
composer format
composer format:test
composer analyse
composer test
Validate Composer metadata before a release:
composer validate --strict
The quality pipeline should complete with zero formatting, PHPStan, or PHPUnit failures.
See Testing and Quality.
📚 Documentation
- Documentation Index
- Installation
- Configuration
- Usage
- Completeness and Badges
- Profile Comments
- Routes
- Events
- Customization
- Security
- Testing and Quality
🔐 Security
Treat every public profile field, URL, link, comment, metadata value, and badge attribute as user-generated or administrator-generated content.
Validate URLs, escape rendered values, authorize profile and comment changes, rate-limit public write endpoints, and avoid exposing private profiles through search, sitemaps, APIs, or public routes.
Security vulnerabilities should be reported privately according to SECURITY.md.
🤝 Contributing
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
🙏 Credits
Built by Eloquent Works.
📄 License
Laravel Persona is open-source software licensed under the MIT License.