Container¶
Since JTL-Shop 5.0.0, a so-called “Dependency Injection Container” is available in the online shop.
In the future, a large part of all JTL-Shop components will be provided via this container. Additionally, the
behaviour of the online shop can be modified or extended by plug-ins via the components registered in the container.
SOLID & dependency inversion¶
The container is used to implement the “Dependency Inversion Principle”.
There is a wealth of information regarding this subject on the internet. Therefore, we recommend that developers first familiarise
themselves with SOLID and with dependency inversion in particular.
Container / Component retrieval¶
<?php
use JTL\Shop;
use Services\JTL\PasswordServiceInterface;
$container = Shop::Container();
$passwordService = $container->get(PasswordServiceInterface::class);
$randomPassword = $passwordService->generate(12);
As you can see, services and other components of JTL-Shop can be obtained via the container.
The container is designed by the PHP-FIG according to PSR-11 (https://www.php-fig.org/psr/psr-11/).
In case you are using an IDE with IntelliSense, we have also added a method to the container for all components
provided by JTL-Shop.
<?php
use JTL\Shop;
use JTL\Services\JTL\PasswordServiceInterface;
$container = Shop::Container();
$passwordService = $container->getPasswordService();
$randomPassword = $passwordService->generate(12);
You can see which components are provided by the JTL-Shop by using the available methods of the
/includes/src/Services/DefaultServicesInterface.php interface.
Testing for existence¶
If you want to see if a component is available, you can do the following.
(Note: All components defined in DefaultServicesInterface are always available.)
<?php
use JTL\Services\JTL\PasswordServiceInterface;
$container = Shop::Container();
if ($container->has(PasswordServiceInterface::class)) {
// component exists
}
Registering custom components¶
You have the option to register custom components in the container.
For this, you first need a class that you want to provide. We recommend that you create an interface
or abstract class for each component. Only then can the decorator pattern be implemented (see below)
<?php
interface HelloWorldGeneratorInterface
{
public function get();
}
class HelloWorldGenerator implements HelloWorldGeneratorInterface
{
public function get()
{
return " Hello World ";
}
}
Now, you can register the relevant component in the container:
<?php
$container = JTL\Shop::Container();
$container->setFactory(HelloWorldGeneratorInterface::class, function($container) {
return new HelloWorldGenerator();
});
Now, the component is available via the container and can be retrieved as follows:
<?php
$container = JTL\Shop::Container();
$HelloWorldGenerator = $container->get(HelloWorldInterface::class);
$HelloWorldGenerator->get(); // "Hello World" will be output
Overwriting components¶
You can replace all registered components in the container.
The requirement for this is that you implement the interface used or, in the case of an abstract class, that it be inherited from
the class.
Attention
When you overwrite a component, this will apply to the entire online shop!
So, be sure to use caution and only overwrite components if your implementation works
reliably.
<?php
class TrimmedHelloWorldGenerator implements HelloWorldGeneratorInterface
{
public function get()
{
return "Hello World";
}
}
$container = Shop::Container();
$container->setFactory(HelloWorldGeneratorInterface::class, function($container) {
return new TrimmedHelloWorldGenerator();
});
Extending components (decorator pattern)¶
You can extend all components available via the container, if an abstract class or interface is available, using the decorator pattern.
Here is an example that extends the “HelloWorldContainer”:
<?php
// Decorator class
class TrimmingHelloWorldGeneratorDecorator implements HelloWorldGeneratorInterface
{
protected $inner;
public function __construct($inner)
{
$this->inner = $inner;
}
public function get()
{
return trim($this->inner->get());
}
}
// Register decorator
$container = Shop::Container();
$originalFactoryMethod = $container->getFactory(HelloWorldGeneratorInterface::class);
$container->setFactory(HelloWorldGeneratorInterface::class, function($container) use ($originalFactoryMethod) {
$inner = $originalFactoryMethod($container);
return new TrimmingHelloWorldGeneratorDecorator($inner);
});
// Use component
$helloWorldGenerator = $container->get(HelloWorldGeneratorInterface::class);
echo $helloWordGenerator->get(); // return "Hello World" instead of " Hello World "
Factory or singleton¶
When you register a component in the container, you have the option to choose between factory and singleton patterns.
<?php
$container = JTL\Shop::Container();
$container->setSingleton(HelloWorldGeneratorInterface::class, function() { /*...*/ });
// or
$container->setFactory(HelloWorldGeneratorInterface::class, function() { /*...*/ });
This is not to be confused with the “factory method”!
Both a singleton and a factory need a factory method to take over the creation of the
relevant object. The factory method can be retrieved for both a singleton and a factory using the
same approach:
<?php
$container = Shop::Container();
$factoryMethod = $container->getFactoryMethod(HelloWorldGeneratorInterface::class);
With a singleton, the factory method is called up only once and only one object exists application-wide. With a factory, the factory method is called again with each call and a new object is created.
Hook for registration, extension, or overwriting of components¶
Components must be registered, extended, or overwritten as soon as possible, otherwise inconsistencies
can occur. Therefore, the HOOK_GLOBALINCLUDE_INC (131) hook should be used for this.
Note
Some components cannot be overwritten because they have already been used previously.
For example, the “DbInterface” component cannot be overwritten.