Laravelのフォームリクエストのジレンマ

Laravelにはフォームリクエストという便利な機能がある。コントローラに適切なフォームリクエストをインジェクションさせると、リクエストがフォームリクエスト内のルールでバリデーションされた上でインジェクションされる。

<?php

namespace App\Http\Requests;

class UserRequest extends \Illuminate\Foundation\Http\FormRequest
{
    public function rules()
    {
        return [
            'name'  => ['string'],
            'email' => ['email'],
        ];
    }
}
<?php

namespace App\Http\Controllers;

class UserController extends Controller
{
    public function store(\App\Http\Requests\UserRequest $request)
    {
        dd($request->validated()); # バリデーション済みのデータが取得できる
    }
}

上記のコードは、コントローラ側で以下のように実装しても同等だ。

<?php

namespace App\Http\Controllers;

class UserControllerWithValidation extends Controller
{
    public function store(\Illuminate\Http\Request $request)
    {
        dd($request->validate([
            'name'  => ['string'],
            'email' => ['email'],
        ]));
    }
}

フォームリクエストを使わない方がコードは短くなる。ではなぜフォームリクエストを使うのか? それはフォームリクエストを使うことでコントローラとバリデーションの双方向の依存を、コントローラからバリデーションだけの一方向の依存にできるからだ。

ではすべてのバリデーションをフォームリクエストで行おう、となりそうだが、じつはフォームリクエストは以下のようなルールで破綻する。

<?php

namespace App\Http\Controllers;

class UserControllerWithValidation extends Controller
{
    public function update(\Illuminate\Http\Request $request, \App\User $user)
    {
        dd($request->validate([
            'name'  => ['string'],
            'email' => ['email', \Illumination\Validation\Rule::unique('users')->ignore($user->id)],
        ]));
    }
}

バリデーションルールがルートと関連したモデルに依存してしまっている。このような、よくある、ちょっと複雑な例にフォームリクエストを使うことは意味がない。

フォームリクエストの利点はコントローラに依存しなくなることだが、ユーザモデルのインスタンスを取得するためにはコントローラに依存するか、もっと酷いことにルートに直接依存することになる。

フォームリクエストは Illuminate\Http\Requests を拡張していて、同様に routeメソッド経由でルートにアクセスでき、そこからユーザモデルのインスタンスを取得できるが、コントローラ側でDIで間接的にルートから取得する方がまだマシだ。

ということで、フォームリクエストはコントローラへの依存を切り離せるので便利だが、依存を切り離すとコントローラに依存しないと用意できない複雑なルールに対応できなくなるので、じっさいにはほとんど使い所はない。

<?php

namespace App\Http\Requests;

class UserRequest extends \Illuminate\Foundation\Http\FormRequest
{
    public function rules()
    {
        $user = $this->route()->parameter('user'); # 持って来ること自体はできるのだが

        return [
            'name'  => ['string'],
            'email' => ['email', \Illumination\Validation\Rule::unique('users')->ignore($user->id)],
        ];
    }
}

バリデーションルールは、素直にコントローラ内なりに書くのがよさそうだ。

LaravelでRefreshDatabaseしているテストをどう安全に運用するか

課題

RefreshDatabaseはテストごとにデータベースをクリアしてくれる便利なトレイトで、データベースを使ったテストを書くときにはほぼ必須だが、運用をミスると消したくないデータベースを消してしまうことがある。

具体的に危険なのは、.env.testingがない場合に.envが使われることにより、.envい記載されたテスト用以外のデータベースにつないでしまい、クリアしてしまうこと。

Laravel 6.8.0以降ではphpunit.xmlSQLiteのインメモリデータベースに接続するように設定されるようになっている(参考)ため、phpunit.xmlを変更していないのであればこの問題は避けられる。

(が、LaravelはRDBMS間の差異を十分に吸収しないため、テストにSQLiteを使うのは実用的ではなく、大抵の場合はこの部分の設定を消して、.env.testingに書くことになるだろう)

解決策

本番環境では、まずそもそもPHPUnitが入っていないはずなのであまり問題ないはず。

問題は開発環境だ。.envは用意せず、.env.localを用意して、アプリケーション動作時にはAPP_ENV=localだけ別途指定する形にすれば、間違って.env.testingがない状態で自動テストを叩くようなことがあっても安全そうだ。が、まだ十分には試していない。

すべてのテストをデータベースの状態に依存させずに書ければそれがベストだと思うけど、なかなか難しいよね。

その他

