? Pending

User tests: Successful: Unsuccessful:

avatar mbabker
mbabker
25 May 2017

Summary of Changes

Our API has some hardcoded conventions as it relates to creating classes in a lot of places and it takes a lot of hackish behavior to be able to do things like replace core services with a custom implementation (one example would be in extensions which replace the JDocument HTML renderer objects with custom implementations). This PR demonstrates a way around this limitation which can allow us to move in a direction where developers would replace services without overloading core classes.

For the JDocument API, a new service factory class is introduced to replace the inner logic of the JDocument::getInstance() and JDocument::loadRenderer() methods. This factory lives in our DI container and an extension can replace the factory definition with any object implementing the Joomla\CMS\Document\FactoryInterface, giving them greater flexibility in the creation of JDocument and JDocumentRenderer objects. More about how to work with the DI container and this method of service overloading can be found in the Framework repository README.

Eventually, this should be able to cause JDocument::getInstance() to be deprecated in favor of using the service factory. The CMS web application class and JFactory would still be aware of the global singleton document object (the one that gets created and used by default right now), and JDocument would lose its stateful getInstance() singleton factory and $instances container.

Testing Instructions

For general users, the CMS should continue to function as it did without this PR. For developers, they should be able to overload the factory service in the container by either setting a new service or extending (decorating) the existing implementation. As a quick example, this could be used for an onAfterInitialise event to replace the factory service in the container.


use Joomla\CMS\Document\FactoryInterface;
use Joomla\DI\Container;

public function onAfterInitialise()
{
    JFactory::getContainer()->extend('document.factory', function (FactoryInterface $originalFactory, Container $container) {
        // This would be the spot where you return your custom FactoryInterface implementation, but maybe that is dependent on a certain extension being enabled
        if (JComponentHelper::isEnabled('com_custommodules') {
            // This will completely replace the original implementation now
            return new CustomFactory;
        }

        // Maybe your custom service depends on a component, and when that component isn't available, you just use the original implementation
        if (!JComponentHelper::isEnabled('com_custommodules') {
            return $originalFactory;
        }

        // Perhaps you defined your custom factory as another service container key, so get that if it is available
        if ($container->exists('my.custom.factory')) {
            return $container->get('my.custom.factory');
        }

        // Or, you could just implement a decorator pattern where your custom implementation receives the original implementation as an argument
        return new DecoratingFactory($originalFactory);
    });
}

JDocument::setFactory() is also added which would enable a developer to replace the factory implementation used in a given JDocument instance. The factory can also be passed into JDocument through its constructor, setting the factory key on the options array.

Documentation Changes Required

Document the new service and this approach to overloading core services.

avatar mbabker mbabker - open - 25 May 2017
avatar mbabker mbabker - change - 25 May 2017
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 25 May 2017
Category Installation Libraries
avatar wilsonge wilsonge - close - 29 May 2017
avatar wilsonge wilsonge - merge - 29 May 2017
avatar wilsonge wilsonge - change - 29 May 2017
Status Pending Fixed in Code Base
Closed_Date 0000-00-00 00:00:00 2017-05-29 12:41:41
Closed_By wilsonge
Labels Added: ?

Add a Comment

Login with GitHub to post a comment