For SiteDash I built a worker queue, based on MySQL, to handle processing tasks asynchronously. There's a central database and separate worker servers inside the same private network that poll for new tasks to execute. These worker servers run PHP, using xPDO 3, to perform the tasks the application server has scheduled.

One problem that would occasionally pop up is that the worker servers would lose connection with the database. The database is on a different server in the network, so that could come from rebooting the database server, a deploy or backup causing high load, network glitch, or just.. gremlins.

Obviously, the worker servers need to talk to the database to be useful, so I started looking at a way to 1) detect the connection was dropped and 2) automatically reconnect if that happens. It turns out to be fairly straightforward (once you know how!).

First, I implemented a check to see if the connection is alive. It does that by checking if a prepared statement (query) could be prepared.

<?php
while (true) {
    $q = 'query that is irrelevant here';
    $stmt = $xpdo->query($q);
    if ($stmt) {
        $stmt->execute();
    }
    else {
        reconnect();
    }
    // Execute task, if any
    sleep(1);
}

function reconnect() {
    global $xpdo;
    $xpdo->connection->pdo = null;
    return $xpdo->connect(null, array(xPDO::OPT_CONN_MUTABLE => true));
}

The workers run in an infinite loop, one loop per second, so this check happens every second. When the statement can't be prepared it's treated as a dropped connection, and we call the reconnect method to restore the connection.

The reconnect happens by unsetting the PDO instance on the xPDOConnection instance. Without that, xPDO thinks it still has a connection, and will continue to fail. Because we don't unset the xPDOConnection instance, we can just call $xpdo->connect() without providing the database connection details again.

With this check in place, the loop can still get stuck in a useless state if there's a reason it can't reconnect. That can have some unintended side effects and makes it harder to detect a problem that needs manual interference, so I also implemented another check.

Every 10 loops, another query is sent to the database with a specific expected response; a simple SELECT <string>. The idea is the same as the check above, see if the statement can't be prepared or doesn't return the expected result, and if so, do something.

Here's what that roughly looks like:

<?php
$wid = 'Worker1';
$loops = 0;
while (true) {
    $loops++;
    
    $q = 'query that is irrelevant here';
    $stmt = $xpdo->query($q);
    if ($stmt) {
        $stmt->execute();
    }
    else {
        reconnect();
    }
    // Execute task, if any
    sleep(1);
    
    // Every 10 loops, check if the connection is alive
    if (($loops % 10) === 0) {
        $alive = $xpdo->query('SELECT ' . $xpdo->quote($wid));
        if (!$alive || $wid !== $alive->fetchColumn()) {
            break;
        }
    }
}

function reconnect() {
    global $xpdo;
    $xpdo->connection->pdo = null;
    return $xpdo->connect(null, array(xPDO::OPT_CONN_MUTABLE => true));
}

In this case, we're not calling the reconnect() method. Instead, we're breaking out of the loop. This way the PHP process can end gracefully, instead of pretending to be churning along properly. When the process ends, supervisord is used to automatically restart it. When a new process is unable of connecting, the logs and monitoring get a lot louder than when a worker silently keeps running, so this system is working nicely.

Now, this obviously isn't the entire worker code for SiteDash. Over time it has grown into 300 lines (not counting the tasks themselves) of worker logging, automatic restarting when a deployment happened, analytics, ability to gracefully kill a process, and dealing with unexpected scenarios like a database connection getting dropped.

Overall this system has managed to keep the processes running quite nicely. There were some issues where certain tasks would cause a worker to get stuck, which have now been resolved, and currently the biggest limiting factor for the worker uptime is deployments. The workers need to restart after a deployment to make sure there is no old code in memory, and I have been fairly busy with adding features to SiteDash (like remote MODX upgrades last week!).

It's also been fun and interesting to try to get a good insight into these background processes and tweaking the monitoring to notify about unexpected events, without triggering too many false negatives. A challenge I'd like to work on in the future is automatically scaling the number of workers if the queue goes over a certain threshold, but for now I can manually launch a couple of extra processes quite quickly if things take too long.

Some fun numbers:

  • Overall, since launching workers as indefinitely running processes, the average worker process was alive for 12,5 hours
  • Since fixing the last known glitch where a process could get stuck executing certain tasks, on October 29th, the average worker stayed online for 2 days, 12 hours and 52 minutes.
  • The longest running workers started on November 1st and stayed up for 12 days, 5 hours and 40 minutes before being restarted due to a deployment.

