Skip to content

Models & Keys ​

FilamentCraft ships ten Eloquent models. You can pick their primary key type and swap any of them for your own subclass.

Primary key type ​

Choose it at install:

bash
php artisan filamentcraft:install --keys=uuid

Without --keys, an interactive install asks, preselecting your User model's key type. The choice is written to config/filamentcraft.php:

php
'database' => [
    'key_type' => 'uuid', // int (default) | uuid | ulid
],
key_typePrimary keysColumn
intAuto-incrementing integersbigint unsigned
uuidOrdered UUIDs, generated on createuuid (native on Postgres, char(36) on MySQL)
ulidLowercase ULIDs, generated on createchar(26)

It covers every FilamentCraft table and every key between them. The models generate the ids, so you don't need to add HasUuids to anything. It works the same on SQLite, MySQL/MariaDB and PostgreSQL.

Set it before the first migrate

The migrations read key_type when they create the tables. Changing it afterwards doesn't convert them. filamentcraft:doctor fails when the config and the tables disagree. Existing installs keep integer keys.

Owner and user columns ​

filamentcraft_sites.owner_id and the user_id columns point at your models, so they are typed after those models, not after key_type:

  • Owner: tenancy.owner_model, else your panel's tenant model.
  • User: auth.providers.users.model.

HasUuids and HasUlids models get uuid and ulid columns. Integer models get bigint. Other string keys get a string column. Set the type yourself when detection can't see the model at migrate time:

php
'database' => [
    'key_type' => 'uuid',
    'owner_key_type' => 'uuid',
    'user_key_type' => 'int',
],

Your own model classes ​

Extend a model to add traits, casts, relations, scopes or observers:

bash
php artisan make:filamentcraft-model Site

This writes app/Models/FilamentCraft/Site.php and registers it in the published config:

php
namespace App\Models\FilamentCraft;

use FilamentCraft\Models\Site as BaseSite;

class Site extends BaseSite
{
    //
}
php
'models' => [
    'site' => App\Models\FilamentCraft\Site::class,
    // template, template_revision, theme, region, media,
    // custom_font, redirect, section_definition, ai_usage, submission
],

FilamentCraft then returns your class everywhere:

  • Site::query(), Site::find(), Site::create(), and static scopes such as Site::live()
  • Relations: $template->site, $site->templates, and HasSite::sites() on your owner model
  • The Filament resources, route model binding, and the package factories

Your global scopes, casts and events apply to all of them.

php
class Site extends BaseSite
{
    use Searchable;

    public function invoices(): HasMany
    {
        return $this->hasMany(Invoice::class); // infers site_id, not custom_site_id
    }

    protected static function booted(): void
    {
        parent::booted();

        static::created(fn (self $site) => Billing::openAccountFor($site));
    }
}

The override must:

  • Extend the FilamentCraft model. Anything else throws at boot and names the config key.
  • Call parent::booted() if you define booted(). FilamentCraft registers its own model hooks there.
  • Keep the table name. The package's migrations and some queries use the filamentcraft_* names directly. filamentcraft:doctor warns if you change $table.

Check the setup ​

bash
php artisan filamentcraft:doctor
Key type [uuid] ........................................... OK
Model [App\Models\FilamentCraft\Site] replaces [Site] ....... OK