1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravelのソースを渡されたら、まずどこから読むか

1
Posted at

既存の Laravel プロジェクトを引き継ぐことになり、クローンしたはいいもののファイルが多すぎてどこから開けばいいのか分からない、という状態になりました。

上司にコードリーディング会を開いてもらい、プロジェクト全体を知るための Docker まわりと、Laravel の処理を追うための routes → コントローラ → Bladeという2段階に分けて教わりました。これが分かってから急に読めるようになったので、その日に教わったことをまとめておきます。

Laravel を触り始めたばかりで、既存プロジェクトの全体像が掴めていない人向けです。

いきなり src を開かず、まず Docker まわりを見る

最初にやりがちなのが、src の中を片っ端から開いて迷子になることでした。先に見るべきは、その下に置いてあるアプリがどんな環境で動いているかのほうです。

ルート直下はだいたいこんな構成になっています。

.
├── docker-compose.yml
├── Dockerfile
└── src/          ← Laravel 本体

docker-compose.yml:build と image の違いだけ押さえる

docker-compose.yml は、複数のコンテナ(app や db)をまとめて定義して、docker compose up 一発で立ち上げるための設定ファイルです。

読むときに一番効くのが、サービスごとの build と image の違いでした。

docker-compose.yml
services:
  app:
    build: .          # ← Dockerfile をビルドしてイメージを作る
    # 省略
  db:
    image: mysql:8.0  # ← ビルドせず、既成のイメージをそのまま使う
    # 省略

compose-build-vs-image.png

  • build … その場で Dockerfile を読んでイメージを作る。今回のプロジェクトでは、PHP のバージョンや拡張モジュールなど、案件固有の設定が必要なアプリ本体がこっち
  • image … 公開されているイメージをダウンロードして使う。今回の MySQL は独自ビルドせず、既成のイメージを使っていた

読む立場からすると、これが分かるだけで作業が減ります。build と書いてあるサービスは Dockerfile を追い、image のサービスは指定されたイメージと Compose 側の設定を確認すればよいからです。

Dockerfile:app の中身が書いてあるだけ

Dockerfile は、app コンテナの中身(実行環境)を定義したファイルです。

Dockerfile
FROM php:8.2-fpm

# 必要な PHP 拡張を入れる
RUN docker-php-ext-install pdo_mysql

# 省略

今回の Dockerfile では、冒頭の FROM を見ると、このアプリが PHP 8.2 をベースに動くことが分かりました。PHP のバージョンを確認したいときは、まずここを見るのが早いです。

src に artisan があったら Laravel

src を開いて、まず目印になるのが artisan というファイルでした。

src/
├── artisan          ← これがあったら Laravel
├── composer.json
├── app/
├── routes/
└── resources/

artisan は Laravel のコマンドラインツールで、マイグレーションやキャッシュクリアをここから実行します。

php artisan migrate
php artisan route:list

ちなみに php artisan route:list はルーティングの一覧を表示するコマンドで、後述の「routes から読む」をコマンド側からやるようなものです。ファイルを開く前にこれを叩くと全体像が掴めます。

vendor が Git に入っていないのは正常

クローンした直後に vendor フォルダが無くて「ファイルが足りないのでは」と一瞬焦りましたが、これが正常でした。

  • vendor … composer.json に書かれた依存ライブラリのインストール先。composer install で生成される
  • node_modules … フロント側の同じもの。package.json からインストールされる

どちらも**.gitignore に入れて Git 管理しない**のが基本です。理由はファイル数と容量が膨大だからで、復元に必要な情報は composer.json / composer.lock に全部あるので、各自が自分の環境で入れれば足ります。

# クローン直後はこれをやらないと動かない
composer install
npm install

裏を返すと、今回のプロジェクトはクローンしただけでは動かず、最初に依存ライブラリのインストールが必要ということでもあります。ここは最初に踏んでおくと無駄に悩まずに済みます。

本体は routes から読む

環境が分かったら、いよいよアプリ側です。結論として、読み始めの入口は routes でした。

今回追った画面では、「どの URL が、どのクラスのどのメソッドに対応するか」が routes に書かれていました。ここを見ると、目的の画面の処理をどこから追えばよいか分かります。

