Resolvers VIII - Updating Template Variables

Using resolvers in a transport package to update template variables.

By Bob Ray  |  August 12, 2025  |  6 min read
Resolvers VIII - Updating Template Variables

Overview

In my previous three articles, I wrote about using resolvers in a transport package to modify snippets, their default properties, and any attached property sets. In this one, we'll look at updating template variables.

Template Variables

Template variables (TVs) are the odd duck of MODX objects. The data they hold is in two separate tables (with a third table holding the intersects that connect the other two). We'll see why in just a bit, but first let's look at their original purpose.

TVs were created because the fields of the modResource object are fixed, and you might want to save a resource-related value for a specific resource when there's no field for it. Say, for example, you want a star rating for some of your pages. There's no field for that, but you can create a "Stars" TV and put a number there that can be used to display a star rating for that page.

One oddity of TVs is that although they hold specific values for individual web pages, they are tied to both templates and resources. A resource can't use a particular TV unless the TV is attached to that resource's template. The modTemplateVarTemplate object is the intersect object that ties TVs to templates. This intersect is necessary because of the many-to-many relationship between TVs and templates. A TV can belong to more than one template, and a template can have more than one TV attached to it.

Another peculiarity of TVs is that their values can be found in two separate tables in the MODX database.

In the table that holds the modTemplateVar object itself, the default_text field holds the default value of the TV, which will be used if the value of the TV is not set for a given resource. This value will appear in any resource where the TV's value is left blank.

For TVs where the value is set for a resource, the value or the TV for that resource is stored in the table that holds the modTemplateVarResource objects. It has only three fields:

tmplvarid  (integer)
contentid  (integer)
value  (string)

The tmplvarid field holds the ID of the TV. The value field holds the value of the TV for a particular resource. The contentid field holds the ID of that resource.

Notice that there's no information here about the template that TV is attached to.

If you want to do a search-and-replace operation on a TV value, you kind of need to come at the problem sideways. What you need is a collection of the specific TVs you want to change the value of. The best method is usually to start with the TV names (as an array), and just ask for TV objects that are in that list of names, so that's what we'll do here.

You can also get your collection of TVs from one or more templates, as long those templates are only connected to your TVs, but that's not a typical case.

You might think you could start with a collection of resources (or their IDs), then get all the modTemplateVarResource objects that have a contentid field that matches any of the resources, then work back to the TV object based on the tmplvarid field. This won't work, though, because TVs where the value is blank for a given resource won't have a modTemplateVarResource record. You won't find the TV object for those resources, and you would miss the opportunity to change the TV's default value.

The Code

The use case we've been using for my last few articles doesn't actually involve TVs, but let's pretend it does and call them TV1 and TV2.

As a reminder, here's the code from the top of the resolver file that does the search-and-replace operations:

function checkContent(array $terms, string $content) {

    foreach ($terms as $term) {
        if (strpos($content, $term) !== false) {
            return true;
        }
    }

    return false;
}

$base = MODX_CORE_PATH . 'components/classextender/model/';

$prefix = $modx->getVersionData()['version'] >= 3
    ? 'MODX\Revolution\\'
    : '';

$uSearch = 'UserData';
$uReplace = 'userData';
$rSearch = 'ResourceData';
$rReplace = 'resourceData';

$searchArray = array(
    $uSearch,
    $rSearch,
);
$replaceArray = array(
    $uReplace,
    $rReplace,
);

Here's the code for correcting the TVs that would go in the Update section of the resolver:

$tvs = array(
    'TV1',
    'TV2'
);

$defaultCount = 0;
$valueCount = 0;
$fixedDefaultCount  = 0;
$fixedValueCount = 0;

foreach ($tvs as $tv) {
    $tvObj = $modx->getObject($prefix . 'modTemplateVar', array('name' => $tv));
    if ($tvObj) {
        /* First handle default value */
        $value = $tvObj->get('default_text');
        if (checkContent($searchArray, $value) {
            $defaultCount++;
            $newValue = str_replace($searchArray, $replaceArray, $value);
            $tvObj->set('default_text', $newValue);
            $tvObj->save();
            $fixedDefaultCount++;

        }

        /* Done with default_text.
           Now do related modTemplateVarResource objs */

        $tvrs = $tvObj->getMany('TemplateVarResources');
        foreach ($tvrs as $tvr) {
            $value = $tvr->get('value');
            if (checkContent($searchArray, $value) {
                $valueCount++;
                /* Found one that needs fixing */
                $newValue = str_replace($searchArray, $replaceArray, $value);
                $tvr->set('value', $newValue);
                $tvr->save();
                $fixedValueCount++;
            }
        }
    }
}

/* Optional: report counts here with something like this:
    $msg = 'Found ' . $defaultCount . ' TVs';
    $modx->log(modX::LOG_LEVEL_INFO, $msg);
    $msg = 'Corrected ' . $fixedDefaultCount . ' TVs';
    $modx->log(modX::LOG_LEVEL_INFO, $msg);
    $msg = 'Found ' . $valueCount . ' TV values';
    $modx->log(modX::LOG_LEVEL_INFO, $msg);
    $msg = 'Corrected ' . $fixedValueCount . ' TV values';
    $modx->log(modX::LOG_LEVEL_INFO, $msg);
*/

The code above is fairly straightforward. We get each TV, and correct the default_text if necessary. Then, we get all the modTemplateVarResources for the current TV, and fix the values if necessary. This is somewhat similar to what we did earlier with the snippets and their default properties.

Coming Up

In my next article, we'll see some simpler code to perform our search-and-replace operation on files.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.