Lithium Hosting companion package for Laravel apps.
Find a file
2026-07-08 21:09:17 +00:00
src initial commit 2026-07-08 21:09:17 +00:00
.gitignore initial commit 2026-07-08 21:09:17 +00:00
composer.json initial commit 2026-07-08 21:09:17 +00:00
LICENSE initial commit 2026-07-08 21:09:17 +00:00
LICENSE.md initial commit 2026-07-08 21:09:17 +00:00
README.md initial commit 2026-07-08 21:09:17 +00:00

Laravel Companion

A small Laravel utility kit that ships two things you reach for over and over again on every model-heavy project:

  • HasLongIdsTrait — auto-populates a stable, URL-safe identifier column on every Eloquent model that uses the trait. Use it as the public route key so your URLs read /orders/g4n7-b2k instead of /orders/123 and so consumers can't enumerate your tables by incrementing an integer.
  • RandomStringGenerator — the fluent string builder under the trait. Use it standalone for any other "give me a unique short code" need (support PINs, voucher codes, registrar registration keys, etc.).

Plus a tiny Observer / ModelObserver pair that pre-builds the standard Eloquent lifecycle hooks if you'd rather subclass than copy them every time.

Installation

composer require lithiumhosting/laravel-companion

No service provider registration; the trait is opt-in per model, the generator is new-able anywhere.

HasLongIdsTrait — public IDs without enumeration

Add a long_id column to your table:

Schema::table('orders', function (Blueprint $table) {
    $table->string('long_id', 6)->unique()->after('id');
});

Use the trait on your model:

use LithiumHosting\LaravelCompanion\Traits\HasLongIdsTrait;

class Order extends Model
{
    use HasLongIdsTrait;
}

On every creating event the trait generates a unique value into the column. The check honors SoftDeletes if the model uses it, so a soft-deleted g4n7-b2k won't be reused while it's still recoverable.

If you want the column to be your public route key:

public function getRouteKeyName(): string
{
    return 'long_id';
}

Customization

Set any of these protected static properties on your model to override the defaults:

Property Default Effect
$longIdColumnName 'long_id' Use a different column name (number, slug, reference, etc.)
$longIdColumnLength 6 Generated string length
$longIdCharset 'lower|numbers' Pipe-delimited combo: lower, upper, numbers, special
$longIdSeparator [] [position, char] — e.g. [3, '-'] produces abc-def

Example for an order-number-style column:

class Order extends Model
{
    use HasLongIdsTrait;

    protected static $longIdColumnName = 'number';
    protected static $longIdColumnLength = 8;
    protected static $longIdCharset = 'upper|numbers';
    protected static $longIdSeparator = [4, '-'];   // "AB12-CD34"
}

RandomStringGenerator — standalone

Inject it via app(RandomStringGenerator::class) or just new RandomStringGenerator() and chain:

use LithiumHosting\LaravelCompanion\Utilities\RandomStringGenerator;

$pin = (new RandomStringGenerator())
    ->setLength(6)
    ->setChars('numbers')
    ->setModel(UserSupportPin::class)
    ->setColumn('pin')
    ->generate();

Available builder methods:

Method Purpose
setLength(int) Output length, default 6
setChars(string $type, string $special = '') Charset combo (lower|upper|numbers|special) + optional explicit special chars
setSeparator([position, char]) Insert a separator every N chars
setPrefix(string) Prepend a literal prefix (e.g. 'SUP-')
setModel(string) Eloquent model class to check for uniqueness against
setColumn(string) Column on that model to dedupe against
requireAllClasses(bool) When true, every charset in setChars() MUST appear in the output at least once
generate(bool $withTrashed = false) Produce the string, dedupe against the model + column. Pass true to also check soft-deleted rows.
getRandomString() Produce a string without DB lookup (handy in seeders / tests)

The generator also runs every candidate through a banned-words filter so generated codes don't accidentally read as profanity, especially relevant when handing customers a code to read back on a phone call.

Observer + ModelObserver

Optional. If you write Eloquent observers and want a default no-op base class with every lifecycle hook (creating, created, updating, ..., restoring, restored) pre-stubbed, extend Observer or implement ModelObserver. Includes a setSlug($slug, $model, $slugColumn = 'slug') helper that uses Str::slug() and deduplicates within the model.

License

This package, laravel-companion is licensed under The MIT License (MIT). Please see License File for more information.

Is it any good?

Yes.

When people first hear about a new product, they frequently ask if it is any good. A Hacker News user remarked:

Note to self: Starting immediately, all raganwald projects will have a "Is it any good?" section in the readme, and the answer shall be "yes.".