Download the PHP package byjesper/laravel-custom-fields without Composer
On this page you can find all versions of the php package byjesper/laravel-custom-fields. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download byjesper/laravel-custom-fields
More information about byjesper/laravel-custom-fields
Files in byjesper/laravel-custom-fields
Package laravel-custom-fields
Short Description PostgreSQL-backed custom fields for Laravel models.
License MIT
Informations about the package laravel-custom-fields
Laravel Custom Fields
PostgreSQL-backed custom fields for Laravel models.
Attach user-defined fields to any Eloquent model, store them as JSONB on the model itself, and keep a typed, queryable history of values in a side index table. Optionally scoped by tenant and/or team.
Features
- JSONB storage on the parent model — values live in a single
custom_field_valuescolumn. No N+1 joins to read. - Typed index table — every change is mirrored to a
custom_field_index_valuestable with typed columns (value_string,value_integer,value_decimal,value_boolean,value_date,value_datetime,value_time,value_uuid,value_text,value_json) so you can filter, sort, and report efficiently. - Temporal history — index rows are append-only with
valid_from/valid_toso the full history of every field value is preserved. - 15 built-in field types — string, text, integer, decimal, boolean, date,
datetime, time, date range, datetime range, time range, select, multi-select,
relationship, json. Bring your own by implementing
CustomFieldTypeHandler. - Temporal validation — date/datetime/time fields support fixed and relative constraints, time step validation, and first-class range fields.
- Conditional visibility — show/hide fields based on other field values
using an
and/orrule tree. - Grouping — two-level groups (
group_level_1,group_level_2) andsort_orderdrive form layout. - Multi-tenant aware — opt-in
tenant_idand/orteam_idcontext columns that scope definitions and index rows. - Optional REST API — apiResource endpoints for managing definitions and updating per-entity values.
- Built-in query builder helper — apply filters against the index table
with operators (
=,!=,<,>,<=,>=,in,like,contains,range,range_contains,range_overlaps,range_within,is_null,is_not_null).
Requirements
- PHP 8.4+
- Laravel 13.x (
illuminate/*^13.0) - PostgreSQL (uses JSONB, GIN indexes, and partial unique indexes via
tpetry/laravel-postgresql-enhanced)
Installation
Publish config and migrations:
This publishes:
config/custom-fields.php- Two migrations:
custom_field_definitionsandcustom_field_index_values
Then run migrations:
Adding the JSONB value column to your model's table
Each model that holds custom fields needs a JSONB column to store its values
(default name custom_field_values):
Quick start
1. Register the entity
In config/custom-fields.php:
2. Add the trait to your model
The trait casts custom_field_values to array and gives you helpers:
The service provider attaches an observer to every model registered in
custom-fields.entities.models that syncs changes to the index table on
saved.
3. Define some fields
Definitions are stored as Eloquent models, so you can seed or create them programmatically:
4. Read and write values
5. Filter on a custom field
Multiple filters at once:
In shorthand filters, a two-item list whose first item is a supported operator
is treated as [operator, value]. Use the normalized shape below when you need
an equality comparison against a list value that starts with an operator string.
Use explicit null operators instead of bare null values:
The lower-level service still accepts the normalized filter shape, which is useful for generated code or API payloads:
Field types
| Type | Index column | Notes |
|---|---|---|
string |
value_string |
Short text, max 512 chars |
text |
value_text |
Long text |
integer |
value_integer |
bigint |
decimal |
value_decimal |
Precision/scale from decimal config |
boolean |
value_boolean |
|
date |
value_date |
Y-m-d |
datetime |
value_datetime |
Normalized through the app timezone |
time |
value_time |
HH:MM[:SS]; optional config.step_minutes |
date_range |
value_json |
['start' => 'Y-m-d', 'end' => 'Y-m-d'] |
datetime_range |
value_json |
['start' => 'Y-m-d H:i:s', 'end' => '...'] |
time_range |
value_json |
['start' => 'HH:MM:SS', 'end' => 'HH:MM:SS'] |
select |
value_string |
config.options; alias enum |
multi_select |
value_json |
config.options; GIN-indexed |
relationship |
value_uuid |
config.target_entity, config.display_field |
json |
value_json |
GIN-indexed |
Temporal validation
Temporal validation rules live in validation_rules. Scalar date and
datetime fields support fixed bounds and relative bounds from today or
now:
Date fields use the today anchor. Datetime fields use the now anchor and
the app timezone. Supported relative units are minutes, hours, days,
weeks, months, and years.
Time fields support fixed bounds and optional step validation:
Range fields store a start / end JSON object in value_json, and temporal
min / max rules are applied to both endpoints. time_range disallows
overnight ranges by default; set config.allow_overnight to true when ranges
like 22:00 → 06:00 should be valid.
Custom types
Implement ByJesper\LaravelCustomFields\Contracts\CustomFieldTypeHandler and add
the class to custom-fields.types.handlers.
Conditional visibility
Supported operators: eq, neq, in, notIn, truthy, falsy. Evaluation
happens in the consuming UI layer (see
byjesper/laravel-custom-fields-filament).
Multi-tenancy and teams
Both are off by default. To enable, flip them in config and run the additive migration generators:
When enabled, the context column(s) are:
- Added to both the
custom_field_definitionsandcustom_field_index_valuestables - Included in unique/composite indexes
- Automatically applied when resolving definitions, reading definitions/index values, and writing index rows
The active context is resolved via ContextResolver. The default
ConfigContextResolver reads from config('custom-fields.tenancy.resolver')
and config('custom-fields.teams.resolver'). Bind your own implementation in
a service provider:
When tenant or team support is enabled and the active context resolves a value,
the package models apply a global scope to CustomFieldDefinition and
CustomFieldIndexValue. Administrative or cross-context tooling can opt out
with withoutGlobalScope(\ByJesper\LaravelCustomFields\Scopes\ContextScope::class).
If an enabled tenant or team context cannot be resolved, scoped reads fail closed
and return no records.
Console commands, queued jobs, and administrative tooling that read package
models outside a resolvable request context must set an active context or call
withoutGlobalScope(\ByJesper\LaravelCustomFields\Scopes\ContextScope::class).
Use database-level isolation such as PostgreSQL RLS as defense-in-depth where
available.
REST API
Disabled by default. To enable:
Routes:
| Method | URI | Action |
|---|---|---|
| GET | definitions |
List definitions |
| POST | definitions |
Create definition |
| GET | definitions/{id} |
Show definition |
| PUT | definitions/{id} |
Update definition |
| DELETE | definitions/{id} |
Delete definition |
| PUT | entities/{entityType}/{entityId}/values |
Update an entity's values |
Authorization
Wire policies in config:
Console commands
| Command | Purpose |
|---|---|
custom-fields:install |
Publish config and migrations. |
custom-fields:setup-tenancy |
Generate an additive migration adding tenant_id context columns. |
custom-fields:setup-teams |
Generate an additive migration adding team_id context columns. |
custom-fields:rebuild-index |
Rebuild the custom_field_index_values table from current JSONB. |
Storage model
The JSONB column on the parent model is the source of truth. The index table
is a derived, queryable projection synced via the CustomFieldIndexObserver.
If they drift, run custom-fields:rebuild-index.
Companion package
For a ready-made Filament v5 admin UI (definition CRUD, form/table column
generators, conditional-visibility validation), install
byjesper/laravel-custom-fields-filament.
Development
The following Composer scripts are available for local quality checks:
The composer test aggregate runs the full package quality gate: lint,
type-check, type coverage, unit tests, parallel tests, and integration tests.
PostgreSQL-backed integration tests run when the following environment variables are present:
When those variables are absent, PostgreSQL-specific integration tests are skipped locally. CI provides a PostgreSQL service and runs them.
License
MIT — see LICENSE.md.
All versions of laravel-custom-fields with dependencies
illuminate/contracts Version ^13.0
illuminate/database Version ^13.0
illuminate/http Version ^13.0
illuminate/routing Version ^13.0
illuminate/support Version ^13.0
tpetry/laravel-postgresql-enhanced Version ^3.6