「プロフェッショナルWebプログラミング Laravel」を読んだ #Laravel

現職だと Web アプリのフレームワークCakePHP を利用してるんだけど、Laravel もう少し知っておきたいな〜、と思って読んでみた。

感想

本は実際に手を動かしながら基本的な機能を触っていく感じで、 Laravel の雰囲気を掴むのにいい感じ。

カバー範囲は広く、 テストや CI 、 Heroku を使ったデプロイまで言及されている。

Laravel はフルスタックフレームワークという感じで、一般的な Web アプリで必要になりそうな機能がかなり簡単に使えるようになってる。

メール送信や Queue を使った非同期処理だったり、スケジューラーだったり。

Docker での開発も sail ですぐ始められる。

テストもユニットテストやフィーチャーテスト、Laravel Dusk でのブラウザテストだったり。

その一方で使い手にとって簡単がゆえに、裏の仕組みは意図的に知ろうとする必要があるのと、そこに踏み込んだときの学習コストはそれなりにかかるかもしれない。

深堀りするというよりは、サクッと全体像を把握したり、取っ掛かりを作っておくのに良い本だなと思いました。

ハマったところ

前提として、手を動かす時は本のバージョン無視してとりあえず最新入れるようにしているので、場合によっては動かなかったりする。

これは自分が悪いんだけど、その上でつまずいたところを備忘録としてメモ。

Laravel Dusk でブラウザテスト

これは調べていた感じ、バージョンと言うよりM1 のせいかな?という雰囲気だったけど、ほどほどで諦めたので定かではない。

フロントエンドの開発

書籍では Laravel Mix を使う前提になっているが、 Laravel Breeze は最新だと Vite になっている。

いくつかエラーを踏んだり動かなくなったりしたけど、原因さえ分かっていると、エラー内容を愚直に対応していけばそれほど苦労はしなかったと思う。

書籍に合わせたかったので、 Mix を使うように修正していくことで解決した。

CakePHP の BelongsToMany でdependent は default が true になっている #CakePHP

アソシエーションの dependant

CakePHP のモデルのアソシエーションには dependant というキーが用意されており、 true に設定することで削除のときに関連付けたモデルのレコードもまとめて削除することができる。

book.cakephp.org

belongsToMany だけデフォルト true

ところが hasOne など他のアソシエーションはデフォルトが false になっているが、 belongsToMany はデフォルトが true になっている。

なので、明示的に設定しなくても関連したモデルのレコードが消されてしまう。

意図しない削除を避けたり他のアソシエーションと一貫性をもたせるたりするためにも、デフォルトは false の方がいいと思って違和感がある。

互換性が理由だった

そこで Cake の実装を見てみると、互換性のために true にしているとコメントがしてあった。

cakephp/BelongsToMany.php at 4.4.6 · cakephp/cakephp · GitHub

過去の PR や Issue を追っかけてみたところ、どうやら関連レコードをどうやっても消してしまうという問題があったようだ。

消さないようにする修正を入れたものの、消すほうが現行の挙動だから、互換性のために default を true にしているというのが経緯らしい。

修正自体はかなり前なので、 CakePHP を長く触っている人なら常識なのかもしれない。

github.com

FakerPHP/Faker で \Faker\Generator::image が使えない #php #laravel

概要

Laravel の Factory で画像のテストデータを作ろうとして $this->faker->image(storage_path('app/public/images')) とかやっても、画像ファイルがうまく生成されず、画像ファイル名も 0 とかで生成されてしまう。

v1.20.0 で非推奨になっており、 Faker2 では削除されるようだ。

カスタムプロバイダーを使うように変更して対応する必要がありそう。

false が返ってきている

tinker を使って試してみると、 false が返ってきている。

ついでに試してみた imageUrl はうまくいっているように見える。

