Updated 15 July 2026

Aureus ERP is a modular ERP built on Laravel and Filament. Every business tracks slightly different data, so a fixed set of fields is never enough.
A distributor wants a “GST Number” on products. A studio wants a “Billable” toggle on tasks. This post shows how Aureus lets users add those fields themselves, with no developer and no migration.
In Filament, a form is defined in code. A developer lists each field in a resource’s form() method.
That is fine until a customer needs a field you did not ship. In an ERP this happens constantly, across products, orders, and contacts.
The old answer was “raise a ticket, we will add a migration and deploy”. That is slow, and it does not scale to hundreds of customers who each want something different.
So the goal was clear. Let a user define a new field from the admin panel, and have it behave like any built-in field everywhere.
The common shortcut is to dump custom values into one JSON column, or into an EAV (entity-attribute-value) table.
Both work, but both hurt later. Filtering, sorting, and reporting on a JSON blob or an EAV join is slow and awkward.
Aureus took the opinionated route: create a real, typed column for each custom field. You keep native indexing, filtering, and sorting, at the cost of running schema changes at runtime.
The design has three parts: a definition, a real column, and the code that makes Eloquent and Filament aware of it.
When a user creates a field, a row is saved in the custom_fields table. The Field model describes it:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
$table->string('code'); // 'gst_number' -> becomes the column name $table->string('name'); // 'GST Number' -> the label $table->string('type'); // text, select, toggle, checkbox_list... $table->string('input_type'); // integer, numeric... $table->boolean('is_multiselect'); $table->json('options'); // for select / radio $table->json('form_settings'); $table->boolean('use_in_table'); $table->json('table_settings'); $table->json('infolist_settings'); $table->string('customizable_type'); // e.g. Webkul\Sale\Models\Order $table->unique(['code', 'customizable_type']); |
The important column is customizable_type. It says exactly which model this field belongs to.

This is the part most people do not expect. A FieldsColumnManager runs a live schema change when a field is saved:
|
1 2 3 4 5 6 7 8 9 10 |
public static function createColumn(Field $field): void { $table = static::getTableName($field); // resolve model -> table Schema::table($table, function (Blueprint $table) use ($field) { if (Schema::hasColumn($table->getTable(), $field->code)) { return; } static::addColumn($table, $field); }); } |
It also chooses the correct column type from the field’s type:
|
1 2 3 4 5 6 7 |
'text' => integer | decimal | string // based on input_type 'textarea','editor','markdown' => 'text' 'select' => $field->is_multiselect ? 'json' : 'string' 'checkbox','toggle' => 'boolean' 'checkbox_list' => 'json' 'datetime' => 'datetime' 'default' => 'string' |
Every custom column is created as nullable(). There are matching updateColumn() and deleteColumn() methods, so deleting a field drops its column.
This runs automatically when the field is saved. The Fields admin page calls the manager in its lifecycle hooks:
|
1 2 3 4 5 |
// CreateField page protected function afterCreate(): void { FieldsColumnManager::createColumn($this->record); } |
|
1 2 3 4 5 |
// EditField page protected function afterSave(): void { FieldsColumnManager::updateColumn($this->record); } |
The result: create a “GST Number” field on Products, and a real gst_number column appears on the products table, at runtime.
A new column is useless if Eloquent does not know about it. A model trait, HasCustomFields, hooks the model lifecycle:
The casts are type-aware, so a multi-select becomes an array, a toggle becomes a boolean, and so on.
On the UI side, four Filament components carry the field through the whole panel: a form field, a table column, an infolist entry, and a filter. A resource merges them into its own schema using a companion trait.
Here is exactly how the Projects module wires this up. It takes two small changes.
1. Add the model trait to make the Project model custom-field aware:
|
1 2 3 4 5 6 |
use Webkul\Field\Traits\HasCustomFields; class Project extends Model { use HasCustomFields; // extends $fillable and $casts at runtime } |
2. Add the Filament trait to the resource, then merge custom fields into the form, table, and infolist:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
use Webkul\Field\Filament\Traits\HasCustomFields; class ProjectResource extends Resource { use HasCustomFields; public static function form(Schema $schema): Schema { return $schema->components([ Section::make('Additional') ->schema(static::mergeCustomFormFields([ Select::make('user_id')->relationship('user', 'name'), // ...your normal fields ])), ]); } public static function table(Table $table): Table { return $table->columns(static::mergeCustomTableColumns([ TextColumn::make('name'), // ...your normal columns ])); } public static function infolist(Schema $schema): Schema { return $schema->components([ Section::make('Additional') ->schema(static::mergeCustomInfolistEntries([ TextEntry::make('user.name'), // ...your normal entries ])), ]); } } |
That is all. Every merge helper accepts include() and exclude() arrays, so you can choose which custom fields appear where.
From now on, any field a user defines for Projects shows up automatically in the form, table, and infolist, with no further code.
You don’t have to build this from scratch. The same implementation is packaged as the Aureus ERP Custom Fields plugin for Filament.

No. Filament schemas are defined in PHP by developers. There is no built-in way for an end user to add a field at runtime.
Aureus adds this with a field definition, a runtime column, and components that render it.
Real columns. Each custom field gets its own typed, nullable column on the model’s table.
This keeps filtering, sorting, and reporting fast and native.
Any model that uses the HasCustomFields trait. The field targets that model through its customizable_type.
Filament’s static schemas are the right default for developers, but an ERP has to bend to each customer’s data.
Aureus closed that gap by treating a custom field as a first-class column: defined by the user, created at runtime, understood by Eloquent, and rendered by Filament. The trade is a live schema change for a field that behaves exactly like any other, everywhere it appears.
Want to see how Aureus ERP can adapt to your data? Tell us about your company and we will walk you through it.
Further reading: Aureus ERP Custom Fields plugin, Laravel schema and migrations, Eloquent casting
Tell us about Your Company

If you have more details or questions, you can reply to the received confirmation email.
Back to Home
Be the first to comment.