›
›
›
  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

Quick start.

Define a field on an Eloquent model, write a value, read it back, and filter records by it.

The Customer model already stores the details every customer shares, such as their name and email address. This guide adds an account tier without changing the customers table. It then writes, reads, validates, and filters that field.

It assumes the package is installed and HasCustomFields is on the model.

1. Define the field

use App\Models\Customer;
use ByRcsc\LaravelCustomFields\Enums\FieldType;
use ByRcsc\LaravelCustomFields\Facades\CustomFields;

CustomFields::define(Customer::class, [
    'key' => 'account_tier',
    'type' => FieldType::Select,
    'options' => ['standard', 'premium'],
    'label' => 'Account tier',
    'default' => 'standard',
    'rules' => ['required' => true],
]);

Run this wherever your application decides its fields exist: a seeder, a console command, or the controller behind an admin screen. It writes one row to custom_field_definitions and returns the CustomFieldDefinition model.

The key is what every later call names the field by. It is lowercase, starts with a letter, and continues with letters, digits, or underscores. label is what validation messages call the field, and defaults to the key with its underscores replaced, so due_date becomes Due date.

Defining the same key twice on the same model, for the same tenant, throws InvalidDefinitionException. So does a select without options, an unknown type, or a misspelled attribute name. See defining fields.

2. Write a value

$customer = Customer::query()->findOrFail(1);

$customer->setCustomField('account_tier', 'premium');

The write persists immediately: there is no save() to follow it with. It also validates against the field's rules first. Writing 'enterprise' would throw Laravel's ValidationException because the field does not offer that option.

3. Read it back

$customer->getCustomField('account_tier');   // 'premium'

Or read every field the current tenant can see, keyed by key:

$customer->customFields;         // Collection: ['account_tier' => 'premium']
$customer->customFields->all();  // ['account_tier' => 'premium']

customFields is an Illuminate\Support\Collection, ordered by each field's sort_order and then its key.

Values come back as the PHP type the field promises. A number is an int or a float, a date is a Carbon instance, a multi_select is a list of strings. See field types and storage.

4. See the default

A customer that has never set the field reads the definition's default:

$fresh = Customer::query()->create([
    'name' => 'Ada Lovelace',
    'email' => 'ada@example.com',
]);

$fresh->getCustomField('account_tier');   // 'standard'

No value row exists for that customer. Defaults are resolved when you read, never written as rows. Changing a definition's default therefore changes what every unset record returns.

Writing null clears a field and returns it to the default:

$customer->setCustomField('account_tier', null);

$customer->getCustomField('account_tier');   // 'standard'

5. Filter by it

Customer::query()->whereCustomField('account_tier', 'premium')->get();

Customer::query()->whereCustomFieldIn('account_tier', ['standard', 'premium'])->get();

Both compile to a comparison on one typed column inside an exists clause, and both compose with everything else on the query:

Customer::query()
    ->where('active', true)
    ->whereCustomField('account_tier', 'premium')
    ->orderByDesc('created_at')
    ->get();

6. Validate a form

The generator hands a form request its rules array:

use App\Models\Customer;
use ByRcsc\LaravelCustomFields\Facades\CustomFields;
use Illuminate\Foundation\Http\FormRequest;

final class StoreCustomerRequest extends FormRequest
{
    public function rules(): array
    {
        return CustomFields::rulesFor(Customer::class, prefix: 'custom_fields');
    }

    public function attributes(): array
    {
        return CustomFields::attributeNamesFor(Customer::class, prefix: 'custom_fields');
    }
}

prefix nests the keys for a form posting its custom fields under one name, so the rules above are keyed custom_fields.account_tier. Without attributes(), a failure message names the key instead of the label.

7. Load a listing without an N+1

Reading custom fields on a page of records costs one extra query for the whole page, as long as you eager load the relation:

Customer::query()->with('customFieldValues')->paginate();

Leave it out and each record fetches its own values. See queries and eager loading.

What to read next

  • Defining fields for every attribute define() accepts.
  • Validation for the per-field rules and what unique guarantees.
  • Tenant scoping if different tenants need different fields.
PreviousInstallation and setupNextDefining fields
View source

On this page

  1. 1. Define the field
  2. 2. Write a value
  3. 3. Read it back
  4. 4. See the default
  5. 5. Filter by it
  6. 6. Validate a form
  7. 7. Load a listing without an N+1
  8. What to read next