たとえば、次のようなルートがあったとします。

routes/web.php
use App\Http\Controllers\PostController;

Route::get('/posts', [PostController::class, 'index']);

この1行で「/posts にアクセスされたら PostController の index() が動く」と読めます。あとはそのクラスへ飛ぶだけです。

PHP Intelephense を入れておく

地味ですが効果が大きかったのがこれです。VS Code に PHP Intelephense を入れておくと、クラス名やメソッド名を Cmd+クリックで定義元にジャンプできます。

コードリーディングでは飛び先を探すだけでも時間がかかるので、定義元へそのまま移動できるだけでかなり楽になりました。

クラスの冒頭に並んでいる呪文の意味

コントローラを開くと、処理の前に見慣れない記述が並んでいます。ここで止まりがちだったので、意味を整理しておきます。

app/Http/Controllers/PostController.php
namespace App\Http\Controllers;   // ①

use App\Models\Post;              // ②

class PostController extends Controller  // ③
{
    public function __construct(...)     // ④
    {
        // 省略
    }
}

① namespace … そのファイルが置かれているディレクトリ構造に対応する名前空間。「このディレクトリ配下のクラスですよ」という宣言で、同じクラス名がぶつかるのを防ぎます。ディレクトリと対応しているので、名前空間を見ればファイルの場所が分かります。

② use … 「このクラスを使いますよ」という宣言。冒頭でフルパスを書いておくことで、以降は Post のようにクラス名だけで書けます(パスの省略)。

③ extends … 継承。親クラス(ここでは Controller)の機能を引き継いだ上で、独自の処理を足します。

④ __construct … コンストラクタ。インスタンスが作られたときに最初に走る初期化処理です。使うサービスやリポジトリの受け取りをここでやっていることが多いので、「このクラスが何に依存しているか」を見る場所でもあります。

return view() からビューへ飛ぶ

今回追った一覧画面では、コントローラのメソッドがこの形で終わっていました。

app/Http/Controllers/PostController.php
public function index()
{
    $posts = Post::all();

    return view('index', compact('posts'));
}

return view(...) は**「画面(ビュー)を返す」**という意味です。ここの引数2つが読めれば、次に開くファイルが決まります。

  • 第1引数 'index' … 表示するビューの名前。resources/views/index.blade.php を指します
  • 第2引数 compact('posts') … ビューに渡す変数。compact は変数名を文字列で並べると ['posts' => $posts] という配列を作ってくれる PHP の関数です

つまり compact('posts') と書いておくと、Blade 側で $posts がそのまま使えます。

resources/views/index.blade.php
@if ($posts->isEmpty())
    <p>投稿がありません</p>
@else
    @foreach ($posts as $post)
        <li>{{ $post->title }}</li>
    @endforeach
@endif

compact に書いた変数名が、そのままテンプレート内の変数名になるので、ここの対応さえ意識しておけば「この $posts はどこから来たの?」で迷わなくなります。

結局どの順番で追えばいいのか

一連の流れを図にするとこうなります。

laravel-request-flow.png

読むときの手順としては、こうなります。

  1. routes で URL からコントローラ/メソッドを特定する
  2. コントローラを開いて、データの取得と加工を読む(__construct の依存もここで確認)
  3. return view('~', compact('~')) の行から、対応する resources/views/~.blade.php へ飛ぶ
  4. Blade で、渡ってきた変数がどう表示に使われているかを見る

まとめ

  • docker-compose.yml の build か image かを見ると、Dockerfileを追うサービスと、指定されたイメージを確認するサービスが分かる
  • vendor / node_modules が無いのは正常。今回のプロジェクトでは、まず composer install と npm install が必要
  • アプリ側は routes が入口。view() の第1引数が Blade のファイル名、compact がそこに渡る変数

既存の Laravel プロジェクトを開いて途方に暮れたら、まず Docker まわりで実行環境を確認する。そのあと、routes から気になる URL のコントローラをたどる。自分はこの順番が分かって、ようやく迷子から抜け出せました。

php artisan route:list が実行できる環境なら、ルーティングの一覧を出してから気になる URL を探すのも分かりやすいです。

参考

関連記事

告知

最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

みなさまからのご応募をお待ちしております。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?