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

Sqids Bundle Laravel Package

roukmoute/sqids-bundle

A Symfony bundle that integrates Sqids, letting you generate short, unique, URL-friendly IDs from numbers and decode them back. It wires Sqids into Symfony's service container for easy dependency injection and reuse across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

roukmoute/sqids-bundle is the Symfony bundle wrapper around sqids/sqids — the library that turns integer sequences into short, obfuscated-looking string IDs (and back). There is no Laravel service provider in this package, so in a Laravel codebase you generally get the behaviour from sqids/sqids and get the configuration patterns documented here.

First steps (Symfony host app):

  1. composer require roukmoute/sqids-bundle
  2. Ensure Roukmoute\SqidsBundle\SqidsBundle is registered in config/bundles.php (Flex recipes may not auto-register it on older skeletons).
  3. Create config/packages/sqids.yaml:
    sqids:
        alphabet: 'abcdefghijklmnopqrstuvwxyz'
        min_length: 8
        blocklist: ['admin', 'cunt']
    
  4. Autowire Sqids\SqidsInterface — the bundle registers the concrete implementation as a service, no manual factory needed.
  5. Clear the container cache (bin/console cache:clear) after touching the YAML.

First steps (Laravel host app):

  1. composer require sqids/sqids
  2. Bind a singleton in AppServiceProvider:
    $this->app->singleton(SqidsInterface::class, fn () => new Sqids(
        alphabet: config('sqids.alphabet'),
        minLength: config('sqids.min_length', 8),
        blocklist: config('sqids.blocklist', []),
    ));
    
  3. Publish a config/sqids.php mirroring the keys above.

First real use case: give auto-increment primary keys a public-facing representation — /orders/86Rf07xd4z instead of /orders/42. Start by writing a HasSqids trait or an SqidsCast so models expose public_id while keeping the integer key internally.

Look first at the bundle's DependencyInjection/Configuration.php for the exact key names and defaults, and at sqids/sqids's SqidsInterface for the two-method API you'll actually call: encode(array $numbers): string and decode(string $id): array.

Implementation Patterns

URL slugs / route model binding. Encode on the way out, decode in route resolution:

Route::get('/orders/{order}', function (string $order) {
    $ids = app(SqidsInterface::class)->decode($order);
    abort_if($ids === [], 404);
    return Order::findOrFail($ids[0]);
});

Wrap this in a Route::bind or a custom cast rather than repeating it per controller.

Composite IDs. encode([$tenantId, $orderId]) packs a tenant scope into the slug; decode() gives you back both, so you can assert tenant ownership before querying. This is the main reason to prefer Sqids over Hashids-style single-value hashing — the input is a sequence, and order is significant (encode([1,2]) !== encode([2,1])).

Configuration as a service-level singleton. Build the Sqids instance once (container singleton / bind with singleton: true). Construction validates the alphabet, so you want that failure at boot, not at request time.

Environment separation. Different alphabets per environment are tempting but toxic: IDs generated in staging won't decode in production and QA links break. Drive alphabet/min_length from a single env var that is identical across environments except local, and treat it as append-only config.

Extension point for tests. Bind a deterministic alphabet (alphabet: 'abcdefghijklmnopqrstuvwxyz', min_length: 0) in the test container so assertions on generated IDs are stable, or bind a fake SqidsInterface that returns "id-{$n}" when you only care about plumbing.

Migration pattern for existing IDs. Never re-encode in place. Add a public_id column, backfill with a queued command, keep the integer column as the source of truth, and add a unique index on the string. The encoder is cheap; the backfill is the only dangerous step.

Gotchas and Tips

  • It is not encryption. Anyone with the alphabet and library can decode your IDs. Min_length only pads; it does not add entropy. Use it for opaque-looking URLs and enumeration friction, never for authorization or secrets.
  • Changing the alphabet or min_length invalidates every ID you've ever issued. There is no versioning in the payload. If you must rotate, version the column (public_id_v2) and keep the decoder for the old alphabet alive during a transition.
  • decode() on garbage returns [] — but it is lenient and can happily decode a mistyped/near-miss string into a different valid number sequence. Always check count($ids) matches what you encoded, and always re-verify existence in the database rather than trusting the decode.
  • Blocklist hits can break the round trip. When the generated ID contains a blocked substring, the encoder bumps the last input and retries, so decode(encode($n)) may not equal $n. If you rely on exact round-trips, add a test that exercises numbers whose IDs collide with your blocklist, and prefer a blocklist over a min_length hack.
  • Alphabet validation is strict: characters must be unique and the alphabet must be at least 3 characters. Duplicates throw at construction — which, because you bound a singleton, surfaces as a boot-time/config:cache error, not a runtime one. Good, but confusing if you only see it in CI.
  • min_length is a floor, not a fixed width. Short numbers still encode to exactly min_length; large composites grow past it. Don't size a DB column to min_length.
  • Symfony 8 signature change (v0.10.0). loadInternal() now declares a return type. Harmless for consumers, but if you subclass Roukmoute\SqidsBundle\DependencyInjection\SqidsExtension and overrode loadInternal() without a return type, you'll hit a fatal signature error — add : void to your override. This release also signals the package tracks Symfony 8 / current PHP, so pin accordingly if you're still on Symfony 6.
  • Bundle ≠ Laravel. There's no SqidsServiceProvider or facade. In Laravel, register the binding manually (see Getting Started) or you'll get "Target [Sqids\SqidsInterface] is not instantiable" on first resolve.
  • Debugging tip: when an ID "doesn't work", log both $id and json_encode($sqids->decode($id)) before the lookup — nine times out of ten it's a URL-encoding issue (the default alphabet is alphanumeric and case-sensitive, so a lowercased slug silently decodes to something else).
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core