
Laravel 11.30 Introduces HasUniqueStringIds for Simple Unique IDs
Laravel 11.30 introduces the HasUniqueStringIds trait, which offers custom unique string identifiers for your models is easier than ever and alternatives to standard auto-incrementing IDs. This trait simplifies handling unique string IDs, making it especially useful for applications requiring non-integer IDs without the hassle of complex route-binding adjustments. In this post, we’ll learn the capabilities of HasUniqueStringIds, review practical applications, and provide real-world examples to illustrate its flexibility and ease of use.
What Is the HasUniqueStringIds Trait?
The HasUniqueStringIds trait in Laravel 11.30 provides a way to add unique, customizable, unique string-based IDs (such as UUIDs or ULIDs) for your models without modifying route binding logic.Previously, the HasUuids and HasUlids traits were available for such needs, but HasUniqueStringIds now unifies these into a more flexible approach.
Practical Use of HasUniqueStringIds with Examples
Let's explore HasUniqueStringIds with some common scenarios where custom string IDs can enhance your application.
1. Basic Usage with Custom UUID
A common use case is generating UUIDs (Universally Unique Identifiers) for each model instance. Using HasUniqueStringIds, you can easily generate UUIDs while keeping your routes compatible.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Product extends Model
{
use \Illuminate\Database\Eloquent\Concerns\HasUniqueStringIds;
protected function newUniqueId(): string
{
return (string) Str::uuid();
}
protected function isValidKey(string $key): bool
{
return Str::isUuid($key);
}
}
In this example:
The newUniqueId() method generates a UUID as a unique ID whenever a new Product is created.
The isValidKey() method checks that the ID is in a valid UUID format, ensuring compatibility across routes and validations.
2. Custom ID with ULID Format
ULIDs (Universally Unique Lexicographically Sortable Identifiers) provide an alternative to UUIDs, especially useful when chronological ordering is necessary. Here’s how to set up HasUniqueStringIds for ULID generation:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Symfony\Component\Uid\Ulid;
class Order extends Model
{
use \Illuminate\Database\Eloquent\Concerns\HasUniqueStringIds;
protected function newUniqueId(): string
{
return (string) new Ulid();
}
protected function isValidKey(string $key): bool
{
return Ulid::isValid($key);
}
}
In this example:
newUniqueId() returns a new ULID for each Order instance.
isValidKey() ensures IDs match the ULID format. This is helpful if you want IDs that are unique, lexicographically sortable, and more readable.
3. Custom Format (e.g., Product Codes)
For some applications, it’s helpful to use readable IDs, like product codes or custom tags, that follow a specific format. Here’s an example of generating an ID in the format PRD-XXXXX (where XXXXX is a random alphanumeric string):
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class InventoryItem extends Model
{
use \Illuminate\Database\Eloquent\Concerns\HasUniqueStringIds;
protected function newUniqueId(): string
{
$random = strtoupper(Str::random(5));
return "PRD-{$random}";
}
protected function isValidKey(string $key): bool
{
return preg_match('/^PRD-[A-Z0-9]{5}$/', $key) === 1;
}
}
In this example:
newUniqueId() creates a product ID in the PRD-XXXXX format, making IDs more readable and recognizable.
isValidKey() checks for adherence to the format, allowing consistent IDs that fit a specific business logic.
4. Tenant-Scoped Unique IDs (Multi-Tenant Applications)
In multi-tenant applications, it’s often necessary to scope unique IDs by tenant. For instance, an ID could include both a tenant prefix and a unique suffix. Here’s an example that generates an ID in the format TENANT-ID-XXXXXX:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class TenantModel extends Model
{
use \Illuminate\Database\Eloquent\Concerns\HasUniqueStringIds;
protected function newUniqueId(): string
{
$tenantId = tenant()->id; // Assuming tenant() helper returns the current tenant ID
$random = strtoupper(Str::random(6));
return "{$tenantId}-{$random}";
}
protected function isValidKey(string $key): bool
{
return preg_match('/^[A-Z0-9]+-[A-Z0-9]{6}$/', $key) === 1;
}
// Optionally override uniqueIds() to ensure uniqueness within each tenant scope
public function uniqueIds(): array
{
return ['id', 'tenant_id'];
}
}
In this example:
newUniqueId() incorporates a tenant ID prefix, making the ID globally unique within the context of each tenant.
isValidKey() verifies that the generated ID meets the desired format.
Why HasUniqueStringIds Matters
The new HasUniqueStringIds trait is a significant addition to Laravel, offering the following advantages:
-
Simplified ID Management: By centralizing ID generation and validation, HasUniqueStringIds removes the need for complex workarounds or custom route-binding logic.
-
Enhanced Flexibility: This trait allows applications to easily adapt to unique ID needs, whether it’s for SEO-friendly URLs, readable product codes, or compliance with third-party systems.
-
Backward Compatibility: Legacy code using HasUuids and HasUlids still functions without modifications, making it easier to adopt this trait in existing projects.
Using HasUniqueStringIds in Routes
Suppose you want routes to include these unique string-based IDs for better readability and SEO benefits. With the new trait, you can bind these custom IDs directly in routes without adjusting resolveRouteBindingQuery(). Example:
Route::get('/products/{product}', function (Product $product) {
return view('product.show', ['product' => $product]);
});
Why This Trait Matters
By using HasUniqueStringIds, Laravel enables you to:
-
Define unique, complex ID patterns without impacting route binding or other core model behaviors.
-
Simplify model creation by eliminating the need for custom binding logic.
-
Adopt a flexible, backward-compatible trait that can evolve with Laravel’s future releases.