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.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require relaticle/custom-fields:^3.1.7
php artisan vendor:publish --provider="Relaticle\CustomFields\CustomFieldsServiceProvider"
Publish the config and migrations (if needed for encryption).
Enable for a Model:
Add the HasCustomFields trait to your Eloquent model:
use Relaticle\CustomFields\Traits\HasCustomFields;
class Product extends Model
{
use HasCustomFields;
}
First Field Definition:
Define fields in a customFields() method (auto-discovered via naming conventions):
public function customFields(): array
{
return [
'description' => [
'type' => 'textarea',
'label' => 'Product Description',
'required' => true,
],
'spec_sheet' => [
'type' => 'file',
'label' => 'Specification Sheet',
'upload_path' => 'products/specs',
],
];
}
Filament Integration:
Use the CustomFieldsWidget in Filament forms/tables:
use Relaticle\CustomFields\Widgets\CustomFieldsWidget;
public static function form(Form $form): Form
{
return $form
->schema([
CustomFieldsWidget::make('product')
]);
}
release_date for a Product):
public function customFields(): array
{
return [
'release_date' => [
'type' => 'date',
'label' => 'Release Date',
'default' => now()->format('Y-m-d'),
],
];
}
php artisan custom-fields:migrate --model=Product --fields=release_date
CustomFieldsWidget.Field Definition Patterns:
'shipping_info' => [
'type' => 'group',
'fields' => [
'weight' => ['type' => 'number', 'label' => 'Weight (kg)'],
'dimensions' => ['type' => 'text', 'label' => 'Dimensions (LxWxH)'],
],
],
'features' => [
'type' => 'repeater',
'fields' => [
'name' => ['type' => 'text'],
'value' => ['type' => 'text'],
],
],
Conditional Logic:
'warranty' => [
'type' => 'select',
'label' => 'Warranty',
'options' => ['1-year', '2-year', 'lifetime'],
'visible' => ['field' => 'is_premium', 'operator' => '==', 'value' => true],
],
field_code is correctly referenced:
'features' => [
'type' => 'repeater',
'visible' => ['field' => 'has_features', 'operator' => '==', 'value' => true],
],
Multi-Tenancy:
tenant_id column (auto-handled):
// In config/custom-fields.php
'tenant_column' => 'tenant_id',
Validation:
'price' => [
'type' => 'number',
'rules' => 'required|min:0.01',
],
Filament Tables:
use Relaticle\CustomFields\Columns\CustomFieldsColumn;
public static function table(Table $table): Table
{
return $table
->columns([
CustomFieldsColumn::make('product')
->fields(['description', 'release_date']),
]);
}
API Responses:
public function toArray($request)
{
return [
'id' => $this->id,
'custom_fields' => $this->customFields->toArray(),
];
}
Events:
use Relaticle\CustomFields\Events\FieldUpdated;
FieldUpdated::listen(function (FieldUpdated $event) {
// Log or trigger side effects
});
Encryption:
api_keys):
'api_key' => [
'type' => 'text',
'encrypted' => true,
],
config/custom-fields.php:
'encryption' => [
'enabled' => true,
'key' => env('CUSTOM_FIELDS_ENCRYPTION_KEY'),
],
Field Name Collisions:
product_sku vs. user_sku).Migration Issues:
php artisan custom-fields:migrate --model=Product --fields=*
custom_fields JSON column. Add it manually if needed:
Schema::table('products', function (Blueprint $table) {
$table->json('custom_fields')->nullable();
});
Conditional Logic Bugs:
$field->isVisible($model); // Manually check visibility
visible for static conditions, visible_if for dynamic checks:
'visible_if' => ['field' => 'is_active', 'operator' => '!=', 'value' => false],
field_code in visibility conditions.Performance:
repeater or group fields in large datasets. Consider denormalizing for Filament tables.// In a service provider
Cache::rememberForever('custom-fields:Product', fn() => Product::first()->customFields());
Field Not Showing?:
customFields() method is autoloaded (use php artisan optimize:clear if stale).config/custom-fields.php under field_types).Encryption Errors:
CUSTOM_FIELDS_ENCRYPTION_KEY is set in .env and matches the config.$decrypted = decrypt($model->custom_fields->api_key);
Filament Widget Issues:
php artisan filament:cache-reset
Custom Field Types:
Relaticle\CustomFields\FieldTypes\BaseField:
namespace App\CustomFields;
use Relaticle\CustomFields\FieldTypes\BaseField;
class CustomSlider extends BaseField
{
public static function getType(): string { return 'custom_slider'; }
public function getWidget(): string { return 'custom-slider'; }
}
config/custom-fields.php:
'field_types' => [
'custom_slider' => \App\CustomFields\CustomSlider::class,
],
Field Components:
php artisan vendor:publish --tag=custom-fields-assets
resources/js/custom-fields.Database Backend:
How can I help you explore Laravel packages today?