Overview
In my previous article, I wrote about using validators in a transport package to check for things necessary for your extra to run properly, and (optionally) to abort the package installation if they are not there. In this one we'll look at resolvers, which can be used to execute PHP code or to transfer files.
How are Resolvers different from Validators?
In many ways the two are similar, especially in the structure of their code. Here's simple example of a resolver's code:
<?php
/**
* Resolver for Example extra
*
* Copyright 2025 yourname <Your email or website URL>
* Created on 01-11-2025
*
* {License would go here}
* @package example
* @subpackage build
*/
/* @var $object xPDOObject */
/* @var $modx modX */
/* @var array $options */
/** @var modTransportPackage $transport */
/* Set up $modx variable for use later */
if ($transport) {
$modx =& $transport->xpdo;
} else {
$modx =& $object->xpdo;
}
switch ($options[xPDOTransport::PACKAGE_ACTION]) {
case xPDOTransport::ACTION_INSTALL:
$modx->log(modX::LOG_LEVEL_INFO, 'Resolver: Install section executing');
/* {your code} */
break;
case xPDOTransport::ACTION_UPGRADE:
$modx->log(modX::LOG_LEVEL_INFO, 'Resolver: Update section executing');
/* {your code} */
break;
case xPDOTransport::ACTION_UNINSTALL:
$modx->log(modX::LOG_LEVEL_INFO, 'Resolver: Uninstall section executing');
/* {your code} */
break;
}
return true;
The code above is almost identical to the validator code we saw in the previous article. It's just missing the lines that check for a condition and return false if it's not met. Aborting the installation like that would theoretically work in a resolver, but as I said in the previous article, I've never seen it done. It wouldn't make much sense, because resolvers, unlike validators, generally run at the end of the installation process, after all files and objects have been installed.
Aborting the process at the end would mean having to undo any changes made during installation. You'd have to remove all DB tables, files, and MODX objects. That would take a lot of complex code that would be difficult to create and test, and even more difficult to maintain.
Resolvers generally execute at the end because they often operate on the objects that have been installed, connecting resources to templates, plugins to system events, child resources to their parents, etc. It's often possible to make those connections without using a resolver, but it's complex, and easier to mess up.
I find it simpler, and more reliable, to do it in a resolver, but for that to work, the objects have to be in place. The MyComponent extra handles this automatically by creating the appropriate resolvers for any objects that need to be connected to other objects.
Like validators, resolvers can do pretty much anything you can create in PHP code (more on this in a bit). Almost every Extra's transport package has one or more resolvers that perform specific job — installing files. Usually, an extra needs files to do its job. It may need CSS files, JS files, images, data files, or other custom files.
The files are transferred in a transport package with what are called "file resolvers". It's not required (the target location for a file resolver can be anywhere), but they almost all go to one of two places: assets/components/packagename/
or core/components/packagename/
. By putting the files in those two places in your development environment, you can transfer them with just a few lines in your build.transport.php
file. That looks something like this:
First, you define the locations in your $sources
array near the top of the build.transport.php
file:
define(PKG_NAME_LOWER, 'yourpackagename');
/* define sources */
$root = dirname(dirname(__FILE__)) . '/';
$sources = array(
'root' => $root,
'build' => $root . '_build/',
/* note that the next two must not have a trailing slash */
'source_core' => $root . 'core/components/' . PKG_NAME_LOWER,
'source_assets' => $root . 'assets/components/' . PKG_NAME_LOWER,
/* ... */
);
Then, you attach the following file resolvers to one of your vehicles like this:
$vehicle->resolve('file', array(
'source' => $sources['source_assets'],
'target' => "return MODX_ASSETS_PATH . 'components/';",
));
$vehicle->resolve('file', array(
'source' => $sources['source_core'],
'target' => "return MODX_CORE_PATH . 'components/';",
));
That's all there is to it. All the core and assets files in the local core/
and assets
directories for your package will go into the package. When the package is installed, the files will be placed in the standard locations — the website's assets/components/packagename/
directory, and the core/components/packagename/
directory.
PHP Resolvers
The section above as about file resolvers. PHP resolvers are handled in a similar way. The file resolver can be attached to any vehicle in the package, since the point at which the files get transferred almost never matters. The PHP resolvers are almost always attached to the last vehicle added to the package in the build.transport.php file
. It's common to attach the file resolvers to that same vehicle.
PHP resolvers are attached to a vehicle just like the validators we saw in the previous article, except that you use $vehicle->resolve()
instead of vehicle->validate()
. Here's an example:
/* In the $sources array of the build.transport.php file, specify the directory containing your resolver files */
$sources = array(
'resolvers' => $root . '_build/resolvers/',
/* ... */
);
/* Later in the code */
$vehicle->resolve('php', array(
'source' => $sources['resolvers'] . 'resolvername.php',
));
The resolvername.php
, as you've probably guessed, is the name of your resolver file containing code like the code in the first code section above (the one with the switch
statement).
Resolver Code
We haven't seen any information about things you can do in the code of resolver itself. We'll see several examples in the upcoming articles, such as using the "Upgrade" section to make changes to the objects installed in earlier versions of your extra.
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.