メインコンテンツまでスキップ

Laravel API 開発の基礎 — Controller・FormRequest・Eloquent・DI

この章について

この章はLaravel未経験者向けの補足章です。Laravel経験者は読み飛ばしても構いません。本書で必要となるLaravelの知識に絞って解説します。

SPAとAPIバックエンドの関係

本書では、フロントエンド(Vue/React)とバックエンド(Laravel)が分離したアーキテクチャを前提としています。

  • Frontend: UI/UXの実装、状態管理、クライアントルーティング、APIの呼び出し
  • Backend: ビジネスロジック、データの永続化、認証・認可、バリデーション

フロントエンドからのAPI呼び出し例

// フロントエンド(Vue/React)からのAPI呼び出し
// この「呼び出される側」を本書で作っていきます

// 注文作成API呼び出しの例
const createOrder = async () => {
const response = await axios.post('/api/orders', {
shipping: {
prefecture: '東京都',
city: '渋谷区',
street: '1-1-1'
},
items: [
{ productId: 1, quantity: 2, unitPrice: 1000 }
]
});

// レスポンス例: { orderId: 123, message: '注文を作成しました' }
console.log(response.data.orderId);
};

RESTful APIの基本

HTTPメソッド用途
GETデータの取得GET /api/orders → 注文一覧取得
POSTデータの作成POST /api/orders → 注文作成
PUT/PATCHデータの更新PUT /api/orders/1 → 注文更新
DELETEデータの削除DELETE /api/orders/1 → 注文削除

Laravelプロジェクトの構造

Laravelプロジェクトの主要なディレクトリ構成を見てみましょう。

laravel-project/
├── app/ # アプリケーションコード
│ ├── Http/
│ │ ├── Controllers/ # リクエストを受け取るクラス
│ │ ├── Requests/ # バリデーションルール
│ │ └── Resources/ # レスポンス整形
│ ├── Models/ # Eloquentモデル(DB操作)
│ └── Providers/ # サービスプロバイダ(DI設定)

├── config/ # 設定ファイル
├── database/
│ └── migrations/ # テーブル定義
├── routes/
│ ├── api.php # APIルーティング ← 本書で主に使用
│ └── web.php # Webルーティング
└── .env # 環境変数(DB接続情報等)

APIルーティング(routes/api.php)

なぜルーティングを学ぶのか

ルーティングはフロントエンドからのHTTPリクエストを適切なコードに振り分ける「入り口」の役割を果たします。どのURLがどの処理を実行するかを定義することで、APIの設計が明確になります。

詳細は Laravel公式ドキュメント - Routing を参照してください。

// routes/api.php
// APIのエンドポイント(URL)を定義するファイル
// ここで定義したルートは自動的に /api プレフィックスが付く

use App\Http\Controllers\OrderController;

// /api/orders へのリクエストを OrderController で処理
Route::prefix('orders')->group(function () {
Route::get('/', [OrderController::class, 'index']); // GET /api/orders
Route::post('/', [OrderController::class, 'store']); // POST /api/orders
Route::get('/{id}', [OrderController::class, 'show']); // GET /api/orders/1
Route::put('/{id}', [OrderController::class, 'update']); // PUT /api/orders/1
Route::delete('/{id}', [OrderController::class, 'destroy']); // DELETE /api/orders/1
});

APIエンドポイントの作成

Controllerの基本

なぜControllerを学ぶのか

Controllerは、フロントエンドからのリクエストを最初に受け取る場所です。リクエストの内容を検証し、必要なビジネスロジックを呼び出し、適切なレスポンスを返すという「交通整理」の役割を果たします。

詳細は Laravel公式ドキュメント - Controllers を参照してください。

Controllerは、HTTPリクエストを受け取り、レスポンスを返すクラスです。

// app/Http/Controllers/OrderController.php

namespace App\Http\Controllers;

use App\Models\Order;
use Illuminate\Http\JsonResponse;