Sometimes you need to define multiple criteria in xPDO schemas to make sure your relations are properly defined. I came across this while working on a project that needed some existing databases integrated. This specific case had one "fl_translations" table that contained the translations for thousands of objects. To get the translations for a specific object, you would need to filter that table on the "originaltable" field. For example, one row might have originaltable=fl_colors and contains the translation for the color in a specific language.

In order to properly define this relation, I went browsing the MODX core schema for some ideas, and I stumbled across a pretty much undocumented feature of xPDO Schemas: relation criteria!

Here's a snippet of the final schema:

Basically, while with a normal relation you would immediately close the <composite> (or <aggregate>) element, but if you want additional filters (criteria) on the relation, you can define that. A simple <criteria> object with a target (I presume but haven't tested that you can add an additional <criteria> object with a target of local), and inside it a JSON object with the fieldname (of the foreign object) and the value it should have (in this case, fl_colors, as that's how it was set up).

This way of defining relations is very powerful and I imagine some really complicated relations could be defined once (in the schema) and reused without knowing all the details by simply calling getMany('RelationAlias').

The core uses this type of relationship for defining the PropertySets relations on elements and also in the modAccess definition. For example, here is a snippet from the core schema for modChunk:

xPDO is quite nifty eh?

Just a quick post here for something that I see asked (or, more accurately, see being done in the wrong way) way too often.

When you're building your own components or data model using xPDO Schemas, you will probably be working with that data in processors, snippets or external applications as well. At some point you will have related tables, and you will love coming across methods like addOne and addMany - they're awesome!

There's one big caveat when using them, and that is to figure out which one to use.

Oh, you use addOne when you want to add *one* related object, and addMany when you want to add multiple in one go? WRONG. Well, okay - there's a small truth in there. But it is not related to the fact if you're in the process of adding only one related object or an array of objects. If you are using the wrong method you can often see this by the lack of relation being added to the extra object(s), like the relation is not "sticking" for some reason.

So do I use addOne or addMany?

If you've read the Defining Relationships documentation page, which is a great introduction to aggregate and composite relations with xPDO schemas, you should have noticed this thing called the cardinality. This little thing is very important and the key to figuring out when you need addOne, and when you need addMany.

You see, if the cardinality is one, you will need to use addOne. If the cardinality is many, you will need to use addMany. And that's all folks.

That's all folks!

Teaser image by Sepehr Ehsani.

As of MODX Revolution 2.2, developers are handed class-based processors to speed up development of the back-end components. These are great, and I have blogged about Class Based Processors in general before with some quick examples, but in this article we'll dive into a particularly awesome one: modObjectGetListProcessor.

