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 For Laravel Laravel Package

guava/sqids-for-laravel

Generate short, YouTube-style unique IDs from numbers in Laravel. This package wraps Sqids with a facade, configuration, validation rules, and Eloquent helpers so you can encode and decode IDs cleanly across your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish.

composer require guava/sqids-for-laravel
php artisan vendor:publish --tag=sqids-config   # confirm tag in the package README

The service provider is auto-discovered, so there is nothing to register. Note the version constraint: ^1.2 is the line that supports Laravel 12; 1.1.x caps illuminate/* below 12 and simply won't resolve on a Laravel 12 app. Pin explicitly and record it in your dependency inventory — this is a small community package with zero dependents.

Where to look first.

  1. config/sqids.php — the only real configuration surface: alphabet, min_length, blocklist. Read it before writing any code; these three values are effectively permanent once IDs are in the wild.
  2. The facade alias and helper names — check the README/composer.json extra.laravel.aliases rather than guessing between Sqids:: / Sqid::.
  3. The HasSqids trait — this is the highest-leverage piece and the reason to use the wrapper instead of the raw sqids/sqids library.

Fastest first use case: hide an auto-incrementing ID in a route.

// config/sqids.php (published)
return [
    'alphabet'   => env('SQIDS_ALPHABET', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'),
    'min_length' => (int) env('SQIDS_MIN_LENGTH', 8),
    'blocklist'  => ['admin', 'login', 'api'],
];
use Guava\Sqids\Facades\Sqids;

Sqids::encode(1042);        // "k3Rt9zQa"  (example)
Sqids::decode('k3Rt9zQa');  // [1042]  — always an array

Spike one low-risk endpoint end to end before touching anything else; that single round trip (encode out, decode in, route binding resolves) surfaces almost every integration issue you'll hit later.

Implementation Patterns

1. Model trait + route binding (use this by default).

Putting HasSqids on a model makes getRouteKey() return the encoded form and makes implicit route-model binding decode inbound values automatically. This removes facade calls from controllers entirely and is the cleanest adoption path:

use Guava\Sqids\Traits\HasSqids;

class Order extends Model
{
    use HasSqids;
}
// Route: /orders/{order} — binding decodes for you.
Route::get('/orders/{order}', fn (Order $order) => new OrderResource($order));

// Outbound URL generation also picks up the encoded key:
route('orders.show', $order);   // /orders/k3Rt9zQa

Keep foreign keys and primary keys numeric. Encoding is a presentation concern — do not store encoded values in the database, and do not migrate the schema. That keeps the change fully reversible.

2. Encode at the API/resource boundary, not in accessors.

Prefer explicit encoding in API resources so the transformation is visible in one place and you never accidentally leak a raw ID from a toArray():

class OrderResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id'       => Sqids::encode($this->id),
            'customer' => Sqids::encode($this->customer_id),
        ];
    }
}

Remember to update OpenAPI schemas and client SDKs — the id field becomes a string, not an integer. Clients with strict typing will break otherwise.

3. Inbound decoding with the validation rule.

The package ships a validation rule so tampered or malformed IDs fail at the FormRequest layer instead of blowing up in the controller:

public function rules(): array
{
    return [
        'order' => ['required', new Sqid()],   // confirm exact class/namespace in README
    ];
}

Then decode explicitly and treat failure as a 404:

$decoded = Sqids::decode($request->string('order'));

// decode() returns [] on invalid input — never pass it straight into find()
abort_if($decoded === [], 404);

$order = Order::findOrFail($decoded[0]);

Add tests for: valid round trip, tampered input, random garbage, an empty string, and route binding with a bad key.

4. Composite / nested resource tokens.

Arrays are first-class, and order is preserved on round trip. This is the pattern for tenant-scoped links where you want to carry two IDs in one opaque-looking segment:

$token = Sqids::encode([$tenantId, $orderId]);   // "9zKq3Rt..."

[$tenantId, $orderId] = Sqids::decode($token);   // destructure, or abort if []

Sqids adds no integrity guarantee on the relationship between the two values, so still authorize the tenant/order pairing after decoding — a valid-looking token for tenant A and order B decodes perfectly happily.

5. Helpers and views.

Use the helper (or facade) for one-off links in Blade, emails, and notifications. For anything repeated, wrap it in a small app-level service (PublicId::encode()) so you have one seam to mock, one place to swap implementations if the package goes stale, and one place to enforce the "never call decode() without checking []" rule.

6. Testing and mocking.

The package binds into the service container, so you can fake it — but in practice most teams don't need to. Encoding is deterministic and CPU-local with no I/O, so round-trip tests are cheap and more valuable than mocks. If you do need determinism, set sqids.alphabet / min_length in phpunit.xml <env> blocks so test output doesn't depend on a developer's .env.

7. Rollout sequencing for existing endpoints.

Spike one endpoint → standardize alphabet/min_length/blocklist → add invalid-ID tests → roll out behind existing authorization → update docs/OpenAPI/clients → broaden. If URLs change, support a dual read path (accept both integer and encoded segment) during the transition rather than a hard cutover, and 301 the old form.

Gotchas and Tips

decode() always returns an array and returns [] on failure. This is the single most common bug. Sqids::decode($x) is not nullable, it does not throw on garbage, and Model::find([]) returns a collection, not null — so a bad ID silently becomes an empty result set or an unexpected 200 instead of a 404. Always guard: abort_if($decoded === [], 404).

This is obfuscation, not encryption, and not authorization. Anyone who knows (or guesses) the alphabet and min_length can decode your IDs. Default alphabet + default min length is trivially reversible. Never use encoded IDs as capability tokens, password reset handles, signed download links, or access control. Your policy checks must remain independent of ID format. Set a custom alphabet at minimum, and treat it as a public-ish secret at best.

Config is baked in by config:cache. The encoder is a singleton resolved from config at container-build time. Two consequences: (a) drift between local/staging/production alphabets produces IDs that don't decode across environments — decide and document this deliberately; (b) runtime config mutation (e.g. per-tenant alphabets, config()->set() mid-request, some Octane patterns) will silently not work. If you need multiple alphabets, bind your own encoder instances rather than mutating config.

Changing alphabet, min_length, or blocklist is a breaking change for every issued ID. Emails already sent, bookmarks, third-party callbacks, printed invoices — all invalidate. Decide these values before launch and write them down as a product-level decision. Rotating them requires a dual-decode window that tries old and new configurations.

min_length is a floor, not a guarantee. Encoded length grows with the number of values and magnitude of the numbers, and blocklist matches cause regeneration with a longer output. Don't build UI that assumes a fixed column width.

blocklist matters more than it looks. Generated strings are short and can spell unfortunate words. Populate the blocklist with your brand name, reserved route words, and anything the compliance team cares about. It costs a few characters occasionally and prevents embarrassing share links.

decode() on garbage can surface an unexpected integer. Sqids is designed to reject invalid input, but do not assume a non-empty decode means the record exists or that the ID was legitimately issued by you. The authorization layer is the backstop, not the ID format.

Debugging: reproduce in php artisan tinker with Sqids::decode(Sqids::encode([1,2,3])) to confirm the container binding and config are live. If route binding misbehaves, check getRouteKey() on the model — a hand-rolled getRouteKeyName() or an explicit {order:id} in the route overrides the trait. If IDs from one environment don't decode in another, diff the cached config: php artisan config:show sqids in each environment, not the .env files.

Extension points: the trait (override getRouteKey() / add an encoded_id accessor), the validation rule (subclass it if you need custom 404 vs 422 semantics), and the container binding (swap in your own wrapper class to get a single seam). If the package goes stale, the fallback is sqids/sqids directly — the encode/decode surface is nearly identical — so keep all call sites behind your own small service class and the migration is a one-file change.

Supply chain: zero dependents, community-maintained, no vendor support. Pin an exact minor version, note it in the dependency inventory with an owner, and review it during each Laravel upgrade. The blast radius is small precisely because the wrapper is thin — but only if you haven't spread Sqids:: calls through 40 controllers.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource