Relation Dropdowns
Turn a raw foreign-key column into a dropdown, searchable combobox, or multi-select resolved against another table with relation().
What relation() does
relation() binds a real, editable column on the current table (e.g. payments.customerNumber) to a lookup against another table. The create/edit form shows a dropdown of that table's rows instead of a bare numeric id input, and the list shows the resolved name in place of the raw id.
$xcrud->relation(
'customerNumber', 'customers', 'customerNumber', 'customerName',
"country = 'USA'", null, false, ' ', null, null, null, true
);
The target table must itself be reachable the same way any other table on the site is - a real table, not blacklisted, with a single-column primary key. It doesn't need its own CRUD page; relation() can read a table nothing else on the site ever browses directly.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
$field | string | - | The real, editable column on this table to bind (e.g. customerNumber). |
$targetTable | string | - | The table to look values up in. |
$targetId | string | - | Column on $targetTable whose value is stored back into $field. |
$targetName | string|array | - | Column(s) shown as the option label. An array (e.g. ['contactFirstName', 'contactLastName']) is joined with $concatSeparator to show something like "First Last". |
$where | array|string|null | null | Narrows which target rows are offered. An array is safe column=>value equality, ANDed together and bound as parameters. A string is trusted, developer-authored raw SQL (same trust level as where_raw()), optionally with {field} placeholders substituted with the current row's own value. |
$orderBy | string|null | null | Sort order for the option list. |
$multi | bool | false | Renders a multi-select instead of a single dropdown; the stored value becomes a comma-joined list of ids. |
$concatSeparator | string | ' ' | Separator used when $targetName is an array of columns. |
$tree | mixed | null | Reserved for a future hierarchical/indented-tree dropdown - not implemented yet; passing a non-default value throws. |
$dependField | string|null | null | Column on $targetTable to filter by, compared against $dependOn's current value. See Cascading / Dependent Dropdowns. |
$dependOn | string|null | null | Name of another relation()-configured field on this table whose current selection drives the filter above. |
$search | bool | false | Renders a searchable combobox (a text input that filters the fetched option list as you type) instead of a plain <select> - useful once there are dozens of options. Combines with $multi for removable chips. |
Filtering the option list
The array form of $where is the safe default - simple equality, parameterized:
$xcrud->relation('catid', 'categories', 'cid', 'name', ['published' => 1]);
The string form is a raw SQL fragment for anything an equality array can't express - it's trusted, developer-authored SQL, never built from user input:
$xcrud->relation('customerNumber', 'customers', 'customerNumber', 'customerName', "country = 'USA'");
Whichever form is used, the whole relation configuration - table, id column, name column(s), where, order, multi - is HMAC-signed with XcrudConfig::$appSecret the same way a base where() filter is, so it can't be retargeted or widened by editing anything client-side.
subselect(): a read-only computed column
Where relation() resolves an editable foreign key, subselect() adds a display-only column computed from an arbitrary correlated SQL expression - nothing is stored back into it, and it's never editable, sortable, or searchable.
$xcrud->subselect(
'orderCount',
'SELECT COUNT(*) FROM orders o WHERE o.customerNumber = payments.customerNumber'
);
$field becomes a new column alongside the table's real ones and must be a valid identifier that isn't already a real column name. $sql is trusted, developer-authored SQL - like relation()'s raw $where string, it is HMAC-signed with XcrudConfig::$appSecret when the page renders and re-verified on every request, so a visitor can't tamper with it, but it is never validated or sandboxed against what you write - keep it server-authored, never assembled from request input.
Cascading dropdowns
$dependField/$dependOn make one relation dropdown's options filter based on another field's currently selected value on the same form - e.g. a City dropdown that only offers cities in the currently-selected Country. See Cascading / Dependent Dropdowns for the full walkthrough, including multi-level chains.