Службы Symfony в модуле
Пока весь код лежит в главном классе модуля, и он разрастается. Новое ядро PrestaShop построено на Symfony, и модуль может пользоваться его механизмом служб. Служба — обычный класс, объект которого создаёт контейнер: он сам передаёт в конструктор всё, что классу нужно, например подключение к базе.
Автозагрузка
Классы с пространством имён хранят в папке src. Чтобы PHP находил их без require_once, в корне модуля создают файл composer.json:
{
"name": "acme/acmehello",
"type": "prestashop-module",
"autoload": {
"psr-4": {
"Acme\\Hello\\": "src/"
}
},
"config": {
"prepend-autoloader": false
}
}Запись psr-4 связывает пространство имён Acme\Hello с папкой src. Затем в папке модуля выполняют команду:
composer dump-autoloadПоявится папка vendor с файлом autoload.php. Подключите его в начале главного файла модуля:
// modules/acmehello/acmehello.php
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
require_once __DIR__ . '/vendor/autoload.php';
}Структура
modules/acmehello/
├── acmehello.php
├── composer.json
├── config/
│ └── services.yml
├── src/
│ └── Repository/
│ └── ReviewRepository.php
├── vendor/
│ └── autoload.php
└── ...Класс службы
Создайте вручную класс, который читает отзывы, — репозиторий:
<?php
// modules/acmehello/src/Repository/ReviewRepository.php
namespace Acme\Hello\Repository;
use Doctrine\DBAL\Connection;
class ReviewRepository
{
private Connection $connection;
private string $prefix;
public function __construct(Connection $connection, string $prefix)
{
$this->connection = $connection;
$this->prefix = $prefix;
}
public function countAll(): int
{
return (int) $this->connection->fetchOne(
'SELECT COUNT(*) FROM ' . $this->prefix . 'acmehello_review'
);
}
}Класс не обращается к Db::getInstance(): подключение к базе и приставку таблиц он получает через конструктор. Connection — подключение библиотеки Doctrine DBAL, которой пользуется новое ядро.
Регистрация службы
Службы модуля описывают в файле config/services.yml:
# modules/acmehello/config/services.yml
services:
acme.hello.review_repository:
class: Acme\Hello\Repository\ReviewRepository
public: true
arguments:
- '@doctrine.dbal.default_connection'
- '%database_prefix%'Первая строка под services — имя службы. arguments перечисляет, что передать в конструктор: запись с @ — другая служба, запись в знаках % — параметр контейнера. public: true разрешает получать службу по имени.
Контейнер кешируется, поэтому после правки файла очистите кеш:
php bin/console cache:clearПолучение службы
В главном классе модуля службу получают методом get():
// modules/acmehello/acmehello.php, метод getContent
$total = $this->get('acme.hello.review_repository')->countAll();
$output .= $this->displayInformation(
$this->trans('Всего отзывов: %count%', ['%count%' => $total], 'Modules.Acmehello.Admin')
);Откройте страницу настроек модуля: над формой появится синяя плашка с числом отзывов.
Проверить, что служба зарегистрирована, можно из консоли:
php bin/console debug:container acme.hello.review_repositoryКоманда напечатает имя класса и аргументы службы.
Где действует файл
| Файл | Где доступны службы |
|---|---|
config/services.yml | В панели управления и в консоли |
config/admin/services.yml | Только в панели |
config/front/services.yml | Только на витрине |
На витрине контейнер меньше: служб панели в нём нет. Если служба нужна в хуке витрины, её описывают в config/front/services.yml и используют в аргументах только то, что там доступно.
Ещё один метод
// modules/acmehello/src/Repository/ReviewRepository.php
public function findLatest(int $limit = 5): array
{
return $this->connection->fetchAllAssociative(
'SELECT id_review, author, rating, date_add FROM ' . $this->prefix . 'acmehello_review
ORDER BY date_add DESC LIMIT ' . (int) $limit
);
}Метод возвращает массив последних отзывов. Он понадобится на странице панели в следующем уроке.
Частая ошибка
Службу добавили в services.yml, а $this->get() отвечает, что такой службы нет. Обычно причин две: не очищен кеш контейнера или папка vendor не создана и класс не находится. Выполните composer dump-autoload в папке модуля и php bin/console cache:clear в корне магазина.