Я перемещаю проект Symfony 3.2 в Symfony 3.3, и я хотел бы использовать DI новые функции. я имею читать документы но пока я могу заставить это работать. Смотрите следующее определение класса:
use Http\Adapter\Guzzle6\Client;
use Http\Message\MessageFactory;
abstract class AParent
{
protected $message;
protected $client;
protected $api_count_url;
public function __construct(MessageFactory $message, Client $client, string $api_count_url)
{
$this->message = $message;
$this->client = $client;
$this->api_count_url = $api_count_url;
}
public function getCount(string $source, string $object, MessageFactory $messageFactory, Client $client): ?array
{
// .....
}
abstract public function execute(string $source, string $object, int $qty, int $company_id): array;
abstract protected function processDataFromApi(array $entities, int $company_id): array;
abstract protected function executeResponse(array $rows = [], int $company_id): array;
}
class AChildren extends AParent
{
protected $qty;
public function execute(string $source, string $object, int $qty, int $company_id): array
{
$url = $this->api_count_url . "src={$source}&obj={$object}";
$request = $this->message->createRequest('GET', $url);
$response = $this->client->sendRequest($request);
}
protected function processDataFromApi(array $entities, int $company_id): array
{
// ....
}
protected function executeResponse(array $rows = [], int $company_id): array
{
// ....
}
}
Вот так мой app/config/services.yml
файл выглядит так:
parameters:
serv_api_base_url: 'https://url.com/api/'
services:
_defaults:
autowire: true
autoconfigure: true
public: false
CommonBundle\:
resource: '../../src/CommonBundle/*'
exclude: '../../src/CommonBundle/{Entity,Repository}'
CommonBundle\Controller\:
resource: '../../src/CommonBundle/Controller'
public: true
tags: ['controller.service_arguments']
# Services that need manually wiring: API related
CommonBundle\API\AParent:
arguments:
$api_count_url: '%serv_api_base_url%'
Но я получаю следующую ошибку:
AutowiringFailedException Не удается автоматически подключить сервис
«CommonBundle \ API \ AChildren»: аргумент «$ api_count_url» метода
«__construct ()» должен иметь подсказку типа или явно указывать значение.
Конечно, я что-то здесь упускаю или просто это невозможно, что приводит меня к следующему вопросу: плохой дизайн ООП или отсутствует функциональность DI-функций Symfony 3.3?
Конечно, я не хочу делать AParent
класс интерфейс, так как я не хочу переопределять методы в классах, реализующих такой интерфейс.
Также я не хочу повторяться и копировать / вставлять одни и те же функции для всех детей.
Идеи? Улики? Совет? Это возможно?
ОБНОВИТЬ
После прочтенияКак управлять распространенными зависимостями с помощью родительских служб«Я попробовал следующее в моем сценарии:
CommonBundle\API\AParent:
abstract: true
arguments:
$api_count_url: '%serv_api_base_url%'
CommonBundle\API\AChildren:
parent: CommonBundle\API\AParent
arguments:
$base_url: '%serv_api_base_url%'
$base_response_url: '%serv_api_base_response_url%'
Но ошибка превращается в:
Атрибут «autowire» на сервисе «CommonBundle \ API \ AChildren» не может быть
наследуется от «_defaults», когда установлен «parent». Переместите своего ребенка
определения в отдельный файл или явно определить этот атрибут в
/var/www/html/oneview_symfony/app/config/services.yml (который
импортировано из «/var/www/html/oneview_symfony/app/config/config.yml»).
Однако я мог бы заставить его работать со следующей настройкой:
CommonBundle\API\AParent:
arguments:
$api_count_url: '%serv_api_base_url%'
CommonBundle\API\AChildren:
arguments:
$api_count_url: '%serv_api_base_url%'
$base_url: '%serv_api_base_url%'
$base_response_url: '%serv_api_base_response_url%'
Это правильный путь? Имеет ли это смысл?
ОБНОВЛЕНИЕ № 2
Следуя инструкциям @Cerad, я сделал несколько модов (см. Код выше и см. Определение ниже), и теперь объекты приходят NULL
? Есть идеи, почему это?
// services.yml
services:
CommonBundle\EventListener\EntitySuscriber:
tags:
- { name: doctrine.event_subscriber, connection: default}
CommonBundle\API\AParent:
abstract: true
arguments:
- '@httplug.message_factory'
- '@httplug.client.myclient'
- '%ser_api_base_url%'
// services_api.yml
services:
CommonBundle\API\AChildren:
parent: CommonBundle\API\AParent
arguments:
$base_url: '%serv_api_base_url%'
$base_response_url: '%serv_api_base_response_url%'
// config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
- { resource: services_api.yml }
Почему объекты NULL
в детском классе?
Интересно. Кажется, вы хотите, чтобы autowire понял, что AChild расширяет AParent, а затем использует определение службы AParent. Я не знаю, было ли это поведение намеренно пропущено или не поддерживается проектом. autowire все еще находится в зачаточном состоянии и сильно развивается.
Я бы предложил зайти в репозиторий di github, проверить проблемы, а затем открыть его, если применимо. Разработчики дадут вам знать, если это дизайн или нет.
В то же время вы можете использовать функциональность родительского сервиса, если вы переместите свое дочернее определение в другой файл сервиса. Это будет работать, потому что материал _defaults применяется только к текущему файлу сервиса.
# services.yml
AppBundle\Service\AParent:
abstract: true
arguments:
$api_count_url: '%serv_api_base_url%'
# services2.yml NOTE: different file, add to config.yml
AppBundle\Service\AChild:
parent: AppBundle\Service\AParent
arguments:
$base_url: 'base url'
И еще одно замечание, немного не относящееся к теме: в public нет необходимости: false, если вы не дурачитесь с автоконфигурацией. По умолчанию все службы определены как частные, если вы специально не объявите их общедоступными.
Обновление — комментарий упомянул что-то об объектах, являющихся нулем Не совсем уверен, что это значит, но я пошел и добавил регистратор в мои тестовые классы. Так:
use Psr\Log\LoggerInterface;
abstract class AParent
{
protected $api_count_url;
public function __construct(
LoggerInterface $logger,
string $api_count_url)
{
$this->api_count_url = $api_count_url;
}
}
class AChild extends AParent
{
public function __construct(LoggerInterface $logger,
string $api_count_url, string $base_url)
{
parent::__construct($logger,$api_count_url);
}
А поскольку существует только одна реализация регистратора psr7, регистратор автоматически подключается и вводится без изменения определения службы.
Обновление 2
Я обновился до S3.3.8 и начал получать:
[Symfony\Component\DependencyInjection\Exception\RuntimeException]
Invalid constructor argument 2 for service "AppBundle\Service\AParent": argument 1 must be defined before. Check your service definition.
Autowire все еще находится в стадии разработки. Не собираюсь тратить усилия на этом этапе, чтобы выяснить, почему. Что-то делать с порядком аргументов. Я вернусь, как только выйдет версия LTS.
Ну, я прошел через ту же проблему. По некоторым причинам мне пришлось создать Symfony 3.3.17 проект для запуска, потому что в моей компании мне пришлось использовать 2 наших пакета, которые все еще застряли в SF 3.3 (я знаю, что вы скажете по этому поводу, но я собираюсь обновить их все в ближайшее время).
Моя проблема была немного другой, поэтому Решение Керада было немного сложно использовать в моем случае. Но я могу предложить (возможно) и решение проблемы, упомянутой в вопросе. В моем случае я использую Symfony Flex управлять моим приложением, даже если это SF 3.3. Таким образом, те, кто знает гибкий, знают, что config.yml больше не существует, и вместо этого есть конфиг папка в корне приложения. Внутри у вас просто есть services.yaml файл. Итак, если вы хотите добавить services2.yaml файл, вы увидите, что он не обнаружен, если вы не переименуете его в services_2.yaml. Но в этом случае это означает, что у вас есть другая среда под названием 2 лайк DEV или же тестовое задание. Не хорошо, правда?
Я нашел решение от Ответ xabbuh в этом выпуске.
Итак, чтобы иметь возможность использовать автопроводку в абстрактных классах с DI в Symfony 3.3 вы все еще можете хранить свои определения служб в одном файле:
CommonBundle\API\AParent:
abstract: true
autoconfigure: false
arguments:
$api_count_url: '%serv_api_base_url%'
CommonBundle\API\AChildren:
parent: CommonBundle\API\AParent
autoconfigure: false
autowire: true
public: false
arguments:
$base_url: '%serv_api_base_url%'
$base_response_url: '%serv_api_base_response_url%'
Дело в том, что
Вы должны быть немного более явным
как сказал xabbuh. Вы не можете наследовать от _дефолт установить общественности, autowire а также Автоконфигурирование если вы используете родитель введите декларацию вашего сервиса. настройка Автоконфигурирование значение в вашей детской службе для ложный важно, потому что, если он не установлен, у вас будет
Служба «CommonBundle \ API \ AChildren» не может иметь «родителя», а также «автоконфигурировать». Имеет смысл на самом деле …
И последнее: если проблема была связана с командой Symfony Console (как в моем случае), не забудьте использовать
tags:
- { name: console.command }
для вашего ребенка службы, потому что Автоконфигурирование установлен в ложный.
Я думаю, что ответ прямо в сообщении об ошибке и не имеет ничего общего с использованием абстрактного класса.
У вас есть автоматическая проводка, настроенная таким образом, что AChildren будет подключен без вашего явного указания? Если это так, вам, вероятно, нужно указать этот аргумент конструктора для каждого дочернего класса.
Увидеть https://symfony.com/doc/current/service_container/parent_services.html за возможную помощь
Кажется, что автопроводка не работает с абстрактными сервисами в Symfony 3.3.8. я создаю вопрос о Github за это.
А пока лично удаляю abstract
вариант и работает отлично.