As the need for speed has been a major factor for both the end users and the search dictators, we have always been trying to figure out ways to speed up delivery of active web pages. While MODX has some great caching options, it still nonetheless has to fetch the cached data and merge it with the template before sending the first data to the web visitors browser. And with an eCommerce focus where stocks, availability and prices can change frequently, there can be more limits of what can or can not be cached.

At Pedalers Pub & Grille, we run bicycle tours in various exotic destinations around the world and while we don't sell the quantity of items that Amazon does, we still have to refresh availability of tours on a daily or more frequent basis. With our first MODX based site, we left our product (bike tour descriptions) pages uncached, building them upon request for each visitor to assure availability was up to date.

As we explored how to make it faster, we hit upon the idea of MODX's caching on all page components, then using a CRON job to delete the cache every 24 hours. To make sure that the next visitor got a cached copy instead of waiting for a new one to be built from scratch, after each cache delete call we had PHP fetch the page and thus rebuild a new cache for that resource.

But we were still stuck with the lag while MODX fetched the cached content and merged it with the template.

Sitting around a campfire enjoying some ice cold Ice Fog IPA in the Yukon after an 100 mile ride with some of our tour guests who were also web coders, an idea was hatched to take the PHP fetch we were currently using and actually save the page being fetched in the domain's root folder and serve the saved html to website visitors. Viola, we still get all the benefits of a CMS plus the blazing speed of serving static pages.

Plenty of experimenting ensued trying to sort out how to determine which pages to call, where to store them and so forth. In the end we settled on the following.

We added our own PageUrl template variable, rather than using MODX's built in alias or menu values. We did this because it let us use whatever url structure we wanted without having to order our tree with subfolders and such. Plus we use it as verification the resource is ready for prime time. If there is no PageUrl value, then the fetch script ignores that resource, thus allowing us to play with new pages in the MODX subdomain without that page appearing on the front facing website.

Gathering the pages was a multi-step function, first to gather a list of all the published resource IDs, then iterate through each of the ID numbers. For each ID we would query the database to see if that resource had a PageUrl saved, if so then fetch the page from MODX, apply some minification, then save it in the location specified by the PageUrl.

Security was also enhanced a bit, as MODX was no longer on the forefront our our main website. Rather is was safely tucked away in a sub-domain btfsplk.mydomain.com, so undesirables visiting the website could probe all they want for admins or managers or whatever to no avail.

Fired by a CRON job, the fetch script requests and saves a fresh copy of entire website once every 24 hours. We can also fire it manually should we update anything or should we have a great day with lots of sales.

The important part of the script we use can be seen below. 

<?php

// Set up a new PDO connection to talk to the MODX Database
try {
    $database = new PDO('mysql:host=localhost;dbname=DatabaseName', "DatabaseUserName", "DatabaseUserPassword");
}
catch (PDOException $e) {
    print "Error!: " . $e->getMessage() . "<br/>";
    return;
}

// Set up query
$mdxcids = $database->query("SELECT id FROM modx_site_content WHERE published=1");

// define full path to folder to be used for saving, can be changed to target test folder during development
$fullurl = '/yourhome/public_html/';

// fetch and loop through data
while ($mdxpub = $mdxcids->fetch(PDO::FETCH_ASSOC)) {
	// define which resource
	$cid = $mdxpub['id'];
	
	// fetch web url value from TVs
	$mdxurl = $database->query("SELECT value FROM modx_site_tmplvar_contentvalues WHERE contentid='$cid' AND tmplvarid=28");
	$mdxurl = $mdxurl->fetch(PDO::FETCH_ASSOC);
	$web_url = $mdxurl['value'];
	
	// check to see if url has been specified yet
	if (!$web_url) { 
    	// if no url, do nothing
	} else {
    	// get the webpage from CMS
    	$contents = file_get_contents('http://btfsplk.mydomain.com/index.php?id=' . $cid);
       
    	// remove comments
    	$contents = preg_replace('/<!--(?!\s*(?:\[if [^\]]+]|<!|>))(?:(?!-->).)*-->/Uis', '', $contents);
    	
    	// minify
    	$contents = preg_replace('/^\s+|\n|\r|\s+$/m', '', $contents);
    		
    	// save new copy
    	file_put_contents($fullurl . $web_url, $contents);
	}
  }

Our website visitors now get up to date, fast loading webpages and we still enjoy all the design and content flexibility of MODX. This approach won't work for all MODX installs, such as blogs with commenting and frequent posts, eCommerce websites with high volume, etc but is great for smaller websites that serve primarily static or slowly changing active pages.