docs(form-builder): WS-5b partial sign-off — SCHEMA v2.3 + ARCH v1.5 §17.4 + addendum Q3
This commit is contained in:
@@ -1,10 +1,23 @@
|
||||
# Crewli — Core Database Schema
|
||||
|
||||
> Source: Design Document v1.3 — Section 3.5
|
||||
> **Version: 2.2** — Updated April 2026
|
||||
> **Version: 2.3** — Updated April 2026
|
||||
>
|
||||
> **Changelog:**
|
||||
>
|
||||
> - v2.3: WS-5b (partial) — `form_field_validation_rules` relational table
|
||||
> replaces the `validation_rules` JSON on `form_fields` and
|
||||
> `form_field_library`. Typed `rule_type` column + per-rule `parameters`
|
||||
> JSON; polymorphic morph owner reuses the WS-5a aliases (`form_field`,
|
||||
> `form_field_library`). Canonicalised legacy keys at backfill: ambiguous
|
||||
> `min`/`max` → `min_value`/`max_value`/`min_length`/`max_length`/
|
||||
> `date_min`/`date_max` by field type; `max_priorities` → `max_selected`.
|
||||
> Skipped keys: `required` (is_required column), `unique` (is_unique
|
||||
> column). Non-validation keys (`tag_categories`, `storage_disk`)
|
||||
> deferred to WS-5b commit 5 — they relocate to a separate
|
||||
> `form_field_configs` table (ARCH §17.5) rather than polluting the
|
||||
> validation-rules table. See ARCH-FORM-BUILDER.md §17.4 and
|
||||
> ARCH-CONSOLIDATION-ADDENDUM-2026-04-24 §Q3 WS-5b Uitvoering.
|
||||
> - v2.2: WS-5a — `form_field_bindings` relational table replaces
|
||||
> `form_fields.binding` and `form_field_library.default_binding` JSON.
|
||||
> Polymorphic morph owner (`form_field` / `form_field_library`) per
|
||||
@@ -1967,7 +1980,6 @@ that aggregates the user's submitted, non-test `form_submissions`.
|
||||
| `label` | string | |
|
||||
| `help_text` | text nullable | |
|
||||
| `options` | JSON nullable | Choice options |
|
||||
| `validation_rules` | JSON nullable | |
|
||||
| `default_is_required` | bool | default: false |
|
||||
| `default_is_filterable` | bool | default: false |
|
||||
| `translations` | JSON nullable | Per-locale overrides |
|
||||
@@ -1977,7 +1989,7 @@ that aggregates the user's submitted, non-test `form_submissions`.
|
||||
| `is_active` | bool | default: true |
|
||||
| `created_at`, `updated_at` | timestamps | |
|
||||
|
||||
**Relations:** `belongsTo` organisation; `hasMany` form_fields via `library_field_id`; `morphMany` form_field_bindings as `owner`
|
||||
**Relations:** `belongsTo` organisation; `hasMany` form_fields via `library_field_id`; `morphMany` form_field_bindings as `owner`; `morphMany` form_field_validation_rules as `owner`
|
||||
**Indexes:** `(organisation_id, field_type)`, `(organisation_id, is_active)`
|
||||
**Unique constraint:** `UNIQUE(organisation_id, slug)`
|
||||
**Global scope:** `OrganisationScope`
|
||||
@@ -1986,6 +1998,11 @@ that aggregates the user's submitted, non-test `form_submissions`.
|
||||
> Bindings moved to the relational `form_field_bindings` table
|
||||
> (ARCH-FORM-BUILDER.md §6.7). See WS-5a in
|
||||
> `/dev-docs/ARCH-CONSOLIDATION-ADDENDUM-2026-04-24.md` §Q3.
|
||||
>
|
||||
> Validation rules moved to the relational `form_field_validation_rules`
|
||||
> table (ARCH-FORM-BUILDER.md §17.4). The column was dropped in WS-5b;
|
||||
> see `/dev-docs/ARCH-CONSOLIDATION-ADDENDUM-2026-04-24.md` §Q3 WS-5b
|
||||
> Uitvoering for the full catalogue and migration notes.
|
||||
|
||||
---
|
||||
|
||||
@@ -2012,7 +2029,6 @@ that aggregates the user's submitted, non-test `form_submissions`.
|
||||
| `help_text` | text nullable | |
|
||||
| `section` | string(100) null | Visual grouping header (independent of `form_schema_section_id`) |
|
||||
| `options` | JSON nullable | Choice options |
|
||||
| `validation_rules` | JSON nullable | min/max/regex/allowed_mime_types/storage_disk/callback, etc. |
|
||||
| `is_required` | bool | default: false |
|
||||
| `is_filterable` | bool | default: false — populates `form_values.value_indexed` / pivot |
|
||||
| `is_portal_visible` | bool | default: true |
|
||||
@@ -2029,13 +2045,18 @@ that aggregates the user's submitted, non-test `form_submissions`.
|
||||
| `created_at`, `updated_at` | timestamps | |
|
||||
| `deleted_at` | timestamp nullable | Soft delete preserves history |
|
||||
|
||||
**Relations:** `belongsTo` schema, section (nullable), libraryField; `hasMany` form_values; `morphMany` form_field_bindings as `owner`
|
||||
**Relations:** `belongsTo` schema, section (nullable), libraryField; `hasMany` form_values; `morphMany` form_field_bindings as `owner`; `morphMany` form_field_validation_rules as `owner`
|
||||
**Indexes:** `(form_schema_id, sort_order)`, `(form_schema_id, is_filterable)`, `(library_field_id)`, `(form_schema_id, slug)`
|
||||
**Soft delete:** yes
|
||||
|
||||
> Bindings moved to the relational `form_field_bindings` table
|
||||
> (ARCH-FORM-BUILDER.md §6.7). See WS-5a in
|
||||
> `/dev-docs/ARCH-CONSOLIDATION-ADDENDUM-2026-04-24.md` §Q3.
|
||||
>
|
||||
> Validation rules moved to the relational `form_field_validation_rules`
|
||||
> table (ARCH-FORM-BUILDER.md §17.4). The column was dropped in WS-5b;
|
||||
> see `/dev-docs/ARCH-CONSOLIDATION-ADDENDUM-2026-04-24.md` §Q3 WS-5b
|
||||
> Uitvoering for the full catalogue and migration notes.
|
||||
|
||||
---
|
||||
|
||||
@@ -2071,6 +2092,38 @@ that aggregates the user's submitted, non-test `form_submissions`.
|
||||
|
||||
---
|
||||
|
||||
### `form_field_validation_rules`
|
||||
|
||||
> Relational home for field and library-field validation rules. One row
|
||||
> per `(owner, rule_type)`. `rule_type` is an app-enforced enum
|
||||
> (`FormFieldValidationRuleType`) — database column is `string(40)` so
|
||||
> the enum can extend without migration. `parameters` JSON carries per-
|
||||
> rule-type configuration (value, pattern, mime_types, etc.); shape is
|
||||
> enforced at the service layer, not the DB.
|
||||
>
|
||||
> Polymorphic owner — morph-map aliases `form_field` and
|
||||
> `form_field_library` (reused from WS-5a). Rules are physical state
|
||||
> (not audit) — they cascade on owner delete (soft or hard) via the
|
||||
> shared `FormFieldChildTablesCascadeObserver`. ARCH §17.4.
|
||||
|
||||
| Column | Type | Notes |
|
||||
| ------------------- | ----------------- | ------------------------------------------------------------------------------ |
|
||||
| `id` | ULID | PK |
|
||||
| `owner_type` | string(40) | morph alias: `form_field` or `form_field_library` |
|
||||
| `owner_id` | ULID | parent row (a `form_fields.id` or `form_field_library.id`) |
|
||||
| `rule_type` | string(40) | `FormFieldValidationRuleType` case (`min_length`, `max_value`, `regex`, `allowed_mime_types`, `callback`, etc.) |
|
||||
| `parameters` | JSON | Per-rule-type bag (e.g. `{"value": 3}` for `min_length`, `{"mime_types":[...]}` for `allowed_mime_types`) |
|
||||
| `error_message_key` | string(100) null | Optional i18n key for custom rejection message |
|
||||
| `created_at`, `updated_at` | timestamps | |
|
||||
|
||||
**Relations:** `morphTo` owner (`form_field` or `form_field_library`)
|
||||
**Indexes:** `(rule_type)`, `(owner_type, owner_id)`
|
||||
**Unique constraint:** `UNIQUE(owner_type, owner_id, rule_type)`
|
||||
**Global scope:** `FormFieldValidationRuleScope` — sibling to `FormFieldBindingScope`, same UNION shape over both owner chains. Escape hatch: `withoutGlobalScope(FormFieldValidationRuleScope::class)`. Base-class extraction deferred to WS-5d per addendum Q3 (premature abstraction from two siblings is still premature).
|
||||
**Soft delete:** no — rules are current state, not audit
|
||||
|
||||
---
|
||||
|
||||
### `form_submissions`
|
||||
|
||||
> One submission per `(schema, subject)` in `single` / `draft_single`
|
||||
|
||||
Reference in New Issue
Block a user