Skip to content

[2.x] Editing attribute-backed records fails because Filament field types read the removed validation_rules attribute #2568

Description

@RaBic
  • 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

  1. Install the Lunar v2 packages from 2.x-dev, including lunarphp/filament and lunarphp/lunar.

  2. Enable strict model access in a service provider:

    use Illuminate\Database\Eloquent\Model;
    
    Model::preventAccessingMissingAttributes(! app()->isProduction());
    // or Model::shouldBeStrict();
  3. Open the admin panel and edit any Product (or any resource that renders an attribute form).

  4. 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:

  1. Minimal: remove the dead ->when(filled($attribute->validation_rules), …) guards from all 10 call sites.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions