「ServiceにFactory Methodで生成したオブジェクト刺せたらいいなー」と調べたらできたのでメモ。
例として、Guzzleを利用し、Qiitaのプロフィール取得APIにアクセスして取得したユーザプロフィールを表示するものを作成しました。
まずはFactoryを作る。
GuzzleのClientを生成するFactoryを作ります。
<?php
namespace App\Factory;
use GuzzleHttp\Client;
class HttpClientFactory
{
public static function create($baseUri)
{
$httpClient = new Client(['base_uri' => $baseUri, 'timeout' => 2]);
return $httpClient;
}
}
HttpClientFactory::create('http://localhost:8080');
のような形でbase_uriを指定したClientオブジェクトを返します。
Serviceを作る
Qiitaから指定したユーザIDのプロフィールを取得するServiceを作ります。
<?php
namespace App\Service;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
class QiitaService
{
private $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function getUser($userId)
{
try {
$response = $this->client->get('api/v2/users/' . $userId);
return json_decode((string) $response->getBody(), true);
} catch (ClientException $e) {
return null;
}
}
}
コンストラクタでClientのオブジェクトを引数としています。このオブジェクトを利用してQiitaのAPIにアクセスしますが、get()で呼び出しているURIはClientオブジェクトにbase_uriが指定されていないと正しく動作しません。では、このServiceにFactoryで生成したClientオブジェクトを注入してみましょう。
Service Containerの設定をする
Service Containerを設定してServiceにClientオブジェクトを注入します。
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
+
+ qiitaClient:
+ class: GuzzleHttp\Client
+ factory: ['App\Factory\HttpClientFactory', 'create']
+ arguments:
+ $baseUri: 'https://qiita.com'
+
+ App\Service\QiitaService:
+ arguments:
+ $client: '@qiitaClient'
まず、qiitaClientでQiita用のClientを生成する設定を行います。 factory: ['App\Factory\HttpClientFactory', 'create']
を指定することで、HttpClientFactoryのcreate()メソッドを呼び出してくれます。argumentsにはcreate()メソッドの引数を指定します。ここでhttps://qiita.com
を指定することで、Qiita用のClientオブジェクトを生成できます。
続いてQiitaServiceのargumentsに@qiitaClientを指定し、Qiita用のClientオブジェクトを注入します。これで完成です。さいごにControllerでこのServiceを使って、プロフィールを表示させます。
ControllerでServiceを使う
ControllerへはautowiringでServiceが注入されるので、こんな感じでOK。
<?php
namespace App\Controller;
use App\Service\QiitaService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
/** @var QiitaService */
private $qiitaService;
public function __construct(QiitaService $qiitaService)
{
$this->qiitaService = $qiitaService;
}
/**
* @param $userId
* @return Response
* @Route("/user/{userId}", name="user")
*/
public function index($userId)
{
$user = $this->qiitaService->getUser($userId);
return $this->render('user/index.html.twig', ['user' => $user]);
}
}
で、実行した結果がこちら。
http://localhost:8000/user/ippey_s
バッチリQiitaからプロフィール情報を取得できました!
今回のサンプルはこちらに公開しています。
https://github.com/Ippey/factory-injection-test