// Controllerクラス: HTTPリクエストを処理する
class OrderController extends Controller
{
/**
* 注文一覧を取得する
* GET /api/orders
*/
public function index(): JsonResponse
{
// Order::all() で全件取得(後述のEloquent ORM)
$orders = Order::all();

// response()->json() でJSON形式のレスポンスを返す
return response()->json([
'data' => $orders
]);
}

/**
* 注文を新規作成する
* POST /api/orders
*/
public function store(CreateOrderRequest $request): JsonResponse
{
// $request->validated() でバリデーション済みデータを取得
$order = Order::create($request->validated());

// 第2引数でHTTPステータスコードを指定(201 = Created)
return response()->json([
'orderId' => $order->id,
'message' => '注文を作成しました'
], 201);
}

/**
* 注文詳細を取得する
* GET /api/orders/{id}
*/
public function show(int $id): JsonResponse
{
// findOrFail: 見つからない場合は自動で404エラー
$order = Order::findOrFail($id);

return response()->json([
'data' => $order
]);
}
}

FormRequestによるバリデーション

なぜバリデーションを学ぶのか

フロントエンドからの入力は常に信頼できるとは限りません。FormRequestを使うことで、不正なデータがビジネスロジックに入り込むのを防ぎ、エラーメッセージも一元管理できます。

詳細は Laravel公式ドキュメント - Validation を参照してください。

FormRequestは、入力値のバリデーション(検証)を担当するクラスです。

// app/Http/Requests/CreateOrderRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

// FormRequest: バリデーションルールを定義するクラス
// バリデーションエラー時は自動的に422ステータスでエラーレスポンスを返す
class CreateOrderRequest extends FormRequest
{
/**
* バリデーションルールを定義
*/
public function rules(): array
{
return [
// 'フィールド名' => ['ルール1', 'ルール2', ...]
'shipping.prefecture' => ['required', 'string', 'max:255'],
'shipping.city' => ['required', 'string', 'max:255'],
'shipping.street' => ['required', 'string', 'max:255'],

// 配列のバリデーション
'items' => ['required', 'array', 'min:1'],

// items.* は配列の各要素を指す
'items.*.productId' => ['required', 'integer', 'min:1'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
'items.*.unitPrice' => ['required', 'integer', 'min:0'],
];
}

/**
* バリデーションエラー時のカスタムメッセージ
*/
public function messages(): array
{
return [
'items.required' => '商品を1つ以上選択してください',
'items.min' => '商品を1つ以上選択してください',
'items.*.quantity.min' => '数量は1以上で指定してください',
];
}
}

API Resourceによるレスポンス整形

API Resourceは、レスポンスのJSON構造を定義するクラスです。

// app/Http/Resources/OrderResource.php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

// JsonResource: レスポンスの形式を定義するクラス
class OrderResource extends JsonResource
{
/**
* レスポンスの構造を定義
* DBのカラム名とAPIのレスポンス名を変換できる
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'status' => $this->status,
// DBでは shipping_prefecture だが、APIでは shippingAddress として返す
'shippingAddress' => [
'prefecture' => $this->shipping_prefecture,
'city' => $this->shipping_city,
'street' => $this->shipping_street,
],
'totalAmount' => $this->total_amount,
'createdAt' => $this->created_at->format('Y-m-d H:i:s'),
];
}
}

// Controllerでの使用例
public function show(int $id): OrderResource
{
$order = Order::findOrFail($id);
return new OrderResource($order);
}

HTTPステータスコード

コード意味使用場面
200OK取得・更新成功
201Created作成成功
204No Content削除成功(レスポンスボディなし)
400Bad Requestリクエスト形式エラー
401Unauthorized認証エラー
403Forbidden認可エラー(権限なし)
404Not Foundリソースが存在しない
422Unprocessable Entityバリデーションエラー
500Internal Server Errorサーバーエラー

Eloquent ORM入門

なぜEloquent ORMを学ぶのか

SQLを直接書く代わりに、PHPのオブジェクトとしてデータベースを操作できるのがEloquent ORMです。これにより、コードの可読性が向上し、リレーションの扱いも簡潔になります。ただし、DDDではEloquentを直接ビジネスロジックに持ち込まないという点が重要になります。

詳細は Laravel公式ドキュメント - Eloquent ORM を参照してください。

Eloquent ORMは、データベース操作をPHPのクラスで行えるようにする仕組みです。

モデルとテーブルの対応

// app/Models/Order.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

// Eloquent Model: DBテーブルに対応するクラス
// Order クラス → orders テーブル(クラス名の複数形・スネークケース)
class Order extends Model
{
// 明示的にテーブル名を指定する場合
// protected $table = 'orders';

// 一括代入を許可するカラム(Mass Assignment対策)
protected $fillable = [
'status',
'shipping_prefecture',
'shipping_city',
'shipping_street',
'total_amount',
];
}

基本的なCRUD操作

// Create(作成)
$order = Order::create([
'status' => 'draft',
'shipping_prefecture' => '東京都',
'shipping_city' => '渋谷区',
'shipping_street' => '1-1-1',
]);

// Read(取得)
$order = Order::find(1); // IDで1件取得(なければnull)
$order = Order::findOrFail(1); // IDで1件取得(なければ404エラー)
$orders = Order::all(); // 全件取得
$orders = Order::where('status', 'draft')->get(); // 条件で複数件取得
$order = Order::where('status', 'draft')->first(); // 条件で1件取得

// Update(更新)
$order = Order::find(1);
$order->status = 'confirmed';
$order->save();

// または一括更新
Order::where('id', 1)->update(['status' => 'confirmed']);

// Delete(削除)
$order = Order::find(1);
$order->delete();

// または直接削除
Order::destroy(1);

リレーション(関連付け)

// app/Models/Order.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Order extends Model
{
/**
* 1対多のリレーション: 注文は複数の注文明細を持つ
* orders テーブル (1) → order_lines テーブル (多)
*/
public function orderLines(): HasMany
{
// OrderLineモデルのorder_idカラムで関連付け
return $this->hasMany(OrderLine::class);
}
}
// app/Models/OrderLine.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class OrderLine extends Model
{
/**
* 多対1のリレーション: 注文明細は1つの注文に属する
*/
public function order(): BelongsTo
{
return $this->belongsTo(Order::class);
}
}
// 使用例

$order = Order::find(1);
$orderLines = $order->orderLines; // 関連する注文明細を取得

// Eager Loading(N+1問題対策)
$orders = Order::with('orderLines')->get();
N+1問題とは?

ループ内で関連データを取得すると、データ件数分のクエリが発行されてパフォーマンスが悪化する問題です。with()を使ったEager Loadingで解決できます。詳細はChapter 13「リポジトリパターン」で解説します。

マイグレーションの詳細は Laravel公式ドキュメント - Migrations を参照してください。

DIコンテナとサービスプロバイダ

なぜDIを学ぶのか

依存性注入(DI)は、クラス間の結合度を下げ、テストしやすいコードを書くための基本技術です。DDDやクリーンアーキテクチャでは、インターフェースと実装を分離するためにDIが不可欠です。

詳細は Laravel公式ドキュメント - Service Container を参照してください。

依存性注入(DI)とは

依存性注入(Dependency Injection)は、クラスが必要とするオブジェクトを外部から渡す設計パターンです。

// NG: クラス内で直接インスタンス化(密結合)
class OrderController
{
public function store(Request $request)
{
// OrderService を直接 new している
// 問題点:
// - テスト時にモックに差し替えられない
// - OrderService の実装が変わると影響を受ける
$service = new OrderService();
$service->createOrder($request->all());
}
}

// OK: コンストラクタで注入(疎結合)
class OrderController
{
public function __construct(
// LaravelのDIコンテナが自動的にインスタンスを注入
private OrderService $service
) {}

public function store(Request $request)
{
// 注入されたインスタンスを使用
// テスト時はモックに差し替え可能
$this->service->createOrder($request->all());
}
}

インターフェースとバインディング

本書では、リポジトリパターンでインターフェースを活用します。

// インターフェース(契約)を定義
interface OrderRepositoryInterface
{
public function findById(int $id): ?Order;
public function save(Order $order): void;
}

// 実装クラス
class EloquentOrderRepository implements OrderRepositoryInterface
{
public function findById(int $id): ?Order { /* ... */ }
public function save(Order $order): void { /* ... */ }
}

サービスプロバイダでのバインディング

サービスプロバイダは、インターフェースと実装クラスを紐付ける設定を担当します。

// app/Providers/RepositoryServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
/**
* インターフェースと実装クラスのバインディング
* 「OrderRepositoryInterface が必要な場所には
* EloquentOrderRepository を注入する」という設定
*/
public array $bindings = [
OrderRepositoryInterface::class => EloquentOrderRepository::class,
];
}

// 使用側(UseCase等)
class CreateOrderUseCase
{
public function __construct(
// インターフェースを型指定
// → LaravelのDIコンテナが EloquentOrderRepository を注入
private OrderRepositoryInterface $repository
) {}
}

DIコンテナが自動的に依存を解決し、インスタンスを注入します。

API認証の基礎

なぜAPI認証を学ぶのか

SPA(シングルページアプリケーション)では、従来のセッション認証ではなくトークンベース認証を使います。ユーザーごとにAPIアクセスを制御し、セキュアなアプリケーションを構築するために必須の知識です。

トークンベース認証の概念

トークンベース認証の流れ

  1. ログイン時にトークンを取得し、localStorageなどに保存
  2. 認証が必要なAPIにはAuthorizationヘッダーでトークンを送信

Laravel Sanctumによる認証

// routes/api.php

// 認証不要のエンドポイント
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);

// 認証が必要なエンドポイント
// auth:sanctum ミドルウェアでトークン認証を要求
Route::middleware('auth:sanctum')->group(function () {
// このグループ内のAPIはトークンがないと401エラー
Route::get('/orders', [OrderController::class, 'index']);
Route::post('/orders', [OrderController::class, 'store']);
Route::get('/user', [UserController::class, 'me']);
Route::post('/logout', [AuthController::class, 'logout']);
});
// app/Http/Controllers/AuthController.php

class AuthController extends Controller
{
public function login(LoginRequest $request): JsonResponse
{
$credentials = $request->validated();

// 認証チェック
if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'メールアドレスまたはパスワードが正しくありません'
], 401);
}

$user = Auth::user();

// APIトークンを発行
$token = $user->createToken('api-token')->plainTextToken;

return response()->json([
'token' => $token,
'user' => $user,
]);
}

public function logout(Request $request): JsonResponse
{
// 現在のトークンを削除
$request->user()->currentAccessToken()->delete();

return response()->json([
'message' => 'ログアウトしました'
]);
}
}

PHP 8.x構文のおさらい

なぜPHP 8の新機能を学ぶのか

PHP 8.0以降で導入された新機能を使うことで、コードがより簡潔で安全になります。特にDDDでは、イミュータブルなオブジェクト(値オブジェクト)を多用するため、readonlyenumは非常に有用です。

本書で使用するPHP 8.xの新機能を確認しておきましょう。

実コードでは declare(strict_types=1); を有効にする

本書のサンプルは説明のため <?php 宣言を省略していますが、実プロジェクトでは各 PHP ファイルの先頭で declare(strict_types=1); を有効にすることを推奨します。型の暗黙変換を禁止するため、たとえば int を期待する引数に float を渡したときに TypeError で即座に検出でき、値オブジェクトの計算で起こりがちな丸めバグを防げます。

コンストラクタプロモーション(PHP 8.0+)

何が便利になるのか: プロパティの宣言と初期化を一度に行えるため、冗長なコードが減ります。特に値オブジェクトのような小さなクラスで威力を発揮します。

// Before: PHP 7以前(冗長)
class Money
{
private int $amount;
private string $currency;

public function __construct(int $amount, string $currency)
{
$this->amount = $amount;
$this->currency = $currency;
}
}

// After: PHP 8.0+(コンストラクタプロモーション)
// プロパティの宣言、コンストラクタ引数、代入を一度に実行
class Money
{
public function __construct(
private int $amount, // 引数宣言と同時にプロパティ定義
private string $currency,
) {}
// プロパティの宣言とコンストラクタでの代入が不要になる
// $this->amount = $amount; のような代入も自動的に行われる
}

// 使い方は同じ
$money = new Money(1000, 'JPY');

ポイント:

  • アクセス修飾子(private, protected, public)を付けることで、プロパティとして自動定義される
  • プロパティの宣言が3行から1行になり、可読性が向上
  • タイプミス(プロパティ名と引数名の不一致)を防げる

readonly修飾子(PHP 8.1+)

何が便利になるのか: 一度設定したら変更できないプロパティを作れます。DDDの値オブジェクトやエンティティIDなど、変更されてはいけないデータを安全に扱えます。

class OrderId
{
public function __construct(
// readonly: 一度設定したら変更不可(イミュータブル)
private readonly int $value
) {}

public function value(): int
{
return $this->value;
}
}

$orderId = new OrderId(1);
echo $orderId->value(); // 1

// $orderId->value = 2; // エラー: readonlyプロパティは変更不可
// コンストラクタ以外での代入は実行時エラーになる

ポイント:

  • コンストラクタでのみ値を設定でき、その後の変更は不可
  • バグの原因となる「意図しない変更」を防げる
  • DDDでは「値オブジェクトは不変(イミュータブル)であるべき」という原則があり、readonlyはこれを強制する

readonly classの便利な使い方(PHP 8.2+):

// PHP 8.2以降は、クラス全体をreadonlyにできる
readonly class OrderId
{
// すべてのプロパティが自動的にreadonly
public function __construct(
private int $value
) {}
}

列挙型 enum(PHP 8.1+)

何が便利になるのか: ステータスなどの固定値を、文字列定数ではなく型安全に扱えます。不正な値の代入を防ぎ、IDEの補完も効くようになります。

// Before: 文字列定数を使った従来の方法(型安全でない)
class Order
{
public const STATUS_DRAFT = 'draft';
public const STATUS_CONFIRMED = 'confirmed';

public string $status; // どんな文字列でも代入できてしまう
}

$order->status = 'typo'; // タイポしてもエラーにならない

// After: Backed Enum(値を持つ列挙型)
enum OrderStatus: string
{
case DRAFT = 'draft'; // DBに保存される値
case CONFIRMED = 'confirmed';
case SHIPPED = 'shipped';
case CANCELLED = 'cancelled';

// enumにもメソッドを定義できる(ビジネスロジックを集約)
public function label(): string
{
return match($this) {
self::DRAFT => '下書き',
self::CONFIRMED => '確定',
self::SHIPPED => '発送済み',
self::CANCELLED => 'キャンセル',
};
}

public function canBeCancelled(): bool
{
return $this === self::DRAFT || $this === self::CONFIRMED;
}

public function isCompleted(): bool
{
return $this === self::SHIPPED;
}
}

// 使用例
$status = OrderStatus::DRAFT;
echo $status->value; // 'draft' (DBに保存する値)
echo $status->label(); // '下書き' (表示用のラベル)

if ($status->canBeCancelled()) {
// キャンセル可能な状態の処理
}

// 文字列からenumに変換(DBから取得した値をenumに変換)
$status = OrderStatus::from('confirmed'); // OrderStatus::CONFIRMED

// 不正な値はエラーになる
$status = OrderStatus::from('typo'); // ValueError例外がスローされる

// エラーを回避したい場合はtryFrom()を使う
$status = OrderStatus::tryFrom('typo'); // null を返す

ポイント:

  • 定義されたケース以外の値は代入できない(型安全)
  • IDEの補完が効くため、タイポを防げる
  • ビジネスロジック(canBeCancelled()など)をenumに持たせられる
  • DBに保存する値(value)と表示用のラベル(label())を分けられる

DDDにおけるenumの活用:

enumは「不変で値によって等価判断する」という値オブジェクトに近い性質を持つため、値オブジェクトと同じ感覚で扱えます(厳密には別概念ですが、ステータスのような「決まった値の集合」を表現するのに最適です)。

null合体演算子とnullsafe演算子

何が便利になるのか: nullチェックを簡潔に書けます。特にnullsafe演算子は、ネストしたプロパティアクセスを安全に行えます。

// Before: 従来のnullチェック(冗長)
$name = isset($user->name) ? $user->name : 'ゲスト';

if ($user !== null && $user->address !== null) {
$city = $user->address->city;
} else {
$city = null;
}

// After: 簡潔に書ける
// null合体演算子(??): 左辺がnullなら右辺を返す
$name = $user->name ?? 'ゲスト';

// null合体代入演算子(??=): 変数がnullなら代入
$name ??= 'デフォルト名'; // $nameがnullの時だけ代入

// nullsafe演算子(?->): nullなら以降の処理をスキップ(PHP 8.0+)
$city = $user?->address?->city; // $userや$addressがnullならnullを返す

// throw式(PHP 8.0+): ??と組み合わせて簡潔に例外をスロー
$order = $this->repository->findById($id)
?? throw new OrderNotFoundException("注文が見つかりません");

実用例:

// API Resourceでよく使うパターン
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name ?? '名前なし',
'email' => $this->email,
// リレーションがnullでも安全にアクセス
'companyName' => $this->user?->company?->name ?? '所属なし',
];
}

// リポジトリでの使用例
public function findById(int $id): Order
{
return $this->model->find($id)
?? throw new OrderNotFoundException("注文ID {$id} が見つかりません");
}

アロー関数(PHP 7.4+)

何が便利になるのか: 短い無名関数を簡潔に書けます。Laravelのコレクション操作で頻繁に使用します。

// Before: 従来の無名関数(冗長)
$doubled = array_map(function ($n) {
return $n * 2;
}, [1, 2, 3]);

// After: アロー関数(短い無名関数)
// fn($n) => $n * 2 で return が自動的に付く
$doubled = array_map(fn($n) => $n * 2, [1, 2, 3]);

// Laravelでよく使うパターン
$orders = Order::all();
$ids = $orders->map(fn($order) => $order->id);

// 外部変数を自動でキャプチャ(use不要)
$threshold = 1000;

// Before: useを明示的に書く必要がある
$filtered = $orders->filter(function ($order) use ($threshold) {
return $order->total >= $threshold;
});

// After: アロー関数は自動で外部変数をキャプチャ
$filtered = $orders->filter(fn($order) => $order->total >= $threshold);

// 複数行の処理が必要な場合は従来の無名関数を使用
$results = $orders->map(function ($order) {
$total = $order->orderLines->sum('subtotal');
return [
'id' => $order->id,
'total' => $total,
];
});

ポイント:

  • 1行で書ける処理はアロー関数で簡潔に
  • 複数行の処理は従来の無名関数を使用
  • 外部変数を自動でキャプチャするため、useが不要

名前付き引数(PHP 8.0+)

何が便利になるのか: 引数の意味が明確になり、可読性が向上します。デフォルト値のある引数を途中でスキップできます。

// Before: 従来の引数指定(位置による)
// どの値が何を表すのか分かりにくい
$money = new Money(1000, 'JPY');

// After: 名前付き引数(引数名を指定)
// 引数の意味が一目で分かる
$money = new Money(
amount: 1000,
currency: 'JPY',
);

// 順序を変えることも可能
$money = new Money(
currency: 'JPY',
amount: 1000,
);

// デフォルト値のある引数をスキップ可能
function createOrder(
string $status = 'draft',
?string $note = null,
bool $priority = false,
) { /* ... */ }

// Before: 途中の引数をスキップできない
createOrder('draft', null, true); // $noteにnullを明示的に渡す必要がある

// After: 名前付き引数なら必要な引数だけ指定できる
createOrder(priority: true); // $status, $note はデフォルト値を使用

// 実用例:バリデーションルールの設定
Route::post('/orders', [OrderController::class, 'store'])
->middleware('throttle:60,1'); // 何の数字か分かりにくい

Route::post('/orders', [OrderController::class, 'store'])
->middleware('throttle', maxAttempts: 60, decayMinutes: 1); // 明確

ポイント:

  • 引数が多い関数で特に有用
  • コードレビュー時に引数の意味が分かりやすい
  • デフォルト値のある引数を途中スキップできる

まとめ

本章では、本書を読み進めるために必要なLaravelの基礎知識を解説しました。

概念説明本書での活用
ControllerHTTPリクエストを処理プレゼンテーション層
FormRequest入力バリデーションプレゼンテーション層
API Resourceレスポンス整形プレゼンテーション層
Eloquent ORMデータベース操作インフラ層(リポジトリ内)
DIコンテナ依存性注入各層の疎結合化
サービスプロバイダバインディング設定インターフェースと実装の紐付け

次章以降では、これらの知識を前提に、DDDとクリーンアーキテクチャの概念を学んでいきます。

次章への準備

次章「なぜDDDが必要なのか」では、従来のLaravel開発で起こりがちな問題を見ていきます。本章で学んだ「Controller」「Eloquent Model」の知識が前提となります。