Labels: enhancement, documentation, cache, developer experience
The current cache system documentation could benefit from more practical, real-world examples showing how to implement caching in custom components using the modern API (Joomla 5/6).
While the Cache API is well-designed and powerful, developers often struggle to find complete implementation examples that show the full picture from service creation to model integration and cache invalidation.
Developers looking to implement caching in custom components face challenges finding:
$caching, $cachetime)A reusable service class that respects Joomla's global cache configuration:
<?php
namespace MyComponent\Component\MyComponent\Site\Service;
use Joomla\CMS\Cache\CacheControllerFactoryInterface;
use Joomla\CMS\Cache\Controller\CallbackController;
use Joomla\CMS\Factory;
/**
* Cache Service for component
*
* Uses Joomla's native cache system with global configuration
*/
class CacheService
{
protected CallbackController $cache;
protected string $defaultGroup = 'com_mycomponent';
public function __construct(array $options = [])
{
// Use global Joomla cachetime from configuration.php
$defaultLifetime = Factory::getApplication()->get('cachetime', 15);
$cacheOptions = [
'defaultgroup' => $options['group'] ?? $this->defaultGroup,
'caching' => true,
'lifetime' => $options['lifetime'] ?? $defaultLifetime,
];
/** @var CallbackController $cache */
$this->cache = Factory::getContainer()
->get(CacheControllerFactoryInterface::class)
->createCacheController('callback', $cacheOptions);
// Disable cache when debug is active
if (JDEBUG) {
$this->cache->setCaching(false);
}
}
/**
* Get cached data or execute callback
*/
public function get(string $cacheId, callable $callback, array $args = []): mixed
{
return $this->cache->get($callback, $args, $cacheId);
}
/**
* Remove specific cache item
*/
public function remove(string $cacheId, ?string $group = null): bool
{
$group = $group ?? $this->defaultGroup;
return $this->cache->cache->remove($cacheId, $group);
}
/**
* Clean entire cache group
*/
public function clean(?string $group = null, string $mode = 'group'): bool
{
$group = $group ?? $this->defaultGroup;
return $this->cache->cache->clean($group, $mode);
}
}Document a consistent approach for generating unique cache keys:
<?php
namespace MyComponent\Component\MyComponent\Site\Helper;
use Joomla\CMS\Factory;
/**
* Cache Helper for key generation and lifetime management
*/
class CacheHelper
{
const CACHE_PREFIX = 'com_mycomponent';
/**
* Generate unique cache key
*/
public static function generateKey(string $type, mixed $identifier = null, array $params = []): string
{
$parts = [self::CACHE_PREFIX, $type];
if ($identifier !== null) {
$parts[] = $identifier;
}
// Hash parameters to ensure unique key
if (!empty($params)) {
$parts[] = md5(serialize($params));
}
return implode('.', $parts);
}
/**
* Get cache lifetime based on type and global configuration
* Uses multipliers relative to Joomla's $cachetime
*/
public static function getLifetime(string $type): int
{
// Get global cachetime (default: 15 minutes)
$baseLifetime = Factory::getApplication()->get('cachetime', 15);
// Apply multipliers per type
$multipliers = [
'list' => 4, // 4x base (60min if base=15)
'item' => 24, // 24x base (360min = 6h if base=15)
'filter' => 4, // 4x base
'count' => 2, // 2x base (30min if base=15)
'search' => 4, // 4x base
];
$multiplier = $multipliers[$type] ?? 4;
return $baseLifetime * $multiplier;
}
/**
* Check if cache is enabled
*/
public static function isCacheEnabled(): bool
{
// Disabled in debug mode
if (JDEBUG) {
return false;
}
// Respect global $caching configuration
return (bool) Factory::getApplication()->get('caching', 0);
}
}Show how to integrate caching in a list model:
<?php
namespace MyComponent\Component\MyComponent\Site\Model;
use Joomla\CMS\MVC\Model\ListModel;
use MyComponent\Component\MyComponent\Site\Service\CacheService;
use MyComponent\Component\MyComponent\Site\Helper\CacheHelper;
class ItemsModel extends ListModel
{
protected ?CacheService $cacheService = null;
public function __construct($config = [])
{
parent::__construct($config);
// Initialize cache service if enabled
if (CacheHelper::isCacheEnabled()) {
$this->cacheService = new CacheService([
'lifetime' => CacheHelper::getLifetime('list')
]);
}
}
/**
* Get cached list of items
*/
public function getItems()
{
if ($this->cacheService) {
// Extract state parameters for cache key
$stateParams = [
'limit' => $this->getState('list.limit'),
'start' => $this->getState('list.start'),
'ordering' => $this->getState('list.ordering'),
'direction' => $this->getState('list.direction'),
'search' => $this->getState('filter.search'),
// Add other filters...
];
$cacheKey = CacheHelper::generateKey('list', 'all', $stateParams);
return $this->cacheService->get(
$cacheKey,
fn() => parent::getItems()
);
}
return parent::getItems();
}
}Show caching for single item details:
<?php
namespace MyComponent\Component\MyComponent\Site\Model;
use Joomla\CMS\MVC\Model\ItemModel;
class ItemModel extends ItemModel
{
protected ?CacheService $cacheService = null;
public function __construct($config = [])
{
parent::__construct($config);
if (CacheHelper::isCacheEnabled()) {
$this->cacheService = new CacheService([
'lifetime' => CacheHelper::getLifetime('item')
]);
}
}
public function getItem($id = null)
{
if ($this->_item === null) {
$id = $id ?? $this->getState('item.id');
if ($this->cacheService && $id) {
$cacheKey = CacheHelper::generateKey('item', $id);
$this->_item = $this->cacheService->get(
$cacheKey,
function() use ($id) {
// Load item logic here
return $this->loadItem($id);
}
);
} else {
$this->_item = $this->loadItem($id);
}
}
return $this->_item;
}
protected function loadItem($id)
{
// Actual loading logic
$table = $this->getTable();
$table->load($id);
return $table;
}
}Document how to invalidate cache after CRUD operations:
<?php
namespace MyComponent\Component\MyComponent\Administrator\Model;
use Joomla\CMS\MVC\Model\AdminModel;
use MyComponent\Component\MyComponent\Administrator\Service\CacheService;
use MyComponent\Component\MyComponent\Site\Helper\CacheHelper;
class ItemModel extends AdminModel
{
protected CacheService $cacheService;
public function __construct($config = [])
{
parent::__construct($config);
$this->cacheService = new CacheService();
}
/**
* Save item and invalidate cache
*/
public function save($data)
{
$result = parent::save($data);
if ($result) {
$id = $this->getState($this->getName() . '.id');
// Clear item cache
$this->cacheService->remove(
CacheHelper::generateKey('item', $id)
);
// Clear all lists (item may appear in filtered lists)
$this->cacheService->clean('com_mycomponent.list', 'group');
// Clear related caches
$this->cacheService->clean('com_mycomponent.filter', 'group');
$this->cacheService->clean('com_mycomponent.count', 'group');
}
return $result;
}
/**
* Delete item and invalidate cache
*/
public function delete(&$pks)
{
$result = parent::delete($pks);
if ($result) {
$ids = is_array($pks) ? $pks : [$pks];
foreach ($ids as $id) {
$this->cacheService->remove(
CacheHelper::generateKey('item', $id)
);
}
// Clear lists
$this->cacheService->clean('com_mycomponent.list', 'group');
}
return $result;
}
}Add manual cache clearing capability:
<?php
namespace MyComponent\Component\MyComponent\Administrator\Controller;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\Language\Text;
class ItemsController extends AdminController
{
/**
* Clear all component cache
*/
public function clearCache()
{
$this->checkToken();
try {
$cacheService = new CacheService();
$cacheService->clean('com_mycomponent', 'group');
$this->setMessage(Text::_('COM_MYCOMPONENT_CACHE_CLEARED'));
} catch (\Exception $e) {
$this->setMessage($e->getMessage(), 'error');
}
$this->setRedirect('index.php?option=com_mycomponent&view=items');
}
}Add button to admin toolbar:
<?php
namespace MyComponent\Component\MyComponent\Administrator\View\Items;
use Joomla\CMS\MVC\View\HtmlView;
use Joomla\CMS\Toolbar\Toolbar;
class HtmlView extends HtmlView
{
protected function addToolbar()
{
// ... other toolbar buttons ...
$toolbar = Toolbar::getInstance();
// Add cache clear button
$toolbar->standardButton('cache')
->text('JTOOLBAR_CLEAR_CACHE')
->icon('fas fa-sync')
->task('items.clearCache');
}
}Document how components should respect Joomla's global cache configuration:
configuration.php:
public $caching = 0; // Enable/disable cache globally (0=off, 1=on)
public $cache_handler = 'file'; // Storage backend: file, memcached, redis, apcu
public $cachetime = 15; // Base cache lifetime in minutesBest Practices:
✅ DO:
$cachetime as base for component TTLs (with multipliers)$caching setting (Factory::getApplication()->get('caching'))JDEBUG = trueCacheControllerFactory to respect $cache_handler❌ DON'T:
I've successfully implemented this pattern in a production component with these results:
This documentation would help developers:
Current documentation that could be expanded:
These examples would fit well in:
docs/building-extensions/performance/caching.mdI'm happy to contribute:
Thank you for considering this improvement! Better cache documentation will help component developers build faster, more scalable Joomla sites. 🚀
Joomla Version: 5.x, 6.x
Related PRs/Issues: N/A
Documentation Repo: https://github.com/joomla/Manual
| Labels |
Added:
No Code Attached Yet
|
||
| Status | New | ⇒ | Closed |
| Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2026-02-04 02:55:49 |
| Closed_By | ⇒ | joomdonation |
it would you be great if you could submit this directly to the manual as a pull request, The repo for contriobuting to the manual is https://github.com/joomla/manual