条件式を書くときは、明示的にわかりやすくするよう心がける。()をつける

条件式を書くときに、条件が長くなっちゃうと、それがぱっと見どういう条件なのかわからなくなるので、カッコとかつけて、読みやすくするのを心がける。

() を付ける

以下、内容は適当だけど、()で囲んだほうが見やすい。

$user->post_id == $post->id && $user->isPost() || $user->isTask();
($user->post_id == $post->id && $user->isPost()) || $user->isTask();

初歩的過ぎるとこだけど、コードレビューしてもらって、気づけたことで今後気をつける。明示的にわかりやすいを意識して書けるようにしていきたい。() 付けるのは初歩的すぎなので他にも意識する(いろんな人のコード読みつつ、実務経験を積みつつ勉強する!)

演算子の優先度

あと、演算子の使い方で、優先順位があるのちゃんと把握してなかったので気をつける。(or より andのほうが優先度高い!)

演算子の優先順位

http://php.net/manual/ja/language.operators.precedence.php

Laravelの認可処理

最近、Laravelの認可処理を実装する際に学んだことを書きます。

やりたいこと

  • ユーザーの役割に応じて、アクセス制御できるようにしたい。例えば、Adminユーザーだったら編集/削除/登録の操作できるけど、それ以外のユーザーの場合は、参照する権限しかない、みたいなことをできるようにしたい。

  • 認可処理で設けた利用制限について、どの処理に対して、どういう認可の処理が適用されてるかを php artisan route:list の実行結果で、すぐわかるようにしたい(なのでミドルウェアによる認可が必要。コントローラーの各アクションに定義すると、どの処理にどの認可処理が適用されてるか、route:list とかでぱっと見わからない)

  • ただ、Laravel 5.1を使っていて、ミドルウェアで認可できるようにしたいんだけ、5.1では対応してないようだったので、can ミドルウェアを laravel 5.1で使えるようにしたい

やったこと

  • Laravelで用意されてる認可処理、ポリシーを使った
  • Laravel 5.1でcanミドルウェアを使えるようする。Laravel 5.3以降で、Laravelが標準で用意しているcanミドルウェアのファイル(Illuminate/Auth/Middleware/Authorize.php)を5.1環境に配置して使えるようにする
  • ルートパラメーターからミドルウェアインスタンスを渡せるようにした(モデル結合ルート)。それを元にポリシーの認可の条件を書けるようにした

Laravelで用意されてる認可処理、ポリシーを使った

Laravelでは、認可処理が2種類用意されてて、詳細はドキュメント。 https://readouble.com/laravel/5.7/ja/authorization.html

  • 1つの認可処理に名前をつけて利用の可否を決定づけるゲート(Gate)

  • 複数の認可処理を記述できるポリシー(Policy)

これ、どういう状況でゲート使って、どういう状況でポリシー使った方が良いか、ちょっとよくわからなかったんだけど、使い分けとして、特定のリソースの操作に対して、認可する場合はポリシーを使って、グローバルで認可の制限をつけたい場合は、ゲートを使うのが良さそう。今回の場合でいうと、特定のリソースに対しての編集/削除/登録の認可になるので、ポリシーを使って書いた。Gateを使って良くなそうな例として、Gateで認可の条件を書いていくとすると、AuthServiceProviderのbootメソッドに、大量に処理を書いていく可能性があり、パッとみて、どういう制御がかかっているかわかりずらくないっていく懸念がありそう。特定のリソースに対する制御の場合は、ポリシーを使って書いていった方がわかりやすくて良さそう。以下、ポリシーを使う場合のざっくりした流れ。詳しいところはドキュメントに載ってる。

(1) xxxリソースに対応したポリシーを作成する

$ php artisan make:policy xxxxPolicy

(2) ポリシーを登録する。AuthServiceProvider の$policies に作成したポリシークラスと対応するEloquentモデルを記述する

(3) 作成したポリシーに認可する条件を記述する

(4) 作成したポリシーを適用したいアクションやミドルウェアに追加する

