philetaylor/hashids-bundle
Symfony bundle integrating Hashids to obfuscate numeric IDs as short, unique, non-sequential strings. Provides services and Twig filters for encoding and decoding IDs with configurable salt, length, and alphabet.
This is a Symfony bundle, not a Laravel package — the first decision is whether you're actually on Symfony. If you're a Laravel developer looking at this repo, note that Laravel already has first-party community options (vinkla/hashids), and this bundle gives you nothing on Laravel. Assume the rest of this assessment targets a Symfony app that wants short, obfuscated public IDs (e.g. /orders/x7Kp2m instead of /orders/18432).
Minimal steps:
composer require philetaylor/hashids-bundle (it wraps hashids/hashids).config/bundles.php — with 1 star and a 2022 release, expect the older Bundle class registration style, not a modern Flex recipe. There is no Flex recipe, so you will hand-write the config.config/packages/hashids.yaml (verify the exact root key against the bundle's DependencyInjection/Configuration.php, since docs are thin):# config/packages/philetaylor_hashids.yaml
philetaylor_hashids:
salt: '%env(HASHIDS_SALT)%'
min_hash_length: 8
alphabet: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
HASHIDS_SALT (bin/console secrets:set HASHIDS_SALT or .env.local). Never commit a production salt.bin/console debug:container hashids (or whatever the service ID actually is — inspect Resources/config/services.xml in the bundle).First use case: replace a raw auto-increment ID in a route with an encoded token, and decode it back in the controller. That's 90% of what this bundle is for; anything more elaborate (Twig filters, Doctrine integration) is usually your own glue code rather than something the bundle ships.
Controller injection. Type-hint the Hashids service (or the bundle's alias) into your action, or constructor-inject it into a service. If the bundle doesn't autowire under a friendly alias, add one in config/services.yaml:
services:
Hashids\Hashids:
alias: hashids # confirm actual service id first
Route-level decoding. Prefer decoding in one place rather than scattering decode() calls. A small HashidParamConverter (or a custom route requirement + listener) keeps controllers clean:
public function show(string $publicId, Hashids $hashids): Response
{
$ids = $hashids->decode($publicId); // returns [] on garbage
if ($ids === []) {
throw $this->createNotFoundException();
}
return $this->render('order/show.html.twig', [
'order' => $this->orders->find($ids[0]),
]);
}
Exposing the public ID on entities. Don't recalculate in templates. Either:
getPublicId(): string method on the entity that calls a statically-injected encoder (ugly but common), orpostLoad listener that stamps a non-persisted $publicId property, or a serializer normalizer that rewrites id → encoded form on output and decodes on input.Twig. The bundle may or may not register a filter. If not, add a tiny extension:
public function hashid(int $id): string { return $this->hashids->encode($id); }
Then {{ path('order_show', {publicId: order.id|hashid}) }}.
Symfony Form / API Platform. Use a data transformer (Form) or an input DTO + denormalizer (API Platform) so encoded IDs are decoded once at the edge and your domain layer only ever sees integers. This is the pattern that scales; sprinkling decode() in controllers does not.
Multiple salt domains. If you need stable-per-tenant or per-entity-type tokens, instantiate additional new Hashids($salt_for_tenant, 8) directly — the bundle gives you one configured instance, not a factory. Keep the bundle for the "default" case.
[], your code 404s). Treat rotation as a migration, not a config tweak.min_hash_length only pads output; it does not add entropy. Two configs with different salts/lengths produce non-interoperable tokens — keep dev/staging/prod aligned or you'll chase phantom decode failures.encode(0) and empty values behave surprisingly ('' in, [] out on decode). Guard decode() for the [] case explicitly and never assume index 0 exists.BIGINT UNSIGNED IDs near the top of the range, or UUIDs, are out of scope. If you're on UUID PKs this package is the wrong tool.composer.json, and seriously consider skipping the bundle: composer require hashids/hashids and one service definition in services.yaml reproduces the entire feature with zero third-party Symfony coupling and a maintained library underneath.composer show philetaylor/hashids-bundle and the bundle's composer.json constraints before adopting; bin/console debug:container and debug:config are your fastest diagnosis tools when the service doesn't appear.hashids/hashids API instead, which is stable and well-documented.How can I help you explore Laravel packages today?