はじめに
こんにちは、未経験からエンジニア転職を目指しているものです。
オンラインスクールで本格的に学習して6ヶ月目に入りました。PHP,Laravelを学習しポートフォリオを作成中です。ポートフォリオ作成手順及び、エラー内容を中心に記録していこうと思います。今回は投稿画面の作成を行いました。本投稿は流れのメモが重点になっておりますので、削除した部分や追加した部分の詳細は割愛致します。
ざっくりと投稿画面を作成
ワイヤーフレーム内容とは違いますが、まずは投稿画面が機能する状態まで作成してみました。
投稿結果が反映されるような画面を作成
エラーが発生
-
エラーの原因
投稿テーブルにcreated_atカラムが存在していなかった。
このカラムはLaravelのlatest()メソッドがデフォルトで使用するため、created_atが無いとエラーになる。
対策内容
latest()の代わりに別の並び替え方法を使用
latest()メソッドはcreated_atをデフォルトで使用するが、別のカラムで並び替えを実施
- コントローラーの修正 PostController.phpのindexメソッドを以下のように修正し解決
public function index()
{
$posts = Post::orderBy('id', 'desc')->get(); // id カラムで並び替え
return view('posts.index', compact('posts'));
}
エラー2
- エラーの原因
Posts テーブルに updated_at や created_at カラムが存在しない発生。
Post::create() メソッドは、デフォルトでタイムスタンプ (updated_at と created_at) を保存しようとするが、それらのカラムがテーブルにないため、このエラーが発生。
対策内容
タイムスタンプは今回は不要な為、モデルでタイムスタンプを無効化し解決
app/Models/Post.phpに以下を追加
public $timestamps = false; // タイムスタンプを無効化
変更した部分
app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class PostController extends Controller
{
// 投稿一覧を表示
public function index()
{
$posts = Post::orderBy('id', 'desc')->get(); // id カラムで並び替え
return view('posts.index', compact('posts'));
}
// 投稿フォームを表示
public function create()
{
return view('posts.create');
}
// 投稿を保存
public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'preferred_gender' => 'nullable|string',
'preferred_group_size' => 'nullable|string',
]);
Post::create([
'title' => $request->title,
'content' => $request->content,
'preferred_gender' => $request->preferred_gender,
'preferred_group_size' => $request->preferred_group_size,
'user_id' => Auth::id(),
]);
return redirect()->route('posts.index')->with('success', '投稿が完了しました!');
}
}
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
// タイムスタンプを無効化
public $timestamps = false;
protected $fillable = [
'title',
'content',
'preferred_gender',
'preferred_group_size',
'user_id',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
database/migrations/2024_11_19_072137_create_posts_table.php
<?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('posts', function (Blueprint $table) {
$table->id(); // 自動的にインクリメントされるID
$table->string('title'); // 投稿タイトル
$table->text('content'); // 投稿内容
$table->string('preferred_gender')->nullable(); // 希望性別(nullを許容)
$table->string('preferred_group_size')->nullable(); // 希望人数(nullを許容)
$table->foreignId('user_id')->constrained()->onDelete('cascade'); // 投稿者のユーザーID(外部キー制約付き)
// $table->timestamps(); // created_at と updated_at のタイムスタンプ
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts'); // posts テーブルを削除
}
};
resources/views/posts/create.blade.php
<x-app-layout>
<div class="max-w-7xl mx-auto p-6">
<h2 class="text-2xl font-bold mb-4">新規投稿</h2>
<form method="POST" action="{{ route('posts.store') }}">
@csrf
<div class="mb-4">
<label for="title" class="block text-sm font-medium text-gray-700">タイトル</label>
<input type="text" id="title" name="title" required class="block w-full border-gray-300 rounded-md shadow-sm">
</div>
<div class="mb-4">
<label for="content" class="block text-sm font-medium text-gray-700">内容</label>
<textarea id="content" name="content" rows="4" required class="block w-full border-gray-300 rounded-md shadow-sm"></textarea>
</div>
<div class="mb-4">
<label for="location" class="block text-sm font-medium text-gray-700">活動場所</label>
<input type="text" id="location" name="location" required class="block w-full border-gray-300 rounded-md shadow-sm">
</div>
<div class="mb-4">
<label for="preferred_gender" class="block text-sm font-medium text-gray-700">希望性別</label>
<select id="preferred_gender" name="preferred_gender" class="block w-full border-gray-300 rounded-md shadow-sm">
<option value="">指定なし</option>
<option value="male">男</option>
<option value="female">女</option>
</select>
</div>
<div class="mb-4">
<label for="preferred_group_size" class="block text-sm font-medium text-gray-700">希望人数</label>
<select id="preferred_group_size" name="preferred_group_size" class="block w-full border-gray-300 rounded-md shadow-sm">
<option value="">指定なし</option>
<option value="one">一人</option>
<option value="two">二人</option>
<option value="three">三人</option>
</select>
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md">投稿する</button>
</form>
</div>
</x-app-layout>
resources/views/posts/index.blade.php
<x-app-layout>
<div class="max-w-7xl mx-auto p-6">
<h2 class="text-2xl font-bold mb-4">投稿一覧</h2>
@foreach ($posts as $post)
<div class="border-b py-4">
<h3 class="text-lg font-semibold">{{ $post->title }}</h3>
<p class="text-gray-600">{{ $post->content }}</p>
<p class="text-gray-500">活動場所: {{ $post->location }}</p>
</div>
@endforeach
</div>
</x-app-layout>
routes/web.php
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\AuthController; // AuthController をインポート
use App\Http\Controllers\PostController; // PostController をインポート
use Illuminate\Support\Facades\Route;
// ホーム画面ルート
@@ -25,4 +26,12 @@
Route::post('/login', [AuthController::class, 'showLoginForm'])->name('login');
Route::post('/login', [AuthController::class, 'login']);
// 投稿関連のルート
Route::middleware(['auth'])->group(function () {
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
});
require __DIR__.'/auth.php';
次回実施内容
- 投稿画面の作成②
最後までご覧いただき本当にありがとうございました!!