Blog Back to all posts View all authors

How To Make a DIY MODX Plugin - Part 1

by Yaroslav Ushak
February 14. 2019

The best plugins are the ones that are made by someone else and ready to use, right? Unfortunately, not all functionalities have dedicated MODX plugins. You must make them. If you do it well, you can reuse these plugins later. All hail to the MODX plugin development!

Now the question is how to make it right?

Let's Make This MODX Plugin!

First, I'll introduce you to MODX plugin development with a simple example of an xPDO object with its schema and a certain output:

Get To Know Folder Structure Overview

Controllers - a folder that contains controllers of the plugin.
Docs - readme, changelogs, docs for the plugin.
Elements - Snippets, Chunks, code.
Lexicon - translations for plugin.
Model - folder that contains object representations and XML schema of costume tables structure.
Processors - Costume processors of our plugin.

Create System Varialbes

It will store main routes to the plugin.

testplugin.core_path - /www/page/core/components/testplugin/
testplugin.assets_url - /page/assets/components/testplugin/

Routes can be different due to the system setup.

Make a Snippet File

Snippet file will be used to render the content of the custom object. Create a file with the snippet to output example content of our plugin in:


It should look like:



$testPlug = $modx->getService('testplugin','TestPlugin',$modx->getOption('testplugin.core_path',null,$modx->getOption('core_path')).'model/testplugin/',$scriptProperties);

if (!($testPlug instanceof TestPlugin)) return '';


$tpl = $modx->getOption('tpl',$scriptProperties,'rowTpl');

$sort = $modx->getOption('sort',$scriptProperties,'name');

$dir = $modx->getOption('dir',$scriptProperties,'ASC');

/* build query */


$c = $modx->newQuery('TestPlugin');


$tests = $modx->getCollection('TestPlugin',$c);



/* iterate */

$output = '';

foreach ($tests as $test) {

   $doodleArray = $test->toArray();

   $output .= $testPlug->getChunk($tpl,$doodleArray);



return $output;

This snippet will get the xPDO object and show it with tpl which must be created under:




with the name rowtpl.chunk.tpl. It should contain simple output template:


<li><strong>[[+name]]</strong> - [[+description]]</li>

Create a base class

It will handle the main behavior of plugin and some main methods:


class TestPlugin

public $modx;
public $config = array();

public function __construct(modX &$modx, array $config = array())
$this->modx =& $modx;
$basePath = $this->modx->getOption('testplugin.core_path', $config, $this->modx->getOption('core_path') . 'components/testplugin/');
$assetsUrl = $this->modx->getOption('testplugin.assets_url', $config, $this->modx->getOption('assets_url') . 'components/testplugin/');
$this->config = array_merge(array(
'basePath' => $basePath,
'corePath' => $basePath,
'modelPath' => $basePath . 'model/',
'processorsPath' => $basePath . 'processors/',
'templatesPath' => $basePath . 'templates/',
'chunksPath' => $basePath . 'elements/chunks/',
'jsUrl' => $assetsUrl . 'js/',
'cssUrl' => $assetsUrl . 'css/',
'assetsUrl' => $assetsUrl,
'connectorUrl' => $assetsUrl . 'connector.php',
), $config);
// add this below

public function getChunk($name, $properties = array())
$chunk = null;
if (!isset($this->chunks[$name])) {
$chunk = $this->modx->getObject('modChunk', array('name' => $name));
if (empty($chunk) || !is_object($chunk)) {
$chunk = $this->_getTplChunk($name);
if ($chunk == false) return false;
$this->chunks[$name] = $chunk->getContent();
} else {
$o = $this->chunks[$name];
$chunk = $this->modx->newObject('modChunk');
return $chunk->process($properties);

private function _getTplChunk($name, $postfix = '.chunk.tpl')
$chunk = false;
$f = $this->config['chunksPath'] . strtolower($name) . $postfix;
if (file_exists($f)) {
$o = file_get_contents($f);
$chunk = $this->modx->newObject('modChunk');
$chunk->set('name', $name);
return $chunk;

XML configuration of the object

It contains the xPDO schema with fields and object name and defines it.

Under /www/site/core/components/testplugin/model/schema/ create file testplugin.mysql.schema.xml whitch will contain object schema:


<?xml version="1.0" encoding="UTF-8"?>
<model package="testplugin" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.0">
<object class="TestPlugin" table="testplugin" extends="xPDOSimpleObject">
<field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default=""/>
<field key="description" dbtype="text" phptype="string" null="false" default=""/>
<field key="createdon" dbtype="datetime" phptype="datetime" null="true"/>
<field key="createdby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
<field key="editedon" dbtype="datetime" phptype="datetime" null="true"/>
<field key="editedby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
<aggregate alias="CreatedBy" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign"/>
<aggregate alias="EditedBy" class="modUser" local="editedby" foreign="id" cardinality="one" owner="foreign"/>

The Final Step!

Build script under the folder  /www/site/core/components/testplugin/_build and create two files. One of them will contain config and another build script whitch will create tables in the base and generate a part of the plugin:

The firsts file is build.config.php whitch contains the constants and conf of the build:

include_once dirname(dirname(dirname(dirname(__FILE__)))).'/config/';

You also can create or owerride settings with
define('MODX_EXAPLE_VAR', 'some value for our variable');

Now create the build script whitch will hadle plugin setup build.schema.php:

require_once dirname(__FILE__).'/build.config.php';
include_once MODX_CORE_PATH . 'model/modx/modx.class.php';

$modx= new modX();
$modx->loadClass('transport.modPackageBuilder','',false, true);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
$sources = array(
'model' => $modx->getOption('testplugin.core_path').'model/',
'schema_file' => $modx->getOption('testplugin.core_path').'model/schema/testplugin.mysql.schema.xml'
$manager= $modx->getManager();
$generator= $manager->getGenerator();

if (!is_dir($sources['model'])) { $modx->log(modX::LOG_LEVEL_ERROR,'Model directory not found!'); die(); }
if (!file_exists($sources['schema_file'])) { $modx->log(modX::LOG_LEVEL_ERROR,'Schema file not found!'); die(); }

$modx->addPackage('testplugin', $sources['model']); // add package to make all models available
$manager->createObjectContainer('TestPlugin'); // created the database table

$modx->log(modX::LOG_LEVEL_INFO, 'Done!');

For displaying the chunk, you may use a static snippet in manager in file field add a file of the snippet, for its better modifying process: 



That's enough for today's post. If you have any questions, feel free to ask them below!


MODx multi-language web app using native modx contexts and Babel

contact us

Have an idea ? Let’s talk

Office in Rzeszow
Office in Warsaw


Fill up the form and we will contact you shortly


Company information

Fill up the form and we will contact you shortly.

ServoCode Sp. z o.o.

Jasionka 954E, 36-002 Jasionka, Poland

NIP: 8133719852

REGON: 364182909

KRS: 0000611643

We are using cookies to provide statistics that help us give you the best experience of our site. You can find out more or switch them off if you prefer. However, by continuing to use the site without changing settings, you are agreeing to our use of cookies. Read more