The modObjectGetList processor is mostly used for populating grids through the modExt Grid implementation, but you could also use it for any other widget that uses a JSON Data Store. And processors aren't limited to being used by connectors for back-end components.. they're also great to keep your code DRY (Don't Repeat Yourself) for use in Snippets!

For this article we'll assume a simple grid though. The techniques displayed can be used to point you in the right direction for other implementations.

The Basics

Here's basically the minimum processor file you can use:

.. and the reason they're so awesome. Brief, super awesome, working code!

The most important thing to note is the public variable $classKey, this is the class name of the object you are going to retrieve. Furthermore you'll see we define the $defaultSortField to the "initiatedon" date field from the schema, and with $defaultSortDirection we make sure we get the latest on top. The $objectType variable is not necessarily required, but allows you to use prefix (lexicon) error messages for some default sanity checks. For example in the update processor it will use the objectType to prefix _err_ns if the primary key is not specified.

We also make sure we return the name of our extended class in the end, as that is used to instantiate the processor when it's called. While you're free to name it whatever you want, I'd advise you to keep it the same as your classKey. That way, when adding a new processor, you can just copy/paste another one and find/replace the old classKey for the new one and you're good to go.

Exploring the Processor Process

Just like an earlier post, dealing with the modObjectUpdateProcessor class based processor, I have created a list of what happens in the processor that you will find below.

  1. Processor instantiated, properties being set.
  2. Using checkPermissions() the processor decides if the user is allowed to access it.
  3. The processor finds lexicon topics to load via getLanguageTopics, which gets its data from the languageTopics variable (as an array).
  4. initialize() is called on the processor, which sets a number of default properties including sort to the defaultSortField (default: name) class variable, and the direction to the defaultSortDirection variable (default: ASC).
  5. process() is called.
  6. beforeQuery() is triggered by process(), and if the result is not a boolean TRUE it will consider the processor to have failed and cancel further execution.
  7. getData() is triggered by process().
  8. The getData() method builds an xPDOQuery object for the classKey type.
  9. The getData() method calls prepareQueryBeforeCount(xPDOQuery $c) allowing you to add additional conditions to the queries. After calling that, it fetches the total amount of results using modX.getCount.
  10. prepareQueryAfterCount(xPDOQuery $c) is called by getData().
  11. The query is sorted with help of the getSortClassKey() method, and the sortAlias, sort and dir properties.
  12. If the limit property is larger than 0 it limits the query and sets an offset.
  13. modX.getCollection is called with your data, it's been retrieved.
  14. Every row is iterated over using the iterate(array $data) method. iterate calls beforeIteration(array $list), and starts looping over the rows.
  15. If the checkListPermission variable is true, the object extends modAccessibleObject and checkPolicy('list') is false, it skips the row.
  16. prepareRow(xPDOObject|modAccessibleObject $object) is called which needs to return an array with the objects' fields. Great method to customize the retrieved data. The array is added to the list.
  17. After iteration over the entire result set afterIteration(array $list) is called.
  18. The data is returned.

Example Usages

Defining Constraints (where field X has value Y)

When adding constraints, we will take our minimum processor and add (actually override) a new function called prepareQueryBeforeCount. This function takes in the xPDOQuery object as parameter, and expects it to be returned as well.

Easy enough we first get the "reservation" value using $this->getProperty(). By specifying a second value we are assigned a default instead of NULL. In this case I'm setting the default value to zero, which makes sure that if there is no reservation passed, it will not return any results - but no results instead (as all rows have a reservation set to > 0).

After getting the reservation variable, we just interact with the xPDOQuery $c as we would in a normal processor (or script) and pass our where condition.

In the end we return the xPDOQuery (this is important!) and we've limited our query to just that reservation.

Modifying the way row data is returned

In some cases, your database set up may not completely match how you want to display that data in the front end. For example, you may have an array (which is stored serialized), which you want returned as one line of text per array key=>value, for rending in a textarea for example.

You will also see that instead of calling simply $object->toArray(), I am passing some additional parameters.

Specifically selecting fields

You could also join tables in the prepareQueryBeforeCount processor, add additional constraints etc.

Are there any more examples you would like to see, or have some to share yourself? Let me know in the comments!

One of the changes in MODX 2.2 is a completely refactored class-based processor system, allowing you to create processors for components more easily than ever. As with everything new and shiny, there's some nice tricks you can use.

I've been using these class-based processors in my latest project, and want to share my experience with it so far, as well as showing some actual, functional code.

What's a class-based processor?

If you've gone through the Developing an Extra tutorial before, you'll know what a processor is: the php code that gets executed by your AJAX connector (or modX::runProcessor) which does whatever needs to be done, and returns JSON.

The class-based part of this all means you can simply use something like the following for a custom "update" processor:

<?php
class fdmPriceCodeUpdateProcessor extends modObjectUpdateProcessor {
    public $classKey = 'fdmPriceCode';
    public $languageTopics = array('frontdeskman:pricing');
    public $objectType = 'fdmp';
}
return 'fdmPriceCodeUpdateProcessor';

... and the MODX core will handle the rest. :) Another example of a class based processor can be found in my What's new in MODX 2.2 post.

What the modObjectUpdateProcessor Process does

