relaticle/custom-fields
Laravel/Filament plugin to add dynamic custom fields to any Eloquent model without migrations. Includes 20+ field types, conditional visibility, tenant isolation, admin UI integration (forms/tables/infolists), CSV import/export, optional encryption, and extensible field types.
The Custom Fields package allows you to replace the default models with your own implementations.
Register your custom models using the CustomFields class:
use Relaticle\CustomFields\CustomFields;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
CustomFields::useCustomFieldModel(YourCustomField::class);
CustomFields::useValueModel(YourCustomFieldValue::class);
CustomFields::useOptionModel(YourCustomFieldOption::class);
CustomFields::useSectionModel(YourCustomFieldSection::class);
}
}
By default, date and date-time fields use different formats depending on context (forms use Y-m-d, tables use M j, Y, etc.). You can set a single consistent format across all Filament components.
use Relaticle\CustomFields\CustomFieldsPlugin;
CustomFieldsPlugin::make()
->dateDisplayFormat('m/d/Y')
->dateTimeDisplayFormat('m/d/Y h:i A')
use Relaticle\CustomFields\CustomFields;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
CustomFields::useDateDisplayFormat('m/d/Y');
CustomFields::useDateTimeDisplayFormat('m/d/Y h:i A');
}
}
The configured format applies to:
| Component | Without config | With config |
|---|---|---|
| Form date pickers | Y-m-d / Y-m-d H:i:s |
Your format |
| Table columns | M j, Y / M j, Y H:i |
Your format |
| Infolist entries | Y-m-d / Y-m-d H:i:s |
Your format |
| Validation messages | M j, Y |
Your format |
::alert{type="info"}
This only affects how dates are displayed. Storage formats (Y-m-d and Y-m-d H:i:s) remain unchanged.
::
Pass null to reset back to component defaults:
CustomFields::useDateDisplayFormat(null);
The configuration file (config/custom-fields.php) allows you to customize all aspects of the Custom Fields package. It uses modern fluent configurators for type safety and better IDE support.
Configure which models can have custom fields:
'entity_configuration' => EntityConfigurator::configure()
->discover(app_path('Models')) // Auto-discover models in this path
->cache(true), // Enable caching for performance
Control which field types are available:
'field_type_configuration' => FieldTypeConfigurator::configure()
->enabled([]) // Empty = all enabled
->disabled(['file-upload']) // Disable specific field types
->discover(true) // Auto-discover custom field types
->cache(enabled: false, ttl: 3400),
Configure package features using the enum-based system:
'features' => FeatureConfigurator::configure()
->enable(
CustomFieldsFeature::FIELD_CONDITIONAL_VISIBILITY,
CustomFieldsFeature::FIELD_ENCRYPTION,
CustomFieldsFeature::FIELD_OPTION_COLORS,
CustomFieldsFeature::UI_TABLE_COLUMNS,
CustomFieldsFeature::UI_TOGGLEABLE_COLUMNS,
CustomFieldsFeature::UI_TABLE_FILTERS,
CustomFieldsFeature::SYSTEM_MANAGEMENT_INTERFACE
)
->disable(
CustomFieldsFeature::SYSTEM_MULTI_TENANCY
),
Configure the custom fields management page:
'management' => [
'slug' => 'custom-fields', // URL slug
'navigation_sort' => -1, // Navigation sort order
'navigation_group' => true, // Group in navigation
'cluster' => null, // Optional cluster assignment
],
Customize table names and paths:
'database' => [
'migrations_path' => database_path('custom-fields'),
'table_names' => [
'custom_field_sections' => 'custom_field_sections',
'custom_fields' => 'custom_fields',
'custom_field_values' => 'custom_field_values',
'custom_field_options' => 'custom_field_options',
],
'column_names' => [
'tenant_foreign_key' => 'tenant_id',
],
],
The package supports these features that can be enabled/disabled:
| Feature | Description |
|---|---|
FIELD_CONDITIONAL_VISIBILITY |
Show/hide fields based on conditions |
FIELD_ENCRYPTION |
Encrypt sensitive field values |
FIELD_OPTION_COLORS |
Color-coded options for select fields |
FIELD_CODE_AUTO_GENERATE |
Auto-generate field codes from names |
FIELD_MULTI_VALUE |
Allow multiple values per field |
FIELD_UNIQUE_VALUE |
Enforce unique constraint per entity type |
FIELD_VALIDATION_RULES |
Enable validation rule configuration |
UI_TABLE_COLUMNS |
Show custom fields as table columns |
UI_TOGGLEABLE_COLUMNS |
Allow users to toggle column visibility |
UI_TOGGLEABLE_COLUMNS_HIDDEN_DEFAULT |
Hide toggleable columns by default |
UI_TABLE_FILTERS |
Enable filtering by custom field values |
UI_FIELD_WIDTH_CONTROL |
Custom field width per field |
SYSTEM_MANAGEMENT_INTERFACE |
Enable the management interface |
SYSTEM_MULTI_TENANCY |
Enable multi-tenant isolation |
SYSTEM_SECTIONS |
Enable field grouping in sections |
::alert{type="info"} If your custom models include tenant-specific scoping logic, you'll need to register a custom tenant resolver to ensure validation works correctly. ::
Limit available field types in production:
'field_type_configuration' => FieldTypeConfigurator::configure()
->enabled([
'text',
'textarea',
'number',
'select',
'checkbox',
'date',
])
->disabled([
'rich-editor', // Disable rich content editors
'markdown-editor', // Disable markdown editor
'file-upload', // Disable file uploads
]),
Optimize for production:
'entity_configuration' => EntityConfigurator::configure()
->discover(app_path('Models'))
->cache(env('CUSTOM_FIELDS_CACHE', true)), // Enable caching
'field_type_configuration' => FieldTypeConfigurator::configure()
->cache(enabled: true, ttl: 3600), // Cache field types
Enable tenant isolation:
'features' => FeatureConfigurator::configure()
->enable(
CustomFieldsFeature::SYSTEM_MULTI_TENANCY,
// ... other features
),
'database' => [
// ... other config
'column_names' => [
'tenant_foreign_key' => 'tenant_id', // Your tenant foreign key
],
],
If you've extended the CustomField or CustomFieldSection models with custom tenant handling (e.g., custom global scopes), register a tenant resolver to ensure validation and queries respect your custom logic:
use Relaticle\CustomFields\CustomFields;
// In your AppServiceProvider or plugin boot method
CustomFields::resolveTenantUsing(fn() => auth()->user()?->company_id);
::alert{type="success"} The custom resolver takes priority over Filament's built-in tenancy, giving you complete control over tenant resolution. ::
Common Patterns:
// Auth-based tenancy
CustomFields::resolveTenantUsing(fn() => auth()->user()?->company_id);
// Header-based (APIs)
CustomFields::resolveTenantUsing(fn() => request()->header('X-Tenant-ID'));
// Session-based
CustomFields::resolveTenantUsing(fn() => session('current_tenant_id'));
::alert{type="success"}
Need help? Check that your resolver returns the correct tenant ID using TenantContextService::getCurrentTenantId() in your application.
::
Use environment variables for flexible configuration:
'entity_configuration' => EntityConfigurator::configure()
->discover(app_path('Models'))
->cache(env('CUSTOM_FIELDS_CACHE', !app()->isLocal())),
'field_type_configuration' => FieldTypeConfigurator::configure()
->cache(enabled: env('CUSTOM_FIELDS_CACHE_TYPES', true)),
How can I help you explore Laravel packages today?