spatie/laravel-prefixed-ids
Generate friendly Stripe-like prefixed IDs for Laravel Eloquent models (e.g., user_xxx). Add a trait to models, create and store prefixed IDs, and resolve models from a prefixed ID via findByPrefixedId or automatic model detection.
Installation:
composer require spatie/laravel-prefixed-ids
Publish the config file:
php artisan vendor:publish --provider="Spatie\PrefixedIds\PrefixedIdsServiceProvider"
Configure Prefixes:
Edit config/prefixed-ids.php to define prefixes for your models:
'prefixes' => [
'user' => \App\Models\User::class,
'test_token' => \App\Models\TestToken::class,
],
First Use Case: Generate a prefixed ID for a model:
$user = new \App\Models\User();
$user->prefixed_id = $user->getPrefixedId(); // Generates e.g., "user_fj39fj3lsmxlxl"
$user->save();
config/prefixed-ids.php (defines prefixes and model mappings).HasPrefixedId trait (adds methods like getPrefixedId()).PrefixedIds (for dynamic model resolution, e.g., getModelClass()).Model Integration:
Use the HasPrefixedId trait in your Eloquent models:
use Spatie\PrefixedIds\HasPrefixedId;
class User extends Model
{
use HasPrefixedId;
}
prefixed_id column (if not exists) and methods like getPrefixedId().Dynamic Prefix Resolution:
Use the PrefixedIds facade to resolve models by prefixed IDs:
$model = PrefixedIds::getModelClass('user_fj39fj3lsmxlxl'); // Returns \App\Models\User
$instance = $model::findByPrefixedId('user_fj39fj3lsmxlxl');
Custom Prefix Logic:
Override getPrefixedId() in your model for custom logic:
public function getPrefixedId(): string
{
return 'custom_' . $this->id;
}
API/URL-Friendly IDs: Use prefixed IDs in routes or APIs for clarity:
Route::get('/users/{prefixed_id}', function ($prefixed_id) {
return User::findByPrefixedId($prefixed_id);
});
Migrations: The package auto-creates the prefixed_id column if missing, but manually adding it ensures control:
Schema::table('users', function (Blueprint $table) {
$table->string('prefixed_id')->unique()->after('id');
});
Seeding: Generate prefixed IDs in seeders:
$user = new User();
$user->prefixed_id = $user->getPrefixedId();
$user->save();
Testing: Use findByPrefixedIdOrFail() to assert model existence:
$this->assertEquals($user, User::findByPrefixedIdOrFail('user_fj39fj3lsmxlxl'));
Prefix Collisions:
Avoid overlapping prefixes (e.g., user and users). Ensure uniqueness in config/prefixed-ids.php.
Database Constraints:
The prefixed_id column is set to unique by default. If you reuse IDs, handle conflicts in your logic:
$user->prefixed_id = $user->getPrefixedId();
if (User::where('prefixed_id', $user->prefixed_id)->exists()) {
$user->prefixed_id .= '_duplicate';
}
Caching Prefixed IDs:
If generating IDs in bulk (e.g., seeding), cache the PrefixedIds resolver to avoid repeated lookups:
$resolver = app(Spatie\PrefixedIds\PrefixedIds::class);
Legacy Systems:
If migrating from auto-increment IDs, ensure prefixed_id is populated before queries:
User::query()->update(['prefixed_id' => function ($query) {
return $query->selectRaw('CONCAT("user_", id)');
}]);
Invalid Prefixes:
If findByPrefixedId() returns null, verify:
config/prefixed-ids.php.HasPrefixedId.prefixed_id column exists in the database.Custom Logic Errors:
Overriding getPrefixedId() may break auto-generation. Test with:
$this->assertStringStartsWith('custom_', $user->getPrefixedId());
Performance:
For large datasets, index the prefixed_id column:
Schema::table('users', function (Blueprint $table) {
$table->string('prefixed_id')->unique()->index();
});
Custom ID Generators:
Extend the Spatie\PrefixedIds\IdGenerator class to support non-random IDs (e.g., UUIDs):
class CustomIdGenerator extends IdGenerator
{
public function generate(): string
{
return Str::uuid()->toString();
}
}
Register it in config/prefixed-ids.php:
'id_generator' => \App\Services\CustomIdGenerator::class,
Dynamic Prefixes: Use model observers or accessors to set prefixes dynamically:
public function getPrefixedIdAttribute()
{
return $this->prefix . '_' . $this->id;
}
API Resources: Format prefixed IDs in API responses:
public function toArray($request)
{
return [
'id' => $this->prefixed_id,
'name' => $this->name,
];
}
Soft Deletes:
Ensure prefixed_id is included in deleted_at queries:
User::withTrashed()->findByPrefixedId('user_fj39fj3lsmxlxl');
How can I help you explore Laravel packages today?