Custom MODX Snippets: Fast PHP Solutions with pdoTools

Typical snippet is unoptimized copy-paste

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:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1033
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1104
  • image_website-_0.webp
    Website development for Red Pear
    554

Typical snippet is unoptimized copy-paste

Imagine your MODX product catalog takes 5 seconds to load, and every category click feels like an eternity. The cause: unoptimized snippets that make dozens of database queries. Typical scenario: a developer copied the first code they found, without thinking about caching. Result: N+1 queries, performance drop, and poor user experience. How to fix it? Use correct patterns.

MODX snippet development often starts with copying the first example from the documentation. The result: N+1 queries, no caching, and vulnerabilities that turn a simple task into a site-wide bottleneck. We use proven architectural patterns: single query, caching, and strict parameter typing. Over 5 years of experience and more than 50 projects on MODX allow us to create snippets that withstand thousands of requests without performance loss.

How to avoid N+1 queries in MODX?

The N+1 problem occurs when you retrieve a collection of resources and then for each one load TV values via getTVValue in a loop. With 100 resources, that's 101 SQL queries. The solution: use JOIN or pdoFetch. The native MODX API allows manual JOIN, but pdoTools does it automatically and faster. More about the problem at N+1 query problem.

Why is snippet caching important?

Caching reduces database load and speeds up pages. Without cache, every snippet call means an SQL query. On a project with a 5000-item catalog, the original snippet took 4 seconds. After adding caching, the time dropped to 0.2 seconds for subsequent requests. Cache should be invalidated when data changes or set a TTL, e.g., 30 minutes.

Snippet structure with safe querying

Native query with JOIN

<?php // Snippet ProductList // Call: [[!ProductList? &category=`5` &limit=`12` &sort=`price`]] // Get parameters (with defaults) $categoryId = (int)($scriptProperties['category'] ?? 0); $limit = (int)($scriptProperties['limit'] ?? 10); $offset = (int)($scriptProperties['offset'] ?? 0); $sortField = $scriptProperties['sort'] ?? 'menuindex'; $sortDir = $scriptProperties['sortdir'] ?? 'ASC'; $tpl = $scriptProperties['tpl'] ?? 'productCard'; // Query to resources via MODX API $c = $modx->newQuery('modResource'); $c->where([ 'parent' => $categoryId, 'published' => 1, 'deleted' => 0, 'class_key' => 'modDocument', ]); // Get TV values via Join $c->innerJoin('modTemplateVarResource', 'TVPrice', [ 'TVPrice.tmplvarid' => $modx->getObject('modTemplateVar', ['name' => 'price'])->id, 'TVPrice.contentid = modResource.id', ]); $c->select('modResource.*, TVPrice.value AS price'); $c->sortby($sortField, $sortDir); $c->limit($limit, $offset); $resources = $modx->getCollection('modResource', $c); if (empty($resources)) return ''; $output = ''; foreach ($resources as $resource) { $data = array_merge($resource->toArray(), [ 'price' => $resource->get('price'), 'link' => $modx->makeUrl($resource->id, '', '', 'full'), 'image' => $resource->getTVValue('product_image'), ]); // Chunk for card output $output .= $modx->getChunk($tpl, $data); } return $output; 

Snippet with pdoTools

<?php // Snippet ProductSearch with pdoTools if (!$modx->loadClass('pdoFetch', MODX_CORE_PATH . 'components/pdotools/model/pdotools/', false, true)) { return 'pdoTools is not installed'; } $pdoFetch = new pdoFetch($modx, $scriptProperties); $pdoFetch->addWhere([ 'modResource.parent' => (int)($scriptProperties['category'] ?? 0), 'modResource.published' => 1, ]); // TV join $pdoFetch->addTVs('price,product_image,short_description'); $result = $pdoFetch->run(); return $result; 

Caching results

