PHPer 向け Next.js 対照表的あんちょこ
Next.js は React ベースの Web アプリケーションフレームワークです。サーバーサイド関数とフロントエンドを組み合わせたハイブリッド構成が PHP に似ているという話を聞き、PHPer との親和性が高いのではないかと思い調査しました。
結果、PHPer 視点で気に入った部分が多かったので、今でもなんやかんや PHP に頼りがちな自分から見たメモを残しておきます。
本記事は App Router を前提としています。
TL;DR
| PHP / Laravel でのアレコレ | Next.js での相当 |
|---|---|
ファイルベースルーティング (.php 拡張子) / ルーティング定義 (Laravel routes) | ファイルベースルーティング (app/ 配下の page.tsx ファイル) |
| (MVC の場合は)コントローラで処理して view を返す | page.tsx (Server Component) でデータ取得 → JSX で描画 |
| 素の PHP / Blade テンプレート | JSX/TSX (React) |
| クライアント側の UI 動作は PHP と直交し任意構成 | use client で作る |
include, require | import |
$_GET, $_POST | searchParams, request.json(), formData() |
1. ルーティング
静的
- PHP:
about.php→/about.php - Next.js:
app/about/page.tsx→/about
動的(パラメータあり)
- PHP:
user.php→/user.php?id=123,/user/123(パラメータをパスに入れる場合は、Laravel routes / Virtual directory 等を使う) - Next.js:
app/users/[id]/page.tsx→/users/123
// app/users/[id]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
return <div>User: {id}</div>;
}
ネスト(サブページ)
app/admin/page.tsx→/adminapp/admin/users/page.tsx→/admin/users
page などのファイル名は Next が予約しており、変更不可です。
Next のファイル階層ベースのルーティングは、old school PHPer には逆に馴染み深いと思いました。
2. MVC パターン相当
PHP(典型例)
// users.php
$users = find_users();
include "views/users.php";
Next.js
Server Component を基本的に使うことになります。
// app/users/page.tsx (デフォルトで Server Component になる)
export default async function Page() {
const users = await fetch("https://example.com/api/users").then((r) =>
r.json(),
);
return (
<ul>
{users.map((u: any) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
page.tsxは基本サーバー側で実行(DB アクセスや秘密鍵読み取りができる)- 画面の一部をブラウザ側で動かすときだけ
use clientする
App Router では Server Component と Client Component の区別が重要です。
3. use client: クライアント側の UI 動作
PHP で吐く HTML ドキュメントに jQuery を加えて DOM 操作する代わりに、Next では React コンポーネントでシームレスに書くことができます。use client ディレクティブを置いたファイルは、クライアント側で動作する React コンポーネントになります。
"use client";
import { useState } from "react";
export default function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>count: {n}</button>;
}
ページ固有のコンポーネントは page.tsx と同じ階層に置くと見通しが良いですが、共通部品は app/ 外の components/ 等、別フォルダに集約しても可です。
SSR が有効な場合、Client Component も初期レンダリングはサーバー側で実行され、hydration によりクライアント側でインタラクティブになるという手順を踏むことに注意してください。
| サーバーで実行 | クライアントで実行 | |
|---|---|---|
| Server Component | する | しない |
| Client Component | する(SSR 有効時) | する |
4. GET/POST エンドポイント
PHP
- パラメータは
$_GET,$_POSTで受け取る - HTML フォームの POST 先は同一ファイルや別エンドポイント
Next.js
典型的なパターンとして 2 つ存在します。
A. Server Functions
フォーム内容を関数に POST するような感覚の機能です。use server ディレクティブは関数内の最上部に配置され、その関数を Server Function としてマークします。すると、その関数はサーバー側で実行されるようになります。
// app/todo/page.tsx
export default function Page() {
async function addTodo(formData: FormData) {
"use server";
const title = String(formData.get("title") || "");
// DB に保存など(サーバーで実行)
// ...
}
return (
<form action={addTodo}>
<input name="title" />
<button type="submit">Add</button>
</form>
);
}
B. API Route(REST エンドポイント)
// app/api/todos/route.ts
export async function POST(req: Request) {
const body = await req.json();
return Response.json({ ok: true, received: body });
}
5. クエリパラメータ
PHP
$page = (int)filter_var($_GET["page"] ?? 1, FILTER_VALIDATE_INT);
Next.js
export default async function Page({
searchParams,
}: {
searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
const params = await searchParams;
const page = Number(params.page ?? 1);
return <div>page={page}</div>;
}
6. テンプレーティング
| Blade (Laravel) | JSX (Next.js) |
|---|---|
{{ $x }} | {x} |
@if / @else | {cond ? A : B} / {cond && A} |
@foreach($xs as $x) | {xs.map(x => ...)} |
@include('partial') | <Partial />(コンポーネント化) |
@extends/@section | app/layout.tsx(レイアウトファイル) |
7. レイアウト(共通ヘッダー・フッター)
PHP
- 相対パスを使った共通ファイルの取り込みによって行うことが多い
header.php,footer.phpをinclude/require
Next.js
- レイアウト定義用のファイルが存在する(よりトップダウン的)
app/layout.tsxが全体レイアウト定義を持つ
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<header>Header</header>
{children}
<footer>Footer</footer>
</body>
</html>
);
}
8. データベースアクセス
PHP
PHP では PDO / Laravel Eloquent などでデータベースアクセスを行います。
Next.js
Next.js には固有のデータベースアクセス機能は存在しないので、Prisma / Drizzle など自由に選択できます。
Server Component があるので、PHP と同様にサーバー側で問い合わせを行うことができます。
9. Cookie
import { cookies } from "next/headers";
export default async function Page() {
const cookieStore = await cookies();
const token = cookieStore.get("token")?.value;
return <div>token={token}</div>;
}
10. Proxy(ミドルウェアレイヤー)の差し込み
proxy.ts をプロジェクト直下に置くことで、リクエストごとに任意の中間処理を行うことができます。
(どうも最近になって middleware.ts から改名されたようです。)
// proxy.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function proxy(req: NextRequest) {
const token = req.cookies.get("token")?.value;
if (!token && req.nextUrl.pathname.startsWith("/admin")) {
return NextResponse.redirect(new URL("/login", req.url));
}
return NextResponse.next();
}
これはちょっと Starlette (FastAPI) っぽいかもしれません。
11. エラーページ
ファイルベースのルーティングにより、エラーページもファイルベースで管理できます。
app/error.tsx: そのルート配下のエラー画面app/not-found.tsx: 404 画面app/loading.tsx: ロード中 UI (Suspense)
A. ファイル階層概観
app/
layout.tsx # 全体レイアウト
page.tsx # /
users/
page.tsx # /users
[id]/
page.tsx # /users/:id
api/
users/
route.ts # /api/users
proxy.ts # 認証/リライト等
.env.local # 環境変数
所感
バックエンド動作とフロントエンド view を近い場所に置いて開発することが多かった PHPer にとっては理解しやすいフレームワークかなと個人的には思います。やっててよかった PHP!
全体的に実用面を重視したフレームワークに感じられ、特に Server Function を使うことで画面のためだけの API を作成する手間を省いたりできるのは、面倒な問題を直接殴れるような感覚になれます。でも、脆弱性だけは勘弁な。




