- Lunar Core version:
2.x-dev (c0c6a81f4c9cc429fb99a8b9c4030828e1f29c18)
- Lunar Filament version:
2.x-dev (1acfb414d026786b951471841891db4aa1e54b76)
- Lunar Admin version:
2.x-dev (9da7134fc7b8b27cc2f73e6cdb447d96caaa7fa5)
- Laravel version:
13.20.0
- Filament version:
5.7.1
- PHP version:
8.5
- Database driver: MySQL, although the error occurs before database access
Expected Behaviour
Editing any record that renders an attribute form (for example a Product in the admin panel) should display the form. No custom validation rules are applied, because Lunar v2 provides no way to configure per-attribute validation rules.
Actual Behaviour
When Laravel strict mode is enabled — commonly via Model::preventAccessingMissingAttributes() or Model::shouldBeStrict() — opening the edit form throws:
Illuminate\Database\Eloquent\MissingAttributeException
The attribute [validation_rules] either does not exist or was not retrieved
for model [Lunar\Core\Models\Attribute].
The exception is thrown from Illuminate\Database\Eloquent\Concerns\HasAttributes::throwMissingAttributeExceptionIfApplicable(), reached while a field type builds its Filament component, e.g. Lunar\Filament\FieldTypes\TranslatedText::getFilamentComponent().
Without strict mode the read silently returns null, so the field types never apply the rules. Combined with the fact that nothing in v2 ever writes validation_rules, these reads are dead v1 references.
Steps To Reproduce
-
Install the Lunar v2 packages from 2.x-dev, including lunarphp/filament and lunarphp/lunar.
-
Enable strict model access in a service provider:
use Illuminate\Database\Eloquent\Model;
Model::preventAccessingMissingAttributes(! app()->isProduction());
// or Model::shouldBeStrict();
-
Open the admin panel and edit any Product (or any resource that renders an attribute form).
-
Observe the MissingAttributeException for validation_rules.
Root Cause
The v2 lunar_attributes schema has no validation_rules column (field configuration is stored in the configuration JSON column), and Lunar\Core\Models\Attribute defines no validation_rules cast or accessor. The v1→v2 upgrade migration explicitly drops validation_rules from the legacy attribute config keys.
However, every Filament field type still reads it as a top-level model attribute:
->when(filled($attribute->validation_rules), fn ($component) => $component->rules($attribute->validation_rules))
This appears at 10 call sites in lunarphp/filament/src/FieldTypes/:
TextField.php:29 (RichEditor)
TextField.php:35 (TextInput)
TranslatedText.php:23
Number.php:22
Dropdown.php:55
File.php:28
ListField.php:21
Toggle.php:21
YouTube.php:18
Vimeo.php:18
validation_rules is never populated in v2: there is no column, no getConfigurationFields() entry defining it, and no configuration->get('validation_rules') read/write path. The filled(...) guard is therefore always false outside strict mode and throws under it.
Relevant source:
Suggested Fix
Because there is no v2 mechanism to set validation_rules, the reads can never be truthy. Two options:
- Minimal: remove the dead
->when(filled($attribute->validation_rules), …) guards from all 10 call sites.
- If per-attribute validation is intended as a v2 feature — the
validation_rules lang keys still ship in lunarphp/filament and lunarphp/lunar admin lang files, which suggests it was planned — add a validation_rules entry to each field type's getConfigurationFields() and read it via $attribute->configuration?->get('validation_rules') so it round-trips through the configuration JSON column instead of a non-existent top-level attribute.
Say the word and I'll provide a PR for option 1 or 2.
Application Workaround
We worked around the issue by registering a class cast on the core Attribute model so the missing key resolves to null:
use Lunar\Core\Models\Attribute;
Attribute::addCasts(['validation_rules' => ValidationRules::class]);
ValidationRules is a small CastsAttributes implementation whose get() returns null (matching non-strict behaviour). A primitive cast such as 'array' does not work here: HasExtendableCasts::addCasts() only merges into getCasts(), while Model::hasAttribute() checks the raw $this->casts property for primitive casts and reaches missing casts only through isClassCastable() → getCasts(). Only a cast class is visible to hasAttribute(), so only a class cast suppresses the exception.
With this workaround:
- Attribute forms render under strict mode.
- Behaviour is identical to production (no rules applied).
- No vendor files are modified.
2.x-dev(c0c6a81f4c9cc429fb99a8b9c4030828e1f29c18)2.x-dev(1acfb414d026786b951471841891db4aa1e54b76)2.x-dev(9da7134fc7b8b27cc2f73e6cdb447d96caaa7fa5)13.20.05.7.18.5Expected Behaviour
Editing any record that renders an attribute form (for example a Product in the admin panel) should display the form. No custom validation rules are applied, because Lunar v2 provides no way to configure per-attribute validation rules.
Actual Behaviour
When Laravel strict mode is enabled — commonly via
Model::preventAccessingMissingAttributes()orModel::shouldBeStrict()— opening the edit form throws:The exception is thrown from
Illuminate\Database\Eloquent\Concerns\HasAttributes::throwMissingAttributeExceptionIfApplicable(), reached while a field type builds its Filament component, e.g.Lunar\Filament\FieldTypes\TranslatedText::getFilamentComponent().Without strict mode the read silently returns
null, so the field types never apply the rules. Combined with the fact that nothing in v2 ever writesvalidation_rules, these reads are dead v1 references.Steps To Reproduce
Install the Lunar v2 packages from
2.x-dev, includinglunarphp/filamentandlunarphp/lunar.Enable strict model access in a service provider:
Open the admin panel and edit any Product (or any resource that renders an attribute form).
Observe the
MissingAttributeExceptionforvalidation_rules.Root Cause
The v2
lunar_attributesschema has novalidation_rulescolumn (field configuration is stored in theconfigurationJSON column), andLunar\Core\Models\Attributedefines novalidation_rulescast or accessor. The v1→v2 upgrade migration explicitly dropsvalidation_rulesfrom the legacy attribute config keys.However, every Filament field type still reads it as a top-level model attribute:
This appears at 10 call sites in
lunarphp/filament/src/FieldTypes/:validation_rulesis never populated in v2: there is no column, nogetConfigurationFields()entry defining it, and noconfiguration->get('validation_rules')read/write path. Thefilled(...)guard is therefore alwaysfalseoutside strict mode and throws under it.Relevant source:
Suggested Fix
Because there is no v2 mechanism to set
validation_rules, the reads can never be truthy. Two options:->when(filled($attribute->validation_rules), …)guards from all 10 call sites.validation_ruleslang keys still ship inlunarphp/filamentandlunarphp/lunaradmin lang files, which suggests it was planned — add avalidation_rulesentry to each field type'sgetConfigurationFields()and read it via$attribute->configuration?->get('validation_rules')so it round-trips through theconfigurationJSON column instead of a non-existent top-level attribute.Say the word and I'll provide a PR for option 1 or 2.
Application Workaround
We worked around the issue by registering a class cast on the core
Attributemodel so the missing key resolves tonull:ValidationRulesis a smallCastsAttributesimplementation whoseget()returnsnull(matching non-strict behaviour). A primitive cast such as'array'does not work here:HasExtendableCasts::addCasts()only merges intogetCasts(), whileModel::hasAttribute()checks the raw$this->castsproperty for primitive casts and reaches missing casts only throughisClassCastable()→getCasts(). Only a cast class is visible tohasAttribute(), so only a class cast suppresses the exception.With this workaround: