はじめに
プロフェッショナルWebプログラミング Laravel〈最新Laravel 9対応〉
のアウトプット
HTTPカーネル
- HTTPカーネル(Http Kernel)は、Laravelのフレームワークで、HTTPリクエストとレスポンスのライフサイクル全体を管理する重要なコンポーネントです。具体的には、アプリケーションに対するすべてのHTTPリクエストを処理し、レスポンスを返すための中心的な役割を果たしています。
HTTPカーネルの主な役割
-
ミドルウェアの処理: HTTPカーネルは、まず最初にミドルウェアを実行します。ミドルウェアは、HTTPリクエストがアプリケーションに到達する前に様々な処理を行う機能です。たとえば、認証やCSRF保護、リクエストのロギングなどが行われます。カーネルはリクエストごとに登録されたミドルウェアを順次処理していきます
-
ミドルウェアにはグローバルで適用されるものや、特定のルートにのみ適用されるものがあります。カーネルがまずミドルウェアを実行することで、リクエストに対して事前に処理を加えたり、必要に応じて早期にレスポンスを返すことができます。
-
ルーティングの決定: ミドルウェアの処理が終わると、カーネルはリクエストのURIに基づいて、どのコントローラやメソッドが対応するのかをルーティングで決定します。ルーティングはリクエストを特定のコントローラメソッドに結びつけ、そのメソッドがリクエストを処理してレスポンスを返す仕組みです。
-
リクエストからレスポンスまでのフロー管理: HTTPカーネルは、リクエストがアプリケーションのコントローラやその他の処理ロジックに到達し、最終的にレスポンスを生成してクライアントに返すまでのフロー全体を管理します。リクエストが処理される過程で、HTTPカーネルは適切なレスポンスを生成する責任があります。
-
リクエストとレスポンスのラッピング: HTTPカーネルは、リクエストとレスポンスをIlluminate\Http\Request および Illuminate\Http\Response オブジェクトにラップします。これにより、リクエストとレスポンスの処理が一貫した形で行われ、Laravelの他のコンポーネントとスムーズに連携します。
-
アプリケーション終了時の処理: 最後に、リクエストが処理されレスポンスがクライアントに送信された後、カーネルはアプリケーションの終了処理を行います。たとえば、セッションの書き込みやデータベース接続の閉鎖などがここで行われます。
bootstrap/app.php
- Laravel 11では、従来の Kernel.php ファイルが削除され、アプリケーション全体の設定はbootstrap/app.php に集約されました。これにより、アプリケーションの初期化プロセスがよりシンプルになり、モジュール化が進みました。ミドルウェア、ルーティング、例外処理などは、すべて bootstrap/app.php 内で設定されるようになっています
- bootstrap/app.php はアプリケーションの初期化時に一度だけ実行される
ミドルウェア
- ルートで指定された処理を実行する前後に任意の処理を実行する機能
サービスコンテナ
- Laravelのインスタンスを管理するサービス
- ミドルウェアも登録されて解決される
- リクエスト時にDIを実施
- ファサード提供 など。Laravelで提供される機能はサービスコンテナを介して提供される
DI
- リクエスト時にサービスコンテナがコンストラクタ、メソッドインジェクションしてくれる
- 引数にインタフェースを使う場合はbind設定が必要
bindとmake
- bind と make は、LaravelでDIを実現するためのサービスコンテナのメソッド
- サービスコンテナにあらかじめインスタンスの生成方法を設定すること
-
$app->bind()
- DIの場面では例えばコンストラクタの引数にインタフェースを設定した場合は実装クラスの紐づけが必要。サービスコンテナがその実装クラスにDIしてくれる
-
$app->make(class)
でビジネスロジックからインスタンスを取得する- make を明示的に使うのは、サービスコンテナから直接インスタンスを取得したいときや、手動で依存性の解決が必要な場合
ファサード
- サービスコンテナにあらかじめ設定されている便利な機能やサービスを、静的メソッドを使って簡単に呼び出せるようにしている仕組み
- 自分でカスタムファサードも作ることができる
サービスプロバイダ
- Laravelのサービスコンテナにさまざまな機能やサービスを登録・設定するためのクラス(bindが書かれているイメージ)。機能毎にサービス・プロバイダのファイルがあるイメージ
- サービス・プロバイダのなかでbindなどをしてサービスコンテナにインスタンスを設定
- Laravel11から?アプリケーションが起動する際に、必要なサービスプロバイダがロードされ(遅延プロバイダ)、その中で必要なクラスやサービスのバインディング、イベントリスナーの設定、ルートの登録などが行われます
サービス・プロバイダの基本的な動作
- Laravelの初期化処理では各サービス・プロバイダのregisterメソッドが実行。全てのregisterメソッド処理が終わると、次にboot処理が呼び出される。registerメソッドは必須、bootメソッドは任意
registerメソッド
アプリケーション内で必要なサービスやクラスをサービスコンテナに登録します。これにより、サービスコンテナを介してクラスの依存関係が自動的に解決され、アプリケーション内の他の場所で利用できるようになります(DI)
bootメソッド
bootメソッドは、全てのプロバイダが登録を終えてから実行されます。これにより、他のサービスプロバイダに依存するサービスや設定を安全に登録することが可能です。
特定の初期処理が必要な場合は、ここに記述する必要があります。
AppServiceProvider
- グローバルな設定やアプリケーション全体に影響を与えるような設定は、AppServiceProvider などのサービスプロバイダに記載するのが一般的
- 設定例(bootメソッド)
- 環境ごとの特定の設定(例:プロダクション環境でHTTPSを強制)
- ページネーションのスタイルを全体で統一する設定
- キャッシュやセッションに関する設定
リポジトリパターン
- ビジネスロジックからデータの保存、取り出しなどをリポジトリ層へ分離し、隠蔽すること
- コードのメンテナンス性やテスト容易性を高める
実装
- ビジネスロジック <- インタフェースをインジェクション
- データアクセス毎に具象クラスを作成
DIをインタフェースにすることのメリット
テスト時にモック差し替えが簡単
class UserService
{
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function getUser($id)
{
return $this->userRepository->find($id);
}
}
use Tests\TestCase;
use App\Repositories\UserRepositoryInterface;
use App\Services\UserService;
class UserServiceTest extends TestCase
{
public function testGetUser()
{
// リポジトリのモックを作成
$mockRepo = $this->createMock(UserRepositoryInterface::class);
// モックがfindメソッドで特定の値を返すように設定
$mockRepo->method('find')->willReturn((object) ['id' => 1, 'name' => 'John Doe']);
// モックをサービスクラスに注入
$service = new UserService($mockRepo);
// サービスメソッドを呼び出して期待通りの結果を確認
$result = $service->getUser(1);
$this->assertEquals('John Doe', $result->name);
}
}
Eloquent
- ORM。DBとモデルを関連付けてデータ操作が可能。1つのテーブルに対して1つのEloquentクラス(モデル)を紐づけて利用
クエリビルダ
- メソッドチェーンを使ってSQLを組み立てて発行する
- クエリビルダの取得はDBファサードから取得する方法とその実体のllluminate/database/Connectionから取得する方法がある
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->get();
foreach ($users as $user) {
echo $user->name;
}
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Facades\App;
public function someMethod()
{
// サービスコンテナからDatabaseManagerインスタンスを取得
$dbManager = App::make(DatabaseManager::class);
// usersテーブルからすべてのユーザーを取得
$users = $dbManager->table('users')->get();
foreach ($users as $user) {
echo $user->name;
}
}
Eloquentとクエリビルダ
基本的には、シンプルなCRUD操作やリレーションがある場面ではEloquentを使い、複雑なクエリやパフォーマンス重視の場面ではクエリビルダを使うのが推奨されます。
// ユーザーとその投稿を取得
$user = User::with('posts')->find(1);
// 条件付きで集約関数やJOINを使ったクエリ
$results = DB::table('orders')
->select('customer_id', DB::raw('SUM(amount) as total'))
->where('status', 'completed')
->groupBy('customer_id')
->having('total', '>', 1000)
->get();
エラーハンドリング
- アプリケーション内でエラーハンドリングされなかった場合、App¥Exceptions¥Handlerクラスがエラーハンドリングを担うようになっている
- catch-tryしたらhandlerは呼ばれない。catchしてもthrowしたら呼ばれる
Handlerクラスの役割
-
カスタム例外処理
- 特定の例外に対してカスタムの処理を追加することができます。例えば、特定の例外が発生した場合に、カスタムのエラーページを表示したり、特定のアクションを実行したりすることが可能です。
-
例外のレポート
- report メソッドをオーバーライドすることで、どの例外を報告するか、どの例外を無視するかを制御できます。
-
例外のレンダリング
- render メソッドをオーバーライドすることで、例外が発生した際にどのようなレスポンスを返すかをカスタマイズできます。例えば、API では JSON レスポンスを返すなどの処理が可能です
- あとは404、419画面をカスタマイズするとか
認証
config/auth.phpで認証設定
// ユーザを認証する方法を定義
'guards' => [
// ガードの名前
'web' => [
// 認証を行うための方法(ドライバー)を指定
// セッションベースの認証を使用することを意味
// ユーザーの認証情報がセッションに保存され、次回以降のリクエストでそのセッションを使用してユーザーを特定する。
// セッションIDを用いた認証ってこと
'driver' => 'session',
// ユーザー情報を取得するための方法「ユーザープロバイダー」を指定
'provider' => 'users',
],
//
'providers' => [
'users' => [
'driver' => 'eloquent',
// App\Models\Userモデルからユーザー情報を取得
'model' => env('AUTH_MODEL', App\Models\User::class),
],
],
],
- webガードを使用することで、セッションベースの認証が行われ、usersプロバイダーからユーザー情報が取得され
- セッションベースとはユーザーがWebアプリケーションにアクセスした際に、そのユーザーの状態(ログイン情報やその他のデータ)をサーバー側で保存し、管理するための仕組み
セッションの流れ
-
ユーザーがログイン:
- ユーザーがログインフォームに必要な情報(例: メールアドレスとパスワード)を入力して送信します。
-
認証の確認:
- サーバーは提供された情報を使用して、データベースや他のストレージからユーザー情報を確認します。認証が成功すると、ユーザーはログインした状態になります。
-
セッションの生成:
- サーバーはユーザーの情報を含むセッションを作成します。これには、セッションIDが生成され、サーバーのメモリやデータベースに保存されます。このセッションIDは、後続のリクエストにおいてユーザーを特定するために使用されます。
-
クッキーの設定:
- サーバーは生成したセッションIDをクッキーとしてユーザーのブラウザに送信します。ブラウザはこのクッキーを保存し、次回以降のリクエストでこのクッキーをサーバーに送信します
-
ユーザーの状態を維持:
- クライアントからのリクエストにセッションIDが含まれると、サーバーはそのIDを使ってどのユーザーがリクエストを行ったのかを特定し、必要なユーザー情報を取得します。これにより、ユーザーはログイン状態を維持できるようになります
パスワードリセット
パスワードリセットの流れ
- ユーザーがパスワードリセットを要求: ユーザーがパスワードリセットフォームに自分のメールアドレスを入力して送信します
- リセットトークンの生成: サーバー側でリセットトークンが生成されusers_password_reset_tokens テーブルにメールアドレスとともに保存されます
- リセットリンクの送信: ユーザーにパスワードリセットリンク(トークン付きURL)がメールで送信されます
- トークンの検証: ユーザーがそのリンクをクリックすると、サーバーはトークンを検証し、パスワードリセット画面を表示します
- 新しいパスワードの設定: トークンが有効であれば、新しいパスワードを設定できます
マイグレーション
- PHPのソースコードでDBに対してcreate分野alter文を発行する仕組み
- aratisanコマンドを使用。phpのコードで定義するため、gitでバージョン管理可能
マイグレーションファイル
- 定義を適用するコードとその定義を削除するコードを記述する
DBへの反映
php artisan migrate
- database/migrationsフォルダにあるマイグレーションファイルでデータベースにまだ反映されていないものを一括で反映させる
全てのマイグレーションを元に戻す
php artisan migrate:reset
直前に実行したマイグレーションに対して行われる
php artisan migrate:rollback
シーダー
- 初期データの投入や動作テストのためのテスト用データの投入の仕組み
Faker
より本番環境に近いテストデータを簡単に作成できるライブラリ
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use Faker\Factory as Faker;
class UserSeeder extends Seeder
{
public function run()
{
$faker = Faker::create();
// ユーザーを10件生成
for ($i = 0; $i < 10; $i++) {
User::create([
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => bcrypt('password'), // 固定のパスワードを設定
]);
}
}
}
Factory
- 大量データの投入
- seederクラスからFactoryクラスを呼び出す
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
protected $model = User::class;
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => bcrypt('password'), // 固定のパスワードを設定
'remember_token' => Str::random(10),
];
}
}
seederからfactoryの使用
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
class UserSeeder extends Seeder
{
public function run()
{
// ユーザーを50件作成
User::factory()->count(50)->create();
}
}
Lavael11のフロント
-
laravelMixからviteに変更された
-
以下の場合はビルドが必要だった。そしてビルド時間が遅く、開発効率が悪くなる傾向があった。viteでビルド時間が短くなったらしい
- SCSS、Less などの CSS プリプロセッサを使用しているとき
- ES6+ や TypeScript などのモダンな JavaScript 構文を使用しているとき
- Vue.js、React などのフロントエンドフレームワークを使用しているとき
- Tailwind CSS などの CSS フレームワークを使用しているとき
-
jQuery やシンプルな JavaScript ファイルを public/ ディレクトリに配置しているときはビルドは必要ない。ただし、resoures以下に配置してビルドしたほうがメリットがある
@vite
- ブラウザが http://localhost:8000 にアクセス
- Laravel アプリケーションのルーティングに従って HTML ページを返す
- Blade テンプレート内の @vite ディレクティブにより、フロントエンドアセットが Vite 開発サーバー(http://localhost:5173)から読み込まれる
- Vite サーバーが JavaScript/CSS ファイルを提供し、HMR 機能によりリアルタイムで変更が反映される。