これで、ポリシーに記述した条件を満たしていないと、アクセスできない/できる みたいなことができる。この後、ブラウザからアクセスしてみて、403とかになれば良さそう。あと、ポリシーとは関係ないけど、ゲートを定義する際の名前の付け方について、

Gateは"誰が"、"何を", "どう", "できる/出来ない" を扱うもので、"誰が"はログインユーザーが第一引数から渡されることで自明なので "何を", "どう" が大切。なので名前の規則としてV-O (SVO) の形にしましょう。

っていうのがあって、名前のつけ方とかも重要なので気をつける。例えば、Adminユーザーだったら投稿できる。みたいなことを考えてみると、(canのFacadeを利用して書いてみることを考えると)、Admin can update Post になるのでupdate-post って感じで、Gate::defineに定義する。

Laravel 5.1でcanミドルウェアを使えるようにする

やったことは、laravel 5.7の Illuminate/Auth/Middleware/Authorize.php を、そのまま5.1のlaravelで独自ミドルウェアとして定義した。

(1)5.7環境で、Kernel.phpの$routeMiddlewareで定義されてる canミドルウェアで定義されてるAuthorizeクラスを確認

(2)それを、Authorizeを、5.1環境で独自ミドルウェアとして定義する

これで、routingにcanミドルウェアを使えるようになった。

ちょっと話が逸れるかもだけど、この作業してるときちょっと悩んだことについて書くと、Authorizeクラスを持ってくるとき、最初5.3から持ってきていて、それだとうまくいかなかった。それで色々やってて、5.7から持ってくるとうまくいった。これは、5.3から5.7の間でコードが変わってたからなんだけど、どう変わってたかというと、

5.6まではcan middlewareでユーザーがログインしているかをチェックしていた。 $this->auth->authenticate() . 5.6? 5.7? でcan middlewareはguestも受け付けるようになったのでログインしているかどうかはチェックしなくなったらしい。なのでcanでログインしてるかしていないかはチェックしないと思って使えば良いので5.7のコードで問題なさそう

めちゃめちゃ勉強になった。あと、githubの使い方で、ブランチ間の差分を見る方法もを知らなかったので、勉強になった。

Authorize.php 5.3と5.7の差分

github.com

Authorize.phphistory https://github.com/laravel/framework/commits/5.7/src/Illuminate/Auth/Middleware/Authorize.php

ルートパラメーターからミドルウェアインスタンスを渡せるようにした(モデル結合ルート)

ここまでやって、canミドルウェアを使えるようになったし、ポリシーも登録できたんだけど、特定のリソースにアクセスする際の認可をポリシーで、うまく書けていなかった。(例えば、自分が投稿したリソースだったら、アクセスできる。他の人はNG。みたいな認可の処理を書きたい)もう少しいうと、ルートパラメーターのidを元にしたリソースのインスタンスを、ポリシーで定義してる認可の条件で使えるようにしたいけど、どうやってやればいいかわからなかった。結果的には、ドキュメントのLaravel 5.1 HTTPルーティングのモデル結合ルートに書いてあることを定義すると、うまく渡せるようになった。(RouteServiceProvider::bootにモデル結合を定義する)

ドキュメント https://readouble.com/laravel/5.1/ja/routing.html#implicit-binding

これでうまくいったんだけど、ここまでくるのにいろいろと悩んでて、その辺りを書くと、canミドルウェアを使って、routingにポリシーを定義する方法がわからず、めちゃめちゃ悩んだ。今振り返ると、ドキュメントにちゃんと書いてあるんだけど、正しく読めていなかった。その際にやったこととしては、Authorize.phpの処理をxdebugでとめまくって、コードの深いところまで、追いまくって、どこの条件でおかしくなってるのか調べたりした。結果的には、やはりドキュメントに書いてある通りに定義されてなかった(引数の指定の仕方がおかしいなど)のが原因だったことがわかったんだけど、定義元ジャンプしまくって、コードを深く追いまくったおかげで、実際どういう処理が書かれてるのかを知れたので、すごく勉強になったと思う。(追ってる間のコード、ちゃんと読めてないのもあるかと思うので、もう少し読み解けるようなりたい。最後のあたりで make ってやつが出てきたりしたけど、よくわかってなくて、その辺り気になってる)