RefreshDatabaseはmigrate:freshコマンドを実行することでデータベースをクリアしている。このコマンドはproduction下では実行前に確認プロンプトを出してくれるのだが、そもそもテストがproductionで実行されることは普通ない。

連想配列への代入は一括でやった方がいいと思う

2020-03-26追記

もっと単純に、一つの操作を複数に分けるな、って話でよかったね。


<?php

$assoc1['key1'] = 'val1';
$assoc1['key2'] = 'val2';
$assoc1['key3'] = 'val3';

$assoc2 = [
    'key1' => 'val1',
    'key2' => 'val2',
    'key3' => 'val3',
];

上記のような場合に、$assoc1の方法より$assoc2の方法がいいと思う。まあ昔からずっと思っていたのだが最近$assoc1の方法で書かれたコードを読んで書き直してとすることがあって再度思って、でもそれはなぜかと聞かれても即答できなそうだったのでちょっと考えた。

まあ単純な話で、

  1. $assoc1の場合は、変数が3回出現しており、その分変数名に間違えがないか(1つ$assoclがあったらどうする)、追加で確認しなければならない。
  2. $assoc1の場合は、代入演算子も3回出現しており、その分よく見ると別の演算子(+=とか)だったりしないか、追加で確認しなければならない。

ということで、あらゆる繰り返しと同様、同じことなのに別々に書くと別々に確認しなければならないので、まとめた方がいいというそれだけの話。もちろん確認だけではなく、変更のときもその分変更が増えるよね。

これで今後$assoc1の書き方をしている人がいたら、ちゃんと根拠を持って注意できる。

ちなみにあまりに当たり前で普通のことだからか、リーダブルコードにもCode Completeにもこの辺に関する説明はなかったと思う。

通勤定期というものを初めて買った話

12月は常駐案件に入っていた。ずっとフルリモートで働いていて、通勤は初めて。そこそこ遠くて交通費もけっこうかかるので、定期を買ってみた。その際なにもわからなかったのでみどりの窓口の人にいろいろ聞いてみたのだが、ちょっとググっただけじゃわからないような情報があったので共有。

(少なくともJR西の)通勤定期は、

  • 区間内ならどこから乗ってどこで下りてもOK.
  • 一日一往復に限らず何度でも乗れる。
  • 休日も使っていいしそもそも通勤に限らない。

という感じで、かなり自由に使っていいらしい。「通勤に限らない」あたりは調べたときにそれっぽい感じなのはわかっていたが、公式サイトには少なくともわかりやすい形では出ていない。まあ禁止されてない=使えると判断しろってことなんだろうか。

知ってる人にはなにを今更なんだろうが、知らないとなかなか調べてもわからないので、もうちょっと周知してもいいんじゃないかな。

金額的にも、一ヶ月で買っても15往復くらいで元が取れる感じ(区間によりそう)だし、なかなかお得感あった。

2019年読んだ本

2019年は割と技術書が読めた。といっても9冊。技術書以外のフィクション以外の本が40冊。フィクションが、小説(ラノベ)80冊ちょいに漫画500冊ちょい。技術書と小説は割と読めたけどほかが減ってた感じっぽい。

今年はそこまで「当たり」って本が少なかったような気がする。今読んだ本眺めて、人におすすめしたいの何冊あるかな。

「交雑する人類」、「病の皇帝「がん」に挑む」、「睡眠こそ最強の解決策である」、「1%の富裕層のお金でみんなが幸せになる方法 実現可能な保証所得が社会を変える」あたりはまあ誰にでも文句なしでおすすめかな。

個人的な趣味に合ったのは「血と汗とピクセル」、「ラディカルズ」、「天才たちの日課」、「若い読者のための経済学史」。

技術書では再読だけど何度読んでもいい「ソフトウェア見積り」、読んでもそこまで面白くないけど学ぶべき「テスト駆動開発入門」あたりか。

それなりにいい本読んでるじゃん。

漫画では新規にブルーピリオド読み始めてはまってる。

Laravel Deployerで変な落ち方してロックが残ってしまった場合の対処法

最近とても忙しい。つらい。


とっても便利なDeployerをLaravel向けにさらに手軽にしたLaravel Deployerですが、この間こんなメッセージが出た。

Deploy locked.
Execute "dep deploy:unlock " to unlock.

書いてある通り、ロックされちゃったっぽい。

通信環境の問題で変な落ち方したのでそれが原因っぽいが、指示通り。./vendor/bin/dep deploy:unlockと叩いても動かない。

