Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ServiceProvider, ServiceContainer 入門

chiroruxx
February 16, 2019

ServiceProvider, ServiceContainer 入門

2019/02/16 Laravel JP Conferenceで発表する資料です。

chiroruxx

February 16, 2019
Tweet

More Decks by chiroruxx

Other Decks in Technology

Transcript

  1. Service Provider
    Service Container
    2/16 Laravel JP Conference
    前田 和人

    View Slide

  2. こんな人におすすめ
    ■ MVCは何となくわかる
    ■ それ以外の機能はあまり知らない
    ■ サービスコンテナを使ったことがない
    ■ サービスコンテナで挫折した

    View Slide

  3. このセッションの目標
    ■ サービスコンテナをざっくり理解する
    ■ サービスコンテナに1つ目のサービスを結合する勇気が
    出る

    View Slide

  4. お話しないこと
    ■ サービスコンテナの細かい仕様・実装
    ■ 豊富な実装例
    ■ サービスディスカバリ

    View Slide

  5. 注意事項
    ■ このセッションでは、理解することを最優先します
    ■ 場合によっては正確ではないことを記載している場合が
    あります

    View Slide

  6. ■ 時間の都合で削らせていただきます。すみません。
    注意事項

    View Slide

  7. 注意事項
    ■ 以下に資料があがってますの
    で、お手元で見ていただくこ
    とをおすすめします。
    ■ https://speakerdeck.com/chi
    roruxx/serviceprovider-
    servicecontainer-ru-men

    View Slide

  8. 自己紹介
    ■ 前田 和人
    ■ @chiroruxxxx
    ■ 弁護士ドットコム株式会社

    View Slide

  9. 自己紹介
    ■ 前田 和人
    ■ @chiroruxxxx
    ■ 弁護士ドットコム株式会社

    View Slide

  10. 弁護士ドットコム

    View Slide

  11. BUSINESS LAWYERS

    View Slide

  12. 本題

    View Slide

  13. なぜサービスコンテナは難しいのか

    View Slide

  14. なぜサービスコンテナは難しいのか
    サービス
    DI
    サービス
    コンテナ
    インター
    フェース
    自動注入

    View Slide

  15. なぜサービスコンテナは難しいのか
    サービス
    DI
    サービス
    コンテナ
    インター
    フェース
    自動注入

    View Slide

  16. DI
    サービス
    DI
    サービス
    コンテナ
    インター
    フェース
    自動注入

    View Slide

  17. ユースケース
    ■ 次のようなサービスクラス(コンポーネント)があるとし
    ます
    ■ SendGridに関する操作を行うサービス
    – SendGrindのコンタクトリストに
    ユーザを追加する
    – SendGridを使用してメールを送信する

    View Slide

  18. SendGrid
    ■ クラウドベースのメール配信サービス
    ■ 送りたい内容をAPIで送るとメール送ってくれる
    ■ メールについて知らなくてもwebの知識でメールを扱え

    View Slide

  19. ユースケース
    ■ 次のようなサービスクラスがあるとします。
    ■ SendGridに関する操作を行うサービス
    – SendGrindのコンタクトリストに
    ユーザを追加する
    – SendGridを使用してメールを送信する

    View Slide

  20. サービスのコード
    class SendGridService
    {
    private $sendGrid;
    public function __construct()
    {
    $this->sendGrid = new SendGrid(config('services.sendgrid.key'));
    }
    public function saveUser(User $user): void
    {
    // コンタクトリストへの追加処理
    }
    public function sendMail(Mail $mail): void
    {
    // メール送信処理
    }
    }

    View Slide

  21. ユースケース
    ■ 統計上の都合により、ユーザ側と管理者側でAPIキーを
    分けることになりました

    View Slide

  22. コードの状態
    ■ 現状はサービスのコンストラクタにAPIキーをべた書き
    public function __construct()
    {
    $this->sendGrid = new SendGrid(config('services.sendgrid.key'));
    }

    View Slide

  23. コードの状態
    ■ UserSendGridクラスとAdminSendGridクラスに分け
    る・・・?
    – 設定の数だけクラスが増えていくの・・・?
    ■ apiSetterメソッドを作る・・・?
    – インスタンスの状態を管理しないといけなくなる
    ■ ⇒DIをしよう!

    View Slide

  24. DIとは?
    ■ 何の略語かとか、日本語訳を載せると
    途端に難しそうにきこえるアレ
    ■ DIとは、インスタンスの生成に必要な設定をコンストラ
    クタの引数で渡してあげること
    – 今回で言うと、SendGridクラス
    ■ インスタンスの設定を状況に合わせて指定できる

    View Slide

  25. 元のサービスのコード
    class SendGridService
    {
    private $sendGrid;
    public function __construct()
    {
    $this->sendGrid = new SendGrid(config('services.sendgrid.key'));
    }
    // …その他の処理
    }

    View Slide

  26. サービスのコード
    class SendGridService
    {
    private $sendGrid;
    public function __construct(SendGrid $sendGrid)
    {
    $this->sendGrid = $sendGrid;
    }
    // …その他の処理
    }

    View Slide

  27. サービスのコード
    class SendGridService
    {
    private $sendGrid;
    public function __construct(SendGrid $sendGrid)
    {
    $this->sendGrid = $sendGrid;
    }
    // …その他の処理
    }
    設定を受け取る

    View Slide

  28. サービスのコード
    class SendGridService
    {
    private $sendGrid;
    public function __construct(SendGrid $sendGrid)
    {
    $this->sendGrid = $sendGrid;
    }
    // …その他の処理
    }
    そのまま
    プロパティにする

    View Slide

  29. 元のコントローラのコード
    // sendgridのcontactに登録
    $sendGridService = new SendGridService();
    $sendGridService->saveUser($user);

    View Slide

  30. 適用後のコントローラのコード
    // sendgridのcontactに登録
    $sendGrid = new SendGrid(config('services.sendgrid.user.key’));
    $sendGridService = new SendGridService($sendGrid);
    $sendGridService->saveUser($user);

    View Slide

  31. ユーザー側のコントローラのコード
    // sendgridのcontactに登録
    $sendGrid = new SendGrid(config('services.sendgrid.user.key’));
    $sendGridService = new SendGridService($sendGrid);
    $sendGridService->saveUser($user);
    設定を生成

    View Slide

  32. ユーザー側のコントローラのコード
    // sendgridのcontactに登録
    $sendGrid = new SendGrid(config('services.sendgrid.user.key’));
    $sendGridService = new SendGridService($sendGrid);
    $sendGridService->saveUser($user);
    生成した設定を渡す

    View Slide

  33. DIのまとめ
    ■ DIとは、インスタンスの生成に必要な設定を引数で渡し
    てあげること
    ■ 設定を外から渡すことで、インスタンスの設定を状況に
    合わせて指定できる

    View Slide

  34. サービス
    DI
    サービス
    コンテナ
    インター
    フェース
    自動注入
    サービスコンテナ
    サービスプロバイダ

    View Slide

  35. コードの問題点①
    ■ メール関連の処理は色々なところで使われる
    ■ 毎回サービスを作成するのはコストがかかる
    $sendGrid = new SendGrid('services.sendgrid.user.key');
    $sendGridService = new SendGridService($sendGrid);

    View Slide

  36. コードの問題点②
    ■ DIを使うと、サービスを使う側でnewする回数が増える
    ■ 設定が3つや4つになったら・・・?
    $sendGrid = new SendGrid('services.sendgrid.user.key');
    $sendGridService = new SendGridService($sendGrid);

    View Slide

  37. コードの問題点②
    ■ newだらけ!
    ■ サービスの生成だけで、すごい幅をとる
    ■ 本当にやりたいことはサービスを生成した後にあるは
    ず・・・
    $settingA = new SettingA();
    $settingB = new SettingB();
    $settingC = new SettingC();
    $settingD = new SettingD();
    $someService = new SomeService($settingA, $settingB, $settingC, $settingD);

    View Slide

  38. コードの問題点
    ■ まとめると・・・
    ■ 各処理ごとにサービスを毎回つくりたくない
    – ⇒サービスをまとめて管理したい
    ■ サービスの生成をロジックに書きたくない
    – ⇒サービスの生成方法をまとめて管理したい

    View Slide

  39. コードの問題点
    ■ まとめると・・・
    ■ 各処理ごとにサービスを毎回つくりたくない
    – ⇒サービスをまとめて管理したい
    – サービスコンテナ
    ■ サービスの生成をロジックに書きたくない
    – ⇒サービスの生成方法をまとめて管理したい
    – サービスプロバイダ

    View Slide

  40. サービスコンテナとは?
    ■ サービスをまとめて管理するツール
    ■ 様々なサービスを入れられるグローバルな連想配列
    ■ 例
    key value
    sendgrid SendGridService
    ga GoogleAnalyticsService
    payment StripeService

    View Slide

  41. サービスコンテナとは?
    ■ グローバルなのでアプリケーションのどこからでも呼び
    出せる
    ■ Laravelに出てくる「app」はサービスコンテナのこと
    ■ resolveメソッドを使うことで
    サービスをコンテナから取得できる
    $controller = resolve(UserController::class);

    View Slide

  42. サービスプロバイダとは?
    ■ サービスコンテナへのデータ登録を管理
    – =サービスの生成方法を管理
    ■ アプリケーションの初期化時にロードされ、
    サービスコンテナの使用準備を行う
    ■ registerメソッドを使用してデータを登録できる

    View Slide

  43. サービスプロバイダとは?
    ■ デフォルトでは、AppServiceProviderというものがある
    ■ AppServiceProviderに追加していくよりも、
    サービスごとにプロバイダを作成していくと良い

    View Slide

  44. サービスプロバイダのコード
    class SendGridServiceProvider extends ServiceProvider
    {
    public function register()
    {
    $this->app->bind('sendgrid', function ($app) {
    $sendGrid = new SendGrid(config('services.sendgrid.user.key'));
    return new SendGridService($sendGrid);
    });
    }
    }

    View Slide

  45. サービスプロバイダのコード
    class SendGridServiceProvider extends ServiceProvider
    {
    public function register()
    {
    $this->app->bind('sendgrid', function ($app) {
    $sendGrid = new SendGrid(config('services.sendgrid.user.key'));
    return new SendGridService($sendGrid);
    });
    }
    }
    key

    View Slide

  46. サービスプロバイダのコード
    class SendGridServiceProvider extends ServiceProvider
    {
    public function register()
    {
    $this->app->bind('sendgrid', function ($app) {
    $sendGrid = new SendGrid(config('services.sendgrid.user.key'));
    return new SendGridService($sendGrid);
    });
    }
    }
    key
    value

    View Slide

  47. コントローラのコード
    // sendgridのcontactに登録
    $sendGridService = resolve('sendgrid');
    $sendGridService->saveUser($user);

    View Slide

  48. コントローラのコード
    // sendgridのcontactに登録
    $sendGridService = resolve('sendgrid');
    $sendGridService->saveUser($user);
    key

    View Slide

  49. サービスコンテナのまとめ
    ■ DIを使うと、コントローラでnewがいっぱい出てくる
    ■ サービスコンテナはサービスをまとめて管理するツール
    ■ サービスコンテナはサービスの入ったグローバルな配列
    ■ サービスの生成方法についてはサービスプロバイダに
    記述する

    View Slide

  50. まとめ

    View Slide

  51. なぜサービスコンテナは難しいのか
    サービス
    DI
    サービス
    コンテナ
    インター
    フェース
    自動注入

    View Slide

  52. DIのまとめ
    ■ DIとは、インスタンスの生成に必要な設定を引数で渡し
    てあげること
    ■ 設定を外から渡すことで、インスタンスの設定を状況に
    合わせて指定できる

    View Slide

  53. サービスコンテナのまとめ
    ■ DIを使うと、コントローラでnewがいっぱい出てくる
    ■ サービスをまとめて管理するツール
    ■ サービスコンテナはサービスの入ったグローバルな配列
    ■ サービスの生成方法についてはサービスプロバイダに
    記述する

    View Slide

  54. ご清聴
    ありがとうございました

    View Slide

  55. サービス
    DI
    サービス
    コンテナ
    インター
    フェース
    自動注入
    自動注入

    View Slide

  56. Tips: 自動注入
    ■ サービスコンテナに登録するときのキーをサービスのク
    ラス名にすると、ちょっとラクに書けます!
    ■ コントローラなどで、サービスを引数に入れると、勝手
    にサービスコンテナからサービスを取得してくれます。

    View Slide

  57. サービスプロバイダのコード
    $this->app->bind(SendGridService::class, function () {
    $sendGrid = new SendGrid(config('services.sendgrid.user.key'));
    return new SendGridService($sendGrid);
    });

    View Slide

  58. サービスプロバイダのコード
    $this->app->bind(SendGridService::class, function () {
    $sendGrid = new SendGrid(config('services.sendgrid.user.key'));
    return new SendGridService($sendGrid);
    });
    keyをクラス名に

    View Slide

  59. コントローラのコード
    public function store(Request $request, SendGridService $sendGridService)
    {
    // …ユーザーのDB保存処理
    // sendgridのcontactに登録
    $sendGridService->saveUser($user);
    // …メール送信
    // …ビューの表示処理
    }

    View Slide

  60. コントローラのコード
    public function store(Request $request, SendGridService $sendGridService)
    {
    // …ユーザーのDB保存処理
    // sendgridのcontactに登録
    $sendGridService->saveUser($user);
    // …メール送信
    // …ビューの表示処理
    }
    型に先ほどのkeyを指定する

    View Slide

  61. Tips: 自動注入
    ■ サービスコンテナに登録するときのキーをサービスのク
    ラス名にすると、ちょっとラクに書けます!
    ■ コントローラなどで、サービスを引数に入れると、勝手
    にサービスコンテナからサービスを取得してくれます。

    View Slide