/

Cascading / Dependent Dropdowns

Make one relation() dropdown's options filter automatically based on another field's currently selected value.

The idea

relation()'s $dependField/$dependOn parameters turn two ordinary relation fields into a parent/child pair: picking a value in the parent field re-fetches the child field's own options, filtered to only what belongs under the newly picked parent - a City field that only shows cities in the currently-selected Country, for example.

$dependOn names the OTHER relation field on this table to watch (it must itself be relation()-configured - there has to be a real dropdown to attach the cascade to). $dependField names the column on the child's own target table to filter by, compared against $dependOn's current value.

A 2-field example

A Country relation and a City relation, wired together - picking a country re-fetches the city dropdown's options (only cities whose country_id matches the country just picked):

$xcrud->relation('country', 'meta_location', 'id', 'local_name', "type = 'CO'");

$xcrud->relation(
    'city', 'meta_location', 'id', 'local_name',
    "type = 'CI'", null, false, ' ', null, 'in_location', 'country'
);

Here 'in_location' is $dependField (the column on meta_location to filter by) and 'country' is $dependOn (the name of the parent relation field on this table). Old xCrud's convention of passing '' rather than omitting a trailing argument is accepted for $where/$orderBy/$tree too - '' is treated the same as leaving it out.

Parameters that matter here

ParameterTypeDefaultDescription
$dependFieldstring|nullnullColumn on this field's own target table to filter by (e.g. in_location).
$dependOnstring|nullnullName of the parent relation() field on this table whose current value drives the filter.

The two must either both be set or both be left out - one without the other has nothing to pair a filter column with, and relation() throws rather than silently ignoring the mismatch.

Multi-level chains

Chains longer than two fields work the same way, one link at a time - each link only knows about its own immediate parent. A Country → Region → City chain against a single self-referencing table:

$xcrud->relation('country', 'meta_location', 'id', 'local_name', "type = 'CO'");

$xcrud->relation(
    'region', 'meta_location', 'id', 'local_name',
    "type = 'RE'", null, false, ' ', null, 'in_location', 'country'
);

$xcrud->relation(
    'city', 'meta_location', 'id', 'local_name',
    "type = 'CI'", null, false, ' ', null, 'in_location', 'region'
);

Picking a country re-fetches region's options (type = 'RE' AND in_location = {country's id}) and resets both region and city, since city's own parent (region) just became stale too. Picking a region similarly re-fetches city's options and resets city. A single-level cascade (e.g. Office → Manager, filtering employees down to one office's own staff) works exactly the same way with just one link.

A second example: Office → Manager

$xcrud->relation('office', 'offices', 'officeCode', 'city');

$xcrud->relation(
    'manager', 'employees', 'employeeNumber', ['firstName', 'lastName'],
    null, null, false, ' ', null, 'officeCode', 'office'
);

Here the manager dropdown's target table is employees, filtered by its own officeCode column against whatever office is currently picked - and the label is built from two columns (firstName, lastName) joined by the default ' ' separator.