Blog Sections Open
Updating Parent Fields When Child Resources Change
A practical pattern for aggregating child TV values back into a parent resource so filters and listings stay fast and consistent.
This article came from a real catalog problem. A project stored product variants as child resources, while the parent resource represented the main catalog item. Faceted filtering needed the parent to “know” which values existed in its children, otherwise filtering became awkward and expensive.
The original solution was a quick, project-specific snippet and plugin pair that updated parent TVs whenever child resources were saved. It was rough, but the pattern is solid and still worth documenting.
The Use Case
Imagine a parent product resource and several child resources for color variants or pattern variants. If your filters run against the parent listing, you often want the parent TV values to reflect the combined values of all children.
That way, the parent can be filtered directly without rebuilding the aggregation on every frontend request.
The Core Snippet
the article used a helper snippet named param_update to collect TV values from child resources, flatten them, remove duplicates, and write the aggregated value back to the parent.
<?php
if ($parent > 0) {
$tvs = ['color' => 10, 'tip_risunka' => 7];
$tvnames = implode(',', array_keys($tvs));
$out = [];
$data = $modx->runSnippet('DocLister', [
'parents' => $parent,
'tvList' => $tvnames,
'tvPrefix' => '',
'api' => $tvnames
]);
$data = json_decode($data, 1);
foreach ($data as $doc) {
foreach ($doc as $key => $val) {
if (isset($tvs[$key])) {
if (strpos($val, '||') !== false) {
$val = explode('||', $val);
foreach ($val as $vv) {
$out[$tvs[$key]][] = $vv;
}
} else {
$out[$tvs[$key]][] = $val;
}
}
}
}
}
The important part is not the exact syntax, but the workflow:
- define which TVs matter
- load child resources and their TV values
- merge all values into a single list per TV
- remove duplicates
- save the aggregated value on the parent resource
Triggering the Update on Save
The source then used a plugin on OnDocFormSave so the parent resource gets refreshed whenever a child or the parent itself is edited.
global $modx;
$tmpl = 3;
$tmpl_p = 2;
switch ($modx->event->name) {
case 'OnDocFormSave': {
if (isset($_POST['parent']) && $_POST['template'] == $tmpl) {
$modx->runSnippet('param_update', ['parent' => $_POST['parent']]);
}
if (isset($_POST['parent']) && $_POST['template'] == $tmpl_p) {
$modx->runSnippet('param_update', ['parent' => $_POST['id']]);
}
}
}
This is the practical bridge between content editing and filter-ready data.
Bulk Rebuilds for Existing Content
The article also included a small module for rebuilding all parent values across the site. That is important because a save-trigger only helps after the logic is in place. Existing content usually needs a one-time backfill.
Why This Pattern Is Useful
- frontend filters can stay simple
- parent resources become easier to query
- you avoid recalculating aggregated values on every request
- catalog behavior stays aligned with the editor workflow
What to Improve in a Modern Project
the article openly admitted that the code was “assembled quickly.” If you adapt this approach today, improve a few things:
- validate empty values more carefully
- wrap database writes in clearer helper methods
- add logging for rebuild failures
- avoid direct SQL string assembly where safer abstractions exist
Still, the main idea remains strong: when child resources define filterable attributes, sometimes the cleanest path is to keep the parent in sync at save time.
Stripping HTML Tags from Resource Content Safely
Use output modifiers to turn HTML-rich resource content into plain text for XML feeds and clean exports.
Why Uncached DocLister Calls Return Nothing
A troubleshooting note for cases where a cached DocLister call works but an uncached call with [[!DocLister]] appears to return nothing in Evolution CMS 3.