Despite the low amount of code we've used, there's a lot of work being done behind the scenes to make sure your update (or other process) is executed like it should. By inspecting the modProcessor class, here's the 14-step process I found for the modObjectUpdateProcessor.

  1. Your processor gets instantiated on the request, properties are being set.
  2. The checkPermissions() function is run which you can use to verify any custom permissions are enabled.
  3. The getLanguageTopics() function is run, which should return an array of lexicon topics to load, which are then loaded.
  4. Processor function initialize(): gets the primary key (from the public variable primaryKeyField which defaults to "id"), gets the object based on the classKey and posted primary key value and if it's an extension of modAccessibleObject it checks the user session against the "save" policy. If the initialialize function doesn't return true, the request is cancelled with an error message.
  5. Processor function beforeSet() is run: by default that function only checks if there are any errors set (by $this->addFieldError('fieldname','error') for example). If this doesn't return true, a failure is returned by the processor.
  6. fromArray() is called on the object (available as $this->object) with all posted properties.
  7. Processor function beforeSave() is run. As with the beforeSet function, this only checks if any errors were registered. If the result is not a boolean true, the processor returns a failure with the value returned as message.
  8. validate() is called on the object. If this doesn't return a positive value, the error messages are set as field errors.
  9. If you set a beforeSaveEvent variable on the class, that event is invoked and if that doesn't return true the save is prevented. While the fireBeforeSaveEvent() function is marked as public, you will probably not want to override this in regular cases.
  10. The saveObject() function is run which simple calls save() on the object. According to the inline comments, this may be overriden for transient and non-persistent object updating.
  11. The fireAfterSaveEvent() function is run, which fires the event you specified in the afterSaveEvent class variable.
  12. The logManagerAction() function is run, which logs a generic message to the Manager Log (available under Reports > Manager Actions).
  13. A success message is returned through the cleanup() function, this actually wraps the pre-2.2 $modx->error->success/failure often used in processors.
  14. The result (success or failure) is parsed into a modProcessorResponse object and returned.

If you look backup, you'll see a lot of bold text. These functions or variables are available for you to override and extend to force it into doing what you need it to do. Note that every processor class (for create, update, getlist, remove and get) has its own functions. If you use an IDE like PhpStorm you can get code hinting, or just dive into the source!

Extending our Processor class to handle Checkboxes

When you've made a beautiful modExt/ExtJS window, and sticked your "active" checkbox (xtype: 'checkbox') in there, you may not exactly get the results you were expecting. Perhaps your schema expects a boolean "true" or "false" (I know mine does!) which would be stored as a tinyint 1 or 0 in the database.. but the form posts a string value "on" if the field is selected, and an empty value if not. Uh oh..

Luckily we've got so many functions we can add to our update processor class to make sure it parses those values into what the object's fromArray (or set) function needs to save it properly.

If you look back to our 14-point list above, you'll see fromArray is executed in step 6, meaning we need to change our property its value before step 6. How does the "beforeSet" function in step 5 sound to you? Sounds like it would work, to me!

If you're not at all familiar with object oriented programming, all the stuff explained so far may sound a bit daunting, but all that we're really doing is overwriting the functions of the original class to do what we want. There's plenty of PHP OOP tutorials on the web should you want to learn more about that.

So, with beforeSet and our expected and needed values in mind, here's our new and improved class which properly handles checkboxes in a MODX 2.2 style processor.

<?php
class fdmPriceCodeUpdateProcessor extends modObjectUpdateProcessor {
    public $classKey = 'fdmPriceCode';
    public $languageTopics = array('frontdeskman:pricing');
    public $objectType = 'fdmp';

    public function beforeSet() {
        $this->setProperty('active',($this->getProperty('active') == 'on'));
        return parent::beforeSet();
    }
}
return 'fdmPriceCodeUpdateProcessor';

I've put it all on one line using a ternary operator and the Processor's getProperty and setProperty functions, but if you prefer a bit more verbose coding, you could also do just this which works exactly the same:

/* ... */
    public function beforeSet() {
        $active = $this->getProperty('active');
        if ($active == 'on') { $active = true; }
        else  { $active = false; }
        $this->setProperty('active',$active);
        return parent::beforeSet();
    }
/* ... */

We could also do something similar in a remove processor, to prevent removing something if it's still linked to another thing. For example the following Processor which prevents removing a "fdmCharacteristic" object if there are still related "fdmRoomTypeCharacteristic" objects.

<?php
class CharacteristicRemoveProcessor extends modObjectRemoveProcessor {
    public $classKey = 'fdmCharacteristic';
    public $languageTopics = array('frontdeskman:property');
    public $objectType = 'rtc';
    
    public function beforeRemove() {
        $chars = $this->modx->getCount('fdmRoomTypeCharacteristic',array('characteristic' => $this->getProperty('id')));
        if ($chars > 0) {
            return $this->modx->lexicon('rtc.remove.roomtypesStillLinked');
        }
        return parent::beforeRemove();
    }
}
return 'CharacteristicRemoveProcessor';

