veelasky/laravel-hashid
Laravel HashId
Automatic HashId generator for your Laravel Eloquent models.
About
Laravel HashId provides an elegant way to add hashed IDs to your Eloquent models. It generates unique, non-sequential hashes for your model IDs and provides convenient methods to work with them.
✨ Latest Features
Version 3.2.3 introduces automatic route key generation, eliminating boilerplate code, along with enhanced secure route model binding and powerful column selection capabilities for full Laravel 11/12 and PHP 8.4 compatibility.
🚀 Automatic Route Key Generation (New in v3.2.3)
Zero boilerplate: The HashableId trait now automatically provides getRouteKey() method:
class User extends Model
{
use HashableId;
// getRouteKey() automatically returns hash - no implementation needed!
}
route('users.show', $user); // Automatically generates: /users/k1jTdv6l
🔒 Secure Route Model Binding (New in v3.2.2)
Security improvement: Route model binding now only accepts valid hash values by default, preventing predictable ID enumeration attacks:
class User extends Model
{
use HashableId;
// Default: only hash resolution, numeric IDs return 404
}
// ✅ Secure: /users/k1jTdv6l works
// ❌ Blocked: /users/1 returns 404 (prevents ID enumeration)
🔥 Column Selection API (New in v3.2.0)
// Get user by hash with specific columns (better performance!)
$user = User::byHash($hash, ['name', 'email']);
// Get user by hash with single column
$user = User::byHash($hash, ['name']);
// Column selection with exception handling
$user = User::byHashOrFail($hash, ['name', 'email']);
Benefits:
- 🚀 Better Performance - Load only the columns you need
- 🔒 Type Safety - Automatic primary key inclusion when required
- 🔄 Backward Compatible - All existing code works unchanged
- 🎯 Smart Defaults -
['*']loads all columns, just like before - 🛡️ Enhanced Security - Prevents ID enumeration attacks by default
- ✅ Zero Boilerplate - No manual
getRouteKey()implementation needed
Compatibility
Modern Laravel Support (Recommended)
| Laravel HashId | PHP Version | Laravel 10 | Laravel 11 | Laravel 12 | Laravel 13 |
|---|---|---|---|---|---|
| 3.2 🌟 | ≥ 8.1 | ✅ | ✅ | ✅ | ✅ |
| 4.x 🚀 | ≥ 8.1 | ✅ | ✅ | ✅ | ✅ |
- 🌟 Stable Release (3.2) - Recommended for production
- 🚀 Development Branch (4.x) - Latest improvements
Full Version Matrix
| Laravel HashId | PHP Version | Laravel 6 | Laravel 7 | Laravel 8 | Laravel 9 | Laravel 10 | Laravel 11 | Laravel 12 | Laravel 13 |
|---|---|---|---|---|---|---|---|---|---|
| 1.x | ≥ 7.0 |
✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| 2.x | ≥ 7.2 |
❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 3.0 | ≥ 7.4 |
❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 3.1 | ≥ 8.0 |
❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| 3.2 🌟 | ≥ 8.1 |
❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| 4.x 🚀 | ≥ 8.1 |
❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
📊 Version Recommendations:
- Laravel 6-9 → Use
3.0or3.1 - Laravel 10+ → Use
3.2(stable) or4.x(development) - Latest features → Use
3.2+with column selection support
Installation
composer require veelasky/laravel-hashid
With Laravel's package auto-discovery, the package will be automatically registered.
Quick Start
Simply add the HashableId trait to any Eloquent model you want to use with HashId:
use Illuminate\Database\Eloquent\Model;
use Veelasky\LaravelHashId\Eloquent\HashableId;
class User extends Model
{
use HashableId;
}
Usage Examples
Basic Usage
$user = User::find(1); // Find user by ID
$user->hash; // Get HashId automatically
// Find by HashId
$user = User::byHash($hash);
$user = User::byHashOrFail($hash); // Throws exception if not found
// Convert between ID and HashId
$hashedId = User::idToHash($id);
$originalId = User::hashToId($hash);
// Query scope
User::query()->byHash($hash)->get();
Column Selection (New in v3.2)
// Load only specific columns for better performance
$user = User::byHash($hash, ['name', 'email']);
// Single column selection
$user = User::byHash($hash, ['name']);
// Column selection with exception handling
$user = User::byHashOrFail($hash, ['name', 'email']);
Persisting HashId to Database
class User extends Model
{
use HashableId;
protected $shouldHashPersist = true; // Persist hash to database
protected $hashColumnName = 'hashid'; // Custom column name (optional)
}
Route Model Binding
The trait automatically overwrites route methods to use HashId:
Route::get('/users/{user}', [UserController::class, 'show']);
class UserController
{
public function show(User $user)
{
// $user resolves automatically by HashId
// Example URL: /users/k1jTdv6l
// Numeric IDs like /users/1 will return 404 by default (secure!)
}
}
🔒 Secure Route Model Binding (Default)
By default, route model binding only accepts valid hash values and returns 404 for plain numeric IDs, preventing predictable ID enumeration attacks:
class User extends Model
{
use HashableId;
// $bindingFallback = false; // Default behavior - only hash resolution
}
// ✅ This works: /users/k1jTdv6l
// ❌ This returns 404: /users/1 (prevents ID enumeration)
Optional Fallback to Numeric ID
If you need to support both hash and numeric ID resolution (not recommended for production), you can enable the fallback:
class User extends Model
{
use HashableId;
protected $bindingFallback = true; // Allow both hash and numeric ID resolution
}
// ✅ Both work: /users/k1jTdv6l AND /users/1
Custom Field Binding
Custom field binding always uses Laravel's default behavior:
Route::get('/users/{user:slug}', [UserController::class, 'show']);
// This will resolve by 'slug' field, not by hash
🚀 Automatic Route Key Generation (New in v3.2.3)
The HashableId trait now automatically provides getRouteKey() method, so you don't need to implement it manually:
class User extends Model
{
use HashableId;
// No manual getRouteKey() implementation needed!
}
// Route generation now automatically uses hash
route('users.show', $user); // Generates: /users/k1jTdv6l
Before this version:
class User extends Model
{
use HashableId;
// Manual implementation was required
public function getRouteKey() {
return $this->hash;
}
}
Benefits:
- ✅ Zero Boilerplate - No manual
getRouteKey()implementation needed - 🔄 Automatic - Works out of the box with the trait
- 🛡️ Consistent - Uses the same hash values as route resolution
- 🔧 Configurable - Respects custom hash column names
- ⬅️ Backward Compatible - Existing custom implementations still work
Validation Rules
use App\Models\User;
use Veelasky\LaravelHashId\Rules\ExistsByHash;
$request->validate([
'user_id' => ['required', new ExistsByHash(User::class)],
]);
Advanced Usage
Repository Pattern Access
// Using the HashId facade
$hashedId = HashId::idToHash($id, User::class);
$originalId = HashId::hashToId($hash, User::class);
// Manual hash ID creation
$hashId = HashId::make('custom-key', 'custom-salt');
Shared Hash Across Models
class User extends Model
{
protected $hashKey = 'shared-hash-key';
}
class Customer extends User { }
$customer = Customer::find(1);
$user = User::find(1);
// Both will have the same hash
echo $customer->hash === $user->hash; // true
Configuration
You can configure HashId behavior using environment variables:
HASHID_SALT=your-custom-salt
HASHID_LENGTH=10
HASHID_ALPHABET=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
Or publish the configuration file:
php artisan vendor:publish --tag=laravel-hashid-config
License
MIT License. Feel free to use this package in your projects!