Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Eloquent Hashids Laravel Package

naabster/laravel-eloquent-hashids

Integrate Hashids with Laravel Eloquent models: automatically encode numeric IDs into short, obfuscated strings and decode them back. Supports route model binding, custom salts, and seamless model lookups for cleaner, safer URLs.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin Laravel integration around vinkla/hashids, giving you short, reversible, non-sequential identifiers for integers. Its job is to keep auto-increment primary keys out of URLs, API payloads and emails while remaining stateless (no lookup table, no extra column).

Minimal setup:

composer require <vendor>/<package> "^1.0"
php artisan vendor:publish --provider="Vinkla\Hashids\HashidsServiceProvider"
// .env — never commit this value, and never change it once links are in the wild
HASHIDS_SALT=some-long-random-string

Where to look first:

  1. config/hashids.php — the default connection plus per-connection salt, length and alphabet.
  2. The service container binding / facade accessor — confirm whether you resolve Hashids::connection('main') or inject the manager, since this determines how you test it.
  3. The provider registration in config/app.php (Laravel 5 packages of this vintage still expect manual provider + alias registration).

First real use case — opaque IDs in routes:

Route::get('users/{user}', 'UserController@show');

// controller
public function show($user)
{
    $id = Hashids::decode($user);        // returns [] on garbage input

    abort_if(empty($id), 404);

    return User::findOrFail($id[0]);
}

If you only do one thing with this package, do that — and then immediately move to route-key binding below, because hand-rolling decode/abort in every controller does not scale.

Implementation Patterns

Route model binding via the route key. The cleanest integration is to teach the model to speak hashids, so controllers and route() helpers stay untouched:

trait HasHashidRouteKey
{
    public function getRouteKey()
    {
        return Hashids::encode($this->getKey());
    }

    public function resolveRouteBinding($value)
    {
        $decoded = Hashids::decode($value);

        return $this->where($this->getKeyName(), $decoded[0] ?? null)->first();
    }
}

On older 5.x point releases without resolveRouteBinding, override getRouteKeyName() is not enough — you need a Route::bind('user', ...) in RouteServiceProvider instead. Check which your Laravel minor supports before copying snippets.

API resources / presenters. Encode at the boundary, never in the model's toArray():

return [
    'id'   => Hashids::encode($this->id),
    'name' => $this->name,
    // relations referenced by id need encoding too
    'team_id' => Hashids::encode($this->team_id),
];

Encoding in toArray() looks convenient until you need the raw integer internally and start calling getRawOriginal() everywhere. Keep DB-facing code numeric, presentation-facing code hashed.

Inbound validation. Decoding twice (once in a form request, once in the controller) is a common smell. Either add a rule:

Validator::extend('hashid', function ($attribute, $value) {
    return count(Hashids::decode($value)) === 1;
});

…or resolve in a form request and pass the model forward via $request->route()->setParameter().

Multiple connections. Use separate connections, not a single global salt, when two entity types must not be mutually decodable — e.g. users and invoices connections. A hashid from one connection will not decode under another, which gives you a cheap cross-tenant / cross-entity guard.

Queues and exports. Anything that stores a URL (mailables, queued notifications, CSV exports, sitemaps) must encode at generation time, not render time. Persisted links outlive config changes.

Gotchas and Tips

  • The salt is the whole security model. Anyone with the salt can decode every ID you have ever issued. Treat it as a secret, keep it in .env, and rotate only with a migration plan — rotation instantly invalidates every link, bookmark, and third-party integration.
  • decode() never throws. It returns [] for anything it cannot parse. Every call site needs an explicit empty check, or you will silently findOrFail(null) and produce confusing 404s.
  • length is cosmetic, not security. A min length of 8 makes IDs look uniform; it does not add entropy. Only the salt and the input range do.
  • Not encryption. Hashids is reversible by design. Do not put anything sensitive in it, and do not use it as a signed-URL substitute.
  • Alphabet constraints. Minimum 16 characters, no spaces, and avoid characters that survive badly in URLs, spreadsheets, or read-aloud support calls. Changing the alphabet changes all output — same blast radius as changing the salt.
  • Case and URL handling. Output is case-sensitive. Normalise nothing, but be aware that proxies, analytics tools, and users retyping links will occasionally mangle it. Consider a 301-to-canonical handler if links are shared widely.
  • Decoded values are strings in arrays. Hashids::decode($x)[0] is a string; comparing it to an int with === will fail. Cast before use.
  • No DB index. You cannot query WHERE hashid = ?. Always decode first, then query by primary key — otherwise you invite full table scans.
  • Testing. Bind a deterministic salt in phpunit.xml (<env name="HASHIDS_SALT" value="test-salt"/>) so fixtures are stable, and assert against encoded values only where the exact string matters.
  • Version 1.0.4 specifically. This release only bumps the required vinkla/hashids to the Laravel 5-compatible line — no API change on your side, but it does change the transitive dependency tree. Update with composer update vinkla/hashids <vendor>/<package> --with-dependencies, then run the suite: the Laravel 5 branch of vinkla/hashids changed its own service provider registration and config publishing semantics, so a stale config/hashids.php from an earlier install may not match the new key names. If php artisan vendor:publish was run under the old version, diff your published config against the new package default rather than assuming it still applies. Pin both packages in composer.json and avoid ^1.0 ranges if you deploy frequently and want reproducible dependency resolution.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package