MODX Evolution to MODX Revolution Migration

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

MODX Evolution to MODX Revolution Site Migration

MODX Evolution and Revolution are fundamentally different systems. Evolution: procedural PHP approach, no OOP, no namespace, API via global $modx object. Revolution: PSR-compliant architecture, xPDO ORM, components (Extras). No direct upgrade path — full content migration required.

Current Evolution Site Analysis

-- Document counts by templates
SELECT t.templatename, COUNT(d.id) as count
FROM site_content d
JOIN site_templates t ON t.id = d.template
GROUP BY t.templatename;

-- TV parameters
SELECT tv.caption, tv.type, COUNT(tvv.id) as used
FROM site_tmplvars tv
LEFT JOIN site_tmplvar_contentvalues tvv ON tv.id = tvv.tmplvarid
GROUP BY tv.id;

-- Custom snippets
SELECT name, LENGTH(snippet) as size FROM site_snippets;

-- Plugins
SELECT name, plugincode FROM site_plugins WHERE disabled = 0;

Content Export from Evolution

// Export script to JSON (run on Evolution server)
$db = new PDO('mysql:host=localhost;dbname=modx_evo;charset=utf8', 'user', 'pass');

$resources = $db->query("
    SELECT d.*, t.templatename
    FROM site_content d
    JOIN site_templates t ON t.id = d.template
    WHERE d.deleted = 0
    ORDER BY d.parent, d.menuindex
")->fetchAll(PDO::FETCH_ASSOC);

foreach ($resources as &$resource) {
    // Get TV values
    $tvs = $db->prepare("
        SELECT tv.name, tvv.value
        FROM site_tmplvar_contentvalues tvv
        JOIN site_tmplvars tv ON tv.id = tvv.tmplvarid
        WHERE tvv.contentid = ?
    ");
    $tvs->execute([$resource['id']]);
    $resource['tvs'] = $tvs->fetchAll(PDO::FETCH_KEY_PAIR);
}

file_put_contents('content-export.json', json_encode($resources, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));

Import to MODX Revolution

// Import script (run on Revolution server after installation)
require_once 'config.core.php';
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';

$modx = new modX();
$modx->initialize('web');

$data = json_decode(file_get_contents('content-export.json'), true);

// Map old templates to new
$templateMap = [
    'default_template' => 1,
    'inner_page'       => 2,
    'catalog'          => 3,
    'product'          => 4,
];

// Map old parent IDs to new (after structure change)
$parentMap = [];

foreach ($data as $evo_resource) {
    $resource = $modx->newObject('modDocument');
    $resource->fromArray([
        'pagetitle'   => $evo_resource['pagetitle'],
        'longtitle'   => $evo_resource['longtitle'],
        'description' => $evo_resource['description'],
        'alias'       => $evo_resource['alias'],
        'introtext'   => $evo_resource['introtext'],
        'content'     => migrateContent($evo_resource['content']),
        'published'   => $evo_resource['published'],
        'publishedon' => $evo_resource['publishedon'],
        'createdon'   => $evo_resource['createdon'],
        'template'    => $templateMap[$evo_resource['templatename']] ?? 1,
        'parent'      => $parentMap[$evo_resource['parent']] ?? 0,
        'menuindex'   => $evo_resource['menuindex'],
    ]);

    if ($resource->save()) {
        $parentMap[$evo_resource['id']] = $resource->id;

        // Migrate TVs
        foreach ($evo_resource['tvs'] as $tvName => $tvValue) {
            $resource->setTVValue($tvName, $tvValue);
        }
    }
}

// Content migration function (Evolution tags → Revolution)
function migrateContent(string $content): string {
    // Evolution uses [*alias*] for links, Revolution uses [[~id]]
    // Replacement requires manual mapping or aliases

    // Remove outdated Evolution tags
    $content = preg_replace('/\[(\*|\+|\!)[^\]]+\]/', '', $content);

    return $content;
}

Snippets and Plugins Migration

Snippets Evolution are written in procedural style with global $modx. Revolution API is different:

// Evolution (old API)
$docs = $modx->getDocuments($parents = [5], $published = 1);

// Revolution (correct API)
$c = $modx->newQuery('modResource');
$c->where(['parent' => 5, 'published' => 1]);
$docs = $modx->getCollection('modResource', $c);

Snippets need rewriting. For typical tasks (output lists, menus, forms) — use ready Revolution Extras: pdoResources, pdoMenu, FormIt.

Image Migration

# Image directory structure is identical (assets/images/)
rsync -avz old-server:/var/www/evo-site/assets/images/ \
    /var/www/revo-site/assets/images/

HTML content paths may need replacement if directory structure changed.

Timeline

Site Type Duration
Simple (up to 50 resources, standard snippets) 1–2 weeks
Medium (100–500 resources, custom snippets) 3–5 weeks
Large (1000+ resources, complex functionality) 2–3 months