Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Laravelへの異常な愛情 または私は如何にして心配するのを止めてEloquentを愛するようになったか

Laravelへの異常な愛情 または私は如何にして心配するのを止めてEloquentを愛するようになったか

武田 憲太郎

March 25, 2023
Tweet

More Decks by 武田 憲太郎

Other Decks in Programming

Transcript

  1. ⾃⼰紹介 武⽥ 憲太郎 / @KentarouTakeda Webアプリケーションエンジニア 10 開発⾔語 好き:TypeScript 得意:PHP

    JSフレームワーク 好き:Angular 得意:Next.js PHPフレームワーク 好き:Symfony 好き・得意:Laravel 正しく書けば⾃然と堅牢な設計になっていくような技術が好き 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  2. Laravelの特徴 – フルスタック • laravel/framework • フロントエンド • ルーティング •

    ビュー • ORM • キャッシュ • メール • ... • laravel/* • SPA • 決済 • OAuth • ... 16 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  3. • Post.php • Eloquentモデル • create_posts_table.php • マイグレーション・テーブル作成 • PostFactory.php

    • ファクトリ(フィクスチャ) • PostSeeder.php • シーダ(初期データ作成) • StorePostRequest.php • 保存時のバリデーション • UpdatePostRequest.php • 更新時のバリデーション • PostController.php • コントローラー • PostPolicy.php • 認可処理 • PostControllerTest • Featureテスト 21 ./artisan make:model Post --all ./artisan make:test PostControllerTest 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  4. モデルとテーブルが対で作成される • postsテーブルとPostモデルが対で作成される • Postモデルは実装が何もない(このままで問題なく動作する) • ファクトリの利⽤だけが宣⾔されている 22 return new

    class extends Migration { public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); // 主キー $table->timestamps(); // タイムスタンプ $table->text('title'); // 「件名」カラムを追加する例 }); } }; class Post extends Model { use HasFactory; } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  5. ファクトリ(ダミーデータの設計図) • テーブルにどんなダミーデー タを割り当てるか定義 • リレーションシップ先のモデ ルは他のファクトリに作成を 移譲できる 23 class

    PostFactory extends Factory { public function definition(): array { return [ // ダミーの文言とユーザーで投稿を作成 'user_id' => User::factory(), 'title' => fake()->sentence(), ]; } } class UserFactory extends Factory { public function definition(): array { return [ 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), ‘password’ => ‘...(省略)', // password 'remember_token' => Str::random(10), ]; } } ※laravel/laravel初期状態 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  6. ダミーデータの⽣成 • 開発⽤データや本番環境初期 データなどまとまった⼀式の データを作成 24 class UserSeeder extends Seeder

    { public function run(): void { // ユーザーを10名作成 User::factory()->count(10)->create(); } } class PostSeeder extends Seeder { public function run(): void { // 作成済ユーザーを取得 $users = User::all(); // 作成済ユーザーに所属する投稿を100件作成 Post::factory()->count(100) ->sequence(fn() => [ 'user_id' => fake()->randomElement($users) ]) ->create(); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  7. コントローラー • index, store, showなどの予約 名。 • ⼀部モデルやリクエストも 渡される。 •

    リクエストが渡されたらモ デルに保存、モデルが渡さ れたらそのまま返却、これ が基本。 25 class PostController extends Controller { public function index() { return Post::all(); // 投稿全件を返却 } public function store(StorePostRequest $request) { // 投稿を作成しそれを返却 return Post::create($request->validated()); } public function show(Post $post) { return $post; // 指定された投稿を1件返却 } /* 省略 */ } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  8. リソースコントローラー • CRUD操作に必要な全てのURLと HTTPメソッドがルートに⼀括登 録される。 • URLパラメータをIDとみなしモデ ルを解決。 • 解決できない場合Not

    Found。 26 https://readouble.com/laravel/9.x/ja/controllers.html#actions-handled-by-resource-controller // URLとそれを処理するクラスを紐付け Route::resource( ‘/posts’, PostController::class ); ※「/posts」にPostControllerを紐付け(⼿動で追記) 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  9. バリデーション • $this->post で操作対象モデル(投稿)にアクセス • $this->user() で認証ユーザーにアクセス • 組み合わせバリデーションやドメインバリデーションも実装可 27

    class StorePostRequest extends FormRequest { public function rules(): array { return [ // 新規作成時のバリデーションルール ]; } } class UpdatePostRequest extends FormRequest { public function rules(): array { return [ // 更新時のバリデーションルール ]; } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  10. 認可 • 認証済ユーザーと操作対象の モデルが渡される。 • 認証済ユーザーに対する認可 可否をboolとして返却。 • falseを返却した場合403エラー。 28

    class PostPolicy { public function viewAny(User $user): bool { // 認証済ユーザーは投稿の一覧表示が可能か? } public function view(User $user, Post $post): bool { // 認証済ユーザーは指定された投稿を閲覧可能か? return $user->is_admin || $post->user_id === $user->id; } public function create(User $user): bool { // 認証済ユーザーは投稿を作成可能か? } } class PostController extends Controller { public function show(Post $post) { // 閲覧可能でない場合403エラー $this->authorize(‘view', $post); return $post; } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  11. Featureテスト(初期状態) • これ⾒よがしに使われていな いuseが2件 29 namespace Tests¥Feature; use Illuminate¥Foundation¥Testing¥RefreshDatabase; use

    Illuminate¥Foundation¥Testing¥WithFaker; use Tests¥TestCase; class PostControllerTest extends TestCase { public function test_example(): void { $response = $this->get('/’); $response->assertStatus(200); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  12. Featureテスト(実装例) • use RefreshDatabase; テスト毎のDBリセット • use WithFaker; Fakerを利⽤ •

    準備 適当な件名で投稿を1件作成 • テスト ID指定でGET • アサーション 作成と返却との件名の⼀致 30 class PostControllerTest extends TestCase { use RefreshDatabase; use WithFaker; public function IDを指定し投稿を1件取得(): void { $post = Post::factory()->create([ 'title' => $this->faker()->sentence(), ]); $response = $this->get("/posts/{$post->id}"); $response->assertJsonFragment([ 'title' => $post->title ]); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  13. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 相関図 31 コマンド2回、⾃動⽣成ファイル9個で以上の構成 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  14. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 32 モデルとテーブルは1対1の対応 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  15. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 33 CRUD操作はリソースルートとして束ねられ、 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  16. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 34 リソースはモデルへ解決された状態で、 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  17. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 35 バリデーション処理や、 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  18. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 36 認可処理が⾏われる。 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  19. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 37 コントローラーは⼊⼒されたモデルを書き戻すのみ 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  20. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 38 テストはシーダやファクトリでダミーデータを⽣成し 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  21. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test 39 通常のリクエストと同じ経路でFeatureテスト 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  22. Policy Route Model Binding Model Controller Table Resource View -

    C View - R View - U View - D Form Request Seeder Factory test Postモデルの全体像 40 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  23. Laravelの全体像 41 Policy Route Model Binding Model Controller Table Resource

    View - C View - R View - U View - D Form Request Seeder Factory test User Policy Route Model Binding Model Controller Table Resource View - C View - R View - U View - D Form Request Seeder Factory test Post Policy Route Model Binding Model Controller Table Resource View - C View - R View - U View - D Form Request Seeder Factory test Comment Policy Route Model Binding Model Controller Table Resource View - C View - R View - U View - D Form Request Seeder Factory test Tag 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel フレームワークが提供する構造 = ベストプラクティス
  24. MVCモデルによる責務分離(とその違反例) • Viewに漏れ出したモ デルの知識 • 表⽰を司ってしまっ たコントローラー • モデルがリソース表 現に関与

    42 Model Controller View Model Controller View Model Controller View $user ->name $html = '<p>' . $post->body . '</p>'; return url( '/posts/' . $this->id} ); 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  25. リソースによる責務分離(とその違反) 43 User Model User Controller users Table User Resource

    View - C View - R View - U View - D Post Model Post Controller posts Table Post Resource View - C View - R View - U View - D return $post->user; Post::where( 'user_id', $user->id ) 責務の境界はレイヤーではなくリソース 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  26. rails generate scaffold • CRUD操作を網羅するコントローラーを作成 • 対応するモデルとそれを格納するテーブル (マイグレーション)を作成 • それらリソースへのルーティングの作成

    • フィクスチャ(ファクトリ)を作成 • テストファイルの作成 44 https://railsdoc.com/rails#rails_scaffold 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel Ruby on Railsと同じ考え⽅
  27. Ruby on Railsのパラダイム 45 builderscon tokyo 2019 Ruby (off|with) the

    Rails https://www.youtube.com/watch?v=g7nU45RgBvw https://speakerdeck.com/shinpeim/ruby-off-with-the-rails?slide=15 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  28. Eloquentと各機能との関係 • Eloquentの利⽤が前提 • 例:認可 • Eloquentによる追加機能 • 例:モデル結合ルート •

    Eloquentを想定 • 例:ジョブとSerializesModels Laravelのほとんどの機能が Eloquentと「何らか」関係 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel 47
  29. Laravelに寄り添う • Eloquent中⼼に考える • Eager Loading vs join • Facadeを使う

    • Laravelの機能を使う 50 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  30. 例1: あるモデルに関連する何かを探す 52 class User extends Model { // Bad

    ユーザーの全投稿を取得 public function getPosts() { return Post::where('user_id', $this->id) ->get(); } } class User extends Model { // Good 投稿へのリレーションシップを宣言 public function posts() { return $this->hasMany(Post::class); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  31. 例2: モデルの値を変形する 53 class User extends Model { // Bad

    フルネームを取得 public function getName() { return $this->last_name . ' ' . $this->first_name; } } class User extends Model { // Good フルネームの取得方法を宣言 protected function name(): Attribute { return Attribute::make( get: fn($_, $attributes) => $attributes['last_name'] . ' ' . $attributes['first_name'] ); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  32. 例3: バリデーション 54 class User extends Model { // Bad

    バリデーション通過した場合のみ保存 public function saveWithValidate() { if($this->isDirty('name')) { throw new DomainException( '名前は変更できません' ); } $this->save(); } } class User extends Model { // Good イベントの購読を宣言 protected static function booted() { static::updating(function (self $user) { if($user->isDirty('name')) { throw new DomainException( '名前は変更できません' ); } }); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  33. 例4: クエリ(検索)のユースケース 55 class User extends Model { // Bad

    「アクティブユーザー」を取得 public static function getActiveUsers() { return self::query()->whereHas( 'posts', fn($q) => $q->where( 'created_at', '>=', now()->subDays(7)) ) ->get(); } } class User extends Model { // Good 「アクティブユーザー」のスコープを宣言 public function scopeWhereActive(Builder $query) { return $query->whereHas( 'posts', fn($q) => $q->where( 'created_at', '>=', now()->subDays(7)) ); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  34. Eloquentの機能で宣⾔的に実装するメリット • 命名規則やタイプヒント要件の存在 • ビルトインメソッドと衝突するリスクが⽐較的低い • 「Laravel標準」による認知負荷の低減 ※Laravel⾃体の学習コストは必要 • 再利⽤可能性

    • 責務の⼩さなローカルスコープをチェーンし⼤きなユースケースを満たす • Observer、Castable、BootableTraitによる処理の切り出し • Interfaceやabstract traitによる契約の宣⾔ • 例: implements Taggable(タグ付け可能・ミューテタ) • 例: implements FullTextSearchable(全⽂検索可能・ローカルスコープ) • use Prunable(モデルの整理・Laravel標準機能) • 仕様管理 🤔 56 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  35. EloquentとDocs as Code 57 return new class extends Migration {

    public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id()->comment('ID'); $table->string('first_name')->comment('名'); $table->string('last_name')->comment('姓'); $table->string('email')->unique() ->comment ('メールアドレス'); $table->string('password')->comment('パスワード'); $table->timestamps(); }); } }; class User extends Model { /** @comment 投稿 */ public function posts() { return $this->hasMany(Post::class); } /** @comment アクティブユーザーへのクエリ */ public function scopeWhereActive() { // 省略 } /** @comment アクティブユーザーか否か */ protected function isActive(): Attribute { return Attribute::make( get: fn() => '省略', ); } } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  36. barryvdh/laravel-ide-helper • プロパティ、リレーションシップ、スコープ、アクセサ、 、コメント、ほぼ 全てに対応 ※更新ペースが遅くLaravelの⽐較的新しい機能に対応していない点に注意 58 /** * App¥Models¥User

    * * @property int $id ID * @property string $first_name 名 * @property string $last_name 姓 * @property string $email メールアドレス * @property string $password パスワード * @property Carbon|null $created_at * @property Carbon|null $updated_at * @property-read Collection<int, ¥App¥Models¥Post> $posts 投稿 * @method static Builder|User newModelQuery() * @method static Builder|User newQuery() * @method static Builder|User query() * @method static Builder|User whereActive() アクティブユーザーへのクエリ * @mixin ¥Eloquent */ 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  37. ./artisan model:show >=9.21.0 2022/07/19 • モデルの実装(宣⾔)をメタデータと して出⼒ • json出⼒による再利⽤可能性 61

    { "class": "App¥¥Models¥¥User", "database": "sqlite", "table": "users", "policy": null, "attributes": [ { "name": "id", "type": "integer", /* 省略 */ }, /* 省略 */ ], "relations": [ { "name": "posts", "type": "HasMany", "related": "App¥¥Models¥¥Post" } ], "observers": [] } 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  38. Laravelに寄り添う • Eloquent中⼼に考える • Eager Loading vs join • Facadeを使う

    • Laravelの機能を使う 62 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  39. Eager loading vs JOIN 63 User::query() ->with('posts') ->get(); User::query() ->join(

    'posts', 'user.id', '=', 'post.user_id' ) ->get(); 検討:どちらを使うか? 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  40. Eager loading vs JOIN 64 User::query() ->with('posts') ->get(); User::query() ->join(

    'posts', 'user.id', '=', 'post.user_id' ) ->get(); 結論:joinとEloquentは混ぜてはいけない 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  41. Eager loading vs JOIN 65 $users = User::query() ->with('posts') ->get();

    get_class($user[0]); // App¥Models¥User $users->count(); // ユーザーの人数 $users = User::query() ->join( 'posts', 'user.id', '=', 'post.user_id' ) ->get(); get_class($users[0]); // App¥Models¥User $users->count(); // 投稿の件数 $users[0]->title // 投稿の件名 理由:「テーブル |-| モデル」の関係が崩れることによる不可解な挙動 • 型はUser • 実体は「ユーザーでも投稿 でもない何か」 • アクティブレコードの利点 を全て失う • バグの温床 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  42. JOINの利⽤ • モデルとテーブルは1対1対応ではあるが全く同じものではない。 • 結果セットを変形するクエリをEloquentから使うのを避ける。 • `group by`句や集約関数も同じ理由で要注意 • モデルなのかレコードセットなのか変数名などで明確に区別。

    66 $rows = User::query()->getQuery() ->join( 'posts', 'user.id', '=', 'post.user_id' ) ->get(); $rows = DB::table('users') ->join( 'posts', 'user.id', '=', 'post.user_id' ) ->get(); 解法1:Eloquentビルダをクエリビルダに変換 解法2:モデルではなくテーブルをクエリする 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  43. モデルなのかレコードセットなのか? 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel 67 Eloquentとクエリビルダを両⽅使った実装の失敗例

    - 第150回 PHP勉強会 https://www.youtube.com/live/qz9Y-WXgxxY?feature=share&t=9672 https://speakerdeck.com/shinpeim/ruby-off-with-the-rails?slide=15 モデルとレコードセットが混ざることによる不可解な挙動
  44. リソースによる責務分離(とその違反) 68 User Model User Controller users Table User Resource

    View - C View - R View - U View - D Post Model Post Controller posts Table Post Resource View - C View - R View - U View - D ユーザーでも 投稿でもない 何か 責務 暴⾛ 複数のモデルへ関⼼が横断する場合どうするか? 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  45. Single Action Controller • 「ダッシュボード画⾯」「マイページ」「LP」 • CRUD操作を束ねる必要が無い 69 User Model

    users Table Awesome Controller View Post Model posts Table Comment Model comments Table 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  46. Eloquent中⼼の設計 70 • Eloquentモデルは宣⾔的に実装 • メタデータを再利⽤する • リソースルートやRoute Model Bindingによりコード量を削減する

    • 「テーブル = モデル = リソース」と考えリソース毎に関⼼を分離する • この範疇に収まらない要件の場合Single Action Controllerを選択 • この場合は設計は何でもアリ • 「テーブル=モデル=リソース」の前提にも⽴たなくて良い 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  47. Laravelに寄り添う • Eloquent中⼼に考える • Eager Loading vs join • Facadeを使う

    • Laravelの機能を使う 71 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel https://twitter.com/taylorotwell/status/1560020999378292736
  48. 利点と⽋点 72 https://readouble.com/laravel/9.x/ja/facades.html#when-to-use-facades ① ② ③ ① 簡潔で覚えやすい ② テストが簡単

    ③ ファットクラスを 意図せず作りがち ④ ファットのフィー ドバックが無い ④ 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  49. 便利すぎるファサード • app.phpでのエイリアス設定 • ファサードを簡潔に「使えてし まう」のはこの設定によるもの。 • エディタも静的解析もこのエイ リアスを認識できない •

    設定を全て削除 74 // app.php 'aliases' => Facade::defaultAliases()->merge([ // 'ExampleClass' => App¥Example¥ExampleClass::class, ])->toArray(), // app.php 'aliases' => [ 'App' => Illuminate¥Support¥Facades¥App::class, 'Arr' => Illuminate¥Support¥Arr::class, 'Artisan' => Illuminate¥Support¥Facades¥Artisan::class, 'Auth' => Illuminate¥Support¥Facades¥Auth::class, // 省略 ], // app.php 'aliases' => [ ], 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  50. ファサードを適度に使う 75 @if(Illuminate¥Support¥Facades¥Auth::user()) {{ Illuminate¥Support¥Facades¥Auth::user()->name }} @else <a>ログイン</a> @endif namespace

    App¥Http¥Controllers; use App¥Models¥Post; use App¥Models¥User; use Illuminate¥Http¥Request; use Illuminate¥Http¥Response; use Illuminate¥Support¥Facades¥Cache; use Illuminate¥Support¥Facades¥Http; use Illuminate¥Support¥Facades¥Session; use Illuminate¥Support¥Facades¥View; • Bladeテンプレート 事実上の利⽤不可 • コントローラー 視覚的なフィードバック 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  51. ファサードごとに個別に⽤意されたテスト ファサードに応じたそれぞれのテスト機能 • Http • URLを指定しレスポンスをモック • リクエストの有無や内容をアサーション • モックされていないリクエストの禁⽌

    • Storage • ファイル作成有無のアサーション • ディレクトリ内容のアサーション • ダミーファイルの⽣成(画像可) • テスト終了時の初期化 • Mail • 送信有無のアサーション • 本⽂とヘッダを区別したアサーション 77 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  52. 何が嬉しいか?(本当にあった怖い話) • あるコミットを機にテストの所要時間が 激増した • 広い範囲から発⽕されるイベントのリスナ を実装。ほとんど全てのテストケースでリ スナが起動されていた🔥 • CIがSaaSのAPIをコールし課⾦発⽣💰

    • ストレージのテストを実装した数⽇後に ローカル環境のディスク容量が溢れた😇 • ステージング環境のS3バケットに正体不 明のダミーファイルが⽇々作られ続けて いた😥 • メーr.....😱😱😱😱 78 abstract class TestCase extends BaseTestCase { use CreatesApplication; public function setUp(): void { parent::setUp(); // テスト時は強制的に全てモック Http::fake(); Http::preventStrayRequests(); Storage::fake(); Mail::fake(); Event::fake(); Bus::fake(); } } インシデントを根絶 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  53. Cast Policy Route Model Binding Model Controller Table Resource View

    - C View - R View - U View - D Form Request Seeder Factory test 何が嬉しいか?(ファサードを使うべき理由) 79 システム外への副作⽤を伴う処理の場合も リクエストレスポンスのテストが容易かつ安全に可能 Facade Fake 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  54. Laravelに寄り添う • Eloquent中⼼に考える • Eager Loading vs join • Facadeを使う

    • Laravelの機能を使う 81 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  55. PHPの機能で書く 82 実装例 こんな時に困る ファイル保存 file_put_contents(...); • Webサーバを並列化したい メール送信 mb_send_mail(...);

    • メール送信をSaaSに乗り換えたい ログ error_log(...); • 本館環境をコンテナ化したい アラート送信 file_put_contents(/*Slack API*/); • 開発環境⽤if⽂が必要 バッチ起動 # crontab * * * * * php artisan ... • バッチサーバがSPOFになりがち • コード管理不可 • ローカル環境で⼀部機能が動かない 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  56. Laravelの機能で書き換える 83 実装例 Laravelでの書き⽅ ファイル保存 file_put_contents(...); Storage::put(...); メール送信 mb_send_mail(...); Mail::to(...)

    ->send(...); ログ error_log(...); Log::warning(...); アラート送信※ file_put_contents(/*Slack API*/); Log::critical(...); バッチ起動 # crontab * * * * * php artisan ... $schedule ->command(...) ->cron('* * * * *'); ※アラート送信 • アプリケーションは「それがクリティカルである」までしか関知しない • 「クリティカルをどう扱うか?」は監視(Observability)の責務 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  57. Laravelに環境を制御させる(ログ出⼒の例) • single • storage/logs/laravel.logに出⼒ • ローカル環境におすすめ • daily •

    storage/logs/に出⼒しローテーション • EC2での運⽤におすすめ • stderr • 標準エラー出⼒へ出⼒。 • monologによる⾼度なカスタマイズが可能 • 本番コンテナはこれ⼀択 • slack • CriticalレベルのみSlack通知 • Critical以外は捨てられる(stackと組み合わせる) • syslog • OSやjournaldに管理させる。 • stack • 複数の設定を同時に使う • 例: stderrでDocker Daemonへ送りつつsingleでコンテ ナ内にも保存、かつslackでのアラートも発報。 84 config('logging.default') • 環境変数「LOG_CHANNEL」を設定 • 無ければ「stack」を設定 config/logging.php https://github.com/laravel/laravel/blob/10.x/config/logging.php 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  58. Laravelに環境を制御させる(ファイル保存の例) 85 if('production' === config('app.env')) { Storage::disk('s3')->put(...); } else {

    Storage::disk('local')->put(...); } services: laravel: environment: # ローカルディスクを指定 FILESYSTEM_DISK: local taskDefinition.addContainer('laravel', { containerName: 'laravel', environment: { # S3を指定 FILESYSTEM_DISK: 's3', }, } Storage::put(...); Bad: • 本番環境はs3に保存 • それ以外はローカルに保存 Good: • ストレージ(抽象)に保存 • 具象の振る舞いは外部から注⼊ docker-compose.yml(ローカル環境) ECSタスク定義(本番環境・CDK) 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel Bad: 下位のモジュールに依存 Good: 抽象に依存
  59. 2022/10/11 [10.x] Uses PHP Native Type Declarations • 戻り値アノテーショ ンをタイプヒントへ

    修正。 • 本体以外の全ファー ストパーティーパッ ケージも対象。 • ライブラリも含む多 くの開発者にとって 痛みの伴う修正。 89 https://github.com/laravel/laravel/pull/6010 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  60. 2022/10/12 [10.x] Uses PHP Native Type Declarations 90 https://github.com/laravel/framework/pull/44545 •

    laravel/laravelが提供 する初期コード や./artisan make:*の ⾃動⽣成も修正の対 象。 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel
  61. 2023/2/18 YouTuber Povilas Korop 93 https://twitter.com/dillinghamdev/status/1626989292227551232 https://www.youtube.com/c/LaravelDaily Laravel10に混乱している。 リソースコントローラーを作成したら返却型にResponseが 追加されていた。

    私はいつも通りview()を返却した。エラーに遭遇した。 確かにResponseではなくViewを返却したが、何か間違えた だろうか?毎回⼿作業で修正するのだろうか? 2023/3/25 PHPerKaigi 2023 / Dr. Strange Laravel