Macでcmd+tab実行時のアプリ切り替え表示を全てのディスプレイで表示する方法
結論
以下のコマンドを実行してログインしなおす。
defaults write com.apple.dock appswitcher-all-displays -bool true
これで思ってたのと違う画面に切り替え表示が出力されているという現象を防ぐことができる。
読書感想文『そろそろはじめるテスト駆動開発』
はじめに
Software Design 2022年3月号の特集『そろそろはじめるテスト駆動開発』を読んだので覚えておきたいなと思ったことをまとめます。
今回は自動テスト編です。
特集のおおまかな内容
著者は日本におけるテスト駆動開発の第一人者、和田卓人さん。「テスト書いてないとかお前それ@t_wadaの前でも同じこと言えんの?」というライオンのアスキーアートでお馴染み方です。
前半では自動テスト、テストファースト、テスト駆動開発がそれぞれどのようなもので、どのような違いがあるのかについて解説している。後半ではテスト駆動開発をするにはどのような手順踏めばよいのか、どのように考えると良いのかをJavaScriptの実際のコードをベース解説している。
今回はその中から自動テストについて書かれている部分について覚えておきたいと思ったことをまとめていきます。
自動テストとは
テストもコードで書いて、そのコードを実行することでテストの実施を自動で行っていく取り組みのこと
自動テストに必須の特性
自動テストで効果をあげるために必須となる性質
自己検証可能
テストの結果を人が見て判断しなくても成功か失敗かがわかること。つまりコードの実行結果が期待しているものかどうかの判断をコンピュータが行える状態。Pass/Failedをアサーションなどで判別して行うのが一般的。
繰り返し可能
テストがいつでもどの環境でも同じように動くこと。ローカル環境、開発環境、CI環境それぞれで同じように動き、テスト後の後処理なども人間が行う必要がない状態
必須ではないが強く推奨される特性
独立していること
テストを行う順番や並列実行による影響を受けないこと。どんな順番でどのタイミングに実行されても変わらない結果が得られる状態
高速であること
コードが自分の考えた通りに動いているかを判断するため、できる限り高速に動く必要がある。実行に時間がかかってしまうようだとテストが億劫になって実行頻度が下がり、コードが壊れたことに気付くのが遅れてしまう。
自動テストのメリット
根拠のある自信
開発しているシステムが想定通り動いていることで自信が生まれるようになります。これが根拠のある自信であり、著者が自動テストの最大の効果であると述べています。自動テストが整備されていれば既存のコードを壊してしまうかもしれないという心理的負担を感じずにリファクタリングを行うことができる。この安心感が継続的な変更と改善を支える。
デバッグを大幅に軽減できる
自動テストは既存のコードを壊してしまってもすぐに気づくことができる回帰テスト(リグレッションテスト)としての効果があります。バグの混入にいち早く気付けるようになることで、デバッグの時間を大幅に削減できます。
詳細なドキュメントになる
実際のソフトウェアと用意されたドキュメントに乖離しているという状態は誰しも経験があると思います。自動テストはもし乖離が起きてもテストが失敗することで乖離に気付くことができます。テストコードはよりコードに近い、詳細なドキュメントとしても活かすことができます。
自動テストの注意点
学習コストがかかる
過去のさまざまな事例からテストを書くコストはデバッグの時間の軽減などで相殺され、むしろ黒字になるのでテストを書き慣れているプログラマーはテストを書いたほうが早く開発を進められる。しかし、テストを描き慣れていないプログラマーはテストの書き方を学びながら開発を進める必要があるので、より時間がかかってしまう。
メンテナンスコストがかかる
自動テストは設計変更の影響を受けるため、テストコードの可読性や変更容易性も意識しないとメンテナンスコストが高くなってしまう。そのため、テストコードも定期的にリファクタリングを行なって適切な状態を保っていく必要がある。
品質保証としてはもの足りない
自動テストによって欠陥を減らし品質を高めることができるが、それによって品質が保証されているというわけではない。自動テストとは別に、コードレビューやペアプログラミングなど、品質を高める施策を行なっていく必要がある。
感想
自動テストのメリットは品質や開発効率という面にあると思っていたので、1番の利点が心理的なものであるというのは意外でした。言われてみると確かに自動テストがあることでリファクタリングを行うときにも、既存の機能にバグを生み出してしまうかもしれないといった恐怖感を低減できる。それによって品質や開発効率の向上という効果が生まれる、というのは納得ができます。メリットを活かすためにも、十分なカバー範囲を持ったテストを実装する必要がありますし、個人でなくチームレベルで自動テストの文化を作っていくためにも、今後は自動テストの手法について学んでいこうとおもいます。
CSS: font-sizeで使うem/remについて調べてみた
今までWeb開発であまりCSSを本格的に学んだことがなく、なんとなくでCSSを書いてきましたが、フロントエンド業務を行うプロジュエクトにアサインされてCSSを学ぶ必要性を感じてきました。ひとまず、HTML/CSSの写経を行なっていて気になったfont-sizeのem/remが何者か気になったので、調べてみました。
em/remとは一体なんなのか
em/remはCSSで指定できる相対的な長さを表す単位で、CSSで定義されているlengthというデータ型に属しています。
em
emは親要素で指定されているfont-sizeとの相対的なサイズを指定します。例えば親要素が16pxの場合、1emでは16px、2emでは32pxになります。emでは複合(compounding)と呼ばれる事象が発生します。これは親要素との相対的なサイズで指定されるためです。例えば以下のようなHTML・CSSが設定されている場合に、タグのOuterは32pxになりますが、タグのInnerは64pxになります。
<div> <span>Outer <span>Inner</span> Outer</span> </div>
div { font-size: 16px; } span { font-size: 2em; }
rem
remは親要素ではなく、ルート要素(タグ)との相対的なサイズを指定します。親要素の影響受けないため、emとは違い複合発生しないことがメリットになります。つまり、以下のようなhtmlとCSSが設定している場合、全て32pxになります。
<div> <span>Outer <span>Inner</span> Outer</span> </div>
html { font-size: 16px; } span { font-size: 2rem; }
Vue.jsのライフサイクルフックを学ぶ
7月からVue.jsを扱うプロジェクトに入ったのでVue.jsをおさらいしています。今回はライフサイクルフックについて学んでいきます。参考は公式ドキュメントです。
ライフサイクルフックとは
インスタンスの状態が変化したタイミングで自動的に呼ばれる関数です。ライフサイクルフックを理解することはVue.jsで効果的な開発をすることにとって、とても重要です。
beforeCreate
Vueインスタンスが作られる前、インスタンスが初期化されるときに呼ばれる。インスタンスプロパティなんかにアクセスすることができないのであんまり使うことはない。あるとしたらVueインスタンスを作る前に、Vue外部と連携を取る必要がある時かな。経験はないです。
created
インスタンスが作られた後に呼ばれます。このタイミングでdata、computed、methodsなどが利用可能になります。dataやcomputedにアクセスできるので、使い所はありそうですね。
beforeMount
Vueインスタンスが作成された後、DOMが初めて構築される直前に呼ばれます。DOMが構築される「前」なのであまり使い所はないですね。
mounted
DOMが構築されたタイミングで呼ばれます。HTMLとして描画されているわけではないこと、子コンポーネントのDOMも構築されていることが保証されるわけでない点に注意が必要です。
beforeUpdate
dataオプションで保持している値が変更され、DOMが更新される前に呼ばれます。更新される前のDOMにアクセスしたいときに有効です。
updated
dataオプションで保持している値が変更され、DOMが更新された後に呼ばれます。update内でDOMの更新を行うと無限ループに陥ってしまうので注意が必要です。また、子コンポーネントが更新されていることは保証されません。呼ばれる回数も多いことから描画コストもかかる上に、ロジックが複雑になりやすいのでできるだけ使うのを避けた方がいいフックです。
activated
コンポーネントが活性化するときに呼ばれます。このフックは
deactivated
コンポーネントが非活性化するときに呼ばれます。このフックは
beforeDestroy
destroy()メソッドを呼んだ時など、Vueインスタンスが破棄される直前に呼ばれます。このフック内では、Vueの各オプションはまだ機能しているため、インスタンス内の情報を利用したい場合に有効になるフックです。
destroyed
Vueインスタンスが破棄された後に呼ばれます。このフックが呼ばれるとき、既にVueの各オプションは機能していないので、Vueインスタンスが持っていた情報にはアクセスできないため使い所は少ないフックです。
errorCaputured
子コンポーネントからエラーが捕捉されたときに呼ばれます。このフックは返り値としてbooleanが定義されています。falseを返すことで親コンポーネントへのエラーの伝播を止めることができます。
まとめ
さらっとVueもライフサイクルフックを全て紹介してみました。activated、deactivatedなんてフックがあるのは知らなかったし、destroyedなんて使うことあんのかな〜って改めて思いました。使うのはほとんどcreated、mountedあたりなんじゃないでしょうか?ひとまず、頭の片隅ででも覚えておけば、開発中に何かあったときにドキュメントを見返して使ってみることができると思うので、是非覚えてみてください。
Vue.js + TypeScriptの始め方
7月から新しいチームに参画しました。Nuxt.js+TypeScriptを使ったプロジェクトで、技術的な難易度は高くないとのことなんですが、Nuxt.jsは会社が推しているフロントエンド技術でもあるようなので、これから学習していこうと思っています。まずは何事も基本から、ということで今回はNuxt.jsの前にVue.s+TypeScriptのコードを書いていくための下準備を行なっていきます。
手順などは公式ドキュメントに従います。
v3.vuejs.org
Vue CLIのインストール
まずはVue CLIをインストールしていきます。業務ではyarnを使っているみたいなので今回はyarnを使います。
yarn global add @vue/cli
以下のコマンドを実行してバージョンが表示されればOKです
vue --version
プロジェクト作成
次にプロジェクトを作ります。下記のコマンドはhello-world-appという名前のプロジェクトを作るコマンドです。
vue create hello-world-app
コマンドを実行するとターミナルが以下のような表示になります。
? Please pick a preset: (Use arrow keys) ❯ Default ([Vue 2] babel, eslint) Default (Vue 3) ([Vue 3] babel, eslint) Manually select features
vue.jsをそのまま使うのであればVue2かVue3で問題ありませんが、今回はTypeScriptを使うので「Manually select features」を選択します。
次は以下のような表示になります。
? Please pick a preset: Manually select features ? Check the features needed for your project: (Press <space> to select, <a> to t oggle all, <i> to invert selection) ❯◉ Choose Vue version ◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◯ Vuex ◯ CSS Pre-processors ◉ Linter / Formatter ◯ Unit Testing ◯ E2E Testing
TypeScriptを使用するので、TypeScriptを選択します。選択するには矢印キーで移動してスペースキーを押します。
下記のようにTypeScript横の丸が塗りつぶされた状態になっていればOKです。
? Please pick a preset: Manually select features ? Check the features needed for your project: ◉ Choose Vue version ◉ Babel ❯◉ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◯ Vuex ◯ CSS Pre-processors ◉ Linter / Formatter ◯ Unit Testing ◯ E2E Testing
エンターキーを押して次に進みます。
あとはお好みで問題ありませんが、参考に僕が選んだ結果を以下に載せておきます。
? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, TS, Linter ? Choose a version of Vue.js that you want to start the project with 2.x ? Use class-style component syntax? No ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes ? Pick a linter / formatter config: Basic ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? (y/N)No
正常にプロジェクトが作成されたら以下のような出力がされているはずです。
🎉 Successfully created project hello-world-app. 👉 Get started with the following commands: $ cd hello-world-app $ yarn serve
次は指示に従ってアプリケーションを動かしてみます。
アプリケーションの実行
以下のコマンドを実行します。
cd hello-world-app yarn serve
ブラウザからhttp://localhost:8081/にアクセスするとVue.js + TypeScriptのWelcomeページが表示されます。
Hello Worldする
僕はどんな言語や技術でもHello Worldと出力することから始めることにしているので、今回もやっていきます。
./src/App.vueを以下のように編集します。
<template> <div id="app"> <h1>Hello World!</h1> </div> </template>
これでhttp://localhost:8081/にアクセスしたら「Hello World!」と出力されているはずです。
まとめ
おつかれさまでした。とはいっても今回は本当に初歩的なことをしているだけですけどね。次からはVue.jsやTypeScriptの構文についてのポストをしていきたいと思います。
LaravelのValidationを使ってみる
5月から参画したチームがLaravelを使っていまして、ちょいちょい不具合修正のチケットなんかを担当し始めた。しかし、Laravelの機能がまだあんまりわかってないもので既存のコードを読むのに時間がかかってしまっています。ということで公式ドキュメントを読んでいろんな機能を使ってみようと思います。今回はその第一弾、Validation機能を使っていきます。
下準備
まずは下準備です。
以下のコマンドでコントローラーを追加します。
php artisan make:controller ValidationController
コントローラーにview表示用のメソッドを追加します。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class ValidationController extends Controller { // 追加 public function index() { return view('validation'); } // 追加 public function validation(Request $request) { echo "name: $request->name<br>"; echo "email: $request->email<br>"; } }
以下のコマンドでviewを追加
touch resources/views/validation.blade.php
resources/views/validation.blade.phpの内容は以下のようにします。
<form method="POST" action="/validation"> @csrf <label>Name</label> <input id="name" name="name" type="text"> <br> <label>Email</label> <input id="email-address" name="email" type="email"> <button type="submit">Submit</button> </form>
最後にweb.phpにValidationController::index()とValidationController::validation()へのルーティング設定を追加します。
<?php use App\Http\Controllers\ValidationController; use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); // 追加 Route::get('/validation', [ValidationController::class, 'index']); Route::post('/validation', [ValidationController::class, 'validation']);
これでhttp://localhost:8000/validationにブラウザからアクセスするとこんな感じのシンプルなフォームが出来上がります。
そしてNameとEmailに適当な文字列を入れてSubmitを押すと以下のような出力がされるはずです。
name: test name email: test@example.com
これで下準備が完了です。
基本的なValidationの使い方
それではValidationの機能を使っていきます。
まずはValidationControllerのvalidationメソッドを以下のように更新します。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class ValidationController extends Controller { public function index() { return view('validation'); } // 更新 public function validation(Request $request) { $request->validate([ 'name' => 'required|max:10', 'email' => 'required|max:20' ]); echo "name: $request->name<br>"; echo "email: $request->email<br>"; } }
Requestのvalidateメソッドが公式ドキュメントで一番最初に紹介されるValidation機能です。今回のコードはリクエストのnameとemail両方を必須項目として、nameを最大10文字、emailを最大20文字の制限をつけています。
ただ、実行していただければわかるかと思いますが、validationでエラーになるデータをPOSTしてもFormの画面に戻されるだけでエラーなどが表示されませんので、エラーメッセージの表示機能を追加してみます。
<form method="POST" action="/validation"> @csrf <label>Name</label> <input id="name" name="name" type="text"> <br> <label>Email</label> <input id="email-address" name="email" type="email"> <button type="submit">Submit</button> </form> {{-- 追加 --}} @if ($errors->any()) <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif
validationがエラーになると自動的に元の画面にリダイレクトされます。$errorsという変数にエラーメッセージが格納されているため、上記のように取り出して画面上に表示することができます。
ためしにNameとEmailをどちらも入力せずにSubmitを押してみると以下のようなエラーメッセージが表示されます。
The name field is required. The email field is required.
どちらの項目も必須項目に設定してあるためfield is requiredのメッセージが出ます。
次はNameを11文字以上、Emailを21文字以上にしてみましょう。
The name must not be greater than 10 characters. The email must not be greater than 20 characters
今度はmust not be greater thanのメッセージが出ました。意図した通りにValidationが効いていますね。
ちなみに今回のような規定のvalidationを使用している場合、resources/lang/en/validation.phpでエラーメッセージを編集することができます。定義されている連想配列の中からrequired、maxとそれぞれのキーを探して値として設定されているエラーメッセージを編集してください。
※上記のファイルは英語用のファイルとして定義されています。日本語用のファイルを設定したい方は以下を参考にしてください。
laravel.com
Form Requestを使ったValidation
次はForm Request機能を使ったValidationを試してみます。
Form Requestはartisanコマンドで作ることができます。
php artisan make:request ValidationRequest
app/Http/Requests/ValidationRequest.phpを以下のように書き換えます。
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ValidationRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // false -> true return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { // 更新 return [ 'name' => 'required|max:15', 'email' => 'required|max:25' ]; } }
Validationの内容が一緒だと変化したかわかりずらいのでそれぞれmaxを15と25に設定してみました。これでコントローラーのValidationが不要になったので削除します。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class ValidationController extends Controller { public function index() { return view('validation'); } // 更新 public function validation(ValidationRequest $request) { $request->validated(); echo "name: $request->name<br>"; echo "email: $request->email<br>"; } }
$request->validated();
のでForm Requestで設定したValidationを呼び出しています。早速使ってみましょう。
The name must not be greater than 15 characters. The email must not be greater than 25 characters.
それぞれ15文字と25文字の制限になりましたね。これでForm RequestのValidationを実装できました。
ちなみにForm Requestではエラーメッセージのカスタムもできます。やってみましょう
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ValidationRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required|max:15', 'email' => 'required|max:25' ]; } // 追加 public function messages() { return [ 'name.required' => 'Name is required.', 'name.max' => 'Name must be less than 15 characters.', 'email.required' => 'Email is required.', 'email.max' => 'Email must be less than 25 characters.', } } }
messages()メソッドはFormRequestクラスに定義されており、それをオーバーライドする形で定義します。requestのキーとValidationのルール名をドットで繋ぐ記法を使っています。上記のコードでそれぞれを未入力のままSubmitすると
Name is required. Email is required.
ちゃんと反映されてますね。
Nameを16文字以上、Emailを26文字以上入力すると
Name must be less than 15 characters. Email must be less than 25 characters.
こちらもちゃんと反映されてますね。
それとattributes()メソッドをオーバーライドすることで汎用的なエラーメッセージを作ることもできます。
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ValidationRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required|max:15', 'email' => 'required|max:25' ]; } // 追加 public function attributes() { return [ 'name' => 'Name', 'email' => 'Email' ]; } public function messages() { return [ '*.required' => ':attribute is required.', 'name.max' => 'Name must be less than 15 characters.', 'email.max' => 'Email must be less than 25 characters.', ]; } }
このようにすることで:attribute
の部分がnameのエラーではName、EmailのエラーではEmailという表示をしてくれるようになります。
Validationのリダイレクト先の指定
Validationでエラーになった場合に元の画面ではなく、特定の指定した画面にリダイレクトさせたい場合があると思います。その場合はValidator::make()
メソッドを使うことができます。
<?php namespace App\Http\Controllers; use App\Http\Requests\ValidationRequest; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; class ValidationController extends Controller { public function index() { return view('validation'); } public function validation(Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required|max:15', 'email' => 'required|max:25' ]); if($validator->fails()) { return redirect('/error'); } echo "name: $request->name<br>"; echo "email: $request->email<br>"; } }
fails()
メソッドはValiationでエラーが発生した際に、trueを返します。なのでエラーが発生したら/error
にリダイレクトするという処理になります。
リダイレクト先が必要になるので以下のようにルーティングを追加してください。
<?php use App\Http\Controllers\ValidationController; use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); Route::get('/validation', [ValidationController::class, 'index']); Route::post('/validation', [ValidationController::class, 'validation']); // 追加 Route::get('/error', function() { return 'ERROR.'; });
これでValidationでエラーが出るデータを入力してsubmitすると
ERROR.
と表示されるはずです。
まとめ
ということでValidation機能を使ってみました。今回はrequiredとmaxしかしようしていませんが、組み込みのValidationルールは数多くあるので是非公式ドキュメントを見て使ってみてください。
サービスコンテナ入門(Laravel)
5月から配属されたチームがLaravelを使ったチームなので、ちゃんと勉強しとこうと思ってLaravelのドキュメントを読み始めました。Laravelを使う上で避けて通れないのが「サービスコンテナ」ということで、本ポストを学んだことの事項とします。
サービスコンテナとは
公式ドキュメントでは「サービスコンテナはクラスの依存関係を管理し依存性の注入(Dependency Injection)を実行するための強力なツールです。」と記載があります。
依存性の注入(Dependency Injection)ってなに?なんのためにあるのっていう方にはこちらの記事がおすすめです。
それでは早速Laravelのコードを書いて学んでいきましょう。
準備
まずはLaravelプロジェクトを作ります。
composer create-project laravel/laravel service-container-example
※最新の公式ドキュメントではLaravel Sailの利用が推奨されているんですが、今開発用に使っているM1 MacBook Airだとエラーが解決できなかったのでcompoerを使ってます。
プロジェクトディレクトリに入ってPHP用のテストサーバを起動します。
cd service-container-example php artisan serve
ブラウザからhttp://localhost:8000にアクセスしてLaravelのWelcomeページが表示されれば準備完了です。
サービスコンテナの使い方
サービスコンテナを使うためのAPIを作っていきます。まずはコントローラーからです。プロジェクトルートディレクトリで以下のコマンドを実行します。
php artisan make:controller HelloController
./app/Http/Controllers/HelloController.phpができるので、indexメソッドを作成します。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class HelloController extends Controller { // 追加 public function index() { return "Hello."; } }
./routes/web.phpにHelloController@index用のルーティングを設定します。
<?php use App\Http\Controllers\HelloController; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); }); // 追加 Route::get('/hello', [HelloController::class, "index"]);
これでhttp://localhost:8000/helloにアクセスしたら「Hello.」と表示されます。
それではこの機能を単純なサービスコンテナを使って実装していきます。
./app/Models/HelloMaker.phpを以下の内容で作成します。
<?php namespace App\Models; class HelloMaker { public function text() { return "Hello from Hello Maker."; } }
以下のコマンドを実行してHelloMakerProvider.phpを作成します。
php artisan make:provider HelloMakerProvider
./app/Providers/HelloMakerProvider.phpが作成されるのでregisterメソッドにサービスコンテナを利用するための処理を追加します。
<?php namespace App\Providers; use App\Models\HelloMaker; use Illuminate\Support\ServiceProvider; class HelloMakerProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { // 追加 $this->app->bind(HelloMaker::class, function() { return new HelloMaker(); }); } /** * Bootstrap services. * * @return void */ public function boot() { // } }
そして./config/app.phpで定義されている連想配列のprovidersの項目に「App\Providers\HelloMakerProvider::class」を追加します。
ファイルは結構大きいので全ては載せませんが追加後のprovidersは私の環境では以下のようになっています。
'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, /* * Package Service Providers... */ /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, // 追加 App\Providers\HelloMakerProvider::class, ],
最後にHelloControllerの内容を以下のように編集します。
<?php namespace App\Http\Controllers; use App\Models\HelloMaker; use Illuminate\Http\Request; class HelloController extends Controller { private $hello_maker; // 追加 public function __construct(HelloMaker $hello_maker) { $this->hello_maker = $hello_maker; } public function index() { return $this->hello_maker->text(); } }
HelloMakerProvider.phpのregisterメソッドの処理を記載することによって、HelloControllerのコンストラクタでHelloMakerが呼ばれたときに自動的に新しいインスタンスが生成されるようになります。
これでhttp://localhost:8000/helloにアクセスしたときに「Hello from Hello Maker.」と表示されます。
ただこれはサービスコンテナを使ってはいますがHelloMakerがそのまま出てきているので依存性の注入の利点が活かせていません。ということで依存性の注入を活かすためにインターフェースを利用したバージョンにしてみましょう。
インターフェースを使ったバージョン
まずは./app/Models/TextMaker.phpをinterfaceとして定義します。
<?php namespace App\Models; interface TextMaker { public function text(); }
HelloMakerをTextMakerの実装にします。
<?php namespace App\Models; // implements TextMakerを追加 class HelloMaker implements TextMaker { public function text() { return "Hello from Hello Maker."; } }
./app/Providers/HelloMakerProvider.phpを以下のように編集します。
<?php namespace App\Providers; use App\Models\HelloMaker; use App\Models\TextMaker; use Illuminate\Support\ServiceProvider; class HelloMakerProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { // 変更 $this->app->bind(TextMaker::class, HelloMaker::class); } /** * Bootstrap services. * * @return void */ public function boot() { // } }
./app/Http/Controllers/HelloController.phpの内容を以下に編集します。
<?php namespace App\Http\Controllers; use App\Models\TextMaker; class HelloController extends Controller { // $hello_maker から$text_makerに変更 private $text_maker; public function __construct(TextMaker $text_maker) { $this->text_maker = $text_maker; } public function index() { return $this->text_maker->text(); } }
これでインターフェースを使ったバージョンの実装ができました。動作は変わっていませんが、HelloControllerではTextMakerしか参照していないのにhttp://localhost:8000/helloにアクセスすると「Hello from Hello Maker.」と表示されるはずです。
ついでにインターフェースの実装を呼び分けるパターンも試してみましょう
実装を呼び分けるパターン
以下のコマンドで./app/Http/Controllers/GoodbyeController.phpを作成します。
php artisan make:controller GoodbyeController
内容は以下のようにします。
<?php namespace App\Http\Controllers; use App\Models\TextMaker; use Illuminate\Http\Request; class GoodbyeController extends Controller { private $text_maker; public function __construct(TextMaker $text_maker) { $this->text_maker = $text_maker; } public function index() { return $this->text_maker->text(); } }
./routes/web.phpにGoodbyeController用のルーティング設定を追加します。
use App\Http\Controllers\GoodbyeController; use App\Http\Controllers\HelloController; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); }); Route::get('/hello', [HelloController::class, "index"]); // 追加 Route::get('/goodbye', [GoodbyeController::class, "index"]);
./app/Models/GoodbyeMaker.phpを作成し、内容を以下のようにします。
<?php namespace App\Models; class GoodbyeMaker implements TextMaker { public function text() { return "Goodbye from Goodbye Maker."; } }
以下のコマンドで./app/Providers/TextMakerProvider.phpを作成します。
php artisan make:provider TextMakerProvider
内容を以下のようにします。
<?php namespace App\Providers; use App\Http\Controllers\GoodbyeController; use App\Http\Controllers\HelloController; use App\Models\GoodbyeMaker; use App\Models\HelloMaker; use App\Models\TextMaker; use Illuminate\Support\ServiceProvider; class TextMakerProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->app->when(HelloController::class) ->needs(TextMaker::class) ->give(HelloMaker::class); $this->app->when(GoodbyeController::class) ->needs(TextMaker::class) ->give(GoodbyeMaker::class); } /** * Bootstrap services. * * @return void */ public function boot() { // } }
./app/Providers/HelloMakerProvider.phpは不要になるので削除します。
最後に./config/app.phpのprovidersからApp\Providers\HelloMakerProvider::classをApp\Providers\TextMakerProvider::classを変更します。
これでhttp://localhost:8000/helloにアクセスすると「Hello from Hello Maker.」と表示され、http://localhost:8000/goodbyeにアクセスすると「Goodbye from Goodbye Maker.」と表示されます。