Contents
Introduction
MODX Architecture
How MODX Works
The MODX Manager
Porting a Site to MODX
Chunks
Template Variables
Plugins
Extras
xPDO
Conclusion
About the Author of this Article
Introduction
MODX is a powerful and complex CMS (Content Management System). Actually, the developers like to refer to it as a CMF (Content Management Framework) because you can actually build your own CMS using MODX as a platform.
This article will only scratch the surface of MODX and stick to its CMS aspect. My intention here is to give you a taste of the features of MODX that are most attractive to PHP developers.
MODX Architecture
MODX Revolution is completely based on xPDO (more on that below). A great comfort to PHP developers is the fact that virtually everything in MODX is a well-crafted PHP class of objects.
The documents themselves (called "resources" in MODX) are objects, but so are the templates, users, user groups, categories, resource groups, code snippets, plugins, chunks of reusable HTML, and just about everything else.
All of these object classes are derived from the xPDO class, which means that you can use the xPDO public methods to create, retrieve, modify, save, and delete them. Once you learn the relatively intuitive xPDO methods, you can work with any piece of the web site in your PHP code. This creates endless opportunities to craft dynamic Web pages that are easy to maintain.
The PHP-friendly architecture of MODX means that it is relatively easy to convert external PHP classes for use with MODX. Many contributions by PHP Classes site members are currently in use on MODX sites around the world.
How MODX Works
Although MODX has many powerful features, its basic operation is quite simple. When a browser requests a MODX page, the MODX core determines the template used by the requested page. After it has been determined that the current user has the right to view the page, the MODX parser retrieves the template from the database, replaces any MODX tags in the template, and delivers the page to the browser.
The most attractive result of this simplicity is that it is your template that is delivered to the browser, not MODX's. There are "themes" that you can download for MODX pages, but they are not often used because it is so easy to create your own template.
The template is standard (X)HTML and there are almost no restrictions on what it can contain. The only significant restriction is that templates cannot contain raw PHP code. In MODX, the PHP code is placed in code "snippets." This makes the site more secure since any uploaded file with PHP code in it will not execute. It also enforces the separation of code and content, which makes the site much easier to maintain.
If you need PHP to perform some operation on your page, you just move the code into a snippet and place a snippet tag in the template (or in the page content). The tag will be replaced with the return value of the snippet. If your snippet produces no output for the page, you simply have your snippet return an empty string and the tag will be replaced with nothing.
Snippets automatically have access to the current resource object (the document being displayed) and the current user object (the user viewing the page, if one is logged in). This makes it almost trivial to use any fields of the resource (pagetitle, longtitle, createdon, publishedon, editedby, publishedby, etc.) and any fields of the user or user profile objects (username, fullname, email, phone, address, zip, etc.).
In addition, you can put arguments (called properties) in the snippet tag and these will also be available in the snippet. The properties can use MODX tags for the argument values so you can send other MODX objects along in the snippet call and make to make them available in the snippet.
Here is a simple MODX template. The MODX tags are all enclosed with [[tag]]
The tag types are as follows:
- Snippet tag:
[[SnippetName]]
- Chunk tag:
[[$ChunkName]]
- System Setting Tag:
[[++SettingName]]
- Resource Field/Template Variable tag:
[[*fieldName/TvName]]
- Link tag:
[[~PageId]]
- Placeholder tag:
[[+PlaceholderName]]
Tags can be nested and if any tag begins with the "!" token, the tag content will be uncached.
Example MODX Template
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-
transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=[[++modx_charset]]" />
<meta name="copyright" content="2011, MODX" />
<title>[[++site_name]] | [[*pagetitle]]</title>
<link href="[[++base_url]]assets/site/simple.css" rel="stylesheet"
type="text/css" />
</head>
<body>
<div id="header">
<div id="logo">
[[*longtitle]]
</div>
</div>
<div id="main">
[[*content]]
</div>
<div id="sidebar">
[[$SideBar]]
</div>
<div id="navigation">
[[Wayfinder? &startId=`0`]]
</div>
<div id="news">
[[!getResources? &parents=`22` &tpl=`MyGrTpl`]]
</div>
<p><a href="[[~[[*id]]]]#" >Back to Top</a></p>
<p><a href="[[~[[++site_start]]]]" >MODX Home
Page</a></p>
<div id="footer">
[[$Footer]]
</div>
</body>
</html>
The MODX Manager
Almost all of your work on a MODX Web site will be done in the MODX Manager.
The left panel of the Manager has three tabs: one shows all the documents on the site (in the Resource tree), one shows all the elements in the Element tree (templates, snippets, chunks, plugins, etc.), and the third shows the raw files on the site.
The Manager's right panel is where the work is done. Panels open there for editing resources and elements, setting System Settings, downloading and installing add-on packages, and much more.
MODX has a quick-edit feature for editing that opens a pop-up window for editing other resources and elements without leaving whatever you are currently working on.
Here is a view of the Create-Edit Resource panel with the Page Settings tab exposed and the rich text editor turned off:
The System Settings grid contains a set of site-wide variables (key and value pairs) that control the site and provide information to snippets and plugins. Every system setting is available in PHP code with $modx->getOption('SettingName')
.
The grid is divided into areas to make it easier to find settings and there is a search box to allow searching for key words in the setting or its description.
Porting a Site to MODX
Porting a site to MODX is a very simple process because the template model is so straightforward. You can paste the code for a page into a MODX template, move the page content and any other elements in the template into the appropriate MODX objects (replacing them with MODX tags) and you're good to go.
The unique page content is moved to the content field of a document and replaced with a [[*content]]
tag, reusable HTML code is moved to an HTML "chunk" and replaced with a [[$ChunkName]]
tag, and PHP code is moved to a "snippet" and replaced with a [[SnippetName]]
tag.
Because MODX puts so few restrictions on the content of a template and (once the MODX tags are replaced) sends the code of the template "as is" to the browser, you can be sure that a site ported to MODX will look exactly like it did before you ported it. Trying to do that with some other CMS platforms can be significantly more challenging, to say the least.
Chunks
MODX chunks are pieces of reusable (X)HTML code. Common sections of web pages, such as menus, page headers, and page footers are often placed in chunks. Whenever a chunk tag, [[$ChunkName]]
, is encountered by the the parser, it is replaced with the content of the named chunk.
This might not sound like great news for PHP developers, since chunks cannot contain any PHP code, but because chunks are standard MODX objects, chunks can be retrieved very easily in PHP code with $modx->getChunk('ChunkName')
.
Better yet, you can pass a PHP associative array as a second argument to getChunk()
. Placeholder tags in the chunk that match array keys, will be replaced with the corresponding array value before the chunk's content is returned.
Say, for example, that you want to display teasers to a selection of documents (e.g. for the home page of a blog). Your chunk might look like this:
<div class = "teaser">
Article: [[+pagetitle]]<br />
Date: [[+publishedon]]<br />
Summary: [[+introtext]]<br />
. . . <a href="[[~[[+id]]]]">Complete article</a>
</div>
Now, the snippet to display the page summaries and links can be this simple:
foreach ($docs as $doc) {
$output .= $modx->getChunk('teaserTpl', $doc->toArray() );
}
return $output;
Every document field is available for use in the chunk and you can completely redesign the output without touching the PHP code. Notice also that the link to the page is created for you using a MODX link tag, and if the Friendly URL system setting is on, it will be a full SEO-friendly URL based on the document's alias.
Template Variables
Template variables are extra resource (document) form fields that you create. They are automatically added to the Create/Edit Resource panel and are available to manager users when creating or editing resources.
One use of template variables is to add extra, displayable fields to documents on the site. They can also be used, however, to provide extra information for snippets or plugins that operate when the page is displayed.
A template variable, for example, might be used to tell a PHP snippet on a particular page which CSS file to use for the page, which images to display on the page, which HTML chunks to insert, or which language to use for the page.
Plugins
The term "plugins" is often used to describe add-on components. In MODX, however, plugins are pieces of PHP code that extend the MODX engine.
Almost every significant event in the operation of MODX fires a System Event. MODX plugins listen for one or more System Events. When those events fire, the plugin code executes. The practical implication of this is that you can make MODX do almost anything you want without altering the core code.
You might, for example, want to perform some operations when a new user is saved to the database (e.g., adding the user to certain user groups, logging the creation, filling some user fields based on what's in the other fields, etc.).
In MODX, you would simply create a plugin that listened for the OnBeforeUserFormSave event. The plugin would have access to the user object and could modify that object, or perform any other operations, before the user was saved to the database.
If you wanted to do something similar with newly created documents, your plugin would listen for the OnBeforeDocFormSave event.
Plugins can easily detect whether the object being saved is new or is an existing object that's being updated, so you could also perform actions when objects are updated rather than created.
Another commonly used System Event is OnWebPagePrerender. Plugins attached to this event have access to the full, finished Web page just before it is sent to the browser and can modify the page at will.
Extras
When it comes to add-on components, MODX is not yet a feature-rich as more mature CMS platforms like WordPress, Drupal, and Joomla!.
There are a number of very handy and powerful add-on components for MODX, however, and more are being developed all the time. Here is a short list of some of the more commonly used components:
- Wayfinder A menu utility that can produce virtually any style of menu automatically based on the Resource tree
- TinyMCE A WYSIWYG editor for resources
- SPForm Creates a simple contact form for your site
- FormIt A more complex form utility with pre- and post-processing
- getResources A powerful and efficient content aggregation and display utility
- Login A login processor with registration and registration confirmation functionality
- NewsPublisher A front-end editor for resource creation and editing
- EZfaq Creates an attractive FAQ page with the content in simple Q: and A: format
xPDO
xPDO is a powerful, efficient, and supremely elegant database layer that allows you to write database-agnostic PHP code for all your database tasks. xPDO currently works with MySQL and Microsoft SQL Server databases, with more to come.
Because MODX is based on xPDO, you can use a handful of relatively simple xPDO methods to work with any object in the MODX database. All you need to know is the class name of the object (e.g., modUser, modResource, modUserGroup, modTemplateVar). If you move your site to a platform using another database engine the xPDO supports, no code will need to change.
Rather than go into detail about xPDO methods, let me just present some simple examples of common tasks in MODX using xPDO. The second argument to the various get()
methods is the criteria for the search. The current resource and current user are always available as $modx->resource
and $modx->user
:
/* Display a particular document */
$resource = $modx->getObject('modResource',
array('pagetitle'=>'MyDocument'));
$output = '<p>Document:' . $resource->get('longtitle') . '</p>';
$output .= '<p>Content:' . $resource->get('content') . '</p>';
/* Get all active users */
$users = $modx->getCollection('modUser',
array('active'=>'1'));
foreach ($users as $user) {
$output .= '<p>Username: ' . $user->get('username') . '</p>';
}
return $output
/* get all published resources that have not been deleted */
$resources = $modx->getCollection('modResource',
array('published'=>'1','deleted'=>'0'));
foreach($resources as $resource) {
$output .= '<div class="resource"><p>Document:' .
$resource->get('longtitle') . '</p>';
$output .= '<p>Content:' . $resource->get('content') . '</p></div>';
}
/* Get the user object for the creator of the current document */
$user = $modx->resource->getOne('CreatedBy');
return $user->get('username');
/* Get the parent object of the current resource */
$parent = $modx->resource->getOne('Parent');
return $parent->get('pagetitle');
/* Get the child objects of the current resource */
$children = $modx->resource->getMany('Children');
foreach ($children as $child) {
$output .= '<p>' . $child->get('pagetitle') . '</p>';
}
/* Publish a set of resources from a list of resource IDs */
$resourceIds = array('12, 23, 32, 45');
foreach ($resourceIds as $id) {
$resource = $modx->getObject('modResource',$id);
$resource->set('published', '1');
$resource->save();
}
In each of the above examples, MODX queries the database to retrieve the desired object or objects. The methods will work with any MODX object. All fields of the object are available with $object->get('fieldName')
. The fields can also be pulled into an associative array with $object->toArray()
.
The selection criteria can be much more complex than they are in these simple examples and all the typical query operations like joins, sorting on various fields, and LIKE queries can be performed.
You can also create your own custom database tables, create an xPDO schema and class and map files for them, and use all the xPDO methods to work with them.
Once you learn the basic xPDO methods, you can perform any database operations in a few lines of code. For those more comfortable with traditional methods, you can also use PDO to query the MODX database or use standard MySQL query language. Using xPDO, however, gives you database independence and a built-in, controllable caching system.
Conclusion
If you want a CMS that does everything you need out of the box, MODX may not be for you (although many users who know no PHP at all use and love MODX).
If, on the other hand, you want to write PHP code to make your web site sit up and beg, it's definitely worth your time to check out MODX and see if it meets your needs.
If you have comments or questions about the aspects of MODX that were covered or not here, please post a comment to this article.
About the Author of this Article
Once I found MODX, I became involved in its development. I am the author of the recently released book MODX: The Official Guide.