はじめに
RubyonRails歴数ヶ月の筆者が、
一週間の期限でアプリを作るRUNTEQアプリウィークに挑戦しました。
また、新たな知識を付けるため、PHPおよびLaravelに挑戦しました。
「起承転結シャッフルストーリーメーカー」というアプリを題材に、
環境構築からMVC実装まで一通りの流れをRailsと比較しながら紹介します。
環境
- Laravel 13.7.0
- PHP 8.4
- MySQL
- Docker(Laravel Sail)
- macOS
1. 環境構築(Laravel Sail)
Sailとは?
Laravel Sailは、DockerベースのLaravel開発環境です。
PHP・MySQL・Redisなど必要なものが全部入りで、自分でDockerfileを書く必要がありません。
# プロジェクト作成(PHP8.4を指定)
curl -s "https://laravel.build/shufflestory?php=84" | bash
cd shufflestory
# 起動
./vendor/bin/sail up
⚠️ 2026年5月時点では
laravel.buildがデフォルトでPHP8.5を指定してしまうバグがあるので
compose.yamlのcontextをruntimes/8.4に修正してから
sail build --no-cacheしました。
マイグレーション実行
./vendor/bin/sail artisan migrate
http://localhostでLaravelのウェルカム画面が表示されれば成功です。
RailsとSailの比較
| Rails | Laravel Sail | |
|---|---|---|
| 環境構築 | rails new |
curl -s laravel.build/... | bash |
| サーバー起動 | rails server |
./vendor/bin/sail up |
| コマンド実行 | rails ... |
./vendor/bin/sail artisan ... |
Sailを使う場合、
php artisanではなく./vendor/bin/sail artisanを使います。
入力するとエラーが出ます。
2. ルーティング設計
今回のアプリに必要なルートはこちらです。
// routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\StoryController;
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('/stories', [StoryController::class, 'index'])->name('stories.index');
Route::get('/stories/create', [StoryController::class, 'create'])->name('stories.create');
Route::post('/stories', [StoryController::class, 'store'])->name('stories.store');
Route::get('/stories/{id}', [StoryController::class, 'show'])->name('stories.show');
Route::get('/stories/shuffle', [StoryController::class, 'shuffle'])->name('stories.shuffle');
RailsとLaravelのルーティング比較
| Rails | Laravel | |
|---|---|---|
| 記述ファイル | config/routes.rb |
routes/web.php |
| GET | get '/stories', to: 'stories#index' |
Route::get('/stories', [StoryController::class, 'index']) |
| POST | post '/stories', to: 'stories#store' |
Route::post('/stories', [StoryController::class, 'store']) |
| 名前付きルート | as: 'stories_index' |
->name('stories.index') |
| ルート呼び出し | stories_path |
route('stories.index') |
ルート名について
Bladeテンプレートではroute()ヘルパーを使ってURLを生成できます。
<!-- URLを直書きしない→URLが変わっても1箇所直すだけでOK -->
<a href="{{ route('stories.create') }}">投稿する</a>
3. コントローラー
作成コマンド
./vendor/bin/sail artisan make:controller HomeController
./vendor/bin/sail artisan make:controller StoryController
HomeController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
public function index()
{
return view('home');
}
}
RailsとLaravelのController比較
| Rails | Laravel | |
|---|---|---|
| 生成コマンド | rails g controller Home |
sail artisan make:controller HomeController |
| ビュー返却 | render 'home' |
return view('home') |
| リダイレクト | redirect_to stories_path |
return redirect()->route('stories.index') |
| パラメータ取得 | params[:title] |
$request->get('title') |
StoryController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\HomeController;
use App\Models\Story;
class StoryController extends Controller
{
public function index()
{
// viewのstories.index.blade.php を表示
return view('stories.index');
}
public function create()
{
// viewのstories.create.blade.php を表示
return view('stories.create');
}
public function store(Request $request)
{
// フォームから送信されたデータを取得
$title = $request->get('title');
$author = $request->get('author');
$intro = $request->get('intro');
$develop = $request->get('develop');
$conversion = $request->get('conversion');
$ending = $request->get('ending');
// storiesテーブルに取得したデータを保存
$story = Story::create([
'title' => $title,
'author' => $author,
'intro' => $intro,
'develop' => $develop,
'conversion' => $conversion,
'ending' => $ending,
]);
// 保存したデータのidを取得して、
//showアクション(views/stories/show/{id})にリダイレクト
return redirect()->route('stories.show', ['id' => $story->id]);
}
public function show($id)
{
// DBからidが一致するデータを1件取得
$story = Story::find($id);
// ビューに$storyを渡して表示
return view('stories.show', ['story' => $story]);
↑キー名 ↑データ
// Blade側では $story として使える
}
}
4. モデルとマイグレーション
モデル作成
./vendor/bin/sail artisan make:model Story
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Story extends Model
{
// 一括代入を許可するカラム(セキュリティ対策)
protected $fillable = [
'title',
'author',
'intro',
'develop',
'conversion',
'ending',
];
}
$fillableとは?
Mass Assignment(一括代入)攻撃を防ぐセキュリティ機能です。
$fillableに書いたカラムだけcreate()での保存を許可します。
fillableがないと悪意あるユーザーがis_admin=trueを送れてしまう
fillableがあれば許可したカラムのみ保存される
セキュリティ的にこれは忘れず設定しましょう。
マイグレーション作成と実行
./vendor/bin/sail artisan make:migration create_stories_table
./vendor/bin/sail artisan migrate
public function up(): void
{
Schema::create('stories', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('author');
$table->text('intro');
$table->text('develop');
$table->text('conversion');
$table->text('ending');
$table->timestamps(); // created_at, updated_at
});
}
RailsとLaravelのモデル・マイグレーション比較
| Rails | Laravel | |
|---|---|---|
| モデル生成 | rails g model Story |
sail artisan make:model Story |
| マイグレーション生成 | rails g migration CreateStories |
sail artisan make:migration create_stories_table |
| マイグレーション実行 | rails db:migrate |
sail artisan migrate |
| 1件取得 | Story.find(id) |
Story::find($id) |
| 全件取得 | Story.all |
Story::all() |
| 作成 | Story.create(params) |
Story::create([...]) |
| ORM名 | ActiveRecord | Eloquent |
5. ビュー(Bladeテンプレート)
作成コマンド
# viewファイルの storys.create.blade.phpを作成します
./vendor/bin/sail artisan make:view stories.create
# Controllerファイルも作れます。 StoryController.phpを作成します
./vendor/bin/sail artisan make:controller StoryController
# ModelもOK
./vendor/bin/sail artisan make:model Story
# マイグレーションファイル作成の際はLaravelの命名規則に従い_(アンダーバー)を使用
./vendor/bin/sail artisan make:migration create_stories_table
作成するとこんなメッセージが流れます。
トップページ(home.blade.php)
<div>
<h1>ShuffleStory</h1>
<p>起承転結を投稿して、シャッフルしてオリジナルストーリーを作ろう!</p>
<a href="{{ route('stories.create') }}">物語を投稿する</a>
<a href="{{ route('stories.index') }}">物語一覧を見る</a>
</div>
投稿フォーム(stories/create.blade.php)
<form method="POST" action="{{ route('stories.store') }}">
@csrf
<div>
<label for="title">タイトル</label>
<input type="text" id="title" name="title" required>
</div>
<div>
<label for="intro">起</label>
<textarea id="intro" name="intro" required></textarea>
</div>
<div>
<label for="develop">承</label>
<textarea id="develop" name="develop" required></textarea>
</div>
<div>
<label for="conversion">転</label>
<textarea id="conversion" name="conversion" required></textarea>
</div>
<div>
<label for="ending">結</label>
<textarea id="ending" name="ending" required></textarea>
</div>
<button type="submit">投稿する</button>
</form>
RailsとLaravelのビュー比較
| Rails(ERB) | Laravel(Blade) | |
|---|---|---|
| ファイル拡張子 | .html.erb |
.blade.php |
| 変数表示 | <%= @story.title %> |
{{ $story->title }} |
| CSRFトークン | <%= csrf_meta_tags %> |
@csrf |
| ルートURL | stories_path |
route('stories.index') |
6. データ確認(tinker)
Railsのrails consoleに相当するのがtinkerです。
./vendor/bin/sail artisan tinker
# 全件取得
> App\Models\Story::all();
# 1件取得
> App\Models\Story::find(1);
まとめ
RailsとLaravelは思想が非常に似ており、
Rails経験者であれば比較的スムーズに入れました。
主な違いは以下の通りです。
| 概念 | Rails | Laravel |
|---|---|---|
| 言語 | Ruby | PHP |
| コマンド | rails |
artisan |
| 変数 | @story |
$story |
| プロパティアクセス | story.title |
$story->title |
| ビュー | ERB | Blade |
| ORM | ActiveRecord | Eloquent |
| 設定ファイル | 規約優先 |
config/に明示的 |
次のステップとして、
シャッフル機能・一覧表示・バリデーションを実装していく予定です。

