Services
The OXID eShop uses the Symfony DI Container to handle services. For more information about DI Container refer to the Symfony DI Container documentation
In a module, you can:
Define service
For example, you need to adjust the price calculation of the product. You can define a single responsible service for this:
interface PriceCalculatorInterface
{
public function getPrice(string $productId, int $amount): Price;
}
class ERPPriceCalculator implements PriceCalculatorInterface
{
public function getPrice(string $productId, int $amount): Price
{
$this->doSomeCalculation();
// ...
return $price
}
}
Register service
Then you can register the service in OXID DI Container. Create in root directory of your module
services.yaml
file and register it there:
services:
SomeCompany\SpecialERPModule\PriceCalculatorInterface:
class: SomeCompany\SpecialERPModule\ERPPriceCalculator
autowire: true
public: true
Note
class argument in services.yaml is optional and can be omitted (when it’s undefined, DI container considers that the ID of the service is the PHP class with fully qualified name). Following 2 examples of service arguments will have the same effect:
services:
SomeCompany\SpecialERPModule\PriceCalculator:
class: SomeCompany\SpecialERPModule\PriceCalculator
public: true
services:
SomeCompany\SpecialERPModule\PriceCalculator:
public: true
We recommend to use the interface namespace as the DI container key for
the service if you have only the one implementation for an interface and
provide class
parameter with the implementation namespace.
So we can inject with autowiring an interface as dependency in our classes
and avoid dependencies on implementations (dependency inversion principle).
public function __construct(PriceCalculatorInterface $priceCalculator)
{
$this->priceCalculator = $priceCalculator;
}
In this example we have dependency on PriceCalculatorInterface (abstraction), but not on ERPPriceCalculator (implementation). PriceCalculatorInterface will be autowired and no changes in the yaml file are needed because the key of the service is the same as provided in the constructor argument type.
Warning
Your service needs to implement ShopAwareInterface, if you want to be able to active it per shop. Otherwise, module services will be active in all subshops, even if the module itself is activated only for one of them!
Inject own, third party module or shop services
You can use your own, shop services or even services of other modules via dependency injection.
use Psr\Log\LoggerInterface;
class ERPPriceCalculator implements PriceCalculatorInterface
{
private $shopLogger;
public function __construct(LoggerInterface $shopLogger)
{
$this->shopLogger = $shopLogger;
}
public function getPrice(string $productId, int $amount): Price
{
$this->shopLogger->info('Log something');
$this->doSomeCalculation();
// ...
return $price;
}
}
In this example a shop service with id ‘PsrLogLoggerInterface’ will be autowired and no changes in the yaml file are needed, because the key of the logger service is the same as provided in the constructor argument type.
Use services in standard classes
Now you have a service and want to use it to extend already existing shop functionality. You can create own Article class where you overwrite the getPrice() method:
class ERPArticle extends Article_parent
{
public function getPrice($amount = 1)
{
$container = ContainerFactory::getInstance()->getContainer();
$erpPriceCalculator = $container->get(PriceCalculatorInterface::class);
return $erpPriceCalculator->getPrice($this->getId(), $amount)
}
}
You just fetch the DI container via the ContainerFactory and then fetch your service. In order to obtain the service, it needs to be marked as public.