Posted

Jul 8, 2014

Tags

Using Custom Processors in MODX Revolution Over AJAX Request

Tutorial answering to good old question, how to use AJAX within MODX front-end contexts?

The widely used solution for responding to AJAX requests is to use static PHP files or create resources that return JSON or HTML, which both are generally accepted methods due documentation having nothing about creating front connector to run processors. The connector way is more developer way of doing things and has certain advantages:

  • OOP way of thinking with direct access to API
  • Less steps for MODX to process content
  • Multitude of different processors to extend
  • Processors have own permission checks

Preface

To start with lets create few directories and choose the “namespace” we are going to use. For tutorial I guess best is 'tutorial'.

Assets Directory

Create base directory 'assets/components/tutorial/'. This will hold our web connector.

Core Directory

Create base directory 'core/components/tutorial/ and following directories under it.

  • processors/
  • processors/web/
  • processors/web/ajax/
I usually split in more directories for different methods. The ajax/ directory could hold directories post/ and get/ also. Another typical processors directory names I use is pre/ and post/. These directories I generally use when loading pages with snippets and processing data on form save. They just make it easier to handle loads of different processors when application grows.

Creating Web Connector

To begin with, lets create the web connector by navigating to newly created directory under assets and open new file web.connector.php with your chosen editor and copy paste the contents of next php snippet to it.

<?php
// Contents of assets/components/tutorial/web.connector.php
if (!empty($_REQUEST['action'])) {
    @session_cache_limiter('nocache');
    define('MODX_REQP',false);
}

require_once dirname(dirname(dirname(dirname(__FILE__)))).'/config.core.php';
require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php';
require_once MODX_CONNECTORS_PATH.'index.php';

define('AJAX_PATH', $modx->getOption('core_path').'components/tutorial/processors/ajax/'); // Change to what you use

if ($modx->user->hasSessionContext($modx->context->get('key'))) {
    $_SERVER['HTTP_MODAUTH'] = $_SESSION["modx.{$modx->context->get('key')}.user.token"];
    $_REQUEST['HTTP_MODAUTH'] = $_SERVER['HTTP_MODAUTH'];
} else { // Allow anonymous access
    $_SESSION["modx.{$modx->context->get('key')}.user.token"] = 0;
    $_SERVER['HTTP_MODAUTH'] = 0;
}
/* Uncomment if you want the connector work with users who have authenticated to manager but not to front end
if (( $modx->user->hasSessionContext('mgr') || $modx->user->hasSessionContext($modx->context->key))) {
    if ($modx->user->hasSessionContext('mgr')) {
        $_SESSION["modx.{$modx->context->key}.user.token"] = $_SESSION["modx.mgr.user.token"];
    }
    $_SERVER['HTTP_MODAUTH'] = $_SESSION["modx.{$modx->context->key}.user.token"];
    $_REQUEST['HTTP_MODAUTH'] = $_SERVER['HTTP_MODAUTH'];
}*/

/* handle request */
$modx->request->handleRequest(array(
    'processors_path' => AJAX_PATH,
    'location' => '',
));

You can uncomment the authetication section if want to use the connector with manager user also. MODX session management is quite picky on which contexts users are logged in and without this small piece of code, users would require to login to front end. This is, if the processors requires any authentication. Also change the AJAX_PATH accordingly or use service class to provide path. In this tutorial I will leave out extra classes completely out.

Creating Processors

I'll demonstrate two different processors, one that uses modObjectGetListProcessor and one completely custom that extends the base modProcessor which allows more custom code.

GetChildren Processor

This is simple processor which fetches child resources when variable 'parent' is set to integer. The processor is really simple due to modObjectGetListProcessor doing all the hard work and you just need to fill in few details. Create this file under core/components/tutorial/processors/ajax/ and name it 'getchildren.class.php'. Do not use caps and always remember to end the file with class.php, just .php postfix does not work.

<?php
// Contents of core/components/tutorial/processors/ajax/getchildren.class.php

class getChildrenProcessor extends modObjectGetListProcessor {
    public $classKey = 'modResource'; // The class key used for query
    public $permission = ''; // Permission required to run processor
    public $defaultSortField = 'pagetitle'; // Which field will be used for sorting

    /**
     * Allow stoppage of process before the query
     * @return boolean
     */
    public function beforeQuery() {
        if (!is_numeric($this->getProperty('parent'))) {
            return false;
        }
        return true;
    }

    public function prepareQueryBeforeCount(xPDOQuery $c) {
        if (!$this->getProperty('parent')) {
            $c->where(array(
                'parent' => $this->getProperty('parent'),
                'context_key' => 'web'
            ));
        }
        return $c;
    }
}
// Always return the class name
return 'getChildrenProcessor';

Noteworthy things to check:/

  • The $permission variable can be set to 'load' so user requires load privileges and anonymous access will be denied
  • Always return the class name
  • Use beforeQuery to stop processor if there is validation issue or anything that would affect the query not completing.
  • Always return the criteria object back from prepareQueryBeforeCount() and prepareQueryAfterCount()

Hello processor

This is small and simple processor extending modProcessor. Why even bother to show is that with extending modProcessor you are more free to do anything you want. Primary thing to remember is to always have process() method which is abstract in base class.

<?php
// Contents of core/components/tutorial/processors/ajax/hello.class.php
class sayHello extends modProcessor {
    public function checkPermissions() { return true; }

    public function process() {
        return $this->outputArray(array(
            'who' => 'Hello user with id: ' . $this->modx->user->id
        ), 1);
    }
}

return 'sayHello';

Open browser and go to addres assets/components/tutorial/web.connector.php?action=hello and you should get message Hello user with id 1 (if default admin) and 0 if anonymous.

Same as with earlier processor, remember to return to return the class name at the end. Also modProcessor does not use the $permission variable but instead you need to use checkPermission() method. By default it returns true so everything is allowed. Just included it in the example to remind it is there.

Closure

This was relative short tutorial overall, the web connector is easy to create and works on all sites if the path is as deep as with the example so config will be loaded correctly. You can also run modx own processors simply setting the processors_path to '' in the connector. Also there is lot more to modProcessor classes to use. You can view all base processors from core/model/modx/modprocessor.class.php and how they work and what methods they do hold. Please do leave a comment if got open question, feel like this needs more attention or just want to say hello.

Share your Thoughts

1 Responses

    1. avatar_r2000 r2000

      Hi, nice tutorial !

      when i launch the connector in my browser http://mysite/alpha/assets/components/tutorial/web.connector.php i have this message :
      {"success":false,"message":"action_err_ns","total":0,"data":[],"object":{"code":404}}

      for getchildren.class.php it's written "Create this file under core/components/tutorial/processors/ajax/ " but i think it should be "Create this file under core/components/tutorial/processors/web/ajax/ "

Your email address will not be published

Please enter your name.
Please enter valid email address.
Please enter your website address.
Please enter your message.
TwitterX Error: Could not load tweets as Twitter responded with the error: 'Invalid or expired token.'.
-->

Contact me with email or add my account to skype

the_dunnock@outlook.com

the_dunnock@outlook.com