$modx
That one variable packs a huge punch. Some argue it's too "monolithic" but personally I like the convenience of weilding all the power of MODX with one PHP object. But that's a topic for a forum thread or something...
This blog post is about a few $modx
methods that you could really benefit from using, if you aren't already. Even seasoned MODXers will hopefully find a few useful little gems here, as I've tried to collect some secret sauce on each method, from MODX Chief Architect Jason Coward. These are in no particular order:
getOption
getOption is a function in the revo implemntation of xPDO but it's also defined in various revo object classes. It makes the process of getting an array element easy and error-free, along with the ability to set a default. It's used like this:
$value = $modx->getOption('key', $scriptProperties, $default);
In the example I used the $scriptProperties
array, which is available inside every Snippet and has all the properties passed to it. You can access Snippet properties by calling the property key as a variable:
return $key; // this would return a string "myValue" where the Snippet's properties had &key=`myValue`
BUT using getOption is much more reliable, and you can set that $default
output easily. Jason recommends pretty much always using getOption. It also comes with some conveniences, like:
$value = $modx->getOption('key', null, $default);
That will fetch the value of 'key' from the MODX config, which cascades from System Settings » Context Settings » (in 2.3+) User Group Settings » User Settings.
To include ClientConfig settings, call the ClientConfig class in your Snippet and use getOption on the array you get from $clientConfig->getSettings()
Which brings me to the point that you can use getOption on any php array! The benefit is the built-in checks and default option.
Update [June 12, 2015]
John Peca here at MODX just brought up a cool use case for getOption:
$modx->getOption('param', $_GET, $default);
// or
$modx->getOption('field', $_POST, $default);
NOTE: I like to run the following static method that Garry Nutting taught me—on all user input. (Even though MODX is supposed to do it already—I'm maybe a bit paranoid here.)
$post = modX::sanitize($_POST, $modx->sanitizePatterns);
// then you can
$value = $modx->getOption('key', $post, $default);
Lastly, there's this lesser-known 4th argument for getOption:
$modx->getOption('key', $array, $default, true);
It's a boolean flag. When true
, getOption will return $default
if the 'key'
exists in the $array
but the value is empty.
getChunk
This one's a doozy. It's a core part of what made MODX so great from the get-go: separation of logic and content. With this method, and it's lighter, sister-method parseChunk
I rarely, if ever, write html inside a Snippet. There are certain cases, like with error messages, that you may decide to code the output into your PHP, but you can so easily expose the templating that it's almost a crime not to.
EDITED 05/31/2015: Jason informed me today that parseChunk
actually is, in some ways, incompatible with the MODX parser and was meant to be deprecated. It's part of some legacy code from Evo days. As such I'm removing any further references to it in this article, because Jason will actually deprecate it for Revo 2.4.
Consider this:
if (!$thingOne) $modx->setPlaceholder('errorOne', '<span class="error">Ooops! Thing One is false.</span>');
...
if (!$thingTwo) $modx->setPlaceholder('errorTwo', '<span class="error">Ooops! Thing Two is false.</span>');
This locks the output to specifics: the html tag, the "error" class, and the text content. What if the error class is used for something else, and it conflicts? What if they really, really need a block-level element, or no HTML wrapper at all? This might be better:
$errorTpl = $modx->getOption('errorTpl', $scriptProperties, 'errorTpl_chunk.default');
if (!$thingOne) {
$modx->setPlaceholder('errorOne', $modx->getChunk($errorTpl, array(
'message' => 'Ooops! Thing One is false.'
)));
}
...
if (!$thingOne) {
$modx->setPlaceholder('errorTwo', $modx->getChunk($errorTpl, array(
'message' => 'Ooops! Thing Two is false.'
)));
}
In your errorTpl Chunk, you can have arbitrary markup. Just add a placeholder for every key, the value of which you want to display:
<div class="row">
<blockquote class="sm-12 medium-6 columns cols error whatevah dawg">
[[+message]]
</blockquote>
</div>
Now someone who can't or shouldn't edit your Snippet can still modify the presentation. Yay!
setPlaceholder
You saw this method used in the example above, but there are actually four variations of placeholder-setting methods. There's pretty good documentation in the modx class reference but I'll paraphrase it here in a different format for convenience and readability. Note I'm using unconventional syntax here, to illustrate what's expected as arguments:
setPlaceholder(key, value) // returns nothing
setPlaceholders(array, prefix) // returns nothing
toPlaceholder(key, value, prefix, separator, restore) // returns an array
toPlaceholders(array, prefix, separator, restore) // returns an array
Break it down:
setPlaceholder
sets a MODX placeholder tag[[+key]]
with thevalue
passed to it.setPlaceholders
sets a MODX tag for each[[+key]]
in thearray
passed to it, with that array element'svalue
. You can set aprefix
in order to namespace your placeholders. For example:
$modx->setPlaceholders(array(
'key1' => 'value1',
'key2' => 'value2'
), 'myPrefix_');
this would set the placeholder [[+myPrefix_key1]]
with value1
and [[+myPrefix_key2]]
with value2
.
toPlaceholder
is similar to setPlaceholder, but you can specify aprefix
AND theseparator
used between the prefix and the key.
The restore option is a lesser-known flag that, when true, will populate the returned array with an element 'restore' containing an array of any overwritten placeholder values. I've yet to find a need for this, but as with anything MODX, when I need it, and find that Jason thought of it already, I fall in love with the software all over again. I'm sure that will happen with this interesting 4th argument.
toPlaceholders
provides the additional arguments/features of toPlaceholder, but it allows nested arrays to be passed to it as the subject. For example, if the array was like this:
array(
'key1' => 'value1',
'key2' => array(
'key2child1' => 'key2child1value',
),
);
and prefix
was 'myPrefix', and separator
was '-', you would get:
[[+myPrefix-key1]] <!-- value1 -->
[[+myPrefix-key2-key2child1]] <!-- key2child1value -->
setPlaceholder Usage
Now that you're an expert on these methods, how might you use them? Well, pretty much any time you call a Snippet twice in a single request, with the same properties, you could instead call the Snippet once and send the output to a placeholder that you can call multiple times without adding any overhead. Consider this example, the likes of which I've actually seen in production sites:
[[!someSnippetToQueryDatabase?
&ids=`[[!someUncacheableSnippet]]`
&properties=`[[!anotherSnippet]]`
&anotherprop=`1`
...
]]
[[!someSnippetToQueryDatabase?
&ids=`[[!someUncacheableSnippet]]` [[-let's process this again]]
&properties=`[[!anotherSnippet]]` [[-this too]]
&anotherprop=`2`
...
]]
It would probably be better to write one Snippet that handles all the custom logic, but let's ignore that for the moment. Often times a powerhouse Snippet like getResources will be called with its property string set like this. It's painful to see all those uncacheable Snippet calls, so here's an alternative that does exactly the same job:
[[!someUncacheableSnippet? &usePlaceholder=`dynamic_ids`]]
[[!anotherSnippet? &usePlaceholder=`dynamic_properties`]]
[[!someSnippetToQueryDatabase?
&ids=`[[+dynamic_ids]]`
&properties=`[[+dynamic_properties]]`
...
]]
[[!someSnippetToQueryDatabase?
&ids=`[[+dynamic_ids]]`
&properties=`[[+dynamic_properties]]`
...
]]
In those custom Snippets, you would change what may have been:
return $output;
to this instead:
$placeholder = $modx->getOption('usePlaceholder', $scriptProperties, '');
if (empty($placeholder)) return $output;
$modx->setPlaceholder($placeholder, $output);
Line by line:
- use getOption to fetch the value of the
&usePlaceholder
property, setting an empty string as default. - If the property is empty, just return the output as usual. This exposes the option to either use a placeholder or not.
- If we get to line 3, there was a value in the property, so use that string as the key for the placeholder tag, and send the output there. In this case we don't return anything.
Another use case is for the sake of readability:
[[getResources:default=`<p class="default">Some Default Output</p>`? &parents=`[[+id]]` ... ]]
While that works, this seems nicer to me, personally:
[[getResources? &toPlaceholder=`output` &parents=`[[+id]]` ... ]]
[[+output:default=`<p class="default">Some Default Output</p>`]]
I mean, if you like looking at a mangled mess of tags and backticks by all means do it the other way. But if you're like me you can wield the power of MODX placeholders!
You can also use setPlaceholder and it's family of methods, inside a Plugin fired OnWebPageInit, for example. This would make those placeholders available to every request, allowing you to use them anywhere in the Resource template. Beware however that this could have performance implications.
toArray
Often times, you use xPDO to retrieve objects from the database, but nearly as often you want to output field values from those objects. You can do this:
$value = $object->get('key');
That's assuming you have the object loaded into the $object
variable. The get
function provides some added reliability but is essentially the same as $object->key
. But what if you want to get all the fields? Populating an array with all the fields of an object is a very common use case, and thus:
$array = $object->toArray();
There are actually a few handy arguments for this method but they're pretty well explained in the docs. Once you have your object fields in an array you can easily render them with setPlaceholders
or getChunk
:)
fromJSON
This is incredibly useful when developing integrations with 3rd-party APIs, or even giving front-end devs a way to pass an array or "map" through a Snippet's properties. For example:
$json = '{
"id":"83dc1bd25f0487ae32603a1a8b304690",
"response-code":"200",
"title":"My Document Title",
"content":"My document content."
}';
$array = $modx->fromJSON($json);
return $modx->getChunk('myDocTpl', $array);
There! You've just consumed JSON data, formatted it with a template Chunk, and returned the output. It's so easy I feel guilty LOL! You can find out more about fromJSON
, its optional arguments, and sister-method toJSON
in the docs.
Update [June 8, 2015] setOption
Ok John Peca here at MODX just showed me this method, and it's so cool I had to tell you about it. It's in the docs here. Note at the bottom where it says:
Using setOption does not permanently update an option as xPDO options are not persisted, but loaded on each request.
To be able to override a System Setting on a per-request basis has all kinds of uses. Here's an example, and the reason why John went hunting for it in the first place :P
So Much More
There's so many other methods available in MODX that help make web development a pleasure, rather than a chore. If you haven't tried these MODX methods, why don't you give them a whirl in your next project? And if you don't use MODX CMS, isn't it time you started? :P