Help us understand the problem. What is going on with this article?

SymfonyでServiceにFactory Methodで生成したオブジェクトを注入する

「ServiceにFactory Methodで生成したオブジェクト刺せたらいいなー」と調べたらできたのでメモ。
例として、Guzzleを利用し、Qiitaのプロフィール取得APIにアクセスして取得したユーザプロフィールを表示するものを作成しました。

まずはFactoryを作る。

GuzzleのClientを生成するFactoryを作ります。

src/Factory/HttpClientFactory
<?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を作ります。

src/Service/QiitaService.php
<?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オブジェクトを注入します。

config/services.yaml
# 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。

src/Controller/UserController.php
<?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
result.png

バッチリQiitaからプロフィール情報を取得できました!

今回のサンプルはこちらに公開しています。
https://github.com/Ippey/factory-injection-test

ippey_s
大阪・枚方のフリーランスWebデベロッパー。主にSymfonyやLaravelなどのPHPフレームワークを使った開発やCraftCMSやWordPressなどのCMSを使ったサイト制作をしています。
https://unplat.info
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした