NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Package Toolkit for Laravel

Key types & schema macros

A package’s schema freezes at its first tagged release, so the key type of the models it relates to is configured up front rather than fixed to bigint — UUID- and ULID-keyed models are supported alongside the auto-incrementing default. KeyType resolves the host’s choice from config:

// config/comments.php
return [
    'key_type' => 'bigint',   // bigint | uuid | ulid (or a KeyType case) — match the host's models
];
use RoundlyConsulting\PackageToolkit\Enums\KeyType;

KeyType::fromConfig('comments.key_type');                  // KeyType::BigInt | KeyType::Uuid | KeyType::Ulid; 'uiid' throws
KeyType::fromConfig('comments.key_type', KeyType::Ulid);   // a different default for a key that is not set (absent, null, blank)

// config('comments.key_type') === 'uiid' — a typo
// throws InvalidConfigurationException:
// Configuration value [comments.key_type] must be one of [bigint, uuid, ulid] (case-insensitive), [uiid] given.

KeyType::fromValue('UUID');   // KeyType::Uuid — trimmed, case-insensitive
KeyType::fromValue('nope');   // KeyType::BigInt — a raw string still falls back silently
Config valueCaseColumns
'bigint'KeyType::BigIntunsignedBigInteger / morphs
'uuid'KeyType::Uuiduuid / uuidMorphs
'ulid'KeyType::Ulidulid / ulidMorphs
A KeyType caseThat case, as-is—
Absent, null or blank ('' / whitespace)The default (KeyType::BigInt unless you pass another)—
Anything else ('uiid', false, an int)Throws InvalidConfigurationException—

fromConfig() accepts a KeyType case or its string value — trimmed and matched case-insensitively. A key that is not set — absent, null or blank ('' or whitespace) — reads as the default (bigint, or the KeyType you pass as the second argument). Any other value, such as a typo’d 'uiid', throws InvalidConfigurationException instead of silently building bigint columns for a UUID-keyed host. fromValue() maps a raw string the same way but still falls back to its default for anything unrecognised — use it for values that are not config.

Schema macros

Register the macros with RegistersBlueprintMacros (see Opt-in traits), then use them in migrations:

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use RoundlyConsulting\PackageToolkit\Enums\KeyType;

$type = KeyType::fromConfig('comments.key_type'); // KeyType::BigInt | KeyType::Uuid | KeyType::Ulid; 'uiid' throws

Schema::create('comments', function (Blueprint $table) use ($type): void {
    $table->id();
    $table->ownerKey('author_id', $type);              // FK column of the right type, indexed
    $table->morphKey('subject', $type);                // subject_type / subject_id morph pair (+ index)
    $table->polymorphicSubject('target', $type, true); // nullable morph pair
    $table->auditable();                               // timestamps() + softDeletes()
});
MacroResult
ownerKey(string $name, KeyType $type, bool $nullable = false, bool $index = true)A single foreign-key column (unsignedBigInteger / uuid / ulid), optionally nullable and indexed. Returns the ColumnDefinition.
morphKey(string $name, KeyType $type, bool $nullable = false)The correct morphs / uuidMorphs / ulidMorphs pair (+ nullable variants) — <name>_type and <name>_id with a composite index.
polymorphicSubject(string $name, KeyType $type, bool $nullable = false)A polymorphic subject column pair — the morph convention under a domain-friendly name.
auditable()timestamps() + softDeletes().

ownerKey() uses $name as the column name as-is — pass author_id for an author_id column — and returns the ColumnDefinition, so further column modifiers chain onto it. morphKey() and polymorphicSubject() use $name as the prefix of the _type / _id pair. The macro logic also lives as plain static methods on RoundlyConsulting\PackageToolkit\Support\BlueprintMacros, called with an explicit Blueprint.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.