Навигация
Присоединяйтесь к нашему Telegram-каналу!☝

Будьте в курсе последних новинок и фишек e-commerce: советы, полезные инструменты и эксклюзивные материалы.

Блог Rss rss_feed

Dependency Injection в PrestaShop 9.2: как правильно использовать сервисы в модулях

Dependency Injection в PrestaShop 9.2: как правильно использовать сервисы в модулях

В старых модулях PrestaShop бизнес-логика нередко находится прямо в контроллере или даже в основном классе модуля:

$api = new ExternalApiClient();
$result = $api->send($data);

Пока модуль небольшой, это работает. Но затем появляется импорт, API, cron, CLI, административная страница — и один и тот же код начинает создаваться в разных местах.

В современном PrestaShop для таких задач используется Dependency Injection (DI) и Symfony Service Container.

Что такое сервис в PrestaShop

Сервис — обычный PHP-класс, выполняющий конкретную задачу.

Например:

namespace Ewonta\MyModule\Service;
final class ProductSynchronizer
{
    public function synchronize(int $productId): void
    {
        // Синхронизация товара
    }
}

Вместо постоянного:

$synchronizer = new ProductSynchronizer();

класс регистрируется в Symfony-контейнере, а PrestaShop передаёт его туда, где он нужен.

Это позволяет отдельно организовать API-клиенты, импорт, расчёты, работу с каталогом и другую бизнес-логику.

Что изменилось в PrestaShop 9.2

Это важный момент для разработчиков новых модулей.

Начиная с PrestaShop 9.2 для конфигурации сервисов можно использовать не только привычный:

config/services.yml

но и PHP-конфигурацию:

config/services.php

Кроме того, поддерживаются файлы для конкретных версий:

services-9.2.yml
services-9.yml
services.yml

PrestaShop выбирает первый подходящий файл в следующем порядке:

services.php
services-9.2.yml
services-9.yml
services.yml

Это позволяет модулю иметь разные определения сервисов для разных поколений PrestaShop без большого количества проверок версии внутри PHP-классов. Эта схема официально поддерживается начиная с PrestaShop 9.2.

Пример services.php

Современная конфигурация может выглядеть так:

<?php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return static function (
    ContainerConfigurator $container
): void {
    $services = $container->services();
    $services
        ->defaults()
        ->autowire()
        ->autoconfigure();
    $services->load(
        'Ewonta\\MyModule\\',
        '../src/*'
    );
};

Теперь Symfony может автоматически определять зависимости классов.

Например:

final class ProductImporter
{
    public function __construct(
        private ProductRepository $repository,
        private ExternalApiClient $apiClient
    ) {
    }
}

Разработчику не нужно вручную создавать ProductRepository и ExternalApiClient.

Контейнер делает это сам.

Почему это лучше new Service()

Главное преимущество DI проявляется не в количестве строк кода.

Представим, что один ProductSynchronizer используется одновременно:

Back Office
CLI
Cron
API

Если в каждом месте писать:

new ProductSynchronizer(...);

каждая точка входа должна знать все его зависимости.

После добавления Logger или API-клиента придётся изменять несколько частей модуля.

При Dependency Injection зависимость описывается один раз:

public function __construct(
    ProductSynchronizer $synchronizer
) {
    $this->synchronizer = $synchronizer;
}

Остальное берёт на себя контейнер.

Современные контроллеры PrestaShop 9

В PrestaShop 9 произошёл ещё один важный архитектурный сдвиг: современные Symfony-контроллеры сами являются сервисами.

Поэтому старый подход:

$this->get('my.service');

уже не является правильной основой для новых контроллеров.

В современных контроллерах зависимости рекомендуется получать через constructor injection или method injection. FrameworkBundleAdminController пока сохраняет часть старого поведения ради совместимости, но он уже помечен как устаревший и должен быть удалён в PrestaShop 10.

Для нового кода лучше сразу строить зависимости явно:

final class ImportController extends PrestaShopAdminController
{
    public function importAction(
        ProductImporter $importer
    ): Response {
        $importer->run();
        // ...
    }
}

Так сразу видно, какие сервисы нужны контроллеру.

Back Office и Front Office — не одно и то же

Здесь есть важная особенность PrestaShop.

На современных страницах Back Office доступен полноценный Symfony-контейнер.

Front Office всё ещё может работать в legacy-контексте, где набор доступных сервисов отличается.

Поэтому сервис, зарегистрированный только в:

config/services.php

не следует автоматически считать доступным из любого фронтового hook.

Для Front Office PrestaShop поддерживает отдельную конфигурацию:

config/front/

Аналогично существуют отдельные области:

config/admin/
config/front/
config/webservice/

Это позволяет загружать только те зависимости, которые действительно нужны конкретной части магазина.

Как выглядит структура современного модуля

Для достаточно крупного модуля структура может быть такой:

mymodule/
├── config/
│   ├── services.php
│   ├── admin/
│   └── front/
├── src/
│   ├── Controller/
│   ├── Service/
│   ├── Repository/
│   ├── Command/
│   └── CommandHandler/
└── mymodule.php

Основной класс модуля при этом перестаёт быть местом, куда складывается вся логика.

mymodule.php отвечает прежде всего за жизненный цикл и интеграцию модуля с PrestaShop, а бизнес-операции распределяются по отдельным классам.

Не превращайте Service в новый огромный класс

Dependency Injection сам по себе не делает архитектуру хорошей.

Можно создать:

MyModuleService.php

на 4000 строк и зарегистрировать его в контейнере.

Технически это будет Symfony-сервис, но проблема никуда не исчезнет.

Лучше разделять ответственность:

ProductImporter
ProductValidator
ImageImporter
StockSynchronizer
MarketplaceApiClient
ImportLogger

Каждый сервис должен иметь понятную задачу.

Тогда его можно независимо использовать из контроллера, CLI-команды, cron или другого сервиса.

Dependency Injection в современном PrestaShop — уже не просто дополнительная возможность Symfony.

В PrestaShop 9 архитектура контроллеров стала ещё сильнее связана с сервисами и явным внедрением зависимостей, а PrestaShop 9.2 расширил возможности конфигурации модулей через services.php и версионные определения сервисов.

Для небольшого модуля не нужно создавать десятки сервисов ради архитектуры.

Но если модуль содержит Back Office, API, импорт, cron, CLI или сложную бизнес-логику, вынесение этой логики в сервисы делает код заметно проще для развития и поддержки.

Официальная документация: Services — PrestaShop Developer Documentation

Был ли этот пост полезен для вас?

    
👈 Присоединяйтесь к нашему Telegram-каналу!

Будьте в курсе последних новинок и фишек e-commerce: советы, полезные инструменты и эксклюзивные материалы.

👈 Присоединяйтесь к нашему Telegram-каналу!

Будьте в курсе последних новинок и фишек e-commerce: советы, полезные инструменты и эксклюзивные материалы.

На данный момент комментариев нет
close

Checkout

close

Избранное

Promo