Skip to main content
Resolve class names
Source Link
Sharky
  • 12k
  • 8
  • 23
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') !== 'Joomla\\CMS\\Extension\\ComponentInterface'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;
        }

        // Service key to override.
        $class = 'Joomla\\CMS\\MVC\\Factory\\MVCFactoryInterface';

        // Check that serviceMVC keyfactory is used and can be overridden.
        if (!$container->has($classMVCFactoryInterface::class) || $container->isProtected($classMVCFactoryInterface::class))
        {
            return;
        }

        // Register the custom MVC factory. Here an anonymous class is used but you can use a concrete class.
        $container->set(
            $classMVCFactoryInterface::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 === 'Joomla\\Component\\Content\\Administrator\\Model\\ArticlesModel'ArticlesModel::class)
                    {
                        return 'MyCustomArticlesModel';
                    }

                    return $class;
                }
            }
        );
    }
}
use Joomla\CMS\MVC\Factory\MVCFactory;
use Joomla\CMS\Plugin\CMSPlugin;
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') !== 'Joomla\\CMS\\Extension\\ComponentInterface')
        {
            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;
        }

        // Service key to override.
        $class = 'Joomla\\CMS\\MVC\\Factory\\MVCFactoryInterface';

        // Check that service key is used and can be overridden.
        if (!$container->has($class) || $container->isProtected($class))
        {
            return;
        }

        // Register the custom MVC factory. Here an anonymous class is used but you can use a concrete class.
        $container->set(
            $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 === 'Joomla\\Component\\Content\\Administrator\\Model\\ArticlesModel')
                    {
                        return 'MyCustomArticlesModel';
                    }

                    return $class;
                }
            }
        );
    }
}
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;
                }
            }
        );
    }
}
Source Link
Sharky
  • 12k
  • 8
  • 23

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\MVC\Factory\MVCFactory;
use Joomla\CMS\Plugin\CMSPlugin;
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') !== 'Joomla\\CMS\\Extension\\ComponentInterface')
        {
            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;
        }

        // Service key to override.
        $class = 'Joomla\\CMS\\MVC\\Factory\\MVCFactoryInterface';

        // Check that service key is used and can be overridden.
        if (!$container->has($class) || $container->isProtected($class))
        {
            return;
        }

        // Register the custom MVC factory. Here an anonymous class is used but you can use a concrete class.
        $container->set(
            $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 === 'Joomla\\Component\\Content\\Administrator\\Model\\ArticlesModel')
                    {
                        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');
        }
    }
}