Laravelをふんわり理解する
これまで私は、フロントエンドはNextjsなどをメインに、バックエンドはJavaだったりをメインに扱ってきました。
今回、php+Laravelのシステムのソースコードを読まなければいけない機会があり、ついでにLaravelをちょっと学んでみようと思いました。
前提と前提
筆者はLaravelどころか、phpも触ったことがありません。
目標としては、書けないけどふんわり理解して、「こんな時はあの辺を見る」みたいなレベルを目指します。(AI時代にこのレベル感の理解が必要かはわかりませんが、私は知っておきたい)
サンプルのソースコードなどはAIにお任せしています。
やること
以下についてみていきます。
- 重要(?)なファイルたちを知る
- controllerでリクエストを受け取る
- controllerでViewを返す
- controllerでJSONを返す
- テンプレートエンジン(Blade)のいろいろ
重要(?)なファイルたちを知る
public/index.php
既存のプロジェクトを理解する観点では不要かもしれませんが、LaravelのFWを知るうえで重要そうなindex.php。
ドキュメントに以下の記載がありました。
The entry point for all requests to a Laravel application is the public/index.php file. All requests are directed to this file by your web server (Apache / Nginx) configuration. (訳:Laravelアプリケーションへのすべてのリクエストのエントリポイントはファイルですpublic/index.php。すべてのリクエストは、Webサーバー(Apache/Nginx)の設定によってこのファイルにルーティングされます。)
nginxなどから、まずはindex.phpを起点に始まる感じですね。
次にapp.phpというファイルに向かう。
// Bootstrap Laravel and handle the request...
/** @var Application $app */
$app = require_once __DIR__.'/../bootstrap/app.php';
$app->handleRequest(Request::capture());
bootstrap/app.php
ここでアプリケーション自体のインスタンスが生成されるようです。
The index.php file loads the Composer generated autoloader definition, and then retrieves an instance of the Laravel application from bootstrap/app.php. The first action taken by Laravel itself is to create an instance of the application / service container.
ここから以下の部分で、web.phpを参照し、ルーティングを定義している。
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
ミドルウェアだったり、例外ハンドラも設定できそうですが、一旦割愛します。
routes/web.php
実際のリクエストを受け取るルーティングを定義しているファイルですね。理解せずに読んでいた時は、とりあえずここに辿り着き、ここから動作とコードのマッピングをしていました。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HelloController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/hello', [HelloController::class, 'index']);
/helloは筆者が追加したものです。
見つけてしまえばシンプルですね。
- メソッド
- パス
を指定することで、ここに到達できる。
上記のコードでは以下のようになっています。
-
/- 直接テンプレートエンジン(Blade)を返している
-
hello-
[HelloController::class, 'index']の部分で、HelloControllerのindexを指定している
-
なんとなく
- index.php
- app.php
- web.php
まずはこの三つの存在を知ることで、挙動を知るための前提ができました。
controllerでリクエストを受け取る
routes/web.php からコントローラに処理を渡すところまではわかりました。次はそのコントローラが実際にリクエストを受け取るところ。
サンプルのHelloControllerはこんな感じ。(AIが生成した適当なサンプルです)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HelloController extends Controller
{
public function index(Request $request)
{
$name = $request->query('name', 'ゲスト');
// ...
}
}
メソッドの引数にRequestを書いておくだけで、Laravelがよしなに渡してくれます。SpringBootで@RequestParamやHttpServletRequestを引数に書くのと同じ感覚ですね。
クエリパラメータは$request->query('name', 'ゲスト')のように取ります。第二引数はデフォルト値です。
?name=test
でアクセスした場合に上記のqueryで取得できます。これがなかった場合に`ゲスト`になる。
他にも、POSTのボディを取得する場合はinput("key")などのメソッド、ヘッダーを取得した場合はheader("key") などがあるようです。
URLのパスパラメータ(/users/{id}の{id})はまたちょっと違っていて、メソッドの引数にそのまま書きます。
Route::get('/users/{id}', [UserController::class, 'show']);
public function show(Request $request, $id)
{
// $id にURLの値が入ってくる
}
controllerでViewを返す
コントローラからViewを返すのはview()ヘルパーを使います。routes/web.phpのデフォルトの実装にあった部分と同じ。
return view('hello', compact('name', 'greeting', 'visitedAt', 'luckyNumber'));
第一引数がテンプレート名(resources/views/hello.blade.php)で、第二引数がテンプレートに渡すデータです。
compact('name', 'greeting', ...)というのはPHPの組み込み関数で、同名の変数をまとめて連想配列にしてくれるやつようです。
web.phpでもあった以下の実装は、resources/views/welcome.blade.phpをそのまま返しているのですね。
Route::get('/', function () {
return view('welcome');
});
controllerでJSONを返す
ViewじゃなくてJSONを返したいときもあるかもしれない。Laravelは配列を返すだけで勝手にJSONにしてくれます。便利だけど怖いですね。
public function apiIndex(Request $request)
{
return [
'name' => $request->query('name', 'ゲスト'),
];
}
これだけでContent-Typeがapplication/jsonのレスポンスになります。
response()->json()でJSONを返すことを明示することもできます。こっちの方が安心感ある。
return response()->json([
'name' => $name,
], 200);
最近書店で以下の本を見かけました。WebAPI開発についてはこっちも流行ってたりするのですかね。
https://amzn.asia/d/0hELGsNQ
テンプレートエンジン(Blade)のいろいろ
Laravel標準のテンプレートエンジンはBladeのようです。
Blade is the simple, yet powerful templating engine that is included with Laravel.
.blade.phpという拡張子でresources/views/に置いておくと、view('hello')で呼び出せます。
値の埋め込み
基本は{{ }}で囲むだけ。
<p class="greeting">{{ $greeting }}</p>
<p class="name">{{ $name }} さん</p>
これは信頼できる値以外には使わないようにしたいですね。
分岐
@if (式)
<p>trueの場合</p>
@else
<p>elseの場合</p>
@endif
レイアウト継承
ヘッダーやフッターを共通化したいときは「親テンプレート」を作っておきます。
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html>
<head><title>@yield('title')</title></head>
<body>
<header>共通ヘッダー</header>
@yield('content')
<footer>共通フッター</footer>
</body>
</html>
子テンプレートからは@extendsで親を指定して、@sectionで中身を差し込みます。
{{-- resources/views/hello.blade.php --}}
@extends('layouts.app')
@section('title', 'Hello')
@section('content')
<p>{{ $greeting }}</p>
@endsection
子の@section('content')が、親の以下の部分に差し込まれる感じですね。
@yield('content')
テンプレートエンジン側でやりたいことと思うことはどれも同じなので、ThymeleafやらJSPにも似ていますね。
おわりに
はじめてLaravelの構成や実装方法を落ち着いて見てみました。SpringBootと通づるところもあるし、全然違うところもあるし。
まだまだユーザーも多く、歴史の長いフレームワークなだけあって、これだけでも汎用性や機能の多さを感じました。
それが原因で、大規模なプロジェクトでは煩雑になるとか、そんな話があるのかもしれませんが、私はまだ分かりません。
そもそもPHP(言語自体)をそんなに知らないので、ふんわり読んでふんわり書いてで進めましたが、リクエストの受け取り・レスポンスの返却あたりで型のゆるさ)が気になりました。(というか動的型付け言語らしいのでそこ気にするの愚問かもです
PHP自体も読み書きできるようにしていこうと思います。
そして引き続き、
- DBの操作
- DI
- インターセプター
- トランザクション
なども気になるので、いろいろ勉強してみたいと思いました。
4月で新しい案件に入ったばかりの方も多い時期かと思います。Laravel学び始めたい・学ばなければいけない、みたいな方のご参考になれば幸いです。
以上です。