Automatically create short hashes for your Laravel models, automatically use them in urls and back-fill existing models.
Database IDs in URLs such as stackoverflow.com/users/12280 never quite feel ok to me
- having the index of an entity allows website size and growth to be estimated
- accessing something that shouldn't be accessible is just a careless developer and a curious visitor away
But at the same time, I didn't want to completely replace the database ID with a UUID, as some packages do - as I was nervous about how this might affect performance.
laravel-model-hash allows you to generate a short, random hash when an item is created while still using auto-incrementing IDs for database relationships.
This package provides a trait which - when attached to a model - creates a random string hash when new instances of the model are created.
The hash is created by shuffling an alphabet, taking the first length characters, and checking for uniqueness in the database (up to a maximum number of times). If a unique hash wasn't found within the maximum attempts, an exception of type UniqueHashNotFoundException
will be thrown and the model instance will not be saved.
The package is configured either globally using a config file, or by defining model-specific properties.
You can install the package via Composer:
composer require adamhopkinson/laravel-model-hash
If you would like to tweak any of the package configuration, you must first publish the config file:
php artisan vendor:publish --provider="AdamHopkinson\LaravelModelHash\LaravelModelHashServiceProvider"
To add a hash to model, add the LaravelModelHash
trait:
use AdamHopkinson\LaravelModelHash\Traits\ModelHash;
class MyModel extends Model
{
use ModelHash;
//...
}
And create a migration to add a field to store the hash - by default, the field is called hash
and has a length of 5:
$table->string('hash', 5)->unique()->index();
You may add a unique and index flag for database performance improvements. If you're adding this trait to an existing model, you will need to use nullable()
until all records have a hash.
Now, when new instances of the model are created, they will be assigned a random string.
If you are adding this package to an existing installation, there is an artisan command to 'backfill' existing models. The syntax is:
php artisan laravelmodelhash:populate
This uses reflection to find all models (in app_path()
) which extend the ModelHash
trait, and adds hash values to any records where the hash is currently null
.
If you would like to only run this for a specific model, use the --model
(or -M
) option - but remember to double-slash the namespace:
php artisan laravelmodelhash:populate --model \\App\\MyModel
There are three configurable parts to the hash:
- the alphabet used (ie the set of allowed characters)
- the name of the hash field
- the length of the hash
And two other configuration properties
- the maximum number of attempts to find a hash which isn't already present in the database before an exception is thrown
- whether to automatically use the hash in route model binding
Each of these is set in the default configuration, which can be overridden in /config/laravelmodelhash.php
(this must first be published - see the Installation section for details).
If you want to change these settings per model, they can be changed by adding the following model properties:
protected $hashName; // string
protected $hashLength; // int
protected $hashAlphabet; // string
protected $hashMaximumAttempts; // int
protected $useHashInRoutes; // boolean
I've gotten this far thanks to the following:
- John Braun and his excellent guide to Laravel Package Development
- Jeff Way of Laracasts fame
- the amazing Laravel community