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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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

Avatar for 武田 憲太郎

武田 憲太郎

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