Skip to content

feat: cross-record (relation-path) visibility conditions [3.x]#161

Open
ManukMinasyan wants to merge 13 commits into
3.xfrom
feat/cross-record-visibility-conditions
Open

feat: cross-record (relation-path) visibility conditions [3.x]#161
ManukMinasyan wants to merge 13 commits into
3.xfrom
feat/cross-record-visibility-conditions

Conversation

@ManukMinasyan

Copy link
Copy Markdown
Collaborator

Summary

Extends the package so a custom-field/section visibility condition can reference a related record via a one/two-hop relation path (e.g. household.projects), not just a field on the same record.

Driving use case (ClickUp 868jtwph3, customer: Los Angeles): show an external-ID Section on Household Member only when the member's Household is associated with specific Projects — i.e. HouseholdMember → Household → Projects.

What's new

  • New condition source ConditionSource::RelationAttribute — the dotted relation path is stored in the existing field_code (no schema/migration change).
  • New operators IS_IN / IS_NOT_IN, set-intersection semantics (string-normalized), exposed only for the relation source.
  • RelationConditionResolver — walks a ≤2-hop dotted path on a record to the set of terminal related keys; reflects the terminal related model (no DB query) for the value picker.
  • Server-side evaluation (VisibilityData::evaluateCondition) — relation conditions resolve against the record's relations and never emit client JS (FrontendVisibilityService bails to null); form fields/sections fall back to a server-side visible() closure. Create form (no record) → fail-open.
  • Management UI (VisibilityComponent) — relation source option, path picker (from config allowlist), IS_IN/IS_NOT_IN operator menu, and a related-record multi-select value picker (with edit-time hydration).
  • Opt-in scoping (RelationConditionConfig, config('custom-fields.visibility')) — per-entity allowlist of relation paths; restrict_to_configured (default false) governs the own-column model-attribute source so existing flag behavior is unchanged.

Backward compatibility

  • No public interface/contract signature changed (the ?Model $record param is on concrete classes only, optional).
  • Inert by default: shipped config does not enable MODEL_ATTRIBUTE_CONDITIONS; restrict_to_configured defaults to legacy behavior.
  • Same-record custom-field conditions keep their exact reactive client-JS path.
  • Ships as a minor (intended v3.2.0).

Test plan

  • vendor/bin/pest --parallel → 685 passed, 0 failed (2633 assertions)
  • vendor/bin/phpstan analyse → no errors (254 files)
  • vendor/bin/pest --type-coverage --min=100 → 100%
  • vendor/bin/pint --test / vendor/bin/rector --dry-run → clean

New coverage: operator set-intersection; resolver (2-hop, null intermediate, empty terminal, invalid path); backend eval (matching / non-matching / no-relation / fail-open); section + field server-side wiring; JS exclusion; per-entity scoping + backward-compat defaults; and a consolidated field/section/backend/JS parity test. Adds a Post belongsToMany Tag test fixture (prior fixtures had only BelongsTo).

Notes

  • N+1 if a relation-conditioned field is placed in a table/export (evaluated per row) — documented inline; eager-load the configured paths there if needed.

Wires RelationAttribute conditions into VisibilityData.evaluate():
relation branch resolves related keys via RelationConditionResolver
before the model-attribute branch; null record fails open. Generalises
the flag-skip guard to cover both relation and model-attribute sources.
Adds hasRelationAttributeConditions() helper. Enables
MODEL_ATTRIBUTE_CONDITIONS in the test environment so both new and
existing condition tests run under the same flag gate.
Apply Rector RemoveNullArgOnNullDefaultParamRector and EncapsedStringsToSprintfRector
to our changed files; fix Pint import ordering and spacing in RelationConditionResolver.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant