DEV3 Technologies home

Short-code generator with collision retry

Generate unambiguous random short codes and rely on the unique index to detect collisions, retrying and growing the length when a space fills up.

LaravelUtilitiesshort-codeuniqueretryrandom

Code

<?php

namespace App\Support;

final class CodeGenerator
{
    /** 32 symbols, no 0/1/l/o so codes survive being read aloud or printed. */
    private const ALPHABET = 'abcdefghijkmnpqrstuvwxyz23456789';

    /** Words that must never be handed out because they are routes. */
    private const RESERVED = ['admin', 'api', 'login', 'logout', 'app', 'static'];

    public static function make(int $length = 6): string
    {
        do {
            $code = '';
            for ($i = 0; $i < $length; $i++) {
                $code .= self::ALPHABET[random_int(0, 31)];
            }
        } while (in_array($code, self::RESERVED, true));

        return $code;
    }
}

How it works

What it solves

Short URLs, invite codes and coupon codes all need a random token that is short, easy to read aloud and unique. Checking "does this code exist?" before inserting is a race; two requests can pick the same code between the check and the write.

How it works

CodeGenerator draws characters from a 32 symbol alphabet with the lookalikes removed (no 0, 1, l or o), using random_int so the output is cryptographically random. It also rejects reserved words, such as route names, before they ever reach the database.

ShortLinkCreator does not check for existence at all. It just inserts and lets the unique index decide. A duplicate raises UniqueConstraintViolationException, which the loop catches and retries with a fresh code. After a few failures in a row the length grows by one character, so a nearly full space heals itself instead of looping forever. A hard cap on attempts turns a truly exhausted space into a clear exception.

This keeps the happy path to a single query and makes correctness the database's job.

Adapting it

Change the alphabet or starting length, point the creator at your own model, and add case sensitivity by widening the alphabet and the column collation together.

Pitfalls

  • The column needs a unique index with a case-insensitive collation if your alphabet is lowercase only, otherwise users can type variants.
  • Do not wrap the insert in an outer transaction on PostgreSQL; the failed statement aborts it.

How to use it

  1. Create a links table with a unique code column and a url column.
  2. Copy both classes into app/Support and app/Services.
  3. Create a link: app(ShortLinkCreator::class)->create('https://example.com/long/path').
  4. Extend CodeGenerator::RESERVED with your own route prefixes.
  5. Resolve a code with Link::where('code', $code)->firstOrFail() in your redirect route.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets