Moving away from declaring functions in MODX Snippets

!function_exists()

I see it in a lot of code, and I'll admit that I did it quite a bit too, but functions should not be declared in the same script that processes output, a.k.a side effects. MODX has an amazing class load structure that makes it easy to move away from this old habit, and we'll talk about it briefly here. 

Using Classes in MODX

You can technically set up your folders & class structure location however you want, but for the sake of this I'll show you the (my?) preferred method and you can thank me later when you're building something big ;)

If you've ever downloaded an extra in MODX and inspected the snippet that was packaged with it, you'll probably be familiar with some of the following markup examples. If not, I highly recommend getting started by checking out how other snippets work on Github or within your own site. MODX has two primary folders used for maintaining packages, "core" and "assets". You will find a "components" folder within each of those.

Generally, if a package has any frontend modifications, whether it be to your site or to the manager, e.g. custom manager pages, they will have a folder in both the core/components/ and the assets/components folders. Packages that only affect processing and declarations with no loaded assets on the frontend will only exist in the core/components folder. In this article, I'll only be talking about adding available functions through a class, which needs no frontend assets so will only need to exist in the core/components folder. 

The basic folder structure for a general functions class named "scratch"  would be /core/components/scratch/model/scratch/. Within the final folder, you would add a scratch.class.php file. 

The first step (after the opening PHP tag) would be to declare your class. 

class scratch
{
    public $config = array();
    function __construct(modX &$modx, array $config = array())
    {
        $this->modx =& $modx;
        $corePath = $this->modx->getOption('scratch.core_path', $config, $this->modx->getOption('core_path') . 'components/scratch/');
        $this->config = array_merge(array(
            'basePath' => $this->modx->getOption('base_path'),
            'corePath' => $corePath,
            'modelPath' => $corePath . 'model/',
        ), $config);
        $this->modx->addPackage('scratch', $this->config['modelPath']);
    }
}

What this is basically doing is constructing the class and making it available to MODX as well as making MODX available to it. Notice, instead of referring to $modx, we will now refer to the $modx class as $this->modx, because MODX has been bound to the class as a public variable. Classes can have both public and private variables and functions. The difference is that a private function or variable can only be called within the class itself, and a public variable or function can be called within a class reference. I'll show examples of how you might use each of those. 

Private vs Public

In the following example, we'll set a private function that is only accessible by the class and then use a public function to call it. To keep a script from modifying set variables and cheating, we make two variables $key and $keyTry private from editing or returning their values. There is an additional method called protected which functions similar to private. However, the key difference is a protected variable can be accessed by other classes extending this class, whereas a private variable can only be accessed by this class. 

class scratch
{
    public $config = array();
    private $keyTry = 0;
    private $key = 'WINNER';
    
    function __construct(modX &$modx, array $config = array())
    {
        $this->modx =& $modx;
        $corePath = $this->modx->getOption('scratch.core_path', $config, $this->modx->getOption('core_path') . 'components/scratch/');
        $this->config = array_merge(array(
            'basePath' => $this->modx->getOption('base_path'),
            'corePath' => $corePath,
            'modelPath' => $corePath . 'model/',
        ), $config);
        $this->modx->addPackage('scratch', $this->config['modelPath']);
    }
    
    private function failedKey(){
        $this->keyTry++;
    }
    
    public function checkKeys($keys = array()){
        if( is_array( $keys ) ) {
            foreach( $keys as $key ) {
                if( $key != $this->$key ){
                    $this->failedKey();
                }
            }
        }
        $this->modx->(xPDO::LOG_LEVEL_DEBUG, "scratch - Key failed $this->keyTry times");
        return ( count ($keys) < $this->$keyTry ); 
    }
}

Namespaces

To load this class into a snippet you need to add a new namespace to the MODX manager by clicking the cog in the top right and selecting Namespaces. From there, you can click Create New. Give it the Name "scratch" and the Core Path "{core_path}components/scratch/".

Snippets

Now we can tie it all together by creating a new Snippet, loading our class, and calling our function.  

$corePath = $modx->getOption('scratch.core_path', null, $modx->getOption('core_path', null, MODX_CORE_PATH) . 'components/scratch/');
$scratch = $modx->getService(
    'scratch',
    'scratch',
    $corePath . 'model/scratch/',
    array(
        'core_path' => $corePath
    )
);

if (!($scratch instanceof scratch))
    return;

$keys = array('Loser','Failed','Try Again','WINNER','Next Time Buddy');

$tpl = ( $scratch->checkKeys( $keys ) ) ? 'tpl.WinnerDetected' : 'tpl.AllLosers';

return $modx->getChunk($tpl);

Wrap Up

This was a pretty basic example, but hopefully, it was enough to get you started in using classes for declaring your functions and snippets for controlling your side effects. If you have any questions feel free to send me a message using my social links found on this site or the comment box below.