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

Events and listeners.

Hook the definition and value models with Laravel's standard Eloquent events; the package ships no event classes of its own.

The package dispatches no events of its own. CustomFieldDefinition and CustomFieldValue are ordinary Eloquent models, so they fire the standard model events, and those are the hook.

EventFired on
creating, createdA value or definition written for the first time
updating, updatedAn existing row changed
saving, savedEither of the above
deleting, deletedA row removed

There are no retrieved-time hooks worth relying on for values, because reads go through a relation that may be eager loaded or already in memory.

Reacting to a value change

The common case is keeping something else in step with a custom field: a search index, a cache, a denormalised column.

use ByRcsc\LaravelCustomFields\Models\CustomFieldValue;

CustomFieldValue::saved(function (CustomFieldValue $value): void {
    $value->model?->searchable();
});

CustomFieldValue::deleted(function (CustomFieldValue $value): void {
    $value->model?->searchable();
});

Register these in a service provider's boot(). The model relation is a MorphTo back to the record, and definition is a BelongsTo to the field.

Handle both halves. Clearing a field deletes its row rather than writing a null, so a listener watching only saved misses every clear.

definition is nullable, because a value can outlive the field that gave it meaning. Check it before reading through it:

CustomFieldValue::saved(function (CustomFieldValue $value): void {
    $key = $value->definition?->key;

    if ($key === 'account_tier') {
        // ...
    }
});

Observers

An observer class works the same way and is easier to test:

use App\Observers\CustomFieldValueObserver;
use ByRcsc\LaravelCustomFields\Models\CustomFieldValue;

CustomFieldValue::observe(CustomFieldValueObserver::class);

Register it in a service provider's boot(). The #[ObservedBy] attribute is not an option, since it goes on the model class and the model belongs to the package.

What fires and what does not

Everything the trait writes fires events. setCustomField() and setCustomFields() save through the model, and clearing a field deletes through it.

custom-fields:prune deletes one row at a time rather than in one statement, specifically so each row fires its deleting and deleted events. A listener keeping a search index in step would otherwise see every delete the trait makes and none of the command's.

What fires nothing is a write made around the models: a mass query()->update() or ->delete() against either table, a saveQuietly(), or anything inside withoutEvents(). Two package behaviours are built on model events and share that blind spot: the type-change guard and the resolved-definitions cache.

Definition events

The package itself listens to saved and deleted on CustomFieldDefinition to clear its cache, and to updating to refuse a type change. Your own listeners run alongside those.

A listener that throws from updating or saving cancels the save, which is one way to add a rule of your own about which fields may change:

use ByRcsc\LaravelCustomFields\Models\CustomFieldDefinition;

CustomFieldDefinition::updating(function (CustomFieldDefinition $definition): void {
    if ($definition->isDirty('key')) {
        throw new RuntimeException('Field keys are fixed once created.');
    }
});

What to read next

  • Changing and deleting fields for the guard built on these events.
  • Console commands for the events prune fires.
  • Testing for asserting on them.
PreviousQueries and eager loadingNextConfiguration
View source

On this page

  1. Reacting to a value change
  2. Observers
  3. What fires and what does not
  4. Definition events
  5. What to read next