Skip to content

Configuration Patterns

Learn how to configure and manage variable key types using runtime registration for type safety and explicit model configuration.

Variable Keys uses strict runtime registration via the VariableKeys facade. Every model using the HasVariablePrimaryKey trait must be explicitly registered.

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,
],
]);
}
}

Registration requires enum instances, not strings:

// ✅ Correct - uses enum
VariableKeys::map([
User::class => ['primary_key_type' => PrimaryKeyType::ULID],
]);
// ❌ Wrong - strings not allowed
VariableKeys::map([
User::class => ['primary_key_type' => 'ulid'], // Type error
]);

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: ModelNotRegisteredException

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')
),
],
]);
}
}

Use environment variables with enum conversion:

.env

APP_PRIMARY_KEY_TYPE=ulid

Service Provider

use Cline\VariableKeys\Enums\PrimaryKeyType;
VariableKeys::map([
User::class => [
'primary_key_type' => PrimaryKeyType::from(
env('APP_PRIMARY_KEY_TYPE', 'id')
),
],
]);
USER_PRIMARY_KEY_TYPE=ulid
ORGANIZATION_PRIMARY_KEY_TYPE=uuid
POST_PRIMARY_KEY_TYPE=id
VariableKeys::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')
),
],
]);

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')
),
];
}
}
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],
]);
}
}
}

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,
],
]);

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],
]);
}
}
VariableKeys::map([
User::class => [
'primary_key_type' => Feature::active('use-ulids')
? PrimaryKeyType::ULID
: PrimaryKeyType::ID,
],
]);

Ensure migrations match model registration:

// Migration
use 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],
]);

Always register in service provider boot() method, never in models:

// ✅ Good
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
VariableKeys::map([...]);
}
}
// ❌ Bad - never in models
class User extends Model
{
public function __construct()
{
VariableKeys::map([...]); // Don't do this
}
}

Explicitly register every model - no fallbacks or wildcards:

// ✅ Good - explicit registration
VariableKeys::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 support
VariableKeys::map([
'*' => ['primary_key_type' => PrimaryKeyType::ULID], // Not supported
]);

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],
]);
/**
* 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],
]);