Passing Data between Two Snippets I

Using $modx->runsnippet() to pass data between two snippets


Variable values don't persist across different snippets, even when they're on the same page or in the same template. In this series of articles, we'll look at several methods for passing values between snippets.

MODX logo

Using runSnippet

If the value(s) you want to pass from one snippet to another are properties of the second snippet, you can use $modx->runSnippet() to pass them. The runSnippet() method takes the name of the snippet to be run as it's first argument, and an array of snippet properties as the second argument.

Say that you want to pass property values from your custom snippet to getResources and your getResources tag looks like this:

[[!getResources?
    &parents=`12`
    &tpl=`MyTplChunk`
    &depth=`5`
    &sortby=`pagetitle`
    &sortdir=`ASC`
]]

Your runSnippet() call would look like this:

$fields = array(
    'parents' => '12',
    'tpl' => 'MyTplChunk',
    'depth' => '5',
    'sortby' => 'pagetitle',
    'sortdir' => 'ASC',
);

return $modx->runSnippet('getResources', $fields);

There are a number of changes, some of which you might not notice, so I'll list them here:

  • The ampersands (&) are gone
  • The equals sign has changed to => with a space on either side
  • Each line ends with a comma
  • Both the keys and values are surrounded by single or double quotes (*not* backticks)

The space on either side of the => is not necessary but it makes the code easier to read.

The example above has no advantages over a getResources snippet tag, but remember that any of the values in the array (the things to the right of the =>) can be variables (see the example below).


Why Would I Want to Do That?

If the arguments to the called snippet (getResources in this case) will be the same on every page, there's not much point in using runSnippet(). But what if you want to customize the getResources call based on the page, or the folder it's under, or the number of children the current page has, or some other criterion?

You might want to customize the Tpl chunk used, the selected parent, the sort order, or some other property. You might be able to make that work with tags and output modifiers in the getResources tag, but that gets ugly in a hurry. It's a pain to debug, might not be reliable if the tags are deeply nested, and worst of all, it's painfully slow because MODX has to parse and process all the tags before even calling getResources.

In some cases, it's almost impossible to make it work. Say, for example, that you want to use a different Tpl chunk on certain holidays, or on weekends, or outside of business hours. Good luck doing that with tags and output modifiers. It's relatively easy in PHP, though, and once you've determined the result, you can just use a variable for the Tpl property in your array to runSnippet().

Here's a simple example that sets a custom Tpl chunk on Halloween and Christmas day:

$month =  date('m');
$day = date('d');

if ($day == 25 && $month == 12) {
    $tpl = 'christmasTpl';
} elseif ($day == 31 && $month == 10) {
    $tpl = 'halloweenTpl';
} else {
    $tpl = 'defaultTpl';
}

$fields = array(
    'parents' => '12',
    'tpl' => $tpl,
    'depth' => '5',
    'sortby' => 'pagetitle',
    'sortdir' => 'ASC',
);

return $modx->runSnippet('getResources', $fields);

Important: Notice that we *don't* put quotes around values that are variable names like $tpl. If we did, the string '$tpl' would be passed to getResources, and since there's no snippet with that name, the call would fail.

As an aside, why did we put the day test before the month test in the if statements? When you have a logical and (&&) in an if statement, the values are evaluated from left to right. If the first one is false, the second one is never evaluated. If we put the month first, the day test would be performed every day in October and December. That's 62 tests. Doing it the other way around, the month test will only execute on the 25th and 31st day of each month. That's only 19 tests of the second argument, (24 minus the months that have less than 31 days). In a logical and, it's always faster if you put the test most likely to fail first.


What You'll Get Back

This sometimes confuses people — runSnippet() always returns a single string containing the return value (output) of the snippet you call (the same is true of a snippet tag — all MODX snippets return a single string). In the case above, the complete output of getResources will be returned as one big string. If you need to do something with each of the resources getResources has aggregated, runSnippet() is probably not what you want. In some cases, you may be able to parse the output using PHP, but it's usually difficult and can be unreliable. In those cases, you're going to want a custom snippet that does some or all of what getResources does with code to handle each collected object.

If snippet you're calling with runSnippet is your own snippet (in other words, both snippets are custom snippets you wrote), the best thing to do is usually to combine the two snippets into a single snippet.

If you still want to use runSnippet(), though, you can return data from the called snippet in the form of a JSON string and convert it to a PHP array in the calling snippet (the one that uses runSnippet()). Here's an example where in the called snippet, the information you want to return has been placed in a PHP array called $data. The code at the end of the called snippet would look like this:

return $modx->toJSON($data)

In the calling snippet, you'd do this to convert the return value back into the original PHP array:

$data =  $modx->runSnippet('someSnippet', $fields);
$dataArray = $modx->fromJSON($data);

Speeding Things Up

The $modx->runSnippet() code is only ten lines long, so you can speed things up slightly by just putting that code (with small modifications) in your snippet as a function, saving the tiny amount of time it takes to make a function call:

function myRunSnippet($snippetName, array $params= array ()) {
    $output= '';
    if ($modx->getParser()) {
        $snippet= $modx->parser->getElement('modSnippet', $snippetName);
        if ($snippet instanceof modSnippet) {
            $snippet->setCacheable(false);
            $output= $snippet->process($params);
        }
    }
    return $output;
}

return myRunSnippet('getResources', $fields);

The time saved is negligible, but one reason you might want to do this is to change the setCacheable argument from false to true if you want the results to be cached. You could also make the cache flag an argument you pass to your function so the results would be cached sometimes, but not others:

function myRunSnippet($snippetName, $cacheFlag = false, array $params = array ()) {
    $output= '';
    if ($modx->getParser()) {
        $snippet= $modx->parser->getElement('modSnippet', $snippetName);
        if ($snippet instanceof modSnippet) {
            $snippet->setCacheable($cacheFlag);
            $output= $snippet->process($params);
        }
    }
    return $output;
}

return myRunSnippet('getResources', true, $fields);

Coming Up

In the next article, we'll look at passing data to another snippet using a file.



Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)


——

Comments (0)


Please login to comment.

  (Login)