{{ $post->title }}
{{-- 自動 HTML エスケープ --}}{!! nl2br(e($post->body)) !!}
{{-- e() でエスケープしてから改行変換 --}} 投稿者: {{ $post->user->name }}ページの作成
親となるページを選択してください。
| この記事の要点 |
|
MVC とは Model(DB との対話)・View(HTML 表示)・Controller(リクエスト処理)の三層に分ける設計パターンです。Laravel での典型的な流れ:
1) ブラウザ
↓ POST /posts
2) Route (routes/web.php)
↓ PostController@store
3) Controller (app/Http/Controllers/PostController.php)
・$request->validate(...)
・Post::create(...) ← Model に保存
↓
4) View (resources/views/posts/show.blade.php)
・{{ $post->title }} ← 表示
↑
5) ブラウザに HTML が返る
// routes/web.php
use App\Http\Controllers\PostController;
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/create', [PostController::class, 'create']);
Route::post('/posts', [PostController::class, 'store']);
Route::get('/posts/{post}', [PostController::class, 'show']);
// 一括(リソースコントローラ)
Route::resource('posts', PostController::class);
# モデル + マイグレーション + コントローラ + リクエストを一括生成
php artisan make:model Post -mcr --requests// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Post extends Model {
use HasFactory;
// ✅ 入力許可するカラムを明示(推奨)
protected $fillable = ['title', 'body', 'user_id', 'published_at'];
// または逆指定(基本これは使わない)
// protected $guarded = ['id'];
// 型キャスト
protected $casts = [
'published_at' => 'datetime',
'meta' => 'array',
];
}
$fillable または $guarded を設定しないと Mass Assignment(一括代入)で 例外が出ます。後述の Post::create($request->all()) も動きません。
// app/Http/Controllers/PostController.php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller {
// 一覧
public function index() {
$posts = Post::orderBy('id', 'desc')->paginate(20);
return view('posts.index', compact('posts'));
}
// 新規作成フォーム表示
public function create() {
return view('posts.create');
}
// 保存 ← 本記事のメイン
public function store(Request $request) {
// 1) バリデーション
$validated = $request->validate([
'title' => 'required|string|max:100',
'body' => 'required|string',
]);
// 2) 認証ユーザを紐付け
$validated['user_id'] = auth()->id();
// 3) モデルに保存(4 通りのいずれか)
// 方法 A: create で一発(fillable 必須)
$post = Post::create($validated);
// 方法 B: new + save
// $post = new Post();
// $post->fill($validated);
// $post->save();
// 方法 C: プロパティに直接代入
// $post = new Post();
// $post->title = $validated['title'];
// $post->body = $validated['body'];
// $post->save();
// 方法 D: リレーション経由
// $post = auth()->user()->posts()->create($validated);
// 4) View へリダイレクト or レンダリング
return redirect()->route('posts.show', $post)
->with('success', '投稿しました');
}
// 詳細表示
public function show(Post $post) {
return view('posts.show', compact('post'));
}
}
// 方法 1: compact() —— もっとも一般的
$post = Post::find(1);
$comments = $post->comments;
return view('posts.show', compact('post', 'comments'));
// 方法 2: 連想配列で直接
return view('posts.show', [
'post' => Post::find(1),
'comments' => Comment::where('post_id', 1)->get(),
]);
// 方法 3: with() チェーン
return view('posts.show')
->with('post', Post::find(1))
->with('comments', Comment::where('post_id', 1)->get());
// 方法 4: withXxx() マジックメソッド
return view('posts.show')
->withPost(Post::find(1)) // → $post
->withComments(Comment::all()); // → $comments
{{-- resources/views/posts/show.blade.php --}}
@extends('layouts.app')
@section('content')
{{ $post->title }}
{{-- 自動 HTML エスケープ --}}
{!! nl2br(e($post->body)) !!}
{{-- e() でエスケープしてから改行変換 --}}
投稿者: {{ $post->user->name }}
コメント ({{ $comments->count() }} 件)
@forelse ($comments as $c)
{{ $c->user->name }}
{{ $c->body }}
@empty
まだコメントはありません
@endforelse
@if (session('success'))
{{ session('success') }}
@endif
@endsection
Controller が肥大化するのを防ぐため、バリデーションは Form Request クラスに切り出すのが定石です:
php artisan make:request StorePostRequest// app/Http/Requests/StorePostRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest {
public function authorize(): bool {
return auth()->check();
}
public function rules(): array {
return [
'title' => 'required|string|max:100|unique:posts,title',
'body' => 'required|string|min:10',
];
}
public function messages(): array {
return [
'title.required' => 'タイトルを入力してください',
'title.unique' => 'そのタイトルは既に使われています',
];
}
}
// Controller
public function store(StorePostRequest $request) {
$post = Post::create($request->validated() + [
'user_id' => auth()->id(),
]);
return redirect()->route('posts.show', $post);
}
public function update(UpdatePostRequest $request, Post $post) {
$post->update($request->validated());
// 個別代入版
// $post->title = $request->input('title');
// $post->body = $request->input('body');
// $post->save();
return redirect()->route('posts.show', $post)
->with('success', '更新しました');
}
// ❌ $fillable 未設定の場合
$post = Post::create(['title' => 'hi', 'body' => 'foo']);
// → Illuminate\Database\Eloquent\MassAssignmentException
// ❌ 危険な書き方
// フォームに を仕込まれると
Post::create($request->all());
// → is_admin まで保存されてしまう
// ✅ 安全な書き方
Post::create($request->only(['title', 'body']));
Post::create($request->validated()); // Form Request
| 属性 | 意味 | 運用 |
|---|---|---|
$fillable = [...] | 許可するカラムを明示(ホワイトリスト) | 推奨 |
$guarded = [...] | 禁止するカラムを明示(ブラックリスト) | 非推奨(漏れがち) |
$guarded = [] | すべて許可 | 絶対避ける |
Q: Post::create() で「Add [field] to fillable」エラー
A: 該当カラムを $fillable 配列に追加してください。id, created_at, updated_at は自動なので不要。
Q: $post->title と $post->getAttribute('title') の違い
A: 同じです。getAttribute は内部で呼ばれるメソッドで、Accessor を経由します。
Q: View で {{ $post }} と書くとどうなる
A: モデルが JSON シリアライズされて表示されます。$casts や $hidden(除外したい属性)も反映されます。
ページの作成
親となるページを選択してください。
子ページはありません
コメントを削除してもよろしいでしょうか?
掲示板