rossbearman / eloquent-calamari
Obfuscate incrementing IDs with Sqids for Laravel and Eloquent.
Requires
- php: ^8.2
- ext-mbstring: *
- illuminate/config: ^10.0 | ^11.0
- illuminate/contracts: ^10.0 | ^11.0
- illuminate/database: ^10.0 | ^11.0
- illuminate/support: ^10.0 | ^11.0
- sqids/sqids: ^0.4.0
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: ^1.13
- orchestra/testbench: ^8.21 | ^v9.0
- phpunit/phpunit: ^10.5
README
Eloquent Calamari integrates the Sqids1 algorithm into Laravel and Eloquent, enabling you to seamlessly use obfuscated, unique IDs in place of your internal auto-incrementing IDs.
- Obfuscate auto-incrementing IDs with short, unique identifiers
- Unique Sqids across models, ID 1 will be represented differently on every model
- Optional route model binding with
SqidBasedRouting
- Transparently handle non-canonical IDs
example.com/link/2fC37YMkO
=== Link::find(1)
example.com/video/TaRfL1RAK
=== Video::find(1)
Getting Started
Require this package with Composer.
composer require rossbearman/eloquent-calamari
Add the HasSqid
and SqidBasedRouting
traits to your models.
use RossBearman\Sqids\HasSqid; use RossBearman\Sqids\SqidBasedRouting; class Customer extends Model { use HasSqid, SqidBasedRouting; }
Create a route for your model.
Route::get('/customer/{customer}', fn (Customer $customer) => $customer);
$customer = Customer::create(['name' => 'Squidward']); $customer->id; // 1 $customer->sqid; // 3irWXI2rFV
example.com/customer/3irWXI2rFV
now returns the Customer details.
Querying
Common query methods are also available.
Customer::findBySqid($sqid); Customer::findBySqidOrFail($sqid); Customer::whereSqid($sqid)->get(); Customer::whereSqidIn($sqids)->get(); Customer::whereSqidNotIn($sqids)->get();
Representation
By default the Sqid is not included in the model's toArray()
or toJson()
output and the ID is not hidden from these. You can use Eloquent's appends
and hidden
properties to achieve this.
class Customer extends Model { use HasSqid, SqidBasedRouting; protected $appends = ['sqid']; protected $hidden = ['id']; }
Custom Routing
You can take advantage of SqidBasedRouting
while still having a different default binding by overriding the model's getRouteKeyName()
method.
class Customer extends Model { use HasSqid, SqidBasedRouting; public function getRouteKeyName(): string { return 'id'; } }
// Routes by ID by default Route::get('/admin/customer/{customer}', fn (Customer $customer) => $customer); // Routes by Sqid when specified Route::get('/customer/{customer:sqid}', fn (Customer $customer) => $customer);
Configuration
By default, Eloquent Calamari generates a random alphabet for each model, by shuffling the default alphabet with the Xoshiro256StarStar algorithm, seeded with a combination of the name of the model and the key set in the config.
This ensures that entities of different models with the same ID will have a unique Sqid, however it is fragile to the model name or app key being changed. If either of these are changed, Sqids will no longer resolve back to the same ID.
Important
It is highly recommended that you explicitly set a pre-shuffled alphabet for each model using the sqids.alphabets
config key, which will disable the shuffling behaviour for that model.
Setting alphabets
Start by publishing the sqids.php
config file to your config
directory.
php artisan vendor:publish --provider="RossBearman\Sqids\SqidsServiceProvider"
Then generate a new alphabet for your model.
php artisan sqids:alphabet "App\Models\Customer"
You can also generate alphabets for multiple models at once.
php artisan sqids:alphabet "App\Models\Customer" "App\Models\Order" "App\Models\Invoice"
Follow the instructions provided by the command to add the new keys to your config/sqids.php
and .env
files.
Confirm alphabet is being used
To ensure that Eloquent Calamari is using the expected alphabet for a specific model, use the following command.
php artisan sqids:check
This will list all the models that have successfully been registered in the config and whether the class string can be resolved to a class in your application.
Setting minimum Sqid lengths
By default, all Sqids will be a minimum of 10 characters. You can adjust this for each model by assigning different values (to a minimum of 3) to the sqids.min_lengths
config array.
'min_lengths' => [ App\Model\Customer::class => 20, App\Model\Order::class => 8, App\Model\Invoice::class => 30, ],
The maximum length of a Sqid is dependent on the input ID and the alphabet used. A more varied alphabet (upper and lower case letters, numbers and symbols) will result in shorter Sqids.
Canonical Sqids
By design, multiple Sqids can resolve to the same number, however Eloquent Calamari will always return the same Sqid for a given number. Furthermore, this is the only Sqid that can be used to access an entity, and any other Sqid that would normally resolve to the same number will be rejected.
This check can be disabled on a per-model basis by adding an entry to the sqids.canonical_checks
config array.
'canonical_checks' => [ App\Model\Customer::class => false, ],
Development
PHPUnit tests:
composer test
PHPStan analysis:
composer analyse
Pint linting:
composer lint
Security
Please email Ross Bearman ross@rossbearman.co.uk if you have discovered a vulnerability in this package.
License
MIT License (MIT). Please see LICENSE.md for more information.