f:id:shimabukuromeg:20181202143121p:plain

まとめ、次やること

  • ポリシーとゲートに違いとか、Laravelで認可の処理書くの、基本的なとこだけど少し詳しくなれたと思う。
  • コードを追っていくやつ、もう少しやってみる。個人的に、ファサードとかDIとかサービスコンテナとかいまいちよくわかってなくて、コード追うやつやりながら、ドキュメントとか読んだりすると理解できそうな気がしてる。あんまりよくわかってないけど。

Laravel学習方法

最近laravel勉強してて、学習するのに重要だなあと思ったポイント備忘録。

  • xdebug
  • Laravelのソースコードを追う
  • Laravelの公式ドキュメント読む
  • PHPのドキュメント読む
  • Laravel本読む
  • 他のlaravelプロジェクトのソースを読む

qiita.com

特にxdebugで処理を止めながら、コードを深いところまで読んでみるってのが重要な気がしてて、そのあとにドキュメントを見ると、ドキュメントがかなり読みやすい感じがありました。それに加えて、Laravel本を参考に色々手を動かしてみるとか、ほかのLaravelプロジェクトを読むとかまで手を伸ばせたら良さそう。

最近 Laravelで学んでること

最近 Laravelでお仕事させてもらっていて、その中で学んだこと、疑問に思ったこと、調べてみたこと、コードレビューで指摘受けて気づけたことなどを書きます。Laravelでどういうことをやってるときの話なのかって部分をもう少しいうと、ざっくりですが「ユーザーを招待して登録する」みたいな機能を作ってる時の話。(書いてることは、あまり関係なさそう。とりあえず、最近やってることです。)

(1)Laravelでパスワードを二回入れて確認する confirmed validation

同じ値を確認用で2回入力する際のバリーデーションについて。パスワードじゃなくても、xxxx_confirmationフィールドでチェックしてくれる。Postする際の処理をのバリデーションをフォームリクエストに定義する。(フォームリクエストで、バリデーションを作るの、なぜかよく忘れてしまう)

公式ドキュメント バリデーション 5.5 Laravel

confirmed フィールドがそのフィールド名+_confirmationフィールドと同じ値であることをバリデートします。例えば、バリデーションするフィールドがpasswordであれば、同じ値のpassword_confirmationフィールドが入力に存在していなければなりません。

class StoreRequest extends Request
{
    /**
     * 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 [
            'password' => 'required|confirmed|max:60',
        ];
    }

    /**
     * 定義済みバリデーションルールのエラーメッセージ取得
     *
     * @return array
     */
    public function messages()
    {
        return [
            'password.required' => 'パスワードの入力は必須です',
            'password.confirmed' => 'パスワードが異なります',
        ];
    }
}

www.nyamucoro.com

(2)viewのデザインのために<br> 使ったら負け。bootstrapだと mt-2 mb-2 とかのmargin増やすutilityあるのでそれ使うと良い。

getbootstrap.com

(3)既にログイン済みだったら〇〇するって処理を、guest middleware(Laravel標準で準備されてるmiddleware)を参考にかく

ログイン済みかログイン済みじゃないかの場合で、処理を分けたいとき、例えば、ユーザー登録で、既にログイン済みだったら、〇〇へリダイレクトして欲しい、という処理が書きたい。Laravel標準で、認証済みかどうか判定し、認証済みの場合は/homeにリダイレクトする、guestというミドルウェアが用意されるので、これを参考にした。独自のミドルウェアを作るときは、

こういう流れだと思っているんだけど、Http/Kernel.phpに登録する、ルーティングに登録する、あたりをちゃんと整理できていないので、ミドルウェアについては、改めて整理したい。登録したミドルウェアをどの処理に適用させるのかっていうあたりを特に整理したい。

