Configuration Patterns
Learn how to configure and manage variable key types using runtime registration for type safety and explicit model configuration.
Runtime Registration
Section titled “Runtime Registration”Variable Keys uses strict runtime registration via the VariableKeys facade. Every model using the HasVariablePrimaryKey trait must be explicitly registered.
Basic Registration
Section titled “Basic Registration”Register models in your service provider’s boot() method:
use Cline\VariableKeys\Facades\VariableKeys;use Cline\VariableKeys\Enums\PrimaryKeyType;use App\Models\{User, Post, Comment};
class AppServiceProvider extends ServiceProvider{ public function boot(): void { VariableKeys::map([ User::class => [ 'primary_key_type' => PrimaryKeyType::ULID, ], Post::class => [ 'primary_key_type' => PrimaryKeyType::ULID, ], Comment::class => [ 'primary_key_type' => PrimaryKeyType::ID, ], ]); }}Type Safety
Section titled “Type Safety”Registration requires enum instances, not strings:
// ✅ Correct - uses enumVariableKeys::map([ User::class => ['primary_key_type' => PrimaryKeyType::ULID],]);
// ❌ Wrong - strings not allowedVariableKeys::map([ User::class => ['primary_key_type' => 'ulid'], // Type error]);Strict Validation
Section titled “Strict Validation”Models using the trait must be registered or an exception is thrown:
use Cline\VariableKeys\Database\Concerns\HasVariablePrimaryKey;
class User extends Model{ use HasVariablePrimaryKey; // Must be registered}
// If not registered:$user = new User();// Throws: ModelNotRegisteredExceptionPackage-Specific Registration
Section titled “Package-Specific Registration”Packages should register their models in their own service providers:
namespace Vendor\Package;
use Cline\VariableKeys\Facades\VariableKeys;use Cline\VariableKeys\Enums\PrimaryKeyType;use Vendor\Package\Models\{Ability, Role};
class PackageServiceProvider extends ServiceProvider{ public function boot(): void { // Register package models VariableKeys::map([ Ability::class => [ 'primary_key_type' => PrimaryKeyType::from( config('package.primary_key_type', 'id') ), ], Role::class => [ 'primary_key_type' => PrimaryKeyType::from( config('package.primary_key_type', 'id') ), ], ]); }}Environment-Based Configuration
Section titled “Environment-Based Configuration”Use environment variables with enum conversion:
.env
APP_PRIMARY_KEY_TYPE=ulidService Provider
use Cline\VariableKeys\Enums\PrimaryKeyType;
VariableKeys::map([ User::class => [ 'primary_key_type' => PrimaryKeyType::from( env('APP_PRIMARY_KEY_TYPE', 'id') ), ],]);Per-Model Environment Variables
Section titled “Per-Model Environment Variables”USER_PRIMARY_KEY_TYPE=ulidORGANIZATION_PRIMARY_KEY_TYPE=uuidPOST_PRIMARY_KEY_TYPE=idVariableKeys::map([ User::class => [ 'primary_key_type' => PrimaryKeyType::from( env('USER_PRIMARY_KEY_TYPE', 'id') ), ], Organization::class => [ 'primary_key_type' => PrimaryKeyType::from( env('ORGANIZATION_PRIMARY_KEY_TYPE', 'id') ), ], Post::class => [ 'primary_key_type' => PrimaryKeyType::from( env('POST_PRIMARY_KEY_TYPE', 'id') ), ],]);Configuration Helpers
Section titled “Configuration Helpers”Create helper methods for consistent configuration:
class AppServiceProvider extends ServiceProvider{ public function boot(): void { $this->registerVariableKeys(); }
protected function registerVariableKeys(): void { VariableKeys::map([ User::class => $this->keyConfig('USER'), Organization::class => $this->keyConfig('ORGANIZATION'), Post::class => $this->keyConfig('POST'), ]); }
protected function keyConfig(string $prefix): array { return [ 'primary_key_type' => PrimaryKeyType::from( env("{$prefix}_PRIMARY_KEY_TYPE", 'id') ), ]; }}Multi-Tenancy Patterns
Section titled “Multi-Tenancy Patterns”Tenant-Specific Key Types
Section titled “Tenant-Specific Key Types”use Cline\VariableKeys\Facades\VariableKeys;use Cline\VariableKeys\Enums\PrimaryKeyType;
class TenantServiceProvider extends ServiceProvider{ public function boot(): void { // Global models use ULIDs VariableKeys::map([ Tenant::class => ['primary_key_type' => PrimaryKeyType::ULID], ]);
// Tenant-scoped models use auto-increment if (tenancy()->initialized) { VariableKeys::map([ Post::class => ['primary_key_type' => PrimaryKeyType::ID], Comment::class => ['primary_key_type' => PrimaryKeyType::ID], ]); } }}Polymorphic Configuration
Section titled “Polymorphic Configuration”Register models with morph types for polymorphic relationships:
use Cline\VariableKeys\Enums\{PrimaryKeyType, MorphType};
VariableKeys::map([ User::class => [ 'primary_key_type' => PrimaryKeyType::ULID, 'morph_type' => MorphType::ULID, ], Organization::class => [ 'primary_key_type' => PrimaryKeyType::UUID, 'morph_type' => MorphType::UUID, ],]);Testing Configuration
Section titled “Testing Configuration”Test-Specific Registration
Section titled “Test-Specific Registration”Override registration in tests:
use Cline\VariableKeys\Facades\VariableKeys;use Cline\VariableKeys\Enums\PrimaryKeyType;
class UserTest extends TestCase{ protected function setUp(): void { parent::setUp();
// Use auto-increment in tests for simplicity VariableKeys::clear(); VariableKeys::map([ User::class => ['primary_key_type' => PrimaryKeyType::ID], ]); }}Feature Flags
Section titled “Feature Flags”VariableKeys::map([ User::class => [ 'primary_key_type' => Feature::active('use-ulids') ? PrimaryKeyType::ULID : PrimaryKeyType::ID, ],]);Migration Consistency
Section titled “Migration Consistency”Ensure migrations match model registration:
// Migrationuse Cline\VariableKeys\Enums\PrimaryKeyType;
Schema::create('users', function (Blueprint $table) { $table->variablePrimaryKey(PrimaryKeyType::ULID); $table->string('name'); $table->timestamps();});
// Model registration (must match)VariableKeys::map([ User::class => ['primary_key_type' => PrimaryKeyType::ULID],]);Best Practices
Section titled “Best Practices”1. Register in Service Providers
Section titled “1. Register in Service Providers”Always register in service provider boot() method, never in models:
// ✅ Goodclass AppServiceProvider extends ServiceProvider{ public function boot(): void { VariableKeys::map([...]); }}
// ❌ Bad - never in modelsclass User extends Model{ public function __construct() { VariableKeys::map([...]); // Don't do this }}2. Explicit Over Implicit
Section titled “2. Explicit Over Implicit”Explicitly register every model - no fallbacks or wildcards:
// ✅ Good - explicit registrationVariableKeys::map([ User::class => ['primary_key_type' => PrimaryKeyType::ULID], Post::class => ['primary_key_type' => PrimaryKeyType::ULID], Comment::class => ['primary_key_type' => PrimaryKeyType::ULID],]);
// ❌ Bad - no wildcard supportVariableKeys::map([ '*' => ['primary_key_type' => PrimaryKeyType::ULID], // Not supported]);3. Consistency Across Models
Section titled “3. Consistency Across Models”Use the same key type across related models:
$keyType = PrimaryKeyType::ULID;
VariableKeys::map([ User::class => ['primary_key_type' => $keyType], Post::class => ['primary_key_type' => $keyType], Comment::class => ['primary_key_type' => $keyType],]);4. Document Decisions
Section titled “4. Document Decisions”/** * Primary Key Strategy: ULID * * Using ULIDs for: * - Distributed database support * - Time-ordered queries * - URL-safe identifiers * - No enumeration attacks */VariableKeys::map([ User::class => ['primary_key_type' => PrimaryKeyType::ULID],]);