Skip to content

MissingAttributeException: visibility_conditions when Model::preventAccessingMissingAttributes() is enabled #149

@erikpach

Description

@erikpach

Description

FormBuilder::getDependentFieldCodes() reads $field->visibility_conditions directly on the CustomField model, but no such column, cast, or accessor exists. With Model::preventAccessingMissingAttributes() enabled (Laravel's strict mode), this throws Illuminate\Database\Eloquent\MissingAttributeException. Without strict mode the access silently returns null, so the bug is masked.

Visibility data is stored in settings->visibility and exposed via CoreVisibilityLogicService::getVisibilityConditions() / getDependentFields(). The FormBuilder appears to use a legacy attribute name that no longer exists on the model.

The expected condition shape (['field' => string]) also no longer matches VisibilityConditionData, which uses field_code, operator, value, source. So even when the access does not throw, the loop never finds dependent codes.

Reproduction

  1. Enable strict mode:
    // AppServiceProvider::boot()
    Model::shouldBeStrict();
  2. Open any Filament form that renders custom fields (e.g. a create/edit modal for an entity using UsesCustomFields).
  3. Exception is thrown.

Stack trace (relevant frames)

Illuminate\Database\Eloquent\MissingAttributeException
The attribute [visibility_conditions] either does not exist or was not retrieved for model [Relaticle\CustomFields\Models\CustomField].

vendor/relaticle/custom-fields/src/Filament/Integration/Builders/FormBuilder.php:48
vendor/relaticle/custom-fields/src/Filament/Integration/Builders/FormBuilder.php:77
vendor/relaticle/custom-fields/src/Filament/Integration/Builders/FormContainer.php:78

Environment

  • relaticle/custom-fields v3.1.6
  • Laravel 13.7.0
  • PHP 8.5.5
  • Filament v5

Suggested fix

Replace direct attribute access with the existing service:

// FormBuilder::getDependentFieldCodes()
private function getDependentFieldCodes(Collection $fields): array
{
    $service = app(CoreVisibilityLogicService::class);
    $dependentCodes = [];

    foreach ($fields as $field) {
        $dependentCodes = array_merge(
            $dependentCodes,
            $service->getDependentFields($field),
        );

        $validationRules = $field->validation_rules;
        if ($validationRules) {
            foreach (['min_date', 'max_date'] as $constraintKey) {
                $constraint = $validationRules->get($constraintKey);
                if (is_array($constraint) && ($constraint['anchor'] ?? null) === 'custom_field' && isset($constraint['field_reference'])) {
                    $dependentCodes[] = $constraint['field_reference'];
                }
            }
        }
    }

    return array_unique($dependentCodes);
}

This removes the dependency on a non-existent attribute and uses the already-canonical visibility API.

Workaround

Subclass CustomField and register via CustomFields::$customFieldModel:

public function getVisibilityConditionsAttribute(): array
{
    $service = app(CoreVisibilityLogicService::class);
    return array_map(
        static fn (string $code): array => ['field' => $code],
        $service->getDependentFields($this),
    );
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions