Laravel (PHP) チートシート

目的別 Laravel 機能リファレンス

目次

ルーティング (Routing)

HTTPリクエストのURLに応じて、実行する処理(コントローラのアクションなど)を定義します。

基本的なルート定義

基本的なHTTPメソッドに対応するルートを定義します。

HTTPメソッド説明コード例 (routes/web.php または routes/api.php)
GETリソースの取得
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
// クロージャを使用
Route::get('/welcome', function () { return 'Welcome!';
});
// コントローラのアクションを使用
Route::get('/users', [UserController::class, 'index']);
POSTリソースの新規作成
Route::post('/users', [UserController::class, 'store']);
PUTリソースの完全な置換/更新
Route::put('/users/{id}', [UserController::class, 'update']);
PATCHリソースの部分的な更新
Route::patch('/users/{id}', [UserController::class, 'updatePartial']);
DELETEリソースの削除
Route::delete('/users/{id}', [UserController::class, 'destroy']);
OPTIONSリソースがサポートするHTTPメソッドの問い合わせ
Route::options('/users', function () { // OPTIONSリクエストに対する処理
});
複数メソッド複数のHTTPメソッドに一致
// GET と POST に一致
Route::match(['get', 'post'], '/login', function () { // ...
});
// 全てのHTTPメソッドに一致
Route::any('/any-path', function () { // ...
});

ルートパラメータ

URLの一部を可変値として受け取ります。

種類説明コード例
必須パラメータ必ずURLに含まれる必要があるパラメータ
// /users/1 などにマッチ
Route::get('/users/{id}', function (string $id) { return 'User ID: ' . $id;
});
// 複数のパラメータ
Route::get('/posts/{postId}/comments/{commentId}', function (string $postId, string $commentId) { return "Post: {$postId}, Comment: {$commentId}";
});
オプションパラメータURLに含まれていなくても良いパラメータ (デフォルト値指定可能)
// /users または /users/guest などにマッチ
Route::get('/users/{name?}', function (string $name = 'guest') { return 'User Name: ' . $name;
});
正規表現制約パラメータの形式を正規表現で制約
// id が数値のみにマッチ
Route::get('/items/{id}', function (string $id) { // ...
})->where('id', '[0-9]+');
// name がアルファベットのみにマッチ
Route::get('/category/{name}', function (string $name) { // ...
})->where('name', '[A-Za-z]+');
// 複数の制約を一括定義 (ヘルパ使用)
Route::pattern('id', '[0-9]+');
Route::get('/products/{id}', function (string $id) { // ...
});
// whereメソッド内で連想配列を使用
Route::get('/order/{id}/{type}', function (string $id, string $type) { // ...
})->where(['id' => '[0-9]+', 'type' => 'regular|express']);
// whereAlpha, whereNumber, whereAlphaNumeric ヘルパ
Route::get('/user/{name}', function (string $name) { /* ... */ })->whereAlpha('name');
Route::get('/item/{id}', function (string $id) { /* ... */ })->whereNumber('id');
Route::get('/product/{code}', function (string $code) { /* ... */ })->whereAlphaNumeric('code');
Route::get('/uuid/{uuid}', function (string $uuid) { /* ... */ })->whereUuid('uuid');

名前付きルート

ルートに名前を付け、URLの生成やリダイレクトを容易にします。

// ルート定義時に名前を付ける
Route::get('/user/profile', [UserProfileController::class, 'show'])->name('profile.show');
// URLの生成
$url = route('profile.show'); // /user/profile が生成される
// パラメータ付きルートのURL生成
Route::get('/user/{id}/profile', [UserProfileController::class, 'showById'])->name('profile.showById');
$url = route('profile.showById', ['id' => 1]); // /user/1/profile が生成される
// リダイレクトで使用
return redirect()->route('profile.show');

ルートグループ

複数のルートに共通の属性(ミドルウェア、プレフィックス、名前空間など)を適用します。

種類説明コード例
ミドルウェアグループ内の全ルートにミドルウェアを適用
Route::middleware(['auth', 'verified'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); Route::get('/settings', [SettingsController::class, 'edit']);
});
コントローラグループ内の全ルートに共通のコントローラを指定
use App\Http\Controllers\OrderController;
Route::controller(OrderController::class)->group(function () { Route::get('/orders/{id}', 'show'); Route::post('/orders', 'store');
});
プレフィックスグループ内の全ルートのURIにプレフィックスを追加
Route::prefix('admin')->group(function () { Route::get('/users', [AdminUserController::class, 'index']); // /admin/users にマッチ Route::get('/products', [AdminProductController::class, 'index']); // /admin/products にマッチ
});
ルート名プレフィックスグループ内の全ルートの名前にプレフィックスを追加
Route::name('admin.')->prefix('admin')->group(function () { Route::get('/users', [AdminUserController::class, 'index'])->name('users.index'); // ルート名は admin.users.index
});
サブドメインルーティング特定のサブドメインに対してルートを定義
Route::domain('{account}.myapp.com')->group(function () { Route::get('user/{id}', function (string $account, string $id) { // ... });
});
組み合わせ複数の属性を組み合わせて適用
Route::middleware(['auth'])->prefix('api/v1')->name('api.v1.')->group(function () { Route::get('/posts', [ApiPostController::class, 'index'])->name('posts.index'); // 他のAPIルート
});

リソースコントローラ

CRUD操作に対応する標準的なルートを一行で定義します。

use App\Http\Controllers\PhotoController;
// 標準的なリソースルートを生成
Route::resource('photos', PhotoController::class);
// 一部のアクションのみを生成
Route::resource('photos', PhotoController::class)->only(['index', 'show']);
// 特定のアクションを除外して生成
Route::resource('photos', PhotoController::class)->except(['create', 'store', 'update', 'destroy']);
// API用のリソースルート (create, edit を除く)
Route::apiResource('videos', VideoController::class);

生成されるルート:

メソッドURIアクションルート名
GET/photosindexphotos.index
GET/photos/createcreatephotos.create
POST/photosstorephotos.store
GET/photos/{photo}showphotos.show
GET/photos/{photo}/editeditphotos.edit
PUT/PATCH/photos/{photo}updatephotos.update
DELETE/photos/{photo}destroyphotos.destroy

ルートモデルバインディング

ルートパラメータに対応するモデルインスタンスを自動的に注入します。

種類説明コード例
暗黙的バインディングルートセグメント名とタイプヒントされた変数名が一致する場合に自動解決
use App\Models\User;
// ルート定義 (routes/web.php)
Route::get('/users/{user}', function (User $user) { // {user} と $user が一致 return $user->email;
});
// コントローラでの利用 (App/Http/Controllers/UserController.php)
public function show(User $user) // タイプヒントされた User モデル
{ return view('user.profile', ['user' => $user]);
}
// ルート定義 (routes/web.php)
Route::get('/users/{user}', [UserController::class, 'show']);
キーのカスタマイズ (暗黙的)`id`以外のカラムでモデルを検索する場合
// ルート定義 (name カラムで検索)
Route::get('/posts/{post:slug}', function (App\Models\Post $post) { return $post;
});
// モデル側でキーをデフォルト指定 (App/Models/Post.php)
public function getRouteKeyName()
{ return 'slug'; // デフォルトで slug カラムを使う
}
// ルート定義 (上記モデル設定があれば :slug は不要)
Route::get('/posts/{post}', function (App\Models\Post $post) { return $post;
});
明示的バインディング`RouteServiceProvider` でバインディングのロジックを明示的に定義
// App/Providers/RouteServiceProvider.php の boot メソッド内
use App\Models\User;
use Illuminate\Support\Facades\Route;
public function boot()
{ Route::model('user_custom', User::class); // {user_custom} パラメータを User モデルにバインド // カスタム解決ロジック Route::bind('user_by_name', function (string $value) { return User::where('name', $value)->firstOrFail(); }); parent::boot();
}
// ルート定義 (routes/web.php)
Route::get('/users/custom/{user_custom}', function (User $user) { // $user は Route::model で解決された User インスタンス return $user;
});
Route::get('/users/name/{user_by_name}', function (User $user) { // $user は Route::bind で解決された User インスタンス return $user;
});

フォールバックルート

他のどのルートにも一致しないリクエストを処理します。

// routes/web.php の最後に定義
Route::fallback(function () { return response()->view('errors.404', [], 404);
});

レート制限

特定のルートやルートグループへのアクセス頻度を制限します。

// App/Providers/RouteServiceProvider.php の configureRateLimiting メソッド内
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;
protected function configureRateLimiting()
{ RateLimiter::for('api', function (Request $request) { // 認証済みユーザーは1分間に1000回、ゲストは10回まで return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip()); }); RateLimiter::for('uploads', function (Request $request) { // 1分間に5回まで (ユーザーIDで制限) return $request->user() ? Limit::perMinute(5)->by($request->user()->id) : Limit::none(); // 未認証ユーザーは許可しない });
}
// ルート定義での適用 (routes/api.php)
Route::middleware(['api', 'throttle:api'])->group(function () { // APIルート
});
// 個別ルートへの適用 (routes/web.php)
Route::post('/photos', [PhotoController::class, 'store'])->middleware('throttle:uploads');

コントローラ (Controller)

リクエストを処理し、レスポンスを返すロジックを担当します。

基本的なコントローラの作成

Artisanコマンドでコントローラを生成します。

php artisan make:controller PhotoController

生成されたコントローラの例 (app/Http/Controllers/PhotoController.php):

namespace App\Http\Controllers;
use Illuminate\Http\Request; // Requestクラスをインポート
class PhotoController extends Controller
{ /** * 写真の一覧を表示する */ public function index() { // ロジックを記述 (例: DBから写真を取得) $photos = \App\Models\Photo::all(); return view('photos.index', ['photos' => $photos]); } /** * 特定の写真を表示する */ public function show(string $id) { $photo = \App\Models\Photo::findOrFail($id); return view('photos.show', ['photo' => $photo]); } // 他のアクションメソッド (create, store, edit, update, destroy)
}

リソースコントローラ

CRUD操作に対応するメソッドをあらかじめ含んだコントローラを生成します。

# 基本的なリソースコントローラ
php artisan make:controller PostController --resource
# モデルも同時に生成・関連付け
php artisan make:controller CommentController --resource --model=Comment

--resource オプションを付けると、index, create, store, show, edit, update, destroy メソッドがスタブとして生成されます。

--api オプションを付けると、API向けに index, store, show, update, destroy メソッドが生成されます (create, edit は含まれない)。

php artisan make:controller Api/ProductController --api

単一アクションコントローラ

一つのアクションのみを持つコントローラを作成します。__invoke メソッドが定義されます。

php artisan make:controller ProvisionServer --invokable

生成されたコントローラの例 (app/Http/Controllers/ProvisionServer.php):

namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProvisionServer extends Controller
{ /** * Handle the incoming request. */ public function __invoke(Request $request) { // 単一アクションのロジックを記述 return 'Server provisioned!'; }
}

ルート定義 (routes/web.php):

use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);

依存性の注入 (DI)

コントローラのコンストラクタやメソッドで、必要なクラス (リポジトリ、サービスなど) をタイプヒントすることで、Laravelのサービスコンテナが自動的にインスタンスを注入します。

namespace App\Http\Controllers;
use App\Repositories\UserRepository; // 注入したいクラス
use Illuminate\Http\Request;
class UserController extends Controller
{ protected $users; // コンストラクタインジェクション public function __construct(UserRepository $users) { $this->users = $users; } // メソッドインジェクション (RequestもDI) public function store(Request $request) { // $request や $this->users を利用して処理 $validatedData = $request->validate([ 'name' => 'required|max:255', 'email' => 'required|email|unique:users', 'password' => 'required|min:8', ]); $user = $this->users->create($validatedData); return redirect('/users')->with('success', 'User created!'); }
}

コントローラでのミドルウェア適用

コントローラのコンストラクタ内で middleware メソッドを呼び出し、特定のミドルウェアを適用できます。

class UserController extends Controller
{ public function __construct() { // このコントローラの全てのアクションに 'auth' ミドルウェアを適用 $this->middleware('auth'); // 'store' と 'update' アクションのみに 'can:update-user' ミドルウェアを適用 $this->middleware('can:update-user')->only(['store', 'update']); // 'index' アクション以外に 'admin' ミドルウェアを適用 $this->middleware('admin')->except(['index']); } // ... アクションメソッド ...
}

リクエスト (Request)

HTTPリクエストに関する情報を取得し、バリデーションを行います。

リクエストインスタンスの取得

方法説明コード例
メソッドインジェクションコントローラのアクションメソッドやクロージャルートで Illuminate\Http\Request をタイプヒントする
use Illuminate\Http\Request;
Route::post('/users', function (Request $request) { $name = $request->input('name'); // ...
});
// UserController.php
public function store(Request $request)
{ $email = $request->input('email'); // ...
}
request() ヘルパどこからでも現在のリクエストインスタンスを取得
$request = request();
$ipAddress = $request->ip();
Request ファサードファサード経由でリクエストインスタンスのメソッドにアクセス (静的呼び出し)
use Illuminate\Support\Facades\Request;
$uri = Request::path(); // Request:: は Illuminate\Http\Request のインスタンスメソッドを呼ぶ

リクエスト情報の取得

メソッド説明コード例 ($requestRequest インスタンス)
path()リクエストパスを取得 (例: users/profile)
$path = $request->path();
is()リクエストパスが特定のパターンに一致するか確認 (ワイルドカード * 使用可)
if ($request->is('admin/*')) { ... }
if ($request->is('users*')) { ... }
routeIs()リクエストが特定の名前に一致するか確認 (ワイルドカード * 使用可)
if ($request->routeIs('admin.*')) { ... }
url()クエリ文字列を含まないURLを取得
$url = $request->url(); // 例: http://example.com/users
fullUrl()クエリ文字列を含む完全なURLを取得
$fullUrl = $request->fullUrl(); // 例: http://example.com/users?page=1
method()HTTPリクエストメソッドを取得 (GET, POST, PUT, PATCH, DELETE)
$method = $request->method();
isMethod()リクエストメソッドが特定のものであるか確認
if ($request->isMethod('post')) { ... }
header()特定のリクエストヘッダの値を取得
$contentType = $request->header('Content-Type');
$token = $request->header('X-CSRF-TOKEN');
$userAgent = $request->header('User-Agent', 'Default Agent'); // 第2引数はデフォルト値
bearerToken()AuthorizationヘッダからBearerトークンを取得
$token = $request->bearerToken();
ip()クライアントのIPアドレスを取得
$ip = $request->ip();
userAgent()クライアントのUser-Agent文字列を取得
$userAgent = $request->userAgent();
getScheme()リクエストのスキーマ(http or https)を取得
$scheme = $request->getScheme();
isSecure()HTTPSリクエストかどうかを判定
if ($request->isSecure()) { ... }
ajax() / isXmlHttpRequest()リクエストがAjaxリクエスト (XMLHttpRequest) かどうかを判定
if ($request->ajax()) { ... }
expectsJson()クライアントがJSONレスポンスを期待しているか (Acceptヘッダをチェック)
if ($request->expectsJson()) { ... }
wantsJson()クライアントがJSONレスポンスを受け入れ可能か(Acceptヘッダをチェック、Ajax含む)
if ($request->wantsJson()) { ... }

入力値の取得

GETパラメータ、POSTパラメータ、JSONペイロードなど、様々な方法で送信された入力値を取得します。

メソッド説明コード例 ($requestRequest インスタンス)
input()指定したキーの入力値を取得 (POST, GET問わず)。ドット記法で配列要素にアクセス可。
// <input type="text" name="username"> の値
$username = $request->input('username');
// デフォルト値を指定
$role = $request->input('role', 'guest');
// 配列入力 user[name] の値
$name = $request->input('user.name');
// JSONペイロード {"product": {"id": 1}} の値
$productId = $request->input('product.id');
query()クエリ文字列 (GETパラメータ) からのみ値を取得
// /search?q=laravel
$searchTerm = $request->query('q');
$page = $request->query('page', 1); // デフォルト値
post()リクエストボディ (POSTパラメータ) からのみ値を取得
// <input type="hidden" name="_token"> の値
$token = $request->post('_token');
string()入力値を文字列として取得(Stringable インスタンス)
$name = $request->string('name')->trim()->upper();
boolean()入力値を真偽値として取得 (1, “1”, true, “true”, “on”, “yes” が true)
$isAdmin = $request->boolean('is_admin');
date()入力値をCarbonImmutableインスタンスとして取得
// 'Y-m-d' フォーマット、'UTC' タイムゾーンでパース
$birthday = $request->date('birthday', 'Y-m-d', 'UTC');
動的プロパティinput() のショートカットとしてプロパティアクセス
$username = $request->username; // $request->input('username') と同じ
all()全ての入力値(クエリ文字列+リクエストボディ)を配列として取得
$allData = $request->all();
only()指定したキーの入力値のみを配列として取得
$credentials = $request->only(['email', 'password']);
except()指定したキー以外の入力値を配列として取得
$userData = $request->except(['_token', 'password_confirmation']);
has()指定したキーの入力値が存在するか確認 (空文字列やnullでもtrue)
if ($request->has('name')) { ... }
if ($request->has(['name', 'email'])) { ... } // 複数キーの存在確認
filled()指定したキーの入力値が存在し、かつ空文字列でないか確認
if ($request->filled('description')) { ... }
if ($request->filled(['title', 'body'])) { ... } // 複数キー
missing()指定したキーの入力値が存在しないか確認
if ($request->missing('optional_field')) { ... }
whenHas()キーが存在する場合にクロージャを実行
$request->whenHas('name', function (string $input) { // 'name' が存在する場合の処理
});
whenFilled()キーが存在し空でない場合にクロージャを実行
$request->whenFilled('email', function (string $input) { // 'email' が存在し空でない場合の処理
}, function () { // 'email' が存在しないか空の場合の処理 (オプション)
});
merge()リクエスト入力にデータを追加(上書き)
$request->merge(['votes' => 0]);

ファイルアップロードの処理

メソッド説明コード例 ($requestRequest インスタンス)
file()指定したキーのアップロードファイルを取得 (UploadedFile インスタンス)
// <input type="file" name="avatar">
$file = $request->file('avatar');
hasFile()指定したキーにファイルがアップロードされたか確認
if ($request->hasFile('photo')) { ... }
isValid() (UploadedFile)ファイルが正常にアップロードされたか確認
if ($request->file('document') && $request->file('document')->isValid()) { // 有効なファイル
}
path() (UploadedFile)アップロードされた一時ファイルのパスを取得
$path = $request->file('avatar')->path();
extension() / clientExtension() (UploadedFile)ファイルの拡張子を取得 (推測 / オリジナル)
$extension = $request->file('image')->extension(); // MIMEタイプから推測
$clientExtension = $request->file('image')->clientExtension(); // 元のファイル名から
getClientOriginalName() (UploadedFile)アップロードされた元のファイル名を取得
$originalName = $request->file('report')->getClientOriginalName();
getClientMimeType() (UploadedFile)アップロードされた元のMIMEタイプを取得
$mimeType = $request->file('report')->getClientMimeType();
getSize() (UploadedFile)ファイルサイズをバイト単位で取得
$size = $request->file('video')->getSize();
store() (UploadedFile)ファイルを指定したディスクの指定したパスに保存 (ファイル名は自動生成)
// 'avatars' ディレクトリ (デフォルトディスク内) に保存
$path = $request->file('avatar')->store('avatars');
// 's3' ディスクの 'photos' ディレクトリに保存
$path = $request->file('photo')->store('photos', 's3');
storeAs() (UploadedFile)ファイルを指定したディスクの指定したパス・ファイル名で保存
// 'avatars' ディレクトリに 'user123.jpg' として保存
$path = $request->file('avatar')->storeAs('avatars', 'user123.jpg');
// 's3' ディスクの 'backups' ディレクトリに 'backup.zip' として保存
$path = $request->file('backup')->storeAs('backups', 'backup.zip', 's3');

リクエストの検証 (Validation)

入力値が期待するルールに適合しているか検証します。

方法説明コード例
validate() メソッド (コントローラ)Request インスタンスの validate メソッドを使用。バリデーション失敗時は自動でリダイレクト(またはValidationExceptionスロー)。
// UserController.php の store メソッド内
public function store(Request $request)
{ $validated = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => ['nullable', 'date'], 'status' => 'in:draft,published', ]); // バリデーションが成功した場合のみ、ここに到達 // $validated には検証済みのデータのみが含まれる Post::create($validated); return redirect('/posts');
}
Form Requestバリデーションロジックを専用のクラスに分離。php artisan make:request StorePostRequest で作成。
// app/Http/Requests/StorePostRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StorePostRequest extends FormRequest
{ /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { // 認可ロジック (例: ログインしているか) return true; // ここでは単純に許可 } /** * Get the validation rules that apply to the request. * * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string> */ public function rules(): array { return [ 'title' => ['required', Rule::unique('posts')->ignore($this->post?->id), 'max:255'], 'body' => 'required|min:10', 'category_id' => 'required|exists:categories,id', 'tags.*' => 'integer|exists:tags,id', // 配列入力のバリデーション ]; } /** * Get custom messages for validator errors. */ public function messages(): array { return [ 'title.required' => 'タイトルは必須です。', 'body.required' => '本文を入力してください。', 'category_id.exists' => '選択されたカテゴリは存在しません。', ]; } /** * Get custom attributes for validator errors. */ public function attributes(): array { return [ 'category_id' => 'カテゴリ', 'tags.*' => 'タグ', ]; }
}
// コントローラでの利用 (メソッドインジェクション)
// PostController.php
use App\Http\Requests\StorePostRequest;
public function store(StorePostRequest $request) // Request の代わりに StorePostRequest をタイプヒント
{ // ここに来る時点でバリデーションと認可は完了している $validated = $request->validated(); // 検証済みデータを取得 Post::create($validated); return redirect('/posts');
}
手動バリデータ作成Validator ファサードを使って手動でバリデータを作成・実行。より細かい制御が可能。
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
public function update(Request $request, string $id)
{ $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:50', 'email' => 'required|email', ], [ 'name.required' => '名前は必須項目です。', 'email.required' => 'メールアドレスは必須項目です。', 'email.email' => '有効なメールアドレスを入力してください。', ]); if ($validator->fails()) { // バリデーション失敗時の処理 // throw new ValidationException($validator); // 例外をスロー return redirect('profile/edit') ->withErrors($validator) // エラーメッセージをセッションに格納 ->withInput(); // 入力値をセッションに格納 } // バリデーション成功 $validated = $validator->validated(); // または $validated = $validator->safe()->all(); (ネストされていない場合) // ... 更新処理 ...
}

利用可能なバリデーションルールは多岐にわたります(required, string, integer, numeric, email, unique, exists, date, min, max, size, in, not_in, file, image, mimes, dimensions, confirmed, url, ip, regex, カスタムルールなど)。詳細は公式ドキュメントを参照してください。

レスポンス (Response)

コントローラやルートクロージャからクライアントへ返す内容(HTML, JSON, リダイレクトなど)を生成します。

基本的なレスポンス

種類説明コード例
文字列単純な文字列を返す
Route::get('/hello', function () { return 'Hello World'; // Content-Type: text/html
});
配列 / Eloquentモデル/コレクション配列やEloquentを返すと自動的にJSONに変換される
Route::get('/api/users', function () { // 配列 // return ['name' => 'Alice', 'age' => 30]; // Eloquentコレクション return App\Models\User::all(); // Content-Type: application/json
});
response() ヘルパResponseFactory インスタンスを生成し、様々なレスポンスを構築
Route::get('/custom', function () { return response('Custom content', 201) // ステータスコード指定 ->header('Content-Type', 'text/plain') ->header('X-Custom-Header', 'value');
});
JSONレスポンス明示的にJSONレスポンスを生成 (Content-Type: application/json)
Route::get('/api/data', function () { return response()->json([ 'message' => 'Data fetched successfully', 'data' => ['id' => 1, 'value' => 'abc'] ], 200); // ステータスコード指定 (デフォルト 200)
});
// JSONPレスポンス (コールバック関数を指定)
Route::get('/api/jsonp', function () { return response() ->json(['status' => 'ok']) ->withCallback(request()->input('callback')); // ?callback=myFunc
});

ビューの返却

Bladeテンプレートなどのビューをレンダリングして返します。

use App\Models\User;
// view() ヘルパを使用
Route::get('/users/{id}', function (string $id) { $user = User::findOrFail($id); // resources/views/users/profile.blade.php を使用 return view('users.profile', ['user' => $user, 'title' => 'User Profile']);
});
// ビューが存在するか確認
if (view()->exists('emails.customer')) { // ...
}
// データを全ビューで共有 (AppServiceProvider の boot メソッドなど)
use Illuminate\Support\Facades\View;
View::share('siteName', 'My Awesome Site');

リダイレクト

ユーザーを別のURLにリダイレクトさせます。

種類説明コード例
redirect() ヘルパ基本的なリダイレクトレスポンスを生成
// 指定したパスへリダイレクト
Route::post('/tasks', function () { // ... タスク作成処理 ... return redirect('/dashboard');
});
// 前のURLへリダイレクト
Route::post('/update/profile', function (Request $request) { // ... 更新処理 ... return back()->with('status', 'Profile updated!'); // withでフラッシュメッセージ追加
});
名前付きルートへroute() メソッドで名前付きルートへリダイレクト
Route::post('/posts', function (Request $request) { // ... 投稿作成処理 ... $post = Post::create(...); // ルート名 'posts.show' へリダイレクト (パラメータ付き) return redirect()->route('posts.show', ['post' => $post->id]);
});
コントローラアクションへaction() メソッドでコントローラのアクションへリダイレクト
use App\Http\Controllers\HomeController;
Route::get('/go-home', function () { return redirect()->action([HomeController::class, 'index']);
});
外部ドメインへaway() メソッドで外部サイトへリダイレクト
Route::get('/laravel-docs', function () { return redirect()->away('https://laravel.com/docs');
});
フラッシュデータ付きリダイレクト先に一時的なデータ(フラッシュデータ)を渡す
Route::post('/settings', function (Request $request) { // ... 設定保存処理 ... return redirect('profile') ->with('status', 'Settings saved successfully!') // キーと値 ->with('type', 'success'); // 複数設定可能
});
// リダイレクト先 (profile.blade.phpなど) で表示
@if (session('status')) <div class="alert alert-{{ session('type', 'info') }}"> {{ session('status') }} </div>
@endif
入力値をフラッシュバリデーション失敗時などに、直前の入力値をリダイレクト先に渡す
// バリデーション失敗時に自動で行われることが多い
return back()->withInput();
// 特定の入力値のみフラッシュ
return back()->withInput( $request->except('password')
);
// リダイレクト先 (フォームなど) で古い値を取得
<input type="email" name="email" value="{{ old('email') }}">

その他のレスポンスタイプ

種類説明コード例
ファイルダウンロードユーザーにファイルをダウンロードさせるレスポンス
// public ディスク内のファイルをダウンロード
Route::get('/download/report', function () { return response()->download(storage_path('app/public/reports/report.pdf'));
});
// ダウンロード時のファイル名を指定
Route::get('/download/image/{id}', function (string $id) { $image = Image::findOrFail($id); return response()->download( storage_path("app/{$image->path}"), "user_image_{$id}.jpg", // ダウンロードファイル名 ['Content-Type' => 'image/jpeg'] // カスタムヘッダ (オプション) );
});
// メモリ上のデータをファイルとしてダウンロード
Route::get('/download/generated', function () { $content = "Generated text content."; return response()->streamDownload(function () use ($content) { echo $content; }, 'generated.txt');
});
ファイル表示 (インライン)ブラウザ内でファイルを表示させるレスポンス (ダウンロードさせない)
Route::get('/view/pdf/{id}', function (string $id) { $document = Document::findOrFail($id); return response()->file(storage_path("app/{$document->path}"), [ 'Content-Disposition' => 'inline; filename="' . basename($document->path) . '"' ]);
});
マクロ (カスタムレスポンス)Response ファクトリにカスタムレスポンスタイプを追加
// AppServiceProvider の boot メソッド内
use Illuminate\Support\Facades\Response;
public function boot()
{ Response::macro('caps', function (string $value) { return Response::make(strtoupper($value)); });
}
// ルートでの使用
Route::get('/shout', function () { return response()->caps('hello world'); // "HELLO WORLD" を返す
});

レスポンスヘッダの追加

return response($content) ->header('Content-Type', $type) ->header('X-Header-One', 'Header Value') ->withHeaders([ // 複数のヘッダを一括設定 'X-Header-Two' => 'Header Value 2', 'X-Header-Three' => 'Header Value 3', ]);

クッキーの操作

レスポンスにクッキーを添付します。

use Illuminate\Support\Facades\Cookie;
Route::get('/set-cookie', function () { // 方法1: response() ヘルパと withCookie() // return response('Cookie set!')->withCookie('my_cookie', 'cookie_value', 60); // 60分有効 // 方法2: Cookie ファサードで生成し、レスポンスに添付 $cookie = Cookie::make('another_cookie', 'another_value', 120); // 120分有効 return response('Another cookie set')->cookie($cookie); // 永続的なクッキー (約5年) // return response('Permanent cookie')->cookie(Cookie::forever('permanent', 'value')); // クッキーの削除 (有効期限を過去にする) // return response('Cookie removed')->withoutCookie('my_cookie'); // または // return response('Cookie removed')->cookie(Cookie::forget('my_cookie'));
});
// リクエストからクッキーを取得
Route::get('/get-cookie', function (Request $request) { $value = $request->cookie('my_cookie'); // リクエストオブジェクトから取得 // または // $value = Cookie::get('my_cookie'); // Cookieファサードから取得 (暗号化解除含む) return 'Cookie value: ' . $value;
});

注意: デフォルトではクッキーは暗号化されます。app/Http/Middleware/EncryptCookies.php$except プロパティで暗号化を除外するクッキーを指定できます。

ミドルウェア (Middleware)

HTTPリクエストがアプリケーションに到達する前、またはレスポンスが返される前に、フィルタリングや処理を実行します。

ミドルウェアの作成

Artisanコマンドでミドルウェアを生成します。

php artisan make:middleware CheckAge

生成されたミドルウェアの例 (app/Http/Middleware/CheckAge.php):

namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckAge
{ /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { // リクエストの前処理 (Before Middleware) if ($request->input('age') <= 18) { // 条件を満たさない場合、リダイレクトなどのレスポンスを返す return redirect('home'); } // 次のミドルウェアまたはコントローラのアクションへリクエストを渡す $response = $next($request); // レスポンスの後処理 (After Middleware) // $response->header('X-Processed-By', 'CheckAgeMiddleware'); return $response; }
}

ミドルウェアの登録

作成したミドルウェアをカーネル (app/Http/Kernel.php) に登録します。

種類場所説明コード例 (app/Http/Kernel.php)
グローバルミドルウェア$middleware プロパティ全てのHTTPリクエストに対して実行される
protected $middleware = [ // \App\Http\Middleware\TrustHosts::class, \App\Http\Middleware\TrustProxies::class, \Illuminate\Http\Middleware\HandleCors::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, // ここにカスタムグローバルミドルウェアを追加 // \App\Http\Middleware\LogRequests::class,
];
ルートミドルウェア (エイリアス)$middlewareAliases (Laravel 9以前は $routeMiddleware) プロパティ特定のルートやルートグループに割り当て可能なミドルウェアに名前(エイリアス)を付ける
protected $middlewareAliases = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, // ここにカスタムミドルウェアのエイリアスを追加 'check.age' => \App\Http\Middleware\CheckAge::class, 'role' => \App\Http\Middleware\CheckRole::class,
];
ミドルウェアグループ$middlewareGroups プロパティ複数のミドルウェアをグループ化し、まとめて適用可能にする (`web`, `api` グループがデフォルトで定義されている)
protected $middlewareGroups = [ 'web' => [ // webルート (routes/web.php) に自動適用 \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ // apiルート (routes/api.php) に自動適用 // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', // レート制限 (エイリアス使用) \Illuminate\Routing\Middleware\SubstituteBindings::class, ],
];
ミドルウェアの優先度$middlewarePriority プロパティグローバルミドルウェアの実行順序を制御(リストの上にあるものが先に実行される)
protected $middlewarePriority = [ \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class,
];

ルートへのミドルウェア割り当て

登録したミドルウェア(エイリアス)をルート定義で使用します。

方法説明コード例 (routes/web.php など)
個別ルートmiddleware() メソッドで指定
// 単一ミドルウェア
Route::get('/profile', [UserProfileController::class, 'show'])->middleware('auth');
// 複数ミドルウェア (配列で指定)
Route::post('/post', [PostController::class, 'store'])->middleware(['auth', 'verified']);
// クラス名を直接指定
use App\Http\Middleware\CheckSubscription;
Route::get('/premium-content', [ContentController::class, 'premium'])->middleware(CheckSubscription::class);
ルートグループグループ定義時に middleware() で指定
Route::middleware(['auth', 'admin'])->prefix('admin')->group(function () { Route::get('/dashboard', [AdminDashboard::class, 'index']); Route::resource('users', AdminUserController::class);
});
コントローラコントローラのコンストラクタ内で middleware() を使用 (前述)
// AdminController.php
public function __construct() { $this->middleware('auth'); $this->middleware('admin')->except('showPublicPage');
}

ミドルウェアパラメータ

ミドルウェアに追加のパラメータを渡すことができます。

// ミドルウェア定義 (app/Http/Middleware/CheckRole.php)
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CheckRole
{ public function handle(Request $request, Closure $next, string $role): Response // 第3引数以降でパラメータを受け取る { if (! $request->user() || ! $request->user()->hasRole($role)) { // 権限がない場合の処理 (例: abort(403)) abort(403, 'Unauthorized action.'); } return $next($request); }
}
// Kernel.php にエイリアス登録
// 'role' => \App\Http\Middleware\CheckRole::class,
// ルート定義でパラメータを指定 (エイリアス名の後にコロン `:` で区切り、カンマ `,` で複数指定可)
Route::put('/post/{id}', function (string $id) { // 編集処理
})->middleware('role:editor'); // 'editor' ロールが必要
Route::delete('/post/{id}', function (string $id) { // 削除処理
})->middleware('role:admin,moderator'); // 'admin' または 'moderator' ロールが必要

Terminable ミドルウェア

レスポンスがブラウザに送信された後に処理を実行したい場合に使用します。ミドルウェアクラスに terminate メソッドを追加します。

// app/Http/Middleware/LogSlowQueries.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response; // Response をインポート
class LogSlowQueries
{ public function handle(Request $request, Closure $next): Response { return $next($request); // 通常の処理 } /** * レスポンス送信後の処理 */ public function terminate(Request $request, Response $response): void // $response を受け取る { // 例えば、リクエスト処理時間が長かった場合にログを記録する $startTime = defined('LARAVEL_START') ? LARAVEL_START : $request->server('REQUEST_TIME_FLOAT'); $duration = microtime(true) - $startTime; if ($duration > 1) { // 1秒以上かかった場合 Log::warning("Slow query detected: {$request->fullUrl()} took {$duration} seconds."); } }
}

terminate メソッドを持つミドルウェアは、グローバルミドルウェアリストまたはルートミドルウェアとして登録する必要があります。PHP-FPMを使用している場合、この機能は自動的に動作します。

HTMLを生成するためのテンプレートエンジン Blade の主要な構文と機能です。

基本的な構文

構文説明コード例 (.blade.php ファイル内)
{{ $variable }}変数の内容を表示 (自動的にHTMLエスケープされる)
<h1>{{ $pageTitle }}</h1>
<p>Hello, {{ $user->name }}.</p>
{!! $variable !!}変数の内容をエスケープせずに表示 (HTMLタグなどをそのまま出力。XSS脆弱性に注意!)
<div class="content"> {!! $htmlContent !!} <!-- $htmlContent が '<b>bold</b>' なら太字で表示 -->
</div>
@php ... @endphpBladeファイル内にPHPコードを直接記述 (多用は非推奨)
@php $counter = 1;
@endphp
<p>Counter: {{ $counter }}</p>
{{-- コメント --}}Bladeコメント (HTMLソースには出力されない)
{{-- これはユーザーには見えないコメントです --}}
<!-- これは通常のHTMLコメント (ソースには残る) -->

制御構文

ディレクティブ説明コード例
@if / @elseif / @else / @endif条件分岐
@if (count($records) === 1) 1レコード見つかりました。
@elseif (count($records) > 1) 複数レコード見つかりました。
@else レコードはありません。
@endif
@if ($user->isAdmin()) <p>管理者です</p>
@endif
@unless / @endunless@if の逆 (条件が false の場合に実行)
@unless (Auth::check()) ログインしてください。
@endunless
@isset / @endisset変数がセットされている場合に実行
@isset($errorMessage) <div class="error">{{ $errorMessage }}</div>
@endisset
@empty / @endempty変数が空 (empty() が true) の場合に実行
@empty($posts) <p>投稿はまだありません。</p>
@endempty
@auth / @else / @endauthユーザーが認証済みの場合に実行
@auth <p>ようこそ、 {{ Auth::user()->name }} さん</p>
@else <a href="/login">ログイン</a>
@endauth
@guest / @endguestユーザーが認証されていない(ゲスト)の場合に実行
@guest <p>ゲストユーザー向けのコンテンツです。</p>
@endguest
@foreach / @endforeach配列やコレクションのループ
<ul>
@foreach ($users as $user) <li>{{ $user->name }}</li>
@endforeach
</ul>
{{-- ループ変数 $loop の利用 --}}
@foreach ($items as $item) <div @if ($loop->first) class="first-item" @endif> Index: {{ $loop->index }} (0から) / Iteration: {{ $loop->iteration }} (1から) / Remaining: {{ $loop->remaining }} / Count: {{ $loop->count }} / Is First: {{ $loop->first ? 'Yes' : 'No' }} / Is Last: {{ $loop->last ? 'Yes' : 'No' }} / Depth: {{ $loop->depth }} / Parent Loop: {{ $loop->parent ? $loop->parent->iteration : 'N/A' }} <p>{{ $item->name }}</p> </div>
@endforeach
@forelse / @empty / @endforelse@foreach と同様だが、配列が空の場合の処理も記述できる
<ul>
@forelse ($products as $product) <li>{{ $product->name }} - ${{ $product->price }}</li>
@empty <li>商品は見つかりませんでした。</li>
@endforelse
</ul>
@for / @endfor一般的な for ループ
@for ($i = 0; $i < 10; $i++) 現在の値は {{ $i }} <br>
@endfor
@while / @endwhile一般的な while ループ
@php $counter = 0; @endphp
@while ($counter < 5) <p>Counter is {{ $counter }}</p> @php $counter++; @endphp
@endwhile
@break / @continueループの中断 / 次のイテレーションへスキップ ($loop 変数と共に使用可能)
@foreach ($users as $user) @if ($user->type === 'banned') @continue // Bannedユーザーはスキップ @endif <li>{{ $user->name }}</li> @if ($loop->iteration >= 5) @break // 5人表示したら終了 @endif
@endforeach
@switch / @case / @default / @endswitchSwitch文
@switch($status) @case('pending') <p>処理待ちです</p> @break @case('processing') <p>処理中です</p> @break @case('completed') <p>完了しました</p> @break @default <p>不明なステータスです</p>
@endswitch
@csrfCSRF保護用の hidden input フィールドを生成
<form method="POST" action="/profile"> @csrf <!-- フォーム要素 -->
</form>
@methodHTMLフォームがサポートしないHTTPメソッド (PUT, PATCH, DELETEなど) を指定するための hidden input フィールドを生成
<form method="POST" action="/posts/{{ $post->id }}"> @csrf @method('PUT') <!-- 更新用フォーム要素 -->
</form>
@include他のBladeファイルをインクルードする
<div class="header"> @include('partials.navigation') <!-- resources/views/partials/navigation.blade.php を読み込む -->
</div>
{{-- データを渡してインクルード --}}
@include('components.alert', ['type' => 'error', 'message' => 'エラーが発生しました'])
{{-- 条件付きインクルード --}}
@includeIf('partials.optional-sidebar')
{{-- 配列の最初の有効なビューをインクルード --}}
@includeFirst(['custom.header', 'partials.header'])
{{-- 変数がtrueの場合のみインクルード --}}
@includeWhen($user->isAdmin(), 'admin.panel')
@each配列の各要素に対して指定したビューをレンダリング
{{-- resources/views/partials/job.blade.php を各 $job に対してレンダリング --}}
@each('partials.job', $jobs, 'job', 'partials.no-jobs')
{{-- 第3引数: ビュー内で使う変数名 --}}
{{-- 第4引数(オプション): 配列が空の場合にレンダリングするビュー --}}

テンプレートの継承

レイアウトを定義し、特定の部分を子ビューで上書きします。

ディレクティブ説明コード例
@extends('layout.name')親レイアウトを指定 (子ビューの最初に記述)
// resources/views/home.blade.php
@extends('layouts.app') <!-- resources/views/layouts/app.blade.php を継承 -->
@section('title', 'ホームページ') <!-- titleセクションを定義 -->
@section('content') <!-- contentセクションを開始 --> <p>ようこそ!</p>
@endsection <!-- contentセクションを終了 -->
@section('sectionName') / @endsection親レイアウトの @yield に対応するコンテンツブロックを定義
@section('sidebar') <h3>サイドバー</h3> <ul>...</ul>
@endsection
{{-- 短いコンテンツの場合 (第2引数にコンテンツを指定) --}}
@section('page_id', 'home-page')
@yield('sectionName')子ビューで定義されたセクションの内容を挿入 (親レイアウトで使用)
<!-- resources/views/layouts/app.blade.php -->
<html>
<head> <title>@yield('title', 'デフォルトタイトル') - マイサイト</title> <!-- デフォルト値も指定可能 -->
</head>
<body> <div class="container"> @yield('content') <!-- 子ビューの content セクションが入る --> </div> <aside> @yield('sidebar') <!-- 子ビューの sidebar セクションが入る --> </aside>
</body>
</html>
@parent子ビューのセクション内で、親レイアウトの同名セクションの内容を挿入
// resources/views/child.blade.php
@extends('layouts.app')
@section('sidebar') @parent <!-- 親レイアウトの sidebar セクションの内容をまず表示 --> <p>子ビューで追加したコンテンツ</p>
@endsection

コンポーネント & スロット

再利用可能なUI部品を作成します。

種類説明コード例
クラスベースコンポーネントphp artisan make:component Alert で作成。View/Components/Alert.phpresources/views/components/alert.blade.php が生成される。
// View/Components/Alert.php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{ public string $type; public string $message; public function __construct(string $type = 'info', string $message) { $this->type = $type; $this->message = $message; } public function render() { return view('components.alert'); // 対応するBladeビューを返す } // コンポーネント内でのみ使うメソッドなど public function alertClass(): string { return 'alert-' . $this->type; }
}
// resources/views/components/alert.blade.php
<div class="alert {{ $alertClass() }}" role="alert"> {{ $message }} {{ $slot }} <!-- デフォルトスロットの表示場所 --> @isset($title) <!-- 名前付きスロットの表示場所 --> <h4>{{ $title }}</h4> @endisset
</div>
// ビューでの利用
<x-alert type="danger" message="操作に失敗しました。"> <!-- デフォルトスロットの内容 --> 詳細はこちら。 <!-- 名前付きスロット --> <x-slot:title> エラー発生 </x-slot>
</x-alert>
{{-- 自己終了タグも可能 --}}
<x-alert type="success" message="成功!" />
匿名コンポーネントPHPクラスを作成せず、resources/views/components/ ディレクトリに直接Bladeファイルを作成するだけ。
// resources/views/components/button.blade.php
@props(['type' => 'button', 'href' => null]) {{-- 受け取る属性を定義 --}}
@if ($href) <a href="{{ $href }}" {{ $attributes->merge(['class' => 'button']) }}> {{ $slot }} </a>
@else <button type="{{ $type }}" {{ $attributes->merge(['class' => 'button']) }}> {{ $slot }} </button>
@endif
// ビューでの利用
<x-button class="is-primary" id="submit-btn">送信</x-button>
<x-button :href="route('home')" class="is-link">ホームへ</x-button> {{-- 属性にPHP変数を渡す場合は : を付ける --}}
@component / @slot (旧式)古いスタイルのコンポーネント呼び出し方法。
@component('components.alert', ['type' => 'warning']) @slot('title') 警告 @endslot これは警告メッセージです。 <!-- デフォルトスロット -->
@endcomponent
属性バッグ $attributesコンポーネントに渡されたHTML属性 (propsで定義されていないもの) を保持。merge(), class(), filter() などで操作可能。
// components/input.blade.php
@props(['name', 'type' => 'text'])
<input name="{{ $name }}" type="{{ $type }}" {{ $attributes->merge(['class' => 'input-field']) }}>
// 利用側
<x-input name="email" type="email" class="is-danger" placeholder="メールアドレス" required />
{{-- class="is-danger" は 'input-field' とマージされる --}}
{{-- placeholder や required はそのまま input タグに追加される --}}

スタック (Stack)

レイアウトの特定箇所に、子ビューからJavaScriptやCSSコードを追加します。

<!-- 親レイアウト (layouts/app.blade.php) -->
<html>
<head> <!-- ... --> @stack('styles') <!-- 'styles' という名前のスタックを定義 -->
</head>
<body> <!-- ... --> @stack('scripts') <!-- 'scripts' という名前のスタックを定義 -->
</body>
</html>
<!-- 子ビュー (home.blade.php) -->
@extends('layouts.app')
@push('styles') <!-- 'styles' スタックにコンテンツを追加 --> <link rel="stylesheet" href="/css/home.css">
@endpush
@push('scripts') <!-- 'scripts' スタックにコンテンツを追加 --> <script src="/js/home.js"></script>
@endpush
@prepend('scripts') <!-- 'scripts' スタックの先頭にコンテンツを追加 --> <script>console.log('Script prepended');</script>
@endprepend

サービスインジェクション (@inject)

ビュー内でサービスコンテナからサービスを取得します。

@inject('metrics', 'App\Services\MetricsService') <!-- 'metrics' という変数名で MetricsService を注入 -->
<div> 現在のユーザー数: {{ $metrics->getActiveUsers() }}
</div>

カスタムBladeディレクティブ

AppServiceProviderboot メソッドなどで、独自のBladeディレクティブを定義できます。

// App/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{ public function boot(): void { // @datetime($timestamp) ディレクティブを定義 Blade::directive('datetime', function (string $expression) { // $expression にはディレクティブに渡された式 ('$timestamp' など) が文字列として入る return "<?php echo ($expression)->format('Y-m-d H:i'); ?>"; }); // @money($amount) ディレクティブ Blade::directive('money', function (string $amount) { return "<?php echo number_format($amount); ?>円"; }); // @admin / @endadmin カスタムif文 Blade::if('admin', function () { return auth()->check() && auth()->user()->isAdmin(); }); } // ...
}
// ビューでの使用例
<p>更新日時: @datetime($post->updated_at)</p>
<p>価格: @money($product->price)</p>
@admin <a href="/admin">管理画面へ</a>
@else <p>管理者ではありません。</p>
@endadmin

データベース (Database) & Eloquent ORM

データベース操作(マイグレーション、クエリビルダ、Eloquent ORM)に関する機能です。

マイグレーション (Migration)

データベーススキーマをバージョン管理します。

Artisan コマンド説明
php artisan make:migration create_users_table新しいマイグレーションファイルを作成 (例: users テーブル作成用)
php artisan make:migration add_votes_to_users_table --table=users既存テーブルを変更するマイグレーションを作成 (--table で対象テーブル指定)
php artisan migrateまだ実行されていないマイグレーションを実行
php artisan migrate --step実行可能なマイグレーションを1つだけ実行
php artisan migrate --path=database/migrations/feature特定のパスにあるマイグレーションのみを実行
php artisan migrate:rollback最後に実行されたマイグレーションのバッチをロールバック (down メソッド実行)
php artisan migrate:rollback --step=3最後の3バッチ分のマイグレーションをロールバック
php artisan migrate:reset全てのマイグレーションをロールバック
php artisan migrate:refresh全てのマイグレーションをロールバックし、再度全て実行 (DBリセット)
php artisan migrate:refresh --seedリフレッシュ後にシーダーも実行
php artisan migrate:fresh全てのテーブルを削除してから、全てのマイグレーションを実行
php artisan migrate:fresh --seedフレッシュ後にシーダーも実行
php artisan migrate:status各マイグレーションの実行状態を表示

マイグレーションファイル (例: database/migrations/YYYY_MM_DD_HHMMSS_create_users_table.php):

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{ /** * Run the migrations. */ public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id(); // increments('id') と primary() のエイリアス $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); // created_at と updated_at カラム (nullableではないTIMESTAMP) }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('users'); }
};

スキーマビルダ (Schema Builder)

マイグレーションファイル内でテーブルやカラムを定義・変更します。

テーブル操作

メソッド説明コード例
Schema::create()新しいテーブルを作成
Schema::create('posts', function (Blueprint $table) { ... });
Schema::table()既存のテーブルを変更
Schema::table('users', function (Blueprint $table) { ... });
Schema::dropIfExists()テーブルが存在すれば削除
Schema::dropIfExists('tasks');
Schema::drop()テーブルを削除 (存在しない場合エラー)
Schema::drop('old_logs');
Schema::rename()テーブル名を変更
Schema::rename('from_table', 'to_table');
Schema::hasTable()テーブルが存在するか確認
if (Schema::hasTable('users')) { ... }
Schema::hasColumn()テーブルにカラムが存在するか確認
if (Schema::hasColumn('users', 'email')) { ... }
// 複数カラムチェック
if (Schema::hasColumns('users', ['name', 'email'])) { ... }

カラム型 (Blueprint $table)

一部の例です。詳細は公式ドキュメントを参照してください。

メソッド説明
id()自動増分BIGINT主キー (id)$table->id();
bigIncrements('col')自動増分BIGINT主キー$table->bigIncrements('custom_id');
string('col', 長さ)VARCHARカラム (デフォルト長255)$table->string('name', 100);
text('col')TEXTカラム$table->text('description');
longText('col')LONGTEXTカラム$table->longText('article_content');
integer('col')INTEGERカラム$table->integer('votes');
unsignedBigInteger('col')符号なしBIGINT (外部キー用)$table->unsignedBigInteger('user_id');
float('col', 精度, スケール)FLOATカラム$table->float('amount', 8, 2);
decimal('col', 精度, スケール)DECIMALカラム$table->decimal('price', 10, 2);
boolean('col')BOOLEANカラム$table->boolean('is_active');
date('col')DATEカラム$table->date('birth_date');
dateTime('col', 精度)DATETIMEカラム$table->dateTime('published_at', 0);
timestamp('col', 精度)TIMESTAMPカラム$table->timestamp('email_verified_at', 0)->nullable();
timestamps(精度)created_at, updated_at TIMESTAMPカラム$table->timestamps();
softDeletes(カラム名, 精度)ソフトデリート用 deleted_at TIMESTAMPカラム$table->softDeletes();
enum('col', ['val1', 'val2'])ENUMカラム$table->enum('status', ['pending', 'completed']);
json('col')JSONカラム$table->json('options');
uuid('col')UUIDカラム$table->uuid('order_uuid');
foreignId('col')符号なしBIGINT外部キーカラム (規約に基づき定義)$table->foreignId('user_id'); // usersテーブルのidを参照
foreignIdFor(モデルクラス, カラム名)モデルクラスに基づき外部キーカラムを定義$table->foreignIdFor(App\Models\Post::class); // post_idカラム
morphs('col')ポリモーフィックリレーション用カラム (col_id, col_type)$table->morphs('commentable');
nullableMorphs('col')NULL許容のポリモーフィックリレーション用カラム$table->nullableMorphs('taggable');
rememberToken()ログイン記憶用 remember_token VARCHAR(100) カラム$table->rememberToken();

カラム修飾子 (メソッドチェーンで指定)

修飾子説明
nullable()カラムがNULL値を許容する$table->string('avatar_url')->nullable();
default(値)カラムのデフォルト値を設定$table->boolean('is_public')->default(false);
unsigned()数値型カラムを符号なしにする$table->integer('age')->unsigned();
unique()カラムにユニーク制約を追加$table->string('email')->unique();
index()カラムにインデックスを追加$table->string('zip_code')->index();
primary()カラムを主キーにする$table->uuid('id')->primary();
after('col')指定したカラムの後にカラムを配置 (MySQLのみ)$table->string('city')->after('address');
comment('コメント')カラムにコメントを追加$table->integer('status')->comment('1: active, 2: inactive');
storedAs('式')生成カラム (Stored)$table->string('full_name')->storedAs('concat(first_name, " ", last_name)');
virtualAs('式')生成カラム (Virtual)$table->integer('age')->virtualAs('YEAR(CURDATE()) - YEAR(birth_date)');
constrained()foreignId に外部キー制約を追加 (テーブル名・カラム名は規約に従う)$table->foreignId('user_id')->constrained();
constrained('テーブル名')テーブル名を指定して外部キー制約を追加$table->foreignId('author_id')->constrained('users');
onDelete('cascade')外部キーの参照先削除時の動作 (cascade, restrict, set null など)$table->foreignId('post_id')->constrained()->onDelete('cascade');

カラム変更・削除

メソッド説明
change()カラムの型や属性を変更 (doctrine/dbal が必要)$table->string('name', 50)->nullable()->change();
renameColumn('from', 'to')カラム名を変更$table->renameColumn('description', 'content');
dropColumn('col')カラムを削除$table->dropColumn('votes'); // 単一カラム $table->dropColumn(['votes', 'avatar']); // 複数カラム

インデックス操作

メソッド説明
$table->primary('id')主キーを追加 (単一カラム)$table->primary('uuid');
$table->primary(['col1', 'col2'])複合主キーを追加$table->primary(['order_id', 'product_id']);
$table->unique('email')ユニークインデックスを追加$table->unique('token', 'user_token_unique'); // インデックス名指定
$table->unique(['col1', 'col2'])複合ユニークインデックスを追加$table->unique(['lat', 'lng']);
$table->index('state')基本的なインデックスを追加$table->index(['col_a', 'col_b'], 'my_index_name');
$table->spatialIndex('location')空間インデックスを追加 (MySQL, PostgreSQL)$table->spatialIndex('coordinates');
$table->fullText('body')全文検索インデックスを追加 (MySQL, PostgreSQL)$table->fullText(['title', 'body']);
$table->dropPrimary('インデックス名')主キーを削除 (通常はテーブル名_primary)$table->dropPrimary('users_pkey');
$table->dropUnique('インデックス名')ユニークインデックスを削除$table->dropUnique('users_email_unique');
$table->dropIndex('インデックス名')インデックスを削除$table->dropIndex('posts_status_index');
$table->dropForeign('外部キー制約名')外部キー制約を削除 (通常はテーブル名_カラム名_foreign)$table->dropForeign(['user_id']); // カラム名で指定 (配列) $table->dropForeign('posts_user_id_foreign'); // 制約名で指定

クエリビルダ (Query Builder)

DB ファサードを使って、SQLクエリをプログラム的に構築・実行します。

基本的な取得

use Illuminate\Support\Facades\DB;
// テーブルの全レコードを取得 (Collection)
$users = DB::table('users')->get();
// 最初のレコードを取得 (stdClass オブジェクト or null)
$user = DB::table('users')->where('name', 'Alice')->first();
// 特定のカラムの値を取得
$email = DB::table('users')->where('id', 1)->value('email');
// 特定のIDのレコードを取得 (first() のショートカット)
$user = DB::table('users')->find(1); // 主キーが 'id' の場合
// 特定のカラムの値のリストを取得 (Collection)
$names = DB::table('users')->where('active', 1)->pluck('name'); // name の Collection
$userRoles = DB::table('users')->pluck('role', 'id'); // id をキー、role を値とする Collection
// 集計関数
$count = DB::table('orders')->count();
$maxPrice = DB::table('products')->max('price');
$avgScore = DB::table('results')->where('subject', 'Math')->avg('score');
$totalSales = DB::table('sales')->sum('amount');
// レコードが存在するか確認
if (DB::table('products')->where('id', 100)->exists()) { ... }
if (DB::table('logs')->where('level', 'error')->doesntExist()) { ... }

Select

// 特定のカラムのみ選択
$users = DB::table('users')->select('name', 'email as user_email')->get();
// 既存の select にカラムを追加
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
// 生のSQL式を使用 (SQLインジェクションに注意)
$users = DB::table('users') ->select(DB::raw('count(*) as user_count, status')) ->where('status', '<>', 1) ->groupBy('status') ->get();

Where句

メソッド説明
where('col', 'value')基本的なWHERE句 (=)DB::table('users')->where('votes', 100)
where('col', 'op', 'value')比較演算子を指定DB::table('products')->where('price', '>', 100)
where('col', '<>', 'value')!= (等しくない)DB::table('orders')->where('status', '<>', 'pending')
where('col', 'like', '%value%')LIKE検索DB::table('posts')->where('title', 'like', 'Laravel%')
where([['col1', 'val1'], ['col2', 'op', 'val2']])複数の条件 (AND) を配列で指定DB::table('users')->where([['status', 1], ['subscribed', '<>', 1]])
orWhere('col', 'value')OR条件を追加DB::table('items')->where('price', '<', 10)->orWhere('stock', 0)
where(function ($query) { ... })高度な条件グループ (括弧)DB::table('users') ->where('votes', '>', 100) ->orWhere(function ($query) { $query->where('name', 'Abigail') ->where('votes', '>', 50); })
whereBetween('col', [val1, val2])範囲内 (BETWEEN)DB::table('orders')->whereBetween('created_at', ['2024-01-01', '2024-12-31'])
whereNotBetween('col', [val1, val2])範囲外 (NOT BETWEEN)DB::table('products')->whereNotBetween('price', [10, 20])
whereIn('col', [val1, val2])リスト内のいずれか (IN)DB::table('users')->whereIn('id', [1, 2, 3])
whereNotIn('col', [val1, val2])リスト内のいずれでもない (NOT IN)DB::table('products')->whereNotIn('category_id', [4, 5])
whereNull('col')カラムがNULLDB::table('posts')->whereNull('published_at')
whereNotNull('col')カラムがNULLでないDB::table('users')->whereNotNull('email_verified_at')
whereDate('col', 'date')日付部分が一致DB::table('logs')->whereDate('created_at', '2024-04-03')
whereMonth('col', 'month')月が一致DB::table('birthdays')->whereMonth('birth_date', '12')
whereDay('col', 'day')日が一致DB::table('events')->whereDay('event_date', '15')
whereYear('col', 'year')年が一致DB::table('archives')->whereYear('created_at', '2023')
whereTime('col', 'op', 'time')時刻部分を比較DB::table('schedules')->whereTime('start_time', '>=', '09:00:00')
whereColumn('col1', 'op', 'col2')他のカラムと比較DB::table('products')->whereColumn('price', '>', 'cost')
whereExists(function ($query) { ... })サブクエリが存在するか (EXISTS)DB::table('users') ->whereExists(function ($query) { $query->select(DB::raw(1)) ->from('orders') ->whereColumn('orders.user_id', 'users.id'); })
whereJsonContains('col', $value)JSONカラムに特定の値が含まれるか (->)DB::table('users')->whereJsonContains('options->languages', 'en')
whereJsonLength('col', $length)JSON配列カラムの長さが一致するかDB::table('posts')->whereJsonLength('tags', 3)
whereFullText('col', 'term')全文検索 (MATCH AGAINST) (要インデックス)DB::table('articles')->whereFullText('body', 'database')

Ordering, Grouping, Limit, Offset

// 並び替え
$users = DB::table('users')->orderBy('name', 'desc')->get(); // 降順
$products = DB::table('products')->orderBy('price', 'asc')->orderBy('name', 'asc')->get(); // 複数カラム
// 最新・最古
$latestPost = DB::table('posts')->latest('published_at')->first(); // published_at が最新
$oldestUser = DB::table('users')->oldest()->first(); // created_at が最古
// ランダム順
$randomUser = DB::table('users')->inRandomOrder()->first();
// グループ化と集計
$report = DB::table('orders') ->select('status', DB::raw('COUNT(*) as total')) ->groupBy('status') ->having('total', '>', 10) // グループ化後の条件 (HAVING) ->get();
// 取得件数制限とスキップ
$users = DB::table('users') ->offset(10) // 最初の10件をスキップ ->limit(5) // 5件取得 ->get();
// ショートカット
$users = DB::table('users')->skip(10)->take(5)->get();

Joins

use Illuminate\Database\Query\JoinClause;
// INNER JOIN
$users = DB::table('users') ->join('contacts', 'users.id', '=', 'contacts.user_id') // (テーブル名, 自カラム, 演算子, 相手カラム) ->join('orders', 'users.id', '=', 'orders.user_id') ->select('users.*', 'contacts.phone', 'orders.price') ->get();
// LEFT JOIN
$posts = DB::table('posts') ->leftJoin('users', 'posts.user_id', '=', 'users.id') ->select('posts.title', 'users.name as author_name') ->get();
// RIGHT JOIN
$results = DB::table('results') ->rightJoin('students', 'results.student_id', '=', 'students.id') ->get();
// CROSS JOIN
$combinations = DB::table('sizes')->crossJoin('colors')->get();
// 高度なJoin句 (クロージャ使用)
DB::table('users') ->join('contacts', function (JoinClause $join) { $join->on('users.id', '=', 'contacts.user_id')->where('contacts.is_primary', '=', 1); }) ->get();
// サブクエリJoin
$latestPosts = DB::table('posts') ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at')) ->where('is_published', 1) ->groupBy('user_id');
$users = DB::table('users') ->joinSub($latestPosts, 'latest_posts', function (JoinClause $join) { $join->on('users.id', '=', 'latest_posts.user_id'); })->get();

Unions

$first = DB::table('users')->whereNull('first_name');
$users = DB::table('users')->whereNull('last_name')->union($first)->get(); // UNION
// $users = DB::table('users')->whereNull('last_name')->unionAll($first)->get(); // UNION ALL

Insert, Update, Delete

// 単一レコード挿入
DB::table('users')->insert([ 'email' => 'kayla@example.com', 'name' => 'Kayla', 'votes' => 0
]);
// 複数レコード挿入
DB::table('users')->insert([ ['email' => 'picard@example.com', 'name' => 'Picard', 'votes' => 1], ['email' => 'janeway@example.com', 'name' => 'Janeway', 'votes' => 2],
]);
// 挿入し、自動増分IDを取得 (PostgreSQLでは非推奨、MySQLでは動作)
// $id = DB::table('users')->insertGetId(['email' => 'john@example.com', 'name' => 'John', 'votes' => 0]);
// Upsert (あれば更新、なければ挿入) (ユニークキー or 主キーが必要)
DB::table('flights')->upsert( [ ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] ], ['departure', 'destination'], // 競合を判断するカラム (Unique Index) ['price'] // 競合した場合に更新するカラム
);
// 更新
$affectedRows = DB::table('users') ->where('id', 1) ->update(['votes' => 10, 'name' => 'Updated Name']);
// JSONカラムの更新
DB::table('users') ->where('id', 1) ->update(['options->enabled' => true]); // options カラムの enabled キーを更新
// 増分・減分 (Increment / Decrement)
DB::table('users')->where('id', 1)->increment('votes'); // votes を 1 増やす
DB::table('users')->where('id', 1)->increment('votes', 5); // votes を 5 増やす
DB::table('posts')->decrement('likes', 3); // likes を 3 減らす
DB::table('products')->where('id', 2)->increment('stock', 10, ['updated_at' => now()]); // 他のカラムも同時に更新
// 削除
DB::table('users')->where('votes', '<', 100)->delete();
// DB::table('users')->delete(); // テーブルの全レコード削除 (注意!)
// DB::table('users')->truncate(); // テーブルを空にする (高速だがロールバック不可)

悲観的ロック (Pessimistic Locking)

// 共有ロック (他のトランザクションは読み取り可能だが更新不可)
DB::table('users')->where('votes', '>', 100)->sharedLock()->get();
// 更新ロック (他のトランザクションは読み取りも更新も不可)
DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
// トランザクション内で使用する必要がある
DB::transaction(function () { $user = DB::table('users')->where('id', 1)->lockForUpdate()->first(); // ... ユーザーに対する更新処理 ... DB::table('users')->where('id', 1)->update(['status' => 'processed']);
});

Eloquent ORM

データベーステーブルと対応するモデルクラスを通じて、オブジェクト指向的な方法でデータを操作します。

モデル定義

php artisan make:model Post --migration --controller --resource

生成されたモデル (app/Models/Post.php):

namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; // ソフトデリートを使う場合
class Post extends Model
{ use HasFactory, SoftDeletes; // トレイトを使用 // テーブル名をカスタマイズ (デフォルトはクラス名の複数形スネークケース 'posts') // protected $table = 'blog_posts'; // 主キー名をカスタマイズ (デフォルトは 'id') // protected $primaryKey = 'post_id'; // 主キーが自動増分でない場合 // public $incrementing = false; // 主キーの型をカスタマイズ (デフォルトは int) // protected $keyType = 'string'; // created_at, updated_at を使用しない場合 // public $timestamps = false; // タイムスタンプのフォーマットをカスタマイズ // protected $dateFormat = 'U'; // Unixタイムスタンプ // 接続するデータベース接続名を指定 (デフォルトは .env の DB_CONNECTION) // protected $connection = 'mysql_secondary'; // マスアサインメントで代入可能な属性 (ホワイトリスト) protected $fillable = [ 'title', 'body', 'user_id', 'published_at', ]; // マスアサインメントで代入を禁止する属性 (ブラックリスト) - fillable とどちらか一方を使用 // protected $guarded = ['id', 'created_at', 'updated_at']; // JSONにシリアライズされる際に隠す属性 protected $hidden = [ 'password', 'remember_token', ]; // JSONにシリアライズされる際に含める属性 (通常は不要) // protected $visible = ['id', 'name']; // 特定の属性を特定の型にキャスト protected $casts = [ 'email_verified_at' => 'datetime', 'published_at' => 'datetime:Y-m-d', // フォーマット指定 'is_admin' => 'boolean', 'options' => 'array', // JSON を配列にキャスト 'settings' => 'object', // JSON を stdClass オブジェクトにキャスト 'price' => 'decimal:2', // 小数点以下2桁 ]; // デフォルトの属性値 // protected $attributes = [ // 'status' => 'draft', // ]; // リレーションシップ定義 (後述) public function user() { return $this->belongsTo(User::class); } public function comments() { return $this->hasMany(Comment::class); }
}

基本的な取得 (Eloquent)

use App\Models\Post;
// 全てのPostを取得 (Eloquent Collection)
$posts = Post::all();
// 主キーで検索 (モデルインスタンス or null)
$post = Post::find(1);
// 主キーで検索し、見つからなければ ModelNotFoundException をスロー
$post = Post::findOrFail(1);
// 複数の主キーで検索
$posts = Post::find([1, 2, 3]);
// クエリビルダと同様のメソッドも利用可能
$activePosts = Post::where('is_active', 1)->orderBy('created_at', 'desc')->get();
$firstPost = Post::where('user_id', 5)->first();
$firstOrFail = Post::where('status', 'published')->firstOrFail();
// firstOrNew: 見つかれば取得、なければ新しいインスタンスを作成 (DB未保存)
$post = Post::firstOrNew(['slug' => 'my-first-post'], ['title' => 'Default Title']);
// firstOrCreate: 見つかれば取得、なければ作成してDBに保存
$post = Post::firstOrCreate(['slug' => 'unique-slug'], ['title' => 'New Post', 'user_id' => 1]);
// updateOrCreate: 条件に合うレコードがあれば更新、なければ作成
$post = Post::updateOrCreate( ['slug' => 'another-post'], // 検索条件 ['title' => 'Updated Title', 'body' => 'Content...'] // 更新または作成する値
);
// 集計
$count = Post::count();
$maxViews = Post::max('views');
// チャンク処理 (大量データをメモリ効率良く処理)
Post::where('status', 'pending')->chunk(100, function ($posts) { // 100件ずつ取得 foreach ($posts as $post) { // 各 $post に対する処理 }
});
// each (chunk と似ているが、個別のモデルを処理)
Post::where('needs_update', true)->each(function ($post) { $post->update(['status' => 'updated']);
});
// カーソル (さらにメモリ効率が良い、1件ずつDBから取得)
foreach (Post::where('large_data', true)->cursor() as $post) { // 処理
}

CRUD操作 (Eloquent)

use App\Models\Post;
use Illuminate\Http\Request;
// 作成 (Create)
// 方法1: new して save()
$post = new Post;
$post->title = '新しい投稿';
$post->body = 'これは投稿本文です。';
$post->user_id = auth()->id(); // 仮
$post->save(); // DBに保存
// 方法2: create() メソッド (マスアサインメント) - fillable/guarded の設定が必要
$post = Post::create([ 'title' => 'マスアサインメントでの作成', 'body' => '本文...', 'user_id' => 1, // 仮 // 'secret_column' => 'これは設定されない' (fillable にない場合)
]);
// 読み取り (Read) - 前述の「基本的な取得」を参照
// 更新 (Update)
// 方法1: find() して save()
$post = Post::find(1);
if ($post) { $post->title = '更新されたタイトル'; $post->is_published = true; $post->save(); // DBに保存
}
// 方法2: update() メソッド (マスアサインメント) - 特定のレコードを更新
$affectedRows = Post::where('id', 1) ->where('user_id', auth()->id()) // 条件指定可能 ->update(['title' => '一括更新タイトル', 'body' => '更新された本文']);
// 削除 (Delete)
// 方法1: find() して delete()
$post = Post::find(1);
if ($post) { $post->delete(); // ソフトデリート有効時は論理削除、無効時は物理削除
}
// 方法2: destroy() メソッド (主キーで削除)
$deletedRows = Post::destroy(1); // 単一ID
$deletedRows = Post::destroy([1, 2, 3]); // 複数ID
$deletedRows = Post::destroy(collect([4, 5])); // Collection
// 方法3: クエリで削除
$deletedRows = Post::where('is_spam', true)->delete(); // ソフトデリート有効時は論理削除
// ソフトデリート関連 (SoftDeletes トレイト使用時)
// 論理削除されたレコードも含めて検索
$allPosts = Post::withTrashed()->get();
$trashedPost = Post::withTrashed()->find(1);
// 論理削除されたレコードのみ検索
$trashedPosts = Post::onlyTrashed()->get();
// 論理削除されたレコードを復元
$post = Post::onlyTrashed()->find(1);
if ($post) { $post->restore();
}
// レコードを物理削除 (ソフトデリート有効時でも強制削除)
$post = Post::find(1);
if ($post) { $post->forceDelete();
}
// クエリで物理削除
Post::where('created_at', '<', '2020-01-01')->forceDelete();
// モデルが論理削除されているかチェック
if ($post->trashed()) { // 削除済み
}

リレーションシップ (Eloquent)

モデル間の関連付けを定義し、関連データを簡単に取得します。

種類説明モデル定義例利用例
1対1 (HasOne)モデルが他のモデルを1つ持つ (例: User – Phone)
// User.php
public function phone() { // 第2引数: 外部キー (デフォルト: user_id) // 第3引数: ローカルキー (デフォルト: id) return $this->hasOne(Phone::class, 'user_id', 'id');
}
// Phone.php
public function user() { return $this->belongsTo(User::class);
}
$user = User::find(1);
$phone = $user->phone; // Phoneモデル取得
$phoneNumber = $user->phone->number;
$phone = Phone::find(1);
$user = $phone->user; // Userモデル取得
1対多 (HasMany)モデルが他のモデルを複数持つ (例: Post – Comment)
// Post.php
public function comments() { // 第2引数: 外部キー (デフォルト: post_id) // 第3引数: ローカルキー (デフォルト: id) return $this->hasMany(Comment::class, 'post_id', 'id');
}
// Comment.php
public function post() { return $this->belongsTo(Post::class);
}
$post = Post::find(1);
$comments = $post->comments; // CommentのCollection
foreach($comments as $comment) { echo $comment->body;
}
$comment = Comment::find(5);
$postTitle = $comment->post->title;
1対1/多 (BelongsTo)モデルが他のモデルに属する (上記 HasOne/HasMany の逆)
// Phone.php / Comment.php
public function user() { // または post() // 第2引数: 外部キー (デフォルト: user_id / post_id) // 第3引数: 所有者キー (デフォルト: id) return $this->belongsTo(User::class, 'user_id', 'id');
}
(上記 HasOne/HasMany の利用例参照)
多対多 (BelongsToMany)モデルが他のモデルを複数持ち、相手もこちらを複数持つ (例: User – Role) 中間テーブルが必要
// User.php
public function roles() { // 第2引数: 中間テーブル名 (デフォルト: role_user) // 第3引数: 中間テーブルの自モデル外部キー (デフォルト: user_id) // 第4引数: 中間テーブルの相手モデル外部キー (デフォルト: role_id) return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id') ->withPivot('created_at') // 中間テーブルのカラムを取得 ->withTimestamps(); // 中間テーブルのタイムスタンプを自動更新
}
// Role.php
public function users() { return $this->belongsToMany(User::class); // 規約通りなら引数省略可
}
$user = User::find(1);
$roles = $user->roles;
foreach($roles as $role) { echo $role->name; // 中間テーブルの値取得 echo $role->pivot->created_at;
}
// リレーションの追加 (中間テーブルにレコード挿入)
$user->roles()->attach(3); // Role ID 3 を追加
$user->roles()->attach([1, 2]); // 複数追加
$user->roles()->attach([1 => ['expires_at' => now()->addYear()]]); // 中間テーブルカラム値も指定
// リレーションの解除
$user->roles()->detach(3);
$user->roles()->detach([1, 2]);
// $user->roles()->detach(); // 全て解除
// リレーションの同期 (指定ID以外は解除)
$user->roles()->sync([1, 3]); // ID 1, 3 のみ関連付ける
$user->roles()->sync([1 => ['active' => true]]); // 中間テーブルカラム値も指定
// 関連付けの切り替え (あれば解除、なければ追加)
$user->roles()->toggle([2, 4]);
Has Many Through中間テーブルを経由して、遠いモデルのコレクションを取得 (例: Country -> Users -> Posts)
// Country.php
public function posts() { // 第2引数: 中間モデル (User) // 第3引数: 中間モデルの外部キー (user_id on posts table) // 第4引数: 最終モデルの外部キー (post_id on posts table) // 第5引数: 自モデルのローカルキー (id on countries table) // 第6引数: 中間モデルのローカルキー (country_id on users table) return $this->hasManyThrough( Post::class, User::class, 'country_id', // Foreign key on users table... 'user_id', // Foreign key on posts table... 'id', // Local key on countries table... 'id' // Local key on users table... );
}
$country = Country::find(1);
$posts = $country->posts; // その国のユーザーの全投稿
ポリモーフィック (1対多)1つのモデルが複数の他のモデルタイプに属することができる (例: Comment -> Post / Video)
// Comment.php
public function commentable() { // morphTo(リレーション名, typeカラム名, idカラム名) return $this->morphTo(); // commentable_type, commentable_id カラムを使用
}
// Post.php / Video.php
public function comments() { // morphMany(関連モデル, リレーション名) return $this->morphMany(Comment::class, 'commentable');
}
$post = Post::find(1);
$comments = $post->comments;
$video = Video::find(1);
$comments = $video->comments;
$comment = Comment::find(1);
$parent = $comment->commentable; // Post or Video モデル
ポリモーフィック (多対多)複数のモデルタイプが、複数の他のモデルタイプと関連付け可能 (例: Tag -> Post / Video) 中間テーブルが必要
// Tag.php
public function posts() { // morphedByMany(関連モデル, リレーション名) return $this->morphedByMany(Post::class, 'taggable'); // taggable_type, taggable_id, tag_id
}
public function videos() { return $this->morphedByMany(Video::class, 'taggable');
}
// Post.php / Video.php
public function tags() { // morphToMany(関連モデル, リレーション名) return $this->morphToMany(Tag::class, 'taggable');
}
$post = Post::find(1);
$tags = $post->tags;
$post->tags()->attach([1, 2]);
$tag = Tag::find(1);
$posts = $tag->posts;
$videos = $tag->videos;

Eager Loading (N+1問題対策)

リレーション先のデータを事前に一括で読み込みます。

// N+1 問題が発生する例 (ループ内で都度クエリ発行)
$posts = Post::all();
foreach ($posts as $post) { echo $post->user->name; // ループ毎に User を取得するクエリが実行される
}
// Eager Loading (with)
$posts = Post::with('user')->get(); // posts を取得する際に users もまとめて取得
foreach ($posts as $post) { echo $post->user->name; // クエリは発行されない
}
// 複数のリレーションを Eager Loading
$posts = Post::with(['user', 'comments'])->get();
// ネストしたリレーションを Eager Loading
$posts = Post::with('comments.user')->get(); // コメントとそのコメントのユーザーを取得
// 特定のカラムのみ Eager Loading
$posts = Post::with('user:id,name')->get(); // userテーブルのidとnameのみ取得
// リレーション取得時に条件を指定
$posts = Post::with(['comments' => function ($query) { $query->where('is_approved', true)->orderBy('created_at', 'desc');
}])->get();
// デフォルトで常に Eager Loading する (モデル側で定義)
// Post.php
protected $with = ['user'];
// 遅延 Eager Loading (既に取得したモデルコレクションに対して後からロード)
$posts = Post::all();
// ... 何らかの処理 ...
$posts->load('user', 'comments'); // 後からリレーションをロード
$posts->loadMissing('tags'); // まだロードされていないリレーションのみロード

リレーションを利用したデータ操作

use App\Models\Post;
use App\Models\Comment;
// 関連モデルの作成 (save)
$post = Post::find(1);
$comment = new Comment(['body' => '素晴らしい投稿です!']);
$post->comments()->save($comment); // post_id が自動的にセットされる
// 関連モデルの作成 (create) - マスアサインメント
$post = Post::find(1);
$comment = $post->comments()->create([ 'body' => 'createメソッドでのコメント', // user_id など他の fillable な属性も指定可能
]);
// BelongsTo リレーションの更新 (associate / dissociate)
$user = User::find(10);
$post = Post::find(1);
$post->user()->associate($user); // post の user_id を 10 に設定
$post->save();
$post->user()->dissociate(); // post の user_id を null に設定
$post->save();
// 多対多リレーションの操作 (attach, detach, sync, toggle) - 前述の BelongsToMany 参照

アクセサ & ミューテタ

モデルの属性値を取得・設定する際にカスタムロジックを挟みます。

// User.php
use Illuminate\Database\Eloquent\Casts\Attribute; // Attribute クラスをインポート
protected function firstName(): Attribute // メソッド名は get[AttributeName]Attribute ではなく、キャメルケース属性名
{ return Attribute::make( // アクセサ (取得時) get: fn (string $value) => ucfirst($value), // $value はDBのカラム値 (first_name) // ミューテタ (設定時) set: fn (string $value) => strtolower($value) // 設定する値を返す );
}
// フルネーム属性 (DBにカラムは存在しない)
protected function fullName(): Attribute
{ return Attribute::make( get: fn ($value, array $attributes) => $attributes['first_name'] . ' ' . $attributes['last_name'] // この属性に対する set は定義しない );
}
// 利用例
$user = User::find(1);
echo $user->first_name; // アクセサが適用され、頭文字が大文字で表示される ("John")
echo $user->full_name; // アクセサが first_name と last_name から計算して表示 ("John Doe")
$user->first_name = 'jane'; // ミューテタが適用され、DBには 'jane' が保存される
$user->save();

モデルイベント

モデルのライフサイクル(作成、更新、削除など)で特定の処理を実行します。

// 方法1: モデル内の $dispatchesEvents プロパティ (推奨)
// User.php
use App\Events\UserCreated;
use App\Events\UserDeleted;
protected $dispatchesEvents = [ 'created' => UserCreated::class, // 作成後に UserCreated イベントを発行 'deleted' => UserDeleted::class, // 削除後に UserDeleted イベントを発行 // 'updating', 'updated', 'saving', 'saved', 'deleting', 'restoring', 'restored', etc.
];
// 方法2: モデル内の boot メソッド (オブザーバーを使わない場合)
// User.php
protected static function boot()
{ parent::boot(); static::creating(function ($user) { // モデルが作成される直前 $user->uuid = (string) Str::uuid(); }); static::updating(function ($user) { // モデルが更新される直前 // Log::info("Updating user: {$user->id}"); }); static::deleted(function ($user) { // モデルが削除された後 // $user->relatedData()->delete(); });
}
// 方法3: オブザーバークラス (ロジックを分離する場合)
// php artisan make:observer UserObserver --model=User
// app/Observers/UserObserver.php
namespace App\Observers;
use App\Models\User;
use Illuminate\Support\Facades\Log;
class UserObserver
{ public function created(User $user): void { Log::info("User created: {$user->id}"); } public function updated(User $user): void { /* ... */ } public function deleted(User $user): void { /* ... */ } public function restored(User $user): void { /* ... */ } public function forceDeleted(User $user): void { /* ... */ } // saving, retrieved, creating, updating, deleting, restoring イベントも利用可能
}
// オブザーバーの登録 (AppServiceProvider の boot メソッド)
use App\Models\User;
use App\Observers\UserObserver;
public function boot()
{ User::observe(UserObserver::class);
}

シーディング (Seeding)

データベースに初期データやテストデータを投入します。

// Seederクラスの作成
php artisan make:seeder UserSeeder
// app/Database/Seeders/UserSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use App\Models\User; // モデルファクトリを使う場合
class UserSeeder extends Seeder
{ public function run(): void { // 方法1: DBファサード DB::table('users')->insert([ 'name' => 'Test User', 'email' => 'test@example.com', 'password' => Hash::make('password'), 'created_at' => now(), 'updated_at' => now(), ]); // 方法2: Eloquentモデル User::create([ 'name' => 'Another User', 'email' => 'another@example.com', 'password' => Hash::make('password'), ]); // 方法3: モデルファクトリ (推奨) - database/factories/UserFactory.php が必要 User::factory()->count(10)->create(); // 10人のダミーユーザーを作成 // 特定の属性を指定してファクトリ実行 User::factory()->create([ 'name' => 'Admin User', 'email' => 'admin@example.com', 'is_admin' => true, ]); }
}
// メインの DatabaseSeeder から他のSeederを呼び出す
// app/Database/Seeders/DatabaseSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{ public function run(): void { $this->call([ UserSeeder::class, PostSeeder::class, CommentSeeder::class, ]); }
}
// Seederの実行
php artisan db:seed // DatabaseSeeder を実行
php artisan db:seed --class=UserSeeder // 特定のSeederを実行
php artisan migrate:fresh --seed // DBをリフレッシュしてSeederを実行

データベーストランザクション

一連のデータベース操作をアトミック(全て成功するか全て失敗するか)に実行します。

use Illuminate\Support\Facades\DB;
// クロージャベース (推奨: 自動的にコミット/ロールバック)
DB::transaction(function () { DB::table('users')->update(['votes' => 1]); // この操作が失敗すると、上の update もロールバックされる DB::table('posts')->delete(); // 例外が発生した場合も自動ロールバック // throw new \Exception('Something went wrong');
}, 5); // 第2引数は再試行回数 (デッドロック時など)
// 手動トランザクション (非推奨だが、細かい制御が必要な場合)
DB::beginTransaction();
try { DB::table('orders')->insert([...]); DB::table('products')->where('id', ...)->decrement('stock'); // 全て成功したらコミット DB::commit();
} catch (\Exception $e) { // 何かエラーが発生したらロールバック DB::rollBack(); // エラー処理 report($e); return response()->json(['error' => 'Transaction failed'], 500);
}

Artisan コンソール

Laravel アプリケーションの開発、管理、保守に役立つコマンドラインインターフェースです。

よく使うコマンド

ターミナルで php artisan [コマンド名] [引数] [オプション] の形式で実行します。

コマンド説明
php artisan list or php artisan利用可能な全コマンドの一覧を表示
php artisan help [コマンド名]特定のコマンドのヘルプを表示 (例: php artisan help make:controller)
php artisan --versionLaravelフレームワークのバージョンを表示
php artisan env現在の環境 (.env) を表示
php artisan upメンテナンスモードを解除
php artisan downアプリケーションをメンテナンスモードにする
php artisan down --secret="bypass-key"メンテナンスモード中に特定のシークレットでアクセス可能にする
php artisan key:generateアプリケーションキー (APP_KEY) を生成・再生成
php artisan config:cache設定ファイルをキャッシュしてパフォーマンス向上
php artisan config:clear設定キャッシュをクリア
php artisan route:cacheルート情報をキャッシュしてパフォーマンス向上
php artisan route:clearルートキャッシュをクリア
php artisan route:list登録されているルートの一覧を表示
php artisan route:list --path=api特定のパスを含むルートのみ表示
php artisan route:list --name=admin.特定の名前を含むルートのみ表示
php artisan view:cacheBladeテンプレートをコンパイル・キャッシュしてパフォーマンス向上
php artisan view:clearコンパイル済みビューキャッシュをクリア
php artisan event:cacheイベントとリスナーをキャッシュ
php artisan event:clearイベントキャッシュをクリア
php artisan optimize設定・ルート・ビューのキャッシュを一括生成 (非推奨になりつつある、config:cache, route:cacheを推奨)
php artisan optimize:clear各種キャッシュをクリア
php artisan storage:linkpublic/storage から storage/app/public へのシンボリックリンクを作成
php artisan serveローカル開発用サーバーを起動 (デフォルト: http://127.0.0.1:8000)
php artisan serve --host=0.0.0.0 --port=8080ホストとポートを指定してサーバー起動
php artisan tinker対話的なREPL (Read-Eval-Print Loop) を起動し、アプリケーションのコードを実行・テスト
php artisan make:controller UserControllerコントローラを作成
php artisan make:controller PostController --resource --model=Postリソースコントローラとモデルを関連付けて作成
php artisan make:model Product -mfsモデル、ファクトリ、シーダー、マイグレーションを同時に作成 (-m: migration, -f: factory, -s: seeder)
php artisan make:migration create_jobs_tableマイグレーションファイルを作成
php artisan make:seeder CategorySeederシーダークラスを作成
php artisan make:factory ItemFactory --model=Itemモデルファクトリを作成
php artisan make:policy PostPolicy --model=Postポリシークラスを作成
php artisan make:middleware CheckAdminミドルウェアクラスを作成
php artisan make:request StoreBlogPostRequestフォームリクエストクラスを作成
php artisan make:event OrderShippedイベントクラスを作成
php artisan make:listener SendShipmentNotification --event=OrderShippedリスナークラスを作成(特定のイベントに関連付け)
php artisan make:notification InvoicePaid通知クラスを作成
php artisan make:job ProcessPodcastキュー投入可能なジョブクラスを作成
php artisan make:mail WelcomeMail --markdown=emails.welcomeMailableクラスを作成(Markdownビューを指定)
php artisan make:component AlertViewコンポーネントクラスとBladeファイルを作成
php artisan make:test UserTest基本的なPHPUnitテストクラスを作成
php artisan make:test LoginTest --unitユニットテストクラスを作成
php artisan make:rule ValidDomainカスタムバリデーションルールを作成
php artisan make:command SendEmailsカスタムArtisanコマンドを作成
php artisan migrateマイグレーションを実行
php artisan migrate:statusマイグレーションの状態を表示
php artisan migrate:rollback最後のマイグレーションをロールバック
php artisan db:seedデータベースシーダーを実行
php artisan db:wipeデータベースを空にする
php artisan queue:workキューワーカーを起動してジョブを処理
php artisan queue:listenキューリスナーを起動(コード変更時に自動再起動)
php artisan queue:failed失敗したキュージョブの一覧を表示
php artisan queue:retry [id|all]失敗したジョブを再試行
php artisan queue:flush失敗したジョブを全て削除
php artisan schedule:runスケジュールされたタスクを(手動で)実行
php artisan schedule:listスケジュールされたタスクの一覧を表示
php artisan testアプリケーションのテストを実行
php artisan test --filter=UserRepositoryTest特定のテストを実行

カスタムコマンド作成

独自のArtisanコマンドを作成して、定型的なタスクを自動化します。

// コマンドクラスの作成
php artisan make:command SendWeeklyReport
// app/Console/Commands/SendWeeklyReport.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\ReportService; // 例: レポート生成サービス
use Illuminate\Support\Facades\Log;
class SendWeeklyReport extends Command
{ /** * The name and signature of the console command. * コマンド名と引数、オプションを定義 * 例: php artisan report:send {user} {--queue} */ protected $signature = 'report:send {--user=* : レポートを送信するユーザーID (複数指定可)} {--type=weekly : レポートの種類 (weekly|monthly)} {--queue : ジョブをキューに入れるか}'; /** * The console command description. * コマンドの説明 (php artisan list で表示される) */ protected $description = '指定されたユーザーに週次レポートを送信します'; // 依存性を注入可能 protected $reportService; public function __construct(ReportService $reportService) { parent::__construct(); $this->reportService = $reportService; } /** * Execute the console command. * コマンド実行時の処理 */ public function handle(): int // 終了コードを返す (Command::SUCCESS, Command::FAILURE, Command::INVALID) { $userIds = $this->option('user'); // --user オプションの値 (配列) $reportType = $this->option('type'); // --type オプションの値 $useQueue = $this->option('queue'); // --queue オプションの有無 (boolean) if (empty($userIds)) { // 引数が必須の場合 // $userId = $this->argument('user'); // {user} 引数の値 // ユーザーに質問する $userIdsInput = $this->ask('送信先のユーザーIDをカンマ区切りで入力してください'); if (!$userIdsInput) { $this->error('ユーザーIDが指定されていません。'); return Command::FAILURE; } $userIds = explode(',', $userIdsInput); } $this->info("{$reportType} レポート送信処理を開始します..."); $progressBar = $this->output->createProgressBar(count($userIds)); $progressBar->start(); foreach ($userIds as $userId) { $userId = trim($userId); if (!is_numeric($userId)) continue; $this->line(" ユーザーID: {$userId} のレポートを処理中..."); if ($useQueue) { // SendReportJob::dispatch($userId, $reportType); // ジョブをキューに投入 $this->comment(" ユーザーID: {$userId} のジョブをキューに追加しました。"); } else { try { $this->reportService->sendReport($userId, $reportType); $this->info(" ユーザーID: {$userId} にレポートを送信しました。"); } catch (\Exception $e) { $this->error(" ユーザーID: {$userId} の送信に失敗しました: " . $e->getMessage()); Log::error("Report sending failed for user {$userId}", ['exception' => $e]); } } $progressBar->advance(); } $progressBar->finish(); $this->newLine(); // 改行 $this->info('レポート送信処理が完了しました。'); // テーブル表示 $headers = ['ID', 'Name', 'Email']; $users = \App\Models\User::whereIn('id', $userIds)->get($headers)->toArray(); $this->table($headers, $users); // 確認を求める // if ($this->confirm('本当に実行しますか?')) { ... } return Command::SUCCESS; // 成功 }
}
// Kernel.php にコマンドを登録 (通常は自動検出されるが、明示的な登録も可能)
// app/Console/Kernel.php
protected $commands = [ \App\Console\Commands\SendWeeklyReport::class,
];
// コマンドの実行
php artisan report:send --user=1 --user=5 --type=monthly
php artisan report:send --queue

その他 (Other Features)

キャッシュ、セッション、イベント、キュー、ファイルストレージ、ヘルパー関数など、よく使われる機能です。

キャッシュ (Cache)

頻繁にアクセスされるデータや計算結果を一時的に保存し、パフォーマンスを向上させます。

use Illuminate\Support\Facades\Cache;
// キャッシュにデータを保存 (put)
Cache::put('user:1:profile', $userProfile, 600); // キー, 値, 有効期間(秒)
Cache::put('settings', $settings, now()->addMinutes(60)); // Carbon インスタンスで有効期限指定
Cache::put('api_key', $apiKey); // 有効期間なし (永続)
// キャッシュが存在しない場合のみ保存 (add) - put と違い上書きしない
$added = Cache::add('lock:process:123', true, 300); // 追加できたら true, 既に存在したら false
// キャッシュにデータを永続的に保存 (forever)
Cache::forever('site_config', $config);
// キャッシュからデータを取得 (get)
$profile = Cache::get('user:1:profile');
$defaultValue = ['theme' => 'light'];
$settings = Cache::get('site_settings', $defaultValue); // 第2引数: デフォルト値 (クロージャも可)
$settings = Cache::get('site_settings', function () { // キャッシュがなかった場合に実行され、結果がキャッシュされる return App\Models\Setting::loadSettings();
});
// キャッシュからデータを取得し、存在しなければクロージャを実行して結果をキャッシュ・取得 (remember)
$posts = Cache::remember('posts:popular', 3600, function () { // キー, 有効期間(秒), クロージャ return App\Models\Post::popular()->limit(10)->get();
});
// rememberForever (remember の永続版)
$allCategories = Cache::rememberForever('categories:all', function () { return App\Models\Category::orderBy('name')->get();
});
// キャッシュからデータを取得して削除 (pull)
$lastToken = Cache::pull('user:1:onetime_token');
// キャッシュにデータが存在するか確認 (has)
if (Cache::has('user:1:profile')) { ... }
// キャッシュ内の値を増減 (increment / decrement)
Cache::increment('page_views'); // 1増やす
Cache::increment('cart_items', 3); // 3増やす
Cache::decrement('remaining_tickets'); // 1減らす
// キャッシュからデータを削除 (forget)
Cache::forget('user:1:profile');
// 全てのキャッシュをクリア (flush) - 注意して使用!
// Cache::flush();
// キャッシュタグ (関連するキャッシュをまとめて管理)
Cache::tags(['people', 'artists'])->put('John', $johnProfile, 3600);
Cache::tags(['people', 'authors'])->put('Jane', $janeProfile, 3600);
$artists = Cache::tags('artists')->get('John'); // タグ経由で取得
Cache::tags('authors')->flush(); // 'authors' タグを持つキャッシュのみ削除
Cache::tags(['people', 'artists'])->flush(); // 複数タグを指定して削除

キャッシュドライバ (file, database, redis, memcached など) は .env ファイルの CACHE_DRIVER で設定します。

セッション (Session)

ユーザーのリクエスト間で状態を維持します(例: ログイン状態、フラッシュメッセージ)。

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session; // Session ファサードを使う場合
// セッションにデータを保存 (put)
session(['user_id' => 123]); // session() ヘルパ (配列)
Session::put('cart', ['item1', 'item2']); // Session ファサード
// セッションにデータを追加 (push - 配列に追加)
Session::push('team_members', 'Alice');
Session::push('team_members', 'Bob'); // team_members は ['Alice', 'Bob'] になる
// セッションからデータを取得 (get)
$userId = session('user_id'); // session() ヘルパ
$cart = Session::get('cart', []); // 第2引数はデフォルト値
$allData = Session::all(); // 全てのセッションデータを取得
// セッションからデータを取得して削除 (pull)
$lastVisited = Session::pull('last_page', '/');
// セッションにデータが存在するか確認 (has)
if (session()->has('user_id')) { ... }
if (Session::exists('auth_token')) { ... } // has と似ているが null でも true を返す
// セッションからデータを削除 (forget)
session()->forget('user_id');
Session::forget(['cart', 'discount']); // 複数削除
// 全てのセッションデータを削除 (flush)
Session::flush();
// 次のリクエストでのみ有効なフラッシュデータを保存 (flash)
Session::flash('status', 'プロファイルが更新されました!');
Session::flash('alert_type', 'success');
// リダイレクトの with() メソッドは内部で flash() を使用している
// return redirect('/profile')->with('status', '更新完了');
// 現在のリクエストのみ有効なデータを保存 (now - フラッシュしない)
// Session::now('ephemeral_message', 'This is shown only once.');
// フラッシュデータを次のリクエストまで保持 (reflash / keep)
// Session::reflash(); // 全てのフラッシュデータを保持
// Session::keep(['status', 'alert_type']); // 特定のフラッシュデータのみ保持
// セッションIDの再生成 (セキュリティ向上: ログイン後など)
// $request->session()->regenerate();

セッションドライバ (file, cookie, database, redis, memcached など) は .env ファイルの SESSION_DRIVER で設定します。web ミドルウェアグループに含まれるミドルウェアによって自動的に開始されます。

イベント & リスナー (Events & Listeners)

アプリケーション内で特定の出来事(イベント)が発生した際に、関連する処理(リスナー)を実行する仕組み(Observer パターン)。

// イベントクラスの作成
php artisan make:event PodcastProcessed
// app/Events/PodcastProcessed.php
namespace App\Events;
use App\Models\Podcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class PodcastProcessed
{ use Dispatchable, SerializesModels; public Podcast $podcast; // public プロパティでデータを渡す public function __construct(Podcast $podcast) { $this->podcast = $podcast; } // ShouldBroadcast インターフェースを実装するとブロードキャストイベントになる
}
// リスナークラスの作成
php artisan make:listener SendPodcastNotification --event=PodcastProcessed
// app/Listeners/SendPodcastNotification.php
namespace App\Listeners;
use App\Events\PodcastProcessed;
use Illuminate\Contracts\Queue\ShouldQueue; // キューで実行する場合
use Illuminate\Queue\InteractsWithQueue;
use App\Notifications\PodcastReadyNotification; // 例: 通知クラス
class SendPodcastNotification // implements ShouldQueue // キューイングする場合
{ // use InteractsWithQueue; // キューイングする場合 public function __construct() { /* ... */ } // handle メソッドでイベントを受け取る public function handle(PodcastProcessed $event): void { // イベントからデータ ($event->podcast) を取得して処理 $podcast = $event->podcast; $podcast->owner->notify(new PodcastReadyNotification($podcast)); // キューイングした場合の試行回数やタイムアウト設定 // public $tries = 5; // public $timeout = 120; // public function retryUntil() { return now()->addMinutes(5); } } // キュージョブが失敗した場合の処理 (ShouldQueue 実装時) // public function failed(PodcastProcessed $event, $exception): void { ... }
}
// イベントとリスナーの登録 (EventServiceProvider)
// app/Providers/EventServiceProvider.php
namespace App\Providers;
use App\Events\PodcastProcessed;
use App\Listeners\SendPodcastNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{ protected $listen = [ PodcastProcessed::class => [ // イベントクラス SendPodcastNotification::class, // 実行するリスナークラス (複数指定可) // OtherListener::class, ], // 他のイベントとリスナーのペア \Illuminate\Auth\Events\Login::class => [ \App\Listeners\LogSuccessfulLogin::class, ], ]; public function boot(): void { // } // 自動検出を有効にするか (デフォルト true) public function shouldDiscoverEvents(): bool { return false; // false の場合は $listen 配列に明示的に書く必要がある }
}
// 自動検出を使う場合、php artisan event:discover で app/Events と app/Listeners をスキャンし、
// $listen 配列を自動生成 (bootstrap/cache/events.php にキャッシュされる)
// キャッシュクリア: php artisan event:clear
// イベントの発行
use App\Events\PodcastProcessed;
use App\Models\Podcast;
$podcast = Podcast::find(1);
PodcastProcessed::dispatch($podcast); // dispatch ヘルパ
// event(new PodcastProcessed($podcast)); // event() ヘルパでも可
// 条件付きディスパッチ
// PodcastProcessed::dispatchIf($condition, $podcast);
// PodcastProcessed::dispatchUnless($condition, $podcast);
// ブロードキャストイベント (リアルタイム通知用) - Laravel Echo と共に使用
// イベントクラスに ShouldBroadcast インターフェースを実装し、broadcastOn() メソッドを定義

キュー (Queues)

時間のかかる処理(メール送信、画像処理など)をバックグラウンドで非同期に実行します。

// ジョブクラスの作成
php artisan make:job ProcessVideo
// app/Jobs/ProcessVideo.php
namespace App\Jobs;
use App\Models\Video;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; // キューで処理されることを示す
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class ProcessVideo implements ShouldQueue // ShouldQueue を実装
{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public Video $video; // キュー接続名、キュー名、遅延時間を指定 // public $connection = 'redis'; // public $queue = 'videos'; // public $delay = 60; // 60秒遅延 // 試行回数 public $tries = 3; // タイムアウト (秒) public $timeout = 120; // 再試行までの待機時間 (秒) or Carbon インスタンス // public $backoff = [10, 30, 60]; // 1回目10秒後, 2回目30秒後, 3回目60秒後に再試行 public function __construct(Video $video) { $this->video = $video; // $this->onQueue('processing'); // キュー名を動的に指定 // $this->delay(now()->addMinutes(5)); // 遅延を動的に指定 } /** * Execute the job. */ public function handle(): void { // ジョブのメイン処理 Log::info("Processing video: {$this->video->title}"); // ...時間のかかる動画処理... $this->video->update(['status' => 'processed']); Log::info("Video processed: {$this->video->title}"); // ジョブを手動で解放し、後で再試行する場合 // if ($this->attempts() < 3) { // $this->release(30); // 30秒後に再試行 // } // ジョブを手動で失敗させる // $this->fail(new \Exception('Video processing failed')); } // ジョブが完全に失敗したときの処理 (tries を超えた場合) public function failed(\Throwable $exception): void { Log::error("Job failed for video {$this->video->id}: " . $exception->getMessage()); // 失敗通知を送るなど }
}
// ジョブのディスパッチ (キューへの投入)
use App\Jobs\ProcessVideo;
use App\Models\Video;
$video = Video::find(1);
ProcessVideo::dispatch($video); // デフォルトのキュー接続・キュー名を使用
// 特定のキュー・接続にディスパッチ
// ProcessVideo::dispatch($video)->onConnection('redis')->onQueue('videos');
// 遅延ディスパッチ
// ProcessVideo::dispatch($video)->delay(now()->addMinutes(10));
// 同期ディスパッチ (キューに入れず即時実行 - テスト用など)
// ProcessVideo::dispatchSync($video);
// ジョブチェーン (前のジョブが成功したら次のジョブを実行)
use App\Jobs\FetchOrders;
use App\Jobs\GenerateReport;
use App\Jobs\SendReport;
use Illuminate\Support\Facades\Bus;
Bus::chain([ new FetchOrders, new GenerateReport, new SendReport,
])->dispatch();
// ->catch(function (\Throwable $e) { /* チェーンのいずれかで失敗した場合 */ })
// ->onConnection('redis')->onQueue('reports') // チェーン全体の設定
// ジョブバッチ (複数のジョブをグループ化し、進捗や完了を追跡)
use App\Jobs\ImportCsvRecord;
$batch = Bus::batch([ new ImportCsvRecord(1, $data1), new ImportCsvRecord(2, $data2), // ...
])->then(function ($batch) { // 全てのジョブが成功した場合 Log::info("Batch {$batch->id} completed successfully.");
})->catch(function ($batch, $e) { // 最初に失敗したジョブがあった場合 Log::error("Batch {$batch->id} failed: " . $e->getMessage());
})->finally(function ($batch) { // 成功・失敗に関わらず最後に実行 // Clean up resources, etc.
})->name('CSV Import Batch') // バッチ名 ->onQueue('imports') ->dispatch();
$batchId = $batch->id; // バッチIDを取得して追跡可能
// $batchStatus = Bus::findBatch($batchId); // バッチ情報を取得

キューワーカーの起動: php artisan queue:work [connection] --queue=[queue_name] --tries=3 --timeout=60

キューの接続設定 (database, redis, sqs, sync など) は config/queue.php.envQUEUE_CONNECTION で行います。

ファイルストレージ (File Storage)

ローカルディスク、Amazon S3、その他のクラウドストレージなど、様々なファイルシステムを統一的なAPIで操作します。

use Illuminate\Support\Facades\Storage;
use Illuminate\Http\Request;
// 設定 (config/filesystems.php)
/*
'disks' => [ 'local' => [ // デフォルト 'driver' => 'local', 'root' => storage_path('app'), 'throw' => false, ], 'public' => [ // 公開アクセス用 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL').'/storage', 'visibility' => 'public', // public or private 'throw' => false, ], 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 'throw' => false, ],
],
'default' => env('FILESYSTEM_DISK', 'local'), // デフォルトディスク
*/
// ディスクインスタンスの取得
$localDisk = Storage::disk('local');
$s3Disk = Storage::disk('s3');
$defaultDisk = Storage::disk(); // デフォルトディスク
// ファイルの保存 (put)
$content = 'これはファイルの内容です。';
Storage::put('my_folder/my_file.txt', $content); // デフォルトディスクに保存
Storage::disk('s3')->put('avatars/user1.jpg', fopen($localImagePath, 'r+')); // S3にストリームから保存
// Request からのファイルアップロード (store/storeAs は内部で Storage::put を使う)
// $path = $request->file('avatar')->store('avatars', 'public'); // public ディスクの avatars に保存
// ファイルの取得 (get)
$content = Storage::get('my_folder/my_file.txt');
if (Storage::disk('s3')->exists('documents/report.pdf')) { $pdfContent = Storage::disk('s3')->get('documents/report.pdf');
}
// ファイルのダウンロード (download) - Response を返す
// return Storage::download('path/to/file.zip');
// return Storage::disk('s3')->download('private/data.csv', 'my_report.csv', $headers);
// ファイルのURL取得 (公開ディスクのみ)
$url = Storage::url('images/logo.png'); // public ディスク内のファイルのURL (/storage/images/logo.png)
// $s3Url = Storage::disk('s3')->url('videos/intro.mp4'); // S3上のURL
// 一時的なURL取得 (S3などプライベートファイル用)
$temporaryUrl = Storage::disk('s3')->temporaryUrl( 'secret/document.pdf', now()->addMinutes(5) // 有効期限
);
// ファイルの存在確認 (exists / missing)
if (Storage::exists('data.json')) { ... }
if (Storage::disk('s3')->missing('config.yaml')) { ... }
// ファイルサイズの取得 (size)
$size = Storage::size('videos/large_video.mp4'); // バイト単位
// 最終更新日時の取得 (lastModified) - Unixタイムスタンプ
$lastModified = Storage::lastModified('logs/app.log');
// ファイルのコピー (copy)
Storage::copy('old/path.txt', 'new/path.txt');
// ファイルの移動 (move)
Storage::move('temp/upload.tmp', 'processed/final.dat');
// ファイルの削除 (delete)
Storage::delete('obsolete/file.old');
Storage::delete(['file1.txt', 'folder/file2.log']); // 複数削除
// ディレクトリ内のファイル一覧取得 (files)
$files = Storage::files('images'); // images ディレクトリ直下のファイルのみ
$allFiles = Storage::allFiles('assets'); // assets ディレクトリ以下の全ファイル (再帰的)
// ディレクトリ内のディレクトリ一覧取得 (directories / allDirectories)
$directories = Storage::directories('users');
$allDirectories = Storage::allDirectories('data');
// ディレクトリの作成 (makeDirectory)
Storage::makeDirectory('new_folder/sub_folder');
// ディレクトリの削除 (deleteDirectory) - 中身も削除される!
Storage::deleteDirectory('temp_files');

ヘルパー関数 (Helper Functions)

Laravel には、配列、文字列、URL、パスなどを便利に扱うための多くのグローバルヘルパー関数が用意されています。

種類主な関数説明
配列Arr::get(), data_get(), Arr::has(), Arr::first(), Arr::last(), Arr::pluck(), Arr::only(), Arr::except(), Arr::flatten(), Arr::sortRecursive(), head(), last()配列の要素アクセス、検索、操作、変形
パスapp_path(), base_path(), config_path(), database_path(), public_path(), resource_path(), storage_path()アプリケーションの主要なディレクトリへの絶対パスを取得
文字列Str::limit(), Str::slug(), Str::studly(), Str::camel(), Str::snake(), Str::plural(), Str::singular(), Str::random(), Str::uuid(), Str::contains(), Str::startsWith(), Str::endsWith(), __(), trans()文字列の操作、変換、生成、検索、翻訳
URLurl(), action(), route(), secure_url(), asset(), secure_asset()URLの生成、アセットURLの生成
その他abort(), abort_if(), abort_unless(), app(), auth(), back(), bcrypt(), cache(), collect(), config(), cookie(), csrf_token(), dd(), dump(), env(), event(), info(), logger(), now(), old(), redirect(), request(), response(), session(), today(), view(), optional(), rescue(), retry(), value()例外処理、DIコンテナアクセス、認証、リダイレクト、ハッシュ化、キャッシュ、コレクション、設定値、クッキー、CSRFトークン、デバッグ、環境変数、イベント発行、ロギング、日時、古い入力値、レスポンス、セッション、ビュー、Null安全操作、例外制御など多岐にわたる。

例:

// 配列操作
$array = ['product' => ['name' => 'Desk', 'price' => 100]];
$name = data_get($array, 'product.name'); // 'Desk'
$filtered = Arr::except($array['product'], ['price']); // ['name' => 'Desk']
// 文字列操作
$slug = Str::slug('Laravel 5 Framework', '-'); // 'laravel-5-framework'
$random = Str::random(10); // ランダムな10文字
// URL生成
$profileUrl = route('profile.show', ['user' => 1]);
$logoUrl = asset('images/logo.png');
// その他
$user = auth()->user(); // 認証済みユーザー取得
logger('An informational message.'); // ログ記録
$collection = collect([1, 2, 3]); // コレクション作成
$configValue = config('app.timezone', 'UTC'); // 設定値取得 (デフォルト値付き)
if (app()->isProduction()) { ... } // 環境チェック

これらのヘルパー関数は、Laravel アプリケーション開発をより効率的かつ簡潔にするために非常に役立ちます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です