(4)コントローラー側のコードで、POSTの処理後に、viewを返すのか、redirectするのか、ちょっと悩んでる。

例えば、「ユーザー登録して、登録完了しました」というページを表示したい場合、POSTのリクエストを処理するアクションがviewを返してくれるように書くのか、完了しました表示してくれるページを返すアクションを別途作って、そこにリダイレクトしてくれる方が良いのかっていうところで悩んだ。今回は、POSTの処理で完了しましたのviewを返すようにしたけど、こういう部分で迷った場合の判断基準がわかっていないので、調べてみる。また、ちょっと話がずれるかもしれないけど、今回この処理を最初redirectするように書いていて、その場合に、例えば、POSTの処理で持っている情報(変数)をredirect先に引き継ぐには、どうしたら良いかってところですごく悩んだりした。sessionに投入して、取り出すみたいな感じでその場合はやったけど、sessionをむやみやたらに使うのはバグの温床なので慎重にしないといけないということだった。postの処理で使ったデータをそのままviewで使いたい場合に、postアクションでviewを返すようにするって感じなのかな。

(5)Sessionにデータを突っ込むのは慎重にする

sessionにデータを突っ込むのは慎重にしないとバグの温床になるので、注意する。バグの温床になる、理由をちゃんと理解できていない気がするので、この部分をもう少し勉強する。sessionに定義される変数を追うのが難しいというか、他の人がコード書くときに、sessionに変数が定義されてることを想定してコード書かないだろうから、影響する可能性があってあまりよくない、ってことかな?それで、sessionを使う場合については、flashを使って、次回1回切りでsessionにデータを突っ込むようにするのが良い(消えるのが保証されている)

例 e$request->session()->flash('user', $user);

公式ドキュメント HTTPセッション 5.5 Laravel

フラッシュデータ

次のリクエスト間だけセッションにアイテムを保存したいことは良くあります。flashメソッドを使ってください。flashメソッドは直後のHTTPリクエストの間だけセッションにデータを保存します。それ以降は削除されます。フラッシュデータは主にステータスメッセージなど継続しない情報に便利です。

(6)ランダムな値の生成方法

ググって見つけたやつ。メモ

substr(base64_encode(md5( mt_rand() )), 0, 30);

(7)Scope

where('xxx', 'yy') みたいに書いてるのを、モデル側でメソッド書くようにして、それを呼び出すようにして、コード読みやすい感じにする

(8)バリデーション(FormRequest)

バリデーションを書く場所いつもどこだっけってなるけど、基本的にPOSTのリクエストするときは、フォームリクエストを作ってバリデーションするようにする。

(9)self::creating データ登録時に自動的にデータを登録するようにする

Railsでいうbefore_createみたいなのを作る。(コントローラー側の責務を減らす)

qiita.com

qiita.com

qiita.com

まとめ

基本的なことばかりっぽいけど、すぐ忘れちゃうので、自分の理解の整理のために書いた。もっとちゃんと理解したいやつがまだまだあるので、次は以下の内容を整理したやつ書く。

  • フォームリクエストのバリデーションの理解を整理する
  • ミドルウェアの理解を整理する
  • 認可の処理についての理解を整理する

CODEBASE βhackathon に参加した話

11/22-11/24の3日間 CODEBASEの βhackathon というイベントに参加してきました。

βhackathon とは

βhackathon とは、プロダクトの設計やワイヤーフレームなどの準備ができていて実際に動く試作品を作りたいと思ってる人と、CODEBASEのプログラミング講座卒業生(学習時間500-800h)が、一緒になって動くものを作っていくイベントです。アイディアを持っている人は動くものが手に入り、プログラミング初学者はより実践的に開発を学べるという趣旨のイベントです。

全体流れ

3日間でざっくり20時間ぐらいの作業

22日19時~22時 オリエンテーション&開発@CODE BASE