And the final example for today extends the modObjectGetListProcessor to join a table and filter on properties, which is done by extending the prepareQueryBeforeCount function. This one also has a couple more variables we're assigning:

<?php
class RoomTypeGetListProcessor extends modObjectGetListProcessor {
    public $classKey = 'fdmRoomType';
    public $languageTopics = array();
    public $defaultSortField = 'name';
    public $defaultSortDirection = 'ASC';
    public $objectType = 'fdmRoomType';

    /**
     * @param xPDOQuery $c
     * @return \xPDOQuery
     */
    public function prepareQueryBeforeCount(xPDOQuery $c) {
        $query = $this->getProperty('property');
        if (!empty($query) && is_numeric($query)) {
            $c->innerJoin('fdmRoomTypeProperty','RTP','fdmRoomType.id = roomtype');
            $c->where(array(
                'RTP.property' => (int)$query
            ));
        }
        return $c;
    }
}
return 'RoomTypeGetListProcessor';

A note on Checkbox fields in modExt

I tend to use the "checkbox" xtype, however as of MODX 2.1 there is also a special "xcheckbox" xtype which always posts a value (1 or 0 if I remember correctly). I'm not sure why I don't use it, but it might be that the class-based processors do properly function with that one. Just didn't check, and I like being in control. :)

"Just wondering.. Are you working on a Property Management component?"

How'd you know?! :)

Was it the "fdmRoomType", "fdmRoomTypeCharacteristic" or "fdmProperty" object that gave it away? :)

This super-duper awesome extra I am working on is called FrontDeskMan and I hope to finish it over the coming few weeks. It will not become available as an open source project however, but most likely will be a licensed and properly supported Extra for the Hospitality industry as a joint op between myself and Jared Loman. You should totally start warming up some local Hotel clients for a new website with integrated property management and reservations for when we launch!

Update 29/4: Thank you Bert Oost of OostDesign for the tip on supporting different getPage configurations!

If you've ever created custom components, chances are that you have had to create accompanying snippets to actually show the data on the front-end. That can be a lot of work, and once data starts piling up you may want to add some pagination to it. This brief article will explain the requirements that getPage puts on your coding to properly paginate custom objects.

Getting the number of Results

First of all, we will need to tell getPage what the total number of results matching any filters set in place by our snippet is. In our snippet, we can easily set up the xPDOQuery object the way we normally would and take that object to get the total amount of results. No - we don't use getCollection and count() the array - that would be an insane waste of processing time. Instead we use the little jewel called modX::getCount, which is actually extended from xPDO::getCount. This will, without retrieving all the data from the database, get the total number of results for your query. Let's show an example of how we could use this - in this example we look up the amount of resources that have a class of modWeblink or modSymLink and are published:

So that will give us a number in the $total var - but that doesn't tell getPage anything - yet! Having looked at the getPage documentation trying to figure out this part, I've overlooked the answer many times and it is surprisingly simple. The documentation tells us there's a totalVar property:

And that's our answer. We need to set a placeholder, of which the key matches the value of the totalVar property: by default that's "total", though users have the ability to change this in their getPage call, so we'll need to get the totalVar property and set a placeholder with that name. Let's update our example accordingly to set the placeholder and that's all there is to it.

Starting at the right offset

Secondly we will need to make sure our snippet understand which results to display - and that it passes this on to our xPDOQuery object. In this case all we need to do is lookout for two properties: limit and offset. Limit will be set by getPage to the &limit property, which indicates how many items to show at once. The offset will be calculated based on the "page" request and the limit per page.

If we would update our example, we could end up having this - which works perfect with getPage for pagination.

You may notice a few important things:

  • We get the total amount of results, and set the total placeholder *before* we limit the query. While the docs say it may ignore the limit property, I like to be sure.
  • We use modX::getOption() (extended from xPDO::getOption) to get the "limit" and "offset" properties from our scriptProperties array available in snippets. If it does not exist we assign default values of 10 and 0 respectively.
  • We use xPDOQuery::limit() to set the limit and offset to the query. We then pass that on to our getCollection call and be on our way to process it further and give the site visitor what they want.

Hopefully this brief article will help more developers making their snippets getPage-proof, which will make it easier for front-end developers to use one tool (getPage) for all their pagination needs. Please feel free to share your thoughts and suggestions below!