PHPackages                             aureuserp/custom-fields - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. aureuserp/custom-fields

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

aureuserp/custom-fields
=======================

A Filament v5 plugin that lets end-users add dynamic custom fields to any Eloquent model + Filament resource at runtime.

914↓75%2PHP

Since Apr 21Pushed 1mo agoCompare

[ Source](https://github.com/aureuserp/custom-fields)[ Packagist](https://packagist.org/packages/aureuserp/custom-fields)[ RSS](/packages/aureuserp-custom-fields/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

Custom Fields for Filament v5
=============================

[](#custom-fields-for-filament-v5)

[![Latest Version on Packagist](https://camo.githubusercontent.com/d2173d2df7d4da84e69ae06042d11afb6d1e9d4f71ea2d348cfe00294b9666b6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6175726575736572702f637573746f6d2d6669656c64732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/aureuserp/custom-fields)[![License](https://camo.githubusercontent.com/354ab4e3a7fa2e4c8a41aecea6b6884ca911bbb949151e8cf8506cb27a96e90c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6175726575736572702f637573746f6d2d6669656c64732e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

   ![Custom Fields — Filament v5 plugin](art/banner.svg)

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.

---

Table of contents
-----------------

[](#table-of-contents)

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick start](#quick-start)
- [API reference](#api-reference)
    - [Eloquent trait](#eloquent-trait)
    - [Filament resource trait](#filament-resource-trait)
    - [Components](#components)
    - [Column manager](#column-manager)
- [Enums](#enums)
- [Real-world example](#real-world-example)
- [Translations](#translations)
- [Publishing resources](#publishing-resources)
- [Testing](#testing)
- [Troubleshooting](#troubleshooting)
- [Security](#security)
- [Contributing](#contributing)
- [Credits](#credits)
- [License](#license)

---

Features
--------

[](#features)

- **Eloquent trait** (`HasCustomFields`) — drop onto any model; custom field codes auto-merge into `$fillable` and `$casts` at runtime
- **Filament resource trait** — 5 one-line merge helpers: `mergeCustomFormFields`, `mergeCustomTableColumns`, `mergeCustomTableFilters`, `mergeCustomTableQueryBuilderConstraints`, `mergeCustomInfolistEntries`
- **Admin CRUD** (`/admin/custom-fields`) — create, edit, sort, soft-delete dynamic fields per resource, with full validation / formatting settings
- **11 field types** via `FieldType` enum — Text, Textarea, Select, Checkbox, Radio, Toggle, CheckboxList, DateTime, Editor, Markdown, ColorPicker
- **8 text input types** via `InputType` enum — Text, Email, Numeric, Integer, Password, Tel, Url, Color
- **Schema manager** (`CustomFieldsColumnManager`) — programmatically add / drop DB columns when fields are created or deleted
- **Table integration** — the defined fields automatically surface as `CustomColumns` (if `use_in_table=true`) and `CustomFilters`
- **Spatie-sortable** — drag-to-reorder fields with `order_column_name=sort`
- **Soft deletes** — recover deleted field definitions
- **Policy + permissions** — Filament Shield compatible, with `view_any_field_field`, `create_field_field` etc.
- **Translations** — `en` and `ar` shipped (navigation + form labels + validation names + setting names)
- **Pest test suite** — architecture + model + policy + trait + components + enums (31 tests)

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11+
- Filament v5+
- `spatie/eloquent-sortable` v4 (already a Filament dependency)

---

Installation
------------

[](#installation)

```
composer require aureuserp/custom-fields
```

The service provider is auto-discovered. The migration is registered via Spatie's package-tools and run on `php artisan migrate`.

```
php artisan migrate
```

Note

**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.

---

Quick start
-----------

[](#quick-start)

### 1. Mark your model

[](#1-mark-your-model)

```
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.

### 2. Extend your Filament resource

[](#2-extend-your-filament-resource)

```
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 */ ]));
    }
}
```

### 3. Define fields in the admin CRUD

[](#3-define-fields-in-the-admin-crud)

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.

---

API reference
-------------

[](#api-reference)

### Eloquent trait

[](#eloquent-trait)

`Webkul\CustomFields\Concerns\HasCustomFields`

Boots three lifecycle listeners (`retrieved`, `creating`, `updating`) that call `loadCustomFields()`:

- **`loadCustomFields()`** — queries `Field::where('customizable_type', get_class($this))`, merges every field's `code` into `$fillable` and applies type-appropriate casts.
- **`mergeFillable(array $attributes)`** — public helper exposed for ad-hoc merging.
- **`mergeCasts($attributes)`** — public helper; accepts either an array (passes through to parent) or a Collection of Field records.
- **`getCustomFields()`** (protected) — override this if you want to scope the query (e.g. to a specific tenant).

Type-to-cast mapping:

Field typeCast`select` (multiselect)`array``select` (single)`string``checkbox`, `toggle``boolean``checkbox_list``array`everything else`string`### Filament resource trait

[](#filament-resource-trait)

`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 = []): array
```

`include=[]` means "all fields"; a non-empty list whitelists field codes. `exclude` blacklists field codes.

### Components

[](#components)

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();
```

### Column manager

[](#column-manager)

`Webkul\CustomFields\CustomFieldsColumnManager`

Three static methods called on Field model lifecycle (create/update/delete):

- `createColumn(Field $field)` — adds the column to the customisable model's table with the appropriate DB type
- `updateColumn(Field $field)` — creates the column if missing (for rename/resurrect scenarios)
- `deleteColumn(Field $field)` — drops the column

The DB type mapping uses `getColumnType()` which routes via `FieldType::tryFrom($field->type)`:

Field typeDB 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`---

Enums
-----

[](#enums)

EnumCases → valuesDefault`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();
```

---

Real-world example
------------------

[](#real-world-example)

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 JSON
```

---

Translations
------------

[](#translations)

Ships 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.

---

Configuration
-------------

[](#configuration)

Every navigation / identity / placement setting is configurable **two ways** — pick whichever fits your project:

### 1. Fluent setters in your panel provider (recommended for per-panel overrides)

[](#1-fluent-setters-in-your-panel-provider-recommended-for-per-panel-overrides)

```
// 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),
])
```

### 2. Publishable config file (recommended for app-wide defaults)

[](#2-publishable-config-file-recommended-for-app-wide-defaults)

```
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,
    ],
];
```

### Resolution order

[](#resolution-order)

When the Resource renders, each value is resolved by the first matching rule:

1. **Fluent setter** on `CustomFieldsPlugin::make()->…()` — highest priority
2. **Config file** `config('custom-fields.*')` — if the setter was not called
3. **Hardcoded fallback** in `getPluginDefaults()` — when both above are `null`

### Full setter → config key map

[](#full-setter--config-key-map)

Fluent setterConfig 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`### Disable the admin CRUD entirely

[](#disable-the-admin-crud-entirely)

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.

---

Publishing resources
--------------------

[](#publishing-resources)

```
php artisan vendor:publish --tag="custom-fields-config"
php artisan vendor:publish --tag="custom-fields-migrations"
php artisan vendor:publish --tag="custom-fields-translations"
```

---

Testing
-------

[](#testing)

```
vendor/bin/pest plugins/aureuserp/custom-fields/tests/Feature
```

**31 tests (114 assertions)** across:

AreaCoverageArchitectureField model extends Eloquent Model + implements Sortable, Plugin implements `Filament\Contracts\Plugin`, SP extends Spatie, no debug calls in shipped codeEnums`FieldType` / `InputType` — all cases have expected values, `default()` works, `tryFrom` returns null for unknownField modelTable name, fillable, casts, SoftDeletes trait, Sortable configPolicyAll 10 CRUD + soft-delete permission methods existEloquent traitTrait exists, applies to host model without error, `mergeFillable` dedups, declares `fill` / `mergeCasts`Filament traitTrait exists, all 5 merge helpers declared, merge helpers combine base + custom arraysComponent API`CustomFields/Entries/Columns/Filters::make()->include()->exclude()` chainable and return `static`Column managerExposes `createColumn`/`updateColumn`/`deleteColumn` static methods---

Troubleshooting
---------------

[](#troubleshooting)

SymptomFix`Class not found` for `Webkul\CustomFields\…``composer dump-autoload && php artisan optimize:clear`Trait methods not firingConfirm the Eloquent trait is on your model (`use HasCustomFields;`) and that `Field::where('customizable_type', …)` returns rowsCustom column doesn't appear in the DBCheck `CustomFieldsColumnManager::createColumn()` ran — it's called from `CreateField::afterCreate()` on save. Verify the host table already exists.Policy denies everythingGenerate Shield policies: `php artisan shield:generate --resource=FieldResource``custom_fields` table missingRun `php artisan migrate`---

Security
--------

[](#security)

Email `support@webkul.com` for security-related reports instead of opening a public issue.

---

Contributing
------------

[](#contributing)

PRs welcome. Before submitting:

```
vendor/bin/pest plugins/aureuserp/custom-fields/tests/Feature
vendor/bin/pint plugins/aureuserp/custom-fields
```

---

Credits
-------

[](#credits)

- [Webkul](https://webkul.com) — plugin author
- [Filament team](https://filamentphp.com) — the excellent admin framework
- [filamentphp/plugin-skeleton](https://github.com/filamentphp/plugin-skeleton) — structural template

---

License
-------

[](#license)

MIT. See [LICENSE.md](LICENSE.md).

###  Health Score

24

—

LowBetter than 31% of packages

Maintenance59

Moderate activity, may be stable

Popularity16

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/b12adc21e990c055e325a3c07339b3d4d313efd49f98a768c1a86fc7776f2b86?d=identicon)[Jitendra Singh](/maintainers/Jitendra%20Singh)

---

Top Contributors

[![suraj-webkul](https://avatars.githubusercontent.com/u/121420732?v=4)](https://github.com/suraj-webkul "suraj-webkul (5 commits)")

### Embed Badge

![Health badge](/badges/aureuserp-custom-fields/health.svg)

```
[![Health](https://phpackages.com/badges/aureuserp-custom-fields/health.svg)](https://phpackages.com/packages/aureuserp-custom-fields)
```

###  Alternatives

[imanghafoori/laravel-nullable

A package to help you write expressive defensive code in a functional manner

151461.3k6](/packages/imanghafoori-laravel-nullable)[brickx/maintenance-switch

Simple plugin to toggle maintenance mode from Filament Panels.

1927.8k1](/packages/brickx-maintenance-switch)[dh2y/think-qrcode

qrcode for thinkphp5

1065.8k](/packages/dh2y-think-qrcode)[akkaweb/cakephp-facebook

CakePHP 3 Facebook Plugin

2114.9k](/packages/akkaweb-cakephp-facebook)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
