
1. 雛形作成とローカル起動
コマンドプロンプトやターミナルを立ち上げ、Webサーバーの公開ディレクトリ(htdocs など)へ移動してからプロジェクトを作成し、VS Codeで開いてローカル開発環境を立ち上げる。
# ① XAMPP等のWebサーバー公開ディレクトリ「htdocs」に移動
cd C:\xampp\htdocs
# (※環境に応じて /var/www/html や、自身の作業用ディレクトリのパスに置き換える)
# ② Composerを使用し、最新のLaravelプロジェクト「TwitterModoki」を新規作成
C:\xampp\htdocs> composer create-project "laravel/laravel=12.*" TwitterModoki
# ③ 作成されたプロジェクトのフォルダ内へ移動
C:\xampp\htdocs> cd TwitterModoki
# ④ 現在のフォルダを対象にしてVS Codeを起動(コマンドからエディタを開く)
C:\xampp\htdocs\TwitterModoki> code .
# ⑤ PHPバージョンエラー対策
C:\xampp\htdocs\laravel-app> composer config platform.php 8.2.12
C:\xampp\htdocs\laravel-app> composer --no-plugins update --no-scripts
※以降は、VSCode内のターミナル画面として説明- 確認事項: 確認事項:
XAMPPのコントロールパネルから「Apache(およびMySQL)」を起動する。
その後、ブラウザでhttp://localhost/TwitterModoki/publicにアクセスし、Laravelの標準Welcome画面が表示されれば正常。
2. データベースの準備とMVCコンポーネントの一括生成
1.phpMyAdminでデータベースtwitter_modokiを作成
2.1で作成したデータベースにアクセスする為に.envファイル編集
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=twitter_modoki
DB_USERNAME=root
DB_PASSWORD=3.データを扱う「Model」、テーブルを定義する「Migration」、制御ロジックを担う「Controller」を一挙に作成する。
# Model、Migration、Controllerをワンコマンドで同時生成
C:\xampp\htdocs\TwitterModoki> php artisan make:model Post -mc- 生成されるファイルとパス:
- Model:
app/Models/Post.php - Migration:
database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php - Controller:
app/Http/Controllers/PostController.php
- Model:
💾 マイグレーション設定(テーブル定義)
生成された database/migrations/xxxx_xx_xx_xxxxxx_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
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id(); // 自動連番のプライマリキー(id)
$table->string('content', 140);// 💡 追加:ツイート本文(VARCHAR型、最大140文字制限)
$table->timestamps(); // created_at(作成日時)と updated_at(更新日時)の自動生成
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
ファイルの編集・保存が完了したら、以下のコマンドを実行してデータベースに反映する。
# マイグレーションを実行し、データベースへ実際にテーブルを構築
C:\xampp\htdocs\TwitterModoki> php artisan migrate💾 モデル設定(大量代入・一括保存の許可)
Laravelの安全機能(Mass Assignment 保護)に対応するため、コントローラーから Post::create() を使って一括で値を書き込めるよう、セーフリスト(保存を許可するカラム)を指定する。
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
// 💡 外部からのデータ書き込み・一括保存を許可するカラムを明示
protected $fillable = ['content'];
}
- 注意: これを設定し忘れると、画面からフォームを送信した際にエラーは出ないが、データベースの
contentカラムが黙って空(または弾かれる)状態になる。
3. ルーティングの定義
「特定のURLに特定の形式(GETやPOST)でアクセスが来たら、コントローラーのどのメソッドを動かすか」というルート対応表を定義する。
routes/web.php
<?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
// ① タイムライン表示(トップページへのGETアクセス)
Route::get('/', [PostController::class, 'index'])->name('posts.index');
// ② ツイート送信(/posts へのフォームPOSTリクエスト)
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');- 補足: 末尾に
->name('名前')をつけておくことで、ビュー側からroute('posts.index')のように名前でURLを逆引きできるようになり、将来的にURLを変更してもコードの書き換えが不要になる。
4. コントローラーの実装(ビジネスロジック)
データの「取得」と「バリデーション・保存」を行う具体的な中身(ロジック)を記述する。
app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use App\Models\Post; // 💡 データベース操作のためにPostモデルをインポート
use Illuminate\Http\Request; // 💡 フォームデータを受け取るためにRequestクラスをインポート
class PostController extends Controller
{
/**
* タイムライン(一覧表示)
*/
public function index()
{
// データベースから最新の投稿順(created_atの降順)で全件取得
$posts = Post::latest()->get();
// resources/views/posts/index.blade.php を呼び出し、取得した変数 $posts を渡す
return view('posts.index', compact('posts'));
}
/**
* ツイートの保存処理
*/
public function store(Request $request)
{
// ① 入力データのバリデーション(チェック)
// 条件(必須入力、かつ最大140文字)を満たさない場合は、自動でエラーメッセージを持ち前の画面に戻る
$request->validate([
'content' => 'required|max:140',
]);
// ② データの保存を実行
Post::create([
'content' => $request->content, // フォームの name="content" から文字列を抽出して格納
]);
// ③ 処理完了後、タイムライン画面(トップページ)へリダイレクト
return redirect()->route('posts.index');
}
}5. ビュー(UI)の作成と配置
画面(ビュー)ファイルを作成・配置し、HTMLとLaravel専用の構文(Blade)を使って画面を構築する。
- 専用フォルダの作成
resources/views/の直下に、postsという名前の新しいフォルダを手動で作成する。- パス:
resources/views/posts/
- パス:
- ファイルの作成 新しく作った
postsフォルダの中に、index.blade.phpという名前のファイルを新規作成する。- パス:
resources/views/posts/index.blade.php - 注意: Laravelで動的な命令(ループやエラー表示など)を使うため、拡張子は必ず
.blade.phpとすること。
- パス:
💾 ビューのコード実装
作成した resources/views/posts/index.blade.php に以下のコードを記述する。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>TwitterModoki</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 font-sans">
<div class="max-w-xl mx-auto my-10 bg-white shadow-md rounded-lg p-6">
<h1 class="text-2xl font-bold text-blue-500 mb-6">TwitterModoki</h1>
<form action="{{ route('posts.store') }}" method="POST" class="mb-8">
@csrf
<textarea name="content" rows="3" class="w-full p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400" placeholder="いまどうしてる?"></textarea>
@error('content')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
<div class="text-right mt-2">
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full">ツイートする</button>
</div>
</form>
<div class="space-y-4 border-t pt-6">
@foreach ($posts as $post)
<div class="p-4 bg-gray-50 rounded-lg border">
<p class="text-gray-800">{{ $post->content }}</p>
<span class="text-xs text-gray-400 block mt-2">{{ $post->created_at->format('Y/m/d H:i') }}</span>
</div>
@endforeach
</div>
</div>
</body>
</html>
- 紐付けのルール: コントローラー側で指定した
return view('posts.index');という記述は、「resources/views/の中にあるpostsフォルダのindex.blade.phpを読み込む」 という命令になる。Laravelではフォルダの区切りを/ではなく.(ドット)で表現する。
Nodeモジュールのインストール
1. パッケージのインストール
ターミナルで以下のコマンドを実行し、package.json に記載されている依存パッケージ(Viteを含む)をすべてインストールします。
PS C:\xampp\htdocs\TwitterModoki> npm install※完了までに数十秒〜数分かかることがあります。
2.アセットをビルド
デザインにCDN経由の Tailwind CSS(<script src="https://cdn.tailwindcss.com"></script>)を使用されていためビルドします。
PS C:\xampp\htdocs\TwitterModoki> npm run build3.自動テスト(Featureテスト)の実装と環境整備
機能が正常に動くか、バリデーションが正しく効くかを検証する自動テスト環境を構築する。XAMPP環境(MySQL)でテスト用DBを分離して安全に実行する手順。
📁 1. テスト専用データベースの作成(初回のみ)
テスト実行時に開発用データを破壊しないよう、テスト専用の空データベースを用意する。
- ブラウザで
http://localhost/phpmyadmin/を開く。 - 新しいデータベースとして
twitter_modoki_testを作成する。
📁 2. テスト環境設定の修正(MySQL用)
プロジェクトルート直下にある phpunit.xml を開き、<php> タグ内を以下のように修正してテスト時の接続先を固定する。
<php>
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_DATABASE" value="twitter_modoki_test"/>
<env name="DB_USERNAME" value="root"/>
<env name="DB_PASSWORD" value=""/>
</php>⚠️ 注意: 修正後、設定が反映されない場合は必ず
php artisan config:clearを実行してキャッシュを飛ばすこと。
📁 3. テストファイルの生成とインポートの修正
VS Codeのターミナルで以下のArtisanコマンドを実行し、テストファイルを生成する。
PS C:\xampp\htdocs\TwitterModoki> php artisan make:test PostTest生成された tests/Feature/PostTest.php を開き、以下のように実装する。
- ※
use App\Models\Post;のインポート漏れに注意すること。 - ※
usernameカラムのNOT NULL制約(エラーコード1364対策)を回避するため、テストデータ生成時およびPOST送信時には必ずusernameも含めること。
<?php
namespace Tests\Feature;
use App\Models\Post; // 💡 必須:モデルのインポート漏れ注意
use Illuminate\Foundation\Testing\RefreshDatabase; // 💡 各テスト実行時にDBをリセットする設定
use Tests\TestCase;
class PostTest extends TestCase
{
use RefreshDatabase; // テストごとにマイグレーションを実行し、データをクリーンにする
/**
* 1. タイムライン画面が正常に表示されるかテスト
*/
public function test_timeline_page_displays_correctly(): void
{
// 💡 username を含めてダミーデータをDBに1件作成
Post::create([
'content' => 'テスト投稿です。',
'username' => 'test_user'
]);
// トップページにGETリクエストを送信
$response = $this->get(route('posts.index'));
// ステータスコードが200(成功)であることを確認
$response->assertStatus(200);
// 画面内に指定の文字列が含まれているか確認
$response->assertSee('TwitterModoki');
$response->assertSee('テスト投稿です。');
}
/**
* 2. 正当なツイートが投稿(保存)できるかテスト
*/
public function test_user_can_post_a_valid_tweet(): void
{
// 💡 送信データに username を含めてPOSTリクエストを送信
$response = $this->post(route('posts.store'), [
'content' => 'Hello Laravel Test!',
'username' => 'test_user'
]);
// データベースに該当データが保存されているか検証
$this->assertDatabaseHas('posts', [
'content' => 'Hello Laravel Test!'
]);
// 処理後にトップページへリダイレクトされるか検証
$response->assertRedirect(route('posts.index'));
}
/**
* 3. 空白投稿のバリデーションが効くかテスト
*/
public function test_content_is_required_to_post(): void
{
// contentを空の状態で送信
$response = $this->post(route('posts.store'), [
'content' => ''
]);
// セッションで'content'カラムにエラーがあるか検証
$response->assertSessionHasErrors(['content']);
// DBにデータが増えていない(レコード数が0件)ことを確認
$this->assertCount(0, Post::all());
}
/**
* 4. 140文字超えの投稿のバリデーションが効くかテスト
*/
public function test_content_cannot_exceed_140_characters(): void
{
// 141文字の文字列を生成して送信
$longString = str_repeat('あ', 141);
$response = $this->post(route('posts.store'), [
'content' => $longString
]);
$response->assertSessionHasErrors(['content']);
$this->assertCount(0, Post::all());
}
}📁 4. 既存の ExampleTest.php の修正
初期状態から存在する tests/Feature/ExampleTest.php は、トップページ(/)への疎通確認テストである。
本編でトップページがDB(postsテーブル)を参照するように変更したため、こちらもDB初期化用のトレイトを追加しないと500エラーで共倒れする。
tests/Feature/ExampleTest.php を開き、以下のように修正する。
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase; // 💡 追加
use Tests\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase; // 💡 追加(これでこのテスト時もテーブルが自動生成される)
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}🏃♂️ 5. キャッシュクリアとテストの実行
設定ファイルの変更を確実に適用させ、すべてのテストを一斉に実行する。
# ① 設定キャッシュを一回クリアする
PS C:\xampp\htdocs\TwitterModoki> php artisan config:clear
# ② テストを実行
PS C:\xampp\htdocs\TwitterModoki> php artisan test
PASS Tests\Unit\ExampleTest
✓ that true is true
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response 0.46s
PASS Tests\Feature\PostTest
✓ timeline page displays correctly 0.01s
✓ user can post a valid tweet 0.04s
✓ content is required to post 0.01s
✓ content cannot exceed 140 characters 0.03s
Tests: 6 passed (14 assertions)
Duration: 0.66s- 成功時の表示:
PASSが並び、最終行にTests: 6 passedと緑色の文字で出力されれば、テスト環境の分離とロジックの担保がすべて完了。
4.動作確認
次のURLにアクセスする。http://localhost/TwitterModoki/public/

何か記入してツイートする

📌 備忘録:開発時セルフチェック要点
本番サーバー(Alias運用)への対応:
自宅サーバーの環境のように、ポートフォリオ用の同一サブドメイン配下で /twitter などの別ディレクトリとして動かす場合は、本番デプロイ時に public/.htaccess の RewriteEngine On の直下に RewriteBase /twitter/ を追記することを忘れない。
419 Page Expired エラー:
フォームを送信してこの画面が出たら、Bladeの <form> 内に @csrf が書き漏れていないかを即座に確認する。
保存されない問題:
投稿ボタンを押してもDBに保存されない場合は、Post.php の protected $fillable = ['content']; の綴りや記述を真っ先に確認する。



コメント