ServiceProvider, ServiceContainer 入門

1439d17fca2c2d5318c6017f8cef657d?s=47 chiroruxx
February 16, 2019

ServiceProvider, ServiceContainer 入門

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

1439d17fca2c2d5318c6017f8cef657d?s=128

chiroruxx

February 16, 2019
Tweet

Transcript

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

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

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

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

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

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

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

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

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

  10. 弁護士ドットコム

  11. BUSINESS LAWYERS

  12. 本題

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

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

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

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

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

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

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

  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 { // メール送信処理 } }
  21. ユースケース ▪ 統計上の都合により、ユーザ側と管理者側でAPIキーを 分けることになりました

  22. コードの状態 ▪ 現状はサービスのコンストラクタにAPIキーをべた書き public function __construct() { $this->sendGrid = new

    SendGrid(config('services.sendgrid.key')); }
  23. コードの状態 ▪ UserSendGridクラスとAdminSendGridクラスに分け る・・・? – 設定の数だけクラスが増えていくの・・・? ▪ apiSetterメソッドを作る・・・? – インスタンスの状態を管理しないといけなくなる

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

    インスタンスの設定を状況に合わせて指定できる
  25. 元のサービスのコード class SendGridService { private $sendGrid; public function __construct() {

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

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

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

    { $this->sendGrid = $sendGrid; } // …その他の処理 } そのまま プロパティにする
  29. 元のコントローラのコード // sendgridのcontactに登録 $sendGridService = new SendGridService(); $sendGridService->saveUser($user);

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

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

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

    SendGridService($sendGrid); $sendGridService->saveUser($user); 生成した設定を渡す
  33. DIのまとめ ▪ DIとは、インスタンスの生成に必要な設定を引数で渡し てあげること ▪ 設定を外から渡すことで、インスタンスの設定を状況に 合わせて指定できる

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

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

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

    = new SendGridService($sendGrid);
  37. コードの問題点② ▪ newだらけ! ▪ サービスの生成だけで、すごい幅をとる ▪ 本当にやりたいことはサービスを生成した後にあるは ず・・・ $settingA =

    new SettingA(); $settingB = new SettingB(); $settingC = new SettingC(); $settingD = new SettingD(); $someService = new SomeService($settingA, $settingB, $settingC, $settingD);
  38. コードの問題点 ▪ まとめると・・・ ▪ 各処理ごとにサービスを毎回つくりたくない – ⇒サービスをまとめて管理したい ▪ サービスの生成をロジックに書きたくない –

    ⇒サービスの生成方法をまとめて管理したい
  39. コードの問題点 ▪ まとめると・・・ ▪ 各処理ごとにサービスを毎回つくりたくない – ⇒サービスをまとめて管理したい – サービスコンテナ ▪

    サービスの生成をロジックに書きたくない – ⇒サービスの生成方法をまとめて管理したい – サービスプロバイダ
  40. サービスコンテナとは? ▪ サービスをまとめて管理するツール ▪ 様々なサービスを入れられるグローバルな連想配列 ▪ 例 key value sendgrid

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

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

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

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

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

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

  50. まとめ

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

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

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

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

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

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

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

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

    new SendGridService($sendGrid); }); keyをクラス名に
  59. コントローラのコード public function store(Request $request, SendGridService $sendGridService) { // …ユーザーのDB保存処理

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

    // sendgridのcontactに登録 $sendGridService->saveUser($user); // …メール送信 // …ビューの表示処理 } 型に先ほどのkeyを指定する
  61. Tips: 自動注入 ▪ サービスコンテナに登録するときのキーをサービスのク ラス名にすると、ちょっとラクに書けます! ▪ コントローラなどで、サービスを引数に入れると、勝手 にサービスコンテナからサービスを取得してくれます。