$ sail tinker
Psy Shell v0.11.5 (PHP 8.1.9 — cli) by Justin Hileman
>>> $faker = \Illuminate\Container\Container::getInstance()->make(\Faker\Generator::class);
=> Faker\Generator {#3653}

>>> $faker->image(storage_path('app/public/images'));
=> false

>>> $faker->imageUrl();
=> "https://via.placeholder.com/640x480.png/00aa44?text=quo"

placeholder.com によるブロック?

もう少し追いかけてみると、テスト画像の生成は \Faker\Provider\Imageplaceholder.com を経由して行われているようなのだが、レスポンスステータスが 403 になっていた。

Stackoverflow で placeholder.com が何か変更したか、 IP ブロックをしているみたいな話を見つけた。

"this is popular problem" って言ってるけど、他に類似の情報は見つけられなかった。(単なる自分の調査力不足かもしれない)

stackoverflow.com

Faker\Provider\Image が非推奨になっていた

FakerPHP/Faker は v1.19.0 を利用していたのだが、v1.20.0 で非推奨になっていたようだ。

Marked the Faker\Provider\Image as deprecated

github.com

どうも外部サービスに依存するのってどうなのよ?不安定になっちゃわない?っていう理由らしい。(それっぽい議論をしている Issue や PR をいくつか見つけた)

どう対応するか

Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead

非推奨のメッセージによれば、やりたいならカスタムプロバイダーを用意する必要がありそうだ。

生成元を placehold.jp に差し替えるっていうネタも見つけたんだが、さすがにちょっとこれはやんちゃ過ぎるかなと。(一応それっぽくは動いた)

stackoverflow.com

前述の Stackoverflow で紹介されていた smknstd/fakerphp-picsum-images も試してみたんだが、こちらも 403 になってしまうようだった。(imageUrl はこちらも大丈夫)

github.com

人間をリソースと呼ばない方がいいと思う

社内に投下したポエムを一部修正してリポスト。

「リソース」っていう表現が嫌い

表題の通りなんですが、僕は人間をリソースと呼ばないほうがいいと思っていて、何ならとても嫌いな表現なんです。

文脈上使わざるを得ないときもありますが。

よく聞くような使われ方だと、「開発リソースが足りない」とかですね。

言葉狩りか?と言われるとそうかもしれないですが、日常的に使う言葉というのは結構大事だと思っていて、連想される思考になっていく(あるいはもうなっている)と考えているからです。

「代替可能」を連想させる

じゃあどういうことを連想するかというと、リソースという言葉は「代替可能な資源」をイメージしやすいと思うんです。

そうすると、表面上のスキルとかスペックで人間を捉えて、例えば組織編成とか要員計画だとか、なんだかパズルみたいに組み合わせるとうまくいくような気がしちゃうんですよね。

エモい話ってわけじゃない

あぁ、別に人間に寄り添うとかそういうエモい話をしているつもりはなくて。 人間はもっと めんどくさい 複雑で、目に見えない変数がたくさんあると思うよって話です。

たぶん採用のオファー面談で、「リソースが足りないんです!」ていう話をしたら最悪の口説き文句だと思います。
(これに共感できない人は多分価値観が根本から違うと思うんで、そういう人もいるんだな、くらいに受け取ってください)

人が違うと別の組織になる

自分はよくサッカーをメタファーにするんですが、たとえばある選手が移籍したチームはどうなるでしょうか。

同じポジションで、似たようなプレースタイルの選手は獲得するかもしれません。あるいは、若手を抜擢したりするかも。

でも絶対に同じようにはできない。

戦術、フォーメーション、他の選手とのコンビネーションを見直し、更には試合ごとに手直しを加えながら「新しいチーム」を作っていくと思います。

おしまい

だから僕としては、人間に「リソース」っていう表現は使わないで、できるだけ具体的な名前を使って、顔を思い浮かべながら話したほうがいいんじゃないかなって思ってます。

おまけ

似たような観点の記事でおすすめを教えてもらったので置いておきます。

www.benlinders.com

phpunit-snapshot-assertions にコントリビュートした #php

スナップショットテストをやりたいなと思って spatie/phpunit-snapshot-assertions を導入してみた。

github.com

ところが PHPUnit でデータプロバイダをつかったパラメータライズドテストをやっていると、どうもうまくいかない。

原因は、データセット名に日本語を含んでいるとスナップショットファイル名の生成に失敗していたからだった。

当該処理の修正箇所も割とすぐ特定できたので、プルリク投げたらサクッととマージしてもらえて、リリースされた 🎉

小さな修正でも貢献できるとうれしい。

github.com

Laravel Sail 環境を PHPStorm でデバッグする #Laravel #PHPStorm

前提

  • Laravel v9.17.0
  • Sail v1.14.10

環境変数を定義

.envxdebug を有効化し、設定を変更する

SAIL_XDEBUG_MODE=develop,debug
SAIL_XDEBUG_CONFIG="client_host=host.docker.internal idekey=PHPSTORM start_with_request=yes"

ブラウザ拡張を使う方法なんかもあるが、 API とかだと必ずしもブラウザ経由とは限らないので、 start_with_request を有効にしておく。

起動する

アプリケーションを起動する

sail up -d

適当なところにブレークポイントを貼る。

デバッグコネクションの Listen を開始する。

アクセスする

ブレークポイントを通るようなリクエストを送る。

ダイアログが出るので、取り敢えず Accept しておく。

Server を設定する

PHP > Servers0.0.0.0 とかで追加されているので、 path mappings を設定する。

Project files を /var/www/html/ にしておけば OK のはず。

他の項目もお好みで。

おわり

設定が終わったらもう一度アクセスしてみて、ブレークポイントで止まることを確認する。

参考

Laravel の補完を PHPStorm で使いやすくする #Laravel #PHPStorm

概要

Laravel のモデルでクエリを組み立てようとすると、 Method 'where' not found と言われてしまう。

このせいで PHPStorm の補完が効かなかったり、警告が出てしまったりする。

<?php
// where のとこで警告が出る(動作は問題ない)
MyModel::where('name', $name)->first();

// query() をかませると平気なんだけれども、面倒
MyModel::query()->where('name', $name)->firstOrFail();

laravel-ide-helper を導入する

laravel-ide-helper で解決できそうなので導入してみる。

IDE補完用のコードを生成してくれるようだ。

github.com

README に従って入れてくだけ。

sail composer require --dev barryvdh/laravel-ide-helper

モデル以外にも役立ちそうなので、全部入れてみる。

sail php artisan ide-helper:generate
sail php artisan ide-helper:models
sail php artisan ide-helper:meta

直接プロダクトコードを上書きする方法と、別ファイルに切り出す方式が選べる。

あくまでヘルパーなので、別ファイルにしてみる。

Do you want to overwrite the existing model files? Choose no to write to _ide_helper_models.php instead (yes/no) [no]:
 > no

方針次第だと思うけど、対象のファイルを .gitignore に入れておくとよさそう。

別の警告が。。

無事補完はできるようになったのだが、別の警告が出るようになってしまった。

ファイルを別にする方式の弊害っぽいが、 Multiple definitions exist for class .. と言われてしまう。

色々調べてみたけれども、対象の警告をオフることにした。(しかし敗北感が。。)

参考