勘を働かせて、php artisan deploy:run deploy:unlockとしたら動いた。

銭けっと行ってきたので雑な感想を

(多分)第3回の銭けっと行ってきた。IT系の技術書に限らない、実業実学書の即売会とのこと。

銭けっと公式WEBサイト - ホーム

11時半くらいに着いたがまだ人は少なく、じっくり見て周れて楽しかった。以下覚えている限りの感想。

リンクは銭けっと公式ページの参加サークル一覧から。リンクや内容に間違え等あったら教えてください。

つくろか実行委員会

「技術書評」という(プログラミング関係に限らない)技術書の短い書評が載った小冊子を頒布されていた。とりあえず「UNIXという考え方」の書評があったので購入。自分のまったく知らない分野の書評はなかなか読む機会もないので楽しい。こういうのでもっとボリュームある本とかも欲しいな。

秘密結社J.O.J.I.

「作ろっ! デジタルノギス」というデジタルノギスの作り方を詳しく解説している小冊子を頒布されていた。こっち系はさっぱりだが、こうやって詳しく解説されているとその気になれば作れちゃう気もする。電子工作的なの昔は憧れもあったんだし、時間があれば手を出してみたい。

じっさいに完成品も展示されていた。ガワの部分けっこうしっかりして見えたが、あれ3Dプリンタ製だったんだな……。

Atelier ωRabbit

「綿を育てて布を作る」という綿花を栽培する話や、栽培した綿花から綿糸を作る話の本、あとバイクのリストアの本を頒布されていた。

じっさいにワタや綿花の種やらが置いてあったり、スピンドルで糸を紡ぐ体験ができるようになっていた展示がもうすごくて。展示でいちばん興味深かったのがここ。

本を買うと綿花の実と、簡単な栽培方法が載ってる小冊子もおまけについてきた。これ読んでると案外簡単そうだし育ててみたくなってくる。

LODハッカソン関西

「SPARQLでオープンデータ検索!」というSPARQLというオープンデータの検索などに使われているちょっとSQLっぽいクエリ言語の本を頒布されていた。

ここでようやくプログラミング系の本。興味ある分野だし普通によさそうな本なので購入。

silverbirder

「はじめてのWeb Components入門」という、まあ名前の通りの本を頒布されていた。

これは普通に行く前から狙っていた本でもあるし購入。サークル主さんがWeb関係のサークルがあまりなくて肩身が狭い的なことを冗談混じりに話していた。確かにねー。

エイトプラス

「教えて!フリーランスのマネーな話」という本を頒布されていた。グラフィックデザイナー視点の話とのこと。

フリーランスの人がフリーランスの生活を語る系の本はもうそれなりに読んでるんだが、飽きずに買ってしまった。同業者の話はやっぱり気になるよね。

親方Project

委託のものも含めると10種類弱くらい、さらにどれもけっこう厚めの本が頒布されていた。

いろいろ気になるものあったんだけど、予算の都合上、見積りの本と勉強会の本だけ購入。なんかおまけでいろいろつけてもらった。

見積りはかなり気になっている分野なのでとりあえず。勉強会の本は、それこそこんな本なかなかないので。

サークル主の方がほかのサークル見に行っていて留守だったのがウケた。

バラエティエンジニアイカタコグループ

Elmの本を頒布されていた。Slackのメンションに関する本も予定していたが落とした、らしい。同人誌即売会っぽい。

これも元々目当てだった本。紙のサンプルはあるけどダウンロードカードのみだった。まだダウンロードしてない。しないと。

その他

ほかにも謎のロボットを展示してるサークルさんとか、工場のプラント設計の話の本を頒布しているサークルさんとか、いろいろ気になるものだらけだった。

読むこと自体が厳しそうな本は今回は購入を遠慮したが、もうちょっと冒険してもよかったかも。まあすでに十分に予算はオーバーしているのだが。

行く前はプログラミング関係以外の本にどれくらい興味湧くかなと思っていたが、なんだかんだでいちばん気になったの綿花の本ってあたり、……。

今回は本自体が目的ということで、あと遅いと帰るのつらいってのもあるけど、早めに行って14時半からのLTは見ずに、早めに帰ったが、もったいなかった気もする。次行くときはその辺もうちょっと検討したい。

それよりなにより、ほんとは俺も本が書きたい。12月からしばらく忙しくて、書くどころか読む時間さえ厳しそうだけど、書きたいなー。

ということで相変わらず雑な感想、以上。