| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Let your end-users add dynamic fields to any Eloquent model + Filament resource at runtime, without writing a single migration. Ships an admin CRUD for managing field definitions, an Eloquent trait that auto-merges the new columns into your model's fillable/casts, a Filament resource trait with five merge helpers that inject fields into forms, tables, filters, infolists, and a runtime schema manager that creates the underlying DB columns for you.
composer require aureuserp/custom-fieldsThe service provider is auto-discovered. The migration is registered via Spatie's package-tools and run on php artisan migrate.
php artisan migrateNote
Migrating from webkul/fields: the custom_fields table migration keeps its original timestamp filename, so existing installations will see it already applied — no duplicate-table errors, no re-run.
use Illuminate\Database\Eloquent\Model;
use Webkul\CustomFields\Concerns\HasCustomFields;
class Employee extends Model
{
use HasCustomFields;
}Any custom field code defined against Webkul\Employee\Models\Employee is now mass-assignable and properly cast.
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Webkul\CustomFields\Filament\Concerns\HasCustomFields;
class EmployeeResource extends Resource
{
use HasCustomFields;
public static function form(Schema $schema): Schema
{
return $schema->components(
static::mergeCustomFormFields([
// your base fields
])
);
}
public static function table(Table $table): Table
{
return $table
->columns(static::mergeCustomTableColumns([ /* base */ ]))
->filters(static::mergeCustomTableFilters([ /* base */ ]));
}
}Visit /admin/custom-fields → New field → pick a resource → pick a field type → configure options/validations → save.
The field now appears in the resource's form, table, filters, and infolist automatically.
Webkul\CustomFields\Concerns\HasCustomFields
Boots three lifecycle listeners (retrieved, creating, updating) that call loadCustomFields():
Type-to-cast mapping:
| Field type | Cast |
|---|---|
| select (multiselect) | array |
| select (single) | string |
| checkbox, toggle | boolean |
| checkbox_list | array |
| everything else | string |
Webkul\CustomFields\Filament\Concerns\HasCustomFields
Five static helpers — each accepts a base array + optional include/exclude lists and returns base + custom:
static::mergeCustomFormFields(array $base, array $include = [], array $exclude = []): array
static::mergeCustomTableColumns(array $base, array $include = [], array $exclude = []): array
static::mergeCustomTableFilters(array $base, array $include = [], array $exclude = []): array
static::mergeCustomTableQueryBuilderConstraints(array $base, array $include = [], array $exclude = []): array
static::mergeCustomInfolistEntries(array $base, array $include = [], array $exclude = []): arrayinclude=[] means "all fields"; a non-empty list whitelists field codes. exclude blacklists field codes.
Each injector is also usable standalone:
use Webkul\CustomFields\Filament\Forms\Components\CustomFields;
use Webkul\CustomFields\Filament\Infolists\Components\CustomEntries;
use Webkul\CustomFields\Filament\Tables\Columns\CustomColumns;
use Webkul\CustomFields\Filament\Tables\Filters\CustomFilters;
CustomFields::make(MyResource::class)
->include(['hobbies'])
->exclude(['internal_notes'])
->getSchema();Webkul\CustomFields\CustomFieldsColumnManager
Three static methods called on Field model lifecycle (create/update/delete):
The DB type mapping uses getColumnType() which routes via FieldType::tryFrom($field->type):
| Field type | DB column type |
|---|---|
| text | string / integer / decimal (based on input_type) |
| textarea, editor, markdown | text |
| select (multiselect) | json |
| select (single), radio, color | string |
| checkbox, toggle | boolean |
| checkbox_list | json |
| datetime | datetime |
| Enum | Cases → values | Default |
|---|---|---|
| FieldType | Text='text', Textarea='textarea', Select='select', Checkbox='checkbox', Radio='radio', Toggle='toggle', CheckboxList='checkbox_list', DateTime='datetime', Editor='editor', Markdown='markdown', ColorPicker='color' | FieldType::Text |
| InputType | Text, Email, Numeric, Integer, Password, Tel, Url, Color | InputType::Text |
Both enums expose a default() static for symbolic-constant fallbacks:
use Webkul\CustomFields\Enums\FieldType;
use Webkul\CustomFields\Enums\InputType;
$type = FieldType::tryFrom($raw) ?? FieldType::default();Here's a full Employee resource adopting the trait. End-users can add a "hobbies" multiselect via the admin CRUD; the field flows through form, table, and infolist automatically.
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Webkul\CustomFields\Filament\Concerns\HasCustomFields;
use Webkul\Employee\Models\Employee;
class EmployeeResource extends Resource
{
use HasCustomFields;
protected static ?string $model = Employee::class;
public static function form(Schema $schema): Schema
{
return $schema->components(static::mergeCustomFormFields([
TextInput::make('name')->required(),
Select::make('department_id')->relationship('department', 'name'),
]));
}
public static function table(Table $table): Table
{
return $table
->columns(static::mergeCustomTableColumns([
TextColumn::make('name'),
]))
->filters(static::mergeCustomTableFilters([]));
}
}And the Eloquent side:
use Webkul\CustomFields\Concerns\HasCustomFields;
class Employee extends Model
{
use HasCustomFields;
protected $fillable = ['name', 'department_id'];
}
// After an admin defines a "hobbies" checkbox_list field:
$employee = Employee::create([
'name' => 'Alice',
'department_id' => 1,
'hobbies' => ['chess', 'hiking'], // ← custom field, automatically fillable + cast to array
]);
$employee->hobbies; // ['chess', 'hiking'] ← automatically cast from JSONShips with en and ar under the custom-fields:: namespace. Publish to customise:
php artisan vendor:publish --tag="custom-fields-translations"The translation file includes 70+ validation rule labels, 200+ Filament setting names, plus navigation and form labels for the admin CRUD.
Every navigation / identity / placement setting is configurable two ways — pick whichever fits your project:
// app/Providers/Filament/AdminPanelProvider.php
use Webkul\CustomFields\CustomFieldsPlugin;
->plugins([
CustomFieldsPlugin::make()
->navigationGroup(__('admin.navigation.setting'))
->navigationLabel('Custom Fields')
->navigationIcon('heroicon-o-puzzle-piece')
->navigationSort(50)
->navigationBadge(fn () => \Webkul\CustomFields\Models\Field::count())
->navigationBadgeColor('primary')
->slug('admin/custom-fields')
->cluster(\App\Filament\Clusters\AdminTools::class),
])php artisan vendor:publish --tag="custom-fields-config"That writes config/custom-fields.php to your app. Edit any key:
// config/custom-fields.php
return [
'navigation' => [
'label' => 'Custom Fields',
'group' => 'Settings',
'icon' => 'heroicon-o-puzzle-piece',
'sort' => 50,
'badge' => null,
'register'=> true,
],
'resource' => [
'register' => true,
'slug' => 'admin/custom-fields',
'cluster' => \App\Filament\Clusters\AdminTools::class,
'model_label' => null,
'plural_model_label' => null,
],
];When the Resource renders, each value is resolved by the first matching rule:
| Fluent setter | Config key |
|---|---|
| navigationLabel($v) | custom-fields.navigation.label |
| navigationGroup($v) | custom-fields.navigation.group |
| navigationIcon($v) | custom-fields.navigation.icon |
| activeNavigationIcon($v) | custom-fields.navigation.active_icon |
| navigationSort($v) | custom-fields.navigation.sort |
| navigationBadge($v) | custom-fields.navigation.badge |
| navigationBadgeColor($v) | custom-fields.navigation.badge_color |
| navigationBadgeTooltip($v) | custom-fields.navigation.badge_tooltip |
| navigationParentItem($v) | custom-fields.navigation.parent_item |
| subNavigationPosition($v) | custom-fields.navigation.sub_position |
| registerNavigation($bool) | custom-fields.navigation.register |
| modelLabel($v) | custom-fields.resource.model_label |
| pluralModelLabel($v) | custom-fields.resource.plural_model_label |
| slug($v) | custom-fields.resource.slug |
| cluster($class) | custom-fields.resource.cluster |
| tenantRelationshipName($v) | custom-fields.resource.tenant_relationship |
| registerResource($bool) | custom-fields.resource.register |
Only want the Eloquent trait + table-injection API, not the admin menu?
CustomFieldsPlugin::make()->registerResource(false),
// or in config/custom-fields.php:
'resource' => ['register' => false],The FieldResource routes are skipped; everything else still works.
php artisan vendor:publish --tag="custom-fields-config"
php artisan vendor:publish --tag="custom-fields-migrations"
php artisan vendor:publish --tag="custom-fields-translations"vendor/bin/pest plugins/aureuserp/custom-fields/tests/Feature31 tests (114 assertions) across:
| Area | Coverage |
|---|---|
| Architecture | Field model extends Eloquent Model + implements Sortable, Plugin implements Filament\Contracts\Plugin, SP extends Spatie, no debug calls in shipped code |
| Enums | FieldType / InputType — all cases have expected values, default() works, tryFrom returns null for unknown |
| Field model | Table name, fillable, casts, SoftDeletes trait, Sortable config |
| Policy | All 10 CRUD + soft-delete permission methods exist |
| Eloquent trait | Trait exists, applies to host model without error, mergeFillable dedups, declares fill / mergeCasts |
| Filament trait | Trait exists, all 5 merge helpers declared, merge helpers combine base + custom arrays |
| Component API | CustomFields/Entries/Columns/Filters::make()->include()->exclude() chainable and return static |
| Column manager | Exposes createColumn/updateColumn/deleteColumn static methods |
| Symptom | Fix |
|---|---|
| Class not found for Webkul\CustomFields\… | composer dump-autoload && php artisan optimize:clear |
| Trait methods not firing | Confirm the Eloquent trait is on your model (use HasCustomFields;) and that Field::where('customizable_type', …) returns rows |
| Custom column doesn't appear in the DB | Check CustomFieldsColumnManager::createColumn() ran — it's called from CreateField::afterCreate() on save. Verify the host table already exists. |
| Policy denies everything | Generate Shield policies: php artisan shield:generate --resource=FieldResource |
| custom_fields table missing | Run php artisan migrate |
Email support@webkul.com for security-related reports instead of opening a public issue.
PRs welcome. Before submitting:
vendor/bin/pest plugins/aureuserp/custom-fields/tests/Feature
vendor/bin/pint plugins/aureuserp/custom-fieldsMIT. See LICENSE.md.
| Back | FazBrowse Home | New Git URL |