<?php // Cache result for 30 minutes $cacheKey = 'products_' . md5(json_encode($scriptProperties)); $cacheOptions = [xPDO::OPT_CACHE_KEY => 'default', xPDO::OPT_CACHE_EXPIRES => 1800]; $cached = $modx->cacheManager->get($cacheKey, $cacheOptions); if ($cached !== null) return $cached; // ... query ... $output = generateOutput($resources); $modx->cacheManager->set($cacheKey, $output, 1800, $cacheOptions); return $output; 

Integration with external API

<?php // Snippet WeatherWidget — weather from OpenWeatherMap $city = $scriptProperties['city'] ?? 'Moscow'; $apiKey = $modx->getOption('weather_api_key'); $tpl = $scriptProperties['tpl'] ?? 'weatherWidget'; $cacheKey = 'weather_' . $city; $cached = $modx->cacheManager->get($cacheKey, [xPDO::OPT_CACHE_EXPIRES => 1800]); if ($cached !== null) { return $modx->getChunk($tpl, $cached); } $url = "https://api.openweathermap.org/data/2.5/weather?q={$city}&appid={$apiKey}&units=metric&lang=ru"; $response = file_get_contents($url); if (!$response) return ''; $data = json_decode($response, true); if (!$data || $data['cod'] !== 200) return ''; $weather = [ 'city' => $data['name'], 'temp' => round($data['main']['temp']), 'feels_like' => round($data['main']['feels_like']), 'description' => $data['weather'][0]['description'], 'icon' => "https://openweathermap.org/img/wn/{$data['weather'][0]['icon']}@2x.png", 'humidity' => $data['main']['humidity'], ]; $modx->cacheManager->set($cacheKey, $weather, 1800); return $modx->getChunk($tpl, $weather); 

Which parameters to pass to a snippet?

[[!ProductList? &category=`[[*id]]` &limit=`12` &tpl=`productCardTpl` &sort=`price` &sortdir=`ASC` ]] 

! before the name — uncached call (dynamic content). Without ! — cached (static block, same for all).

Approach comparison table

Aspect Native MODX pdoTools With caching
Number of SQL queries N+1 (collection + TV) 1 (single JOIN) 1 time, then from cache
Ease of writing Requires manual JOIN Automatic TV loading Additional 3 lines
Performance on 1000 resources ~2-3 s ~0.3-0.5 s ~0.01 s after first request
Filter support Manual where addition Built-in addWhere Not affected

Typical snippet parameters table

Parameter Type Description Default
category int ID of parent resource 0
limit int Number of records to display 10
sort string Sort field menuindex
sortdir string Sort direction (ASC/DESC) ASC
tpl string Name of chunk for output productCard

Common mistakes in snippet development

  • N+1 queries: getting TV via getTVValue in a loop. Solution: JOIN or pdoFetch.
  • Lack of input filtering: parameters without type casting (category as string instead of int).
  • Ignoring caching: each snippet call loads the DB, even if data hasn't changed.
  • Hardcoded resource IDs: use placeholders and parameters.
Checklist: how to avoid mistakes
  • [ ] Use prepared statements
  • [ ] Cast all input parameters to required type
  • [ ] Add caching with a unique key
  • [ ] Use pdoFetch for complex queries
  • [ ] Document parameters and chunks

What's included in the work

  • Snippet source code with comments.
  • Documentation: parameter descriptions, call examples, logic description.
  • Caching setup: TTL and key selection.
  • Installation instructions: adding the snippet, creating chunks, testing.
  • Support guarantee: we'll help with any questions within a month after delivery.

Turnkey snippet development process

  1. Analysis: study the requirements, what data to output, from where, filtering conditions.
  2. Design: determine query structure, choose method (native or pdoTools), plan caching.
  3. Implementation: write PHP snippet code and template chunks.
  4. Testing: check edge cases (empty categories, 0 records), measure execution time.
  5. Deployment: place on production server, configure cache, provide call documentation.

Timeline and cost

Timeline: from 0.5 day for a simple snippet to 5 days for a complex one with API integration. We provide an accurate estimate after reviewing the project. Cost is calculated individually.

Contact us to discuss your project. Order snippet development right now — get fast and secure code. Over 5 years of experience with MODX. Reach out, and we'll evaluate the task and propose the optimal solution.