今回作るアプリの説明、チーム分け、タスクを洗い出した後、各チームに分かれて開発に取り組みました。チームは3チームに分かれて、ざっくりとした役割分担だと、フロントのデザインを作るチーム、作りたい機能を実現するのに少し調査が必要なタスクをこなすチーム、Scaffold&リレーションを作って基本的な機能を作っていくチーム、というような感じ。

23日10時~20時 開発@ペンション

ペンションに到着後、ひたすら開発。優先度が高いissueから消化していく感じ。夜はBBQ

24日10時〜20時 発表&BBQ@ペンション

起床後、ひたすら開発。優先度が高いissueから消化していく感じ。夜は成果発表&寿司&ピザ

やってみての感想/振り返り

  • 今回のハッカソンで、久しぶりにRailsを触って、忘れているところがめちゃめちゃ多かったので、改めてRails勉強し直したいなという気持ちになった。(もともとが、Railsチュートリアルを2周とオリジナルアプリ制作に取り組んでいたぐらいだったので、しばらく触ってないと、ほんとすごく忘れちゃうなーってのを実感した。。。最近出たRailsの本をとりあえずサクッと読んで思い出したい)プラスに考えると、自分がどれぐらい忘れてるか把握できてよかった。

  • gitの使い方がすごく勉強になった。3チームで作業していて、conflictがめちゃめちゃ起きたので、conflictを解消する方法を学べてよかった。(単純にgit rebaseするだけならいいけど、conflict起きた際に、複数のcommitを一つにして、更にrebaseして....、みたいなことが何回か起きて、そのあたりのgitの使い方がちゃんと理解できてないので、キャッチアップする。以下、よく使ったコマンドメモ)

# これで、直近のコミット4つを一つのコミットにする(このコマンド叩いた後、コミットでpickになってるのをfに変更する)
git rebase -i HEAD~4
# ログをみる
git log --one-line --graph --all
# 強制的にpush
git push --force-with-lease origin feature/xxxxx
  • 最終的に今回作ったアプリは、ユーザー登録(devices)、このアプリで使う各データの基本的なCRUDの処理、チャット機能(Action Cable)、フロントのデザインもある程度いい感じに仕上げれた、というような感じで各チームがいい感じにまとまって、とりあえずベータ版を作るのを仕上げられたのでよかったのではないかと思います。とはいえ、細かいところ、荒いところがまだまだたくさんあると思いますし、issueも全体でみると14/46の消化だったので、今回取り組めなかったところや、時間がかかったところなどは、新ためて整理して、次回にいかしていきたいと思います。

まとめ

今回のハッカソンは、ローケーションがほんとに凄く良くて最高でした。みんなでワイワイしながら開発して、サービス作って、BBQしてと、凄くよかったです。運営や事前準備をしてくださった皆さんほんとにありがとうございました!!!凄く良いイベントだったので、次回もぜひ参加したいです。

最後にCODEBASEのプログラミング講座の5期生を募集してるとのことです。プログラミングに限った話じゃないけど、みんなでワイワイしながら一緒に学ぶ体験ってめちゃめちゃ良いなあと思うので、webが好きで気になっている人がいたらぜひ見てみてください!!!

Laravel admin 作業メモ

やったこと

この記事を参考にLarave-adminでCRUDの処理をできるようにする。

tac-blog.tech

エラー発生

Productsのリソースを扱うサンプルのところで、次のエラーが出た。

f:id:shimabukuromeg:20181025065208p:plain

Config error.

Disk [admin] not configured, please add a disk config in `config/filesystems.php`.

エラーへの対策

エラーメッセージを見るに設定ファイルがおかしそう。Laravel Adminの公式ページにある Upload to local するときの config/filesystems.php: の設定をそのままコピーするとなおった。

公式ページ

Laravel-admin

f:id:shimabukuromeg:20181025065612p:plain

修正箇所(config/filesystems.php

f:id:shimabukuromeg:20181025065658p:plain

動作確認

laravel を立ち上げ直して、改めてアクセスする

f:id:shimabukuromeg:20181025070155p:plain

ちゃんと登録できた。

f:id:shimabukuromeg:20181025070116p:plain

めっちゃ便利だ