›
byrcsc/laravel-custom-fields · 1.x
Every exception the package throws, what triggers it, and which layer it comes from.
The package fails loudly. A key it does not recognise, a value it cannot store, and a filter it cannot compile portably all throw rather than returning null or an empty result, because each of those would be found much later.
| Exception | Extends | Thrown when |
|---|---|---|
InvalidDefinitionException | InvalidArgumentException | define() was given something it cannot honour |
InvalidValueException | InvalidArgumentException | A value cannot be stored as, or read back as, the field's type |
InvalidTenantResolverException | InvalidArgumentException | custom-fields.tenant_resolver is not a resolver |
UnknownCustomFieldException | RuntimeException | The current tenant has no such field on that model |
UnsavedModelException | RuntimeException | A write against a record that has never been saved |
UnfilterableFieldException | RuntimeException | A filter the package will not compile |
CannotChangeFieldTypeException | RuntimeException | A type change on a definition that already has values |
All of them live in ByRcsc\LaravelCustomFields\Exceptions.
There is no shared package base class. Each one extends an SPL exception directly, so there is no single
catchthat covers the package and nothing else. Catch the specific class you mean, orInvalidArgumentExceptionandRuntimeExceptionif you need both halves.
Two more come from outside the package and are worth knowing here.
Illuminate\Validation\ValidationException is what a failed rule throws, on a
form and on a write. Illuminate\Database\Eloquent\MassAssignmentException is
what writing a definition or a value around the facade throws, because both
models guard every attribute.
These fire against code you are holding in your head, so every message names the field and says what would have been acceptable.
Everything define() refuses:
Unknown custom field type [file]. Use one of: text, textarea, ...select or multi_select with no options, or options on a type that has
none, or an options list that is not strings.unique on a multi_select or json.sort_order that is not a whole number.The configured resolver is missing, is not a class, or does not implement
TenantResolver. It is thrown the first time the resolver is needed rather than
at boot, so the application still boots and the failure names the config key
instead of arriving as a container error from three frames down.
A key that is not defined for the model under the tenant currently being acted
for. Thrown by getCustomField(), setCustomField(), setCustomFields(), and
both filter scopes.
No custom field [account_tier] is defined on [App\Models\Customer] while the current
tenant is [globex].The message says which tenant was current, because the same key is often defined
for a different one. CustomFields::definition() is the read path that answers
null instead, which makes it the way to ask whether a field exists.
A write against a record with no key yet. Values are stored against the record's key, so save it first.
The type layer, not the rules layer. An array handed to a text field, an
unparseable date, a bare string handed to a multi_select, a value with no JSON
form.
It also covers the read direction: a stored row whose column does not hold what its field's type says it should throws rather than coming back as null. That is only reachable by writing the values table around the trait, which is exactly the case worth hearing about rather than papering over.
With validate_on_write on, most wrong-typed values are reported as validation
failures first, because the rules run before the type layer. Turning validation
off does not turn this off. See type errors are not validation
errors.
A filter the package refuses to compile, because no answer it could give would be the same answer on every supported engine. Three cases:
json field.whereCustomField() on a multi_select given a list rather than one option.whereCustomFieldIn() on a multi_select or a json field.See filtering records.
A type change on a definition with values behind it. The message names the field, both types, and how many values are in the way. See changing and deleting fields.
Most of these are developer errors and should reach your error tracker rather
than a catch. Three are worth handling:
use ByRcsc\LaravelCustomFields\Exceptions\UnknownCustomFieldException;
use Illuminate\Validation\ValidationException;
try {
$customer->setCustomFields($request->validated()['custom_fields'] ?? []);
} catch (UnknownCustomFieldException) {
// a key the tenant's field set no longer has
} catch (ValidationException $exception) {
// a value that broke a rule
}UnknownCustomFieldException is the one that fires on input rather than on
code, because a form can be posted after the field behind it was deleted.
CannotChangeFieldTypeException is worth catching in an admin screen that lets
a tenant edit its own fields, so the refusal reads as a message rather than a 500.