User tests: Successful: Unsuccessful:
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.
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.
Document the new service and this approach to overloading core services.
Status | New | ⇒ | Pending |
Category | ⇒ | Installation Libraries |
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:
?
|