›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-custom-fields
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Defining fields
  • Field types and storage
  • Tenant scoping
  • Reading and writing values
  • Validation
  • Filtering records

Operations

  • Changing and deleting fields
  • Queries and eager loading
  • Events and listeners

Reference

  • Configuration
  • Console commands
  • Exceptions
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Defining fields
  • Field types and storage
  • Tenant scoping
  • Reading and writing values
  • Validation
  • Filtering records

Operations

  • Changing and deleting fields
  • Queries and eager loading
  • Events and listeners

Reference

  • Configuration
  • Console commands
  • Exceptions
  • Testing
  • Troubleshooting

byrcsc/laravel-custom-fields · 1.x

Installation and setup.

Install Laravel Custom Fields, create its two tables, and choose how the current tenant is resolved.

1. Install the package

composer require byrcsc/laravel-custom-fields

The service provider is discovered automatically. It registers the CustomFields facade, the custom-fields:prune command, and the tenant resolver binding.

2. Publish the configuration

php artisan vendor:publish --tag=custom-fields-config

Publish before you migrate. The migrations read custom-fields.tables.definitions, custom-fields.tables.values, and custom-fields.model_key_type, and changing any of the three after the tables exist takes a migration of your own. Every key and its default is listed under configuration.

The one you have to decide now is model_key_type, which shapes the model_id column on the values table:

// config/custom-fields.php
'model_key_type' => env('CUSTOM_FIELDS_MODEL_KEY_TYPE', 'int'),
Valuemodel_id columnUse it when
intunsignedBigIntegerYour models use auto-incrementing ids
uuiduuidYour models use UUID primary keys
ulidulidYour models use ULID primary keys
stringstringKeys are strings of some other shape

Anything else throws an InvalidArgumentException while the migration runs, naming the four it accepts.

One key type for the whole installation. The values table holds rows for every model you add the trait to, so mixing an integer-keyed model and a UUID-keyed one is not supported. Pick string if you genuinely have both.

3. Run the migrations

php artisan vendor:publish --tag=custom-fields-migrations
php artisan migrate

Two tables are created:

TableWhat it holds
custom_field_definitionsOne row per field per model class per tenant
custom_field_valuesOne row per field per record per tenant, in typed columns

There is no foreign key between them. That is deliberate: deleting a definition leaves its values in place until custom-fields:prune takes them.

4. Add the trait to a model

use ByRcsc\LaravelCustomFields\Concerns\HasCustomFields;
use Illuminate\Database\Eloquent\Model;

final class Customer extends Model
{
    use HasCustomFields;
}

The trait adds getCustomField(), setCustomField(), setCustomFields(), a customFields accessor, a customFieldValues relation, and the whereCustomField() and whereCustomFieldIn() scopes. Its internals are all prefixed with customField, because a method of the same name on your model would win over a trait method and break writes quietly.

Values are stored against the record's morph class, so a morph map alias is respected the same way it is on any other polymorphic relation. Set your morph map before defining fields: definitions written under the class name are not found once the alias is in place.

5. Choose a tenant resolver

custom-fields.tenant_resolver defaults to null, which means detect:

  • With spatie/laravel-multitenancy installed, you get the bundled SpatieTenantResolver and configure nothing.
  • Without it, you get NullTenantResolver, under which every definition is global and every read sees the same set.

Detection is enough for both of those. Name a class to override it. A spatie application that wants one shared set of fields rather than a set per tenant points the key at NullTenantResolver explicitly, and any other source of truth is a class of your own with one method:

use ByRcsc\LaravelCustomFields\Contracts\TenantResolver;

final class SubdomainTenantResolver implements TenantResolver
{
    public function currentTenant(): ?string
    {
        return request()->route('tenant');
    }
}
// config/custom-fields.php
'tenant_resolver' => App\Tenancy\SubdomainTenantResolver::class,

The class is resolved from the container, so it may take constructor dependencies. A value that is not a TenantResolver throws InvalidTenantResolverException the first time the resolver is needed, naming the config key, rather than failing at boot as a container error.

See tenant scoping for what the resolver's answer changes.

Verify the installation

use ByRcsc\LaravelCustomFields\Enums\FieldType;
use ByRcsc\LaravelCustomFields\Facades\CustomFields;

CustomFields::define(Customer::class, [
    'key' => 'internal_reference',
    'type' => FieldType::Text,
]);

CustomFields::definitions(Customer::class);   // Collection: ['internal_reference' => ...]

If that returns the definition, the tables exist, the facade is bound, and the resolver is answering. Delete the definition afterwards with CustomFieldDefinition::query()->where('key', 'internal_reference')->delete().

What to read next

  • Quick start to take a field from definition to filter.
  • Configuration for every key and its default.
  • Tenant scoping for what the resolver's answer changes.
PreviousIntroductionNextQuick start
View source

On this page

  1. 1. Install the package
  2. 2. Publish the configuration
  3. 3. Run the migrations
  4. 4. Add the trait to a model
  5. 5. Choose a tenant resolver
  6. Verify the installation
  7. What to read next