In J4 it is possible to override models and the rest of MVC classes. To do that, you need a system plugin to register a custom MVC factory during onAfterExtensionBoot
or onBeforeExtensionBoot
event.
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\MVC\Factory\MVCFactory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Component\Content\Administrator\Model\ArticlesModel;
use Joomla\DI\Container;
use Joomla\Event\EventInterface;
final class PlgSystemOverride extends CMSPlugin
{
public function onAfterExtensionBoot(EventInterface $event)
{
// Test that a component is being booted.
if ($event->getArgument('type') !== ComponentInterface::class)
{
return;
}
// Test that this is com_content component.
if ($event->getArgument('extensionName') !== 'content')
{
return;
}
// Get the service container.
$container = $event->getArgument('container');
if (!($container instanceof Container))
{
return;
}
// Check that MVC factory is used and can be overridden.
if (!$container->has(MVCFactoryInterface::class) || $container->isProtected(MVCFactoryInterface::class))
{
return;
}
// Register the custom MVC factory. Here an anonymous class is used but you can use a concrete class.
$container->set(
MVCFactoryInterface::class,
static fn () => new class ('Joomla\\Component\\Content') extends MVCFactory
{
protected function getClassName(string $suffix, string $prefix)
{
$class = parent::getClassName($suffix, $prefix);
// Override the backend article list model with a custom class.
if ($class === ArticlesModel::class)
{
return 'MyCustomArticlesModel';
}
return $class;
}
}
);
}
}
Then in your custom class extend the original class and add the overridden populateState()
method. Additionally, depending on class name, you might need to declare $context
and $filterFormName
properties. This is because they're derived from class name when not set explicitly.
use Joomla\Component\Content\Administrator\Model\ArticlesModel;
class MyCustomArticlesModel extends ArticlesModel
{
protected $context = 'com_content.articles';
protected $filterFormName = 'filter_articles';
protected function populateState($ordering = 'a.modified', $direction = 'DESC')
{
parent::populateState($ordering, $direction);
}
}
Alternatively to all of this, you can also change the default ordering by setting the appropriate session key, also using a system plugin:
use Joomla\CMS\Plugin\CMSPlugin;
final class PlgSystemOverride extends CMSPlugin
{
protected $app;
public function onAfterRoute()
{
// Only run for logged in users in com_content backend.
if (!$this->app->isClient('administrator') || $this->app->getInput()->get('option') !== 'com_content' || $this->app->getIdentity()->guest)
{
return;
}
// Session key to use.
$key = 'com_content.articles.list.fullordering';
// Set it only if it hasn't been set yet.
if (!$this->app->getUserState($key))
{
$this->app->setUserState($key, 'a.modified DESC');
}
}
}