#symfony #design-patterns
#symfony #шаблоны проектирования
Вопрос:
В будущем веб-приложении мне нужно извлекать данные из разных API (Soap / Rest / Custom …), А иногда смешивать результаты из 2, 3 или всех API одновременно.
Для каждого API я буду выполнять одинаковые действия, например: getLastDatas, setOneData, putDatas2externalDB и т.д…
В контроллере я мог бы сделать что-то вроде:
$someFreshResults = $this->get('app.products')->getFreshResults($para1, $para2, $arrayOfAPI);
с помощью $arrayOfAPI создается список из 1, 2 или всех имен API.
Поскольку все API разные (и иногда нетривиальные), я думаю, было бы неплохо объявить сервис по API, но очень плохая и уродливая идея внедрить все эти сервисы в службы app.products и выполнить для них цикл с помощью чего-то вроде :
public function getFreshResults($para1, $para2, $API) {
foreach($API as $oneApi) {
oneResult = $this->get($oneAPI)->getLastDatas($para1);
... work with oneResult ...
}
}
Как правильно это сделать в приложении symfony?
Комментарии:
1. Знаете ли вы, что вы можете использовать объявление аргументов в своих службах. yml-файл для внедрения сервиса в другой с аннотацией «@MyService» ?
2. Это не столько вопрос Symfony, сколько вопрос о правильных шаблонах проектирования, и поэтому, возможно, вполне основан на мнениях.
Ответ №1:
Я бы предложил написать отдельные сервисы (но реализующие общий интерфейс) для каждого API, который вы хотите использовать, а затем написать сервис, который объединяет эти отдельные клиенты API. Ваша служба агрегации может иметь такой API:
<?php
class MyAggregrator
{
/** @var ApiClientInterface[] */
private $clients = [];
public function registerClient(string $name, ApiClientInterface $client)
{
$this->clients[$name] = $client;
}
public function getProducts()
{
foreach ($this->clients as $name => $client) {
...
}
}
}
Регистрация вашей службы агрегации в контейнере может выглядеть примерно так:
services:
my_aggregator:
class: MyAggregrator
calls:
- [registerClient, ["foo", "@api1"]]
- [registerClient, ["bar", "@api2"]]
Вы могли бы даже немного упростить эту конфигурацию, пометив своих клиентов API, а затем написать компиляторный проход, который извлекает каждую помеченную службу и регистрирует их в службе агрегации. Для получения дополнительной информации о помеченных службах см. http://symfony.com/doc/current/service_container/tags.html
Комментарии:
1. Спасибо. Кажется, это хорошее начало, поскольку я, вероятно, мог бы загрузить только необходимый api для конкретного запроса с помощью компилятора.
2. Нет, проход компилятора выполняется один раз при сборке контейнера, а не для каждого запроса. Вы могли бы передать API, которые вы хотите использовать в качестве аргумента метода или чего-то еще.
Ответ №2:
Может быть, вы могли бы создать класс с помощью api. И объявляйте каждый класс api как сервис.
В ваших службах.yml-файл
services:
app.products:
class: AppBundleProducts
arguments: ["@api1", "@api2", ...]
А затем в вашем Products.php класс обслуживания:
class Products
{
private $api1;
private $api2;
public function __construct($api1, $api2) //you must declare in the constructor the arguments declared in services.yml
{
$this->api1 = $api1;
$this->api2 = $api2;
}
Комментарии:
1. Это то, что я сделал в первой версии. Но что, если у меня 20 или 30 разных API, но иногда действительно нужно вызывать только 1 или 2?