8番ホームページ(プロトタイプ)
Session の基礎
問題
以下のような間違い探しゲーム:「8番ホームページ」を作成してください。
- そのホームページには、1番から8番までページがある
- 1番から順に進んでいき、無事8番ページにたどり着ければゲームクリアとする
- 各ページのURL は、
/page/{number}(number は1から8) である - 1番ページには、基準となるページの内容と、「先に進む」ボタンのみがある。これをクリックすると、2番ページに進む
- 2番から7番ページ(以下、「途中ページ」という)には、「先に進む」ボタンと、「元に戻る」ボタンがある
- 「先に進む」ボタンは、次のページ(
/page/{number + 1}) にリンクしており、「元に戻る」ボタンは、今のページ(/page/{number})にリンクしている - プレイヤーは、訪れた途中ページが、1番ページと同じ(正常)なら先に進まなければならず、1番ページと違う(異変)なら元に戻らなければならない
- 途中ページが正常か、異変かは、ランダムに決まる
- 選択を間違った場合(途中ページが、正常なのに戻ってしまったり、異変なのに先に進んでしまった場合)、リンク先のページへは表示されず、1番ページにリダイレクトされる
- 正しく選択した場合、そのリンク先(先に進む場合は次の番号のページ、元に戻る場合は同じ番号のページ)へ遷移する
- なお、URL を直に指定して、次に遷移されるべきでないページに移動しようとした場合(たとえば、2番ページから7番ページへ遷移しようとした場合)、1番ページにリダイレクトされる
今回は、プロトタイプ作成なので、 view の作り込みや、間違いの実装は不要です。
各ページの状態を Session で管理して、正しくページ遷移するように実装してください。
問題は、以下の手順で解いてください。
- Red:小さいテストを作成し、失敗を確認してください
- Green:テストを成功させてください
- Refactor:整理・整頓してください
- 必要に応じて、1から3を繰り返してください
ヒント
背景知識
- セッションについて: 公式ドキュメント
- テスト中にセッションを固定する方法: withSession() メソッド
便利なアサーションの例
アサーションの調べ方 も合わせてご覧ください。
今回は、以下を使うのではないかと思います。
-
assertStatus($code)(HTTP テスト : HTTP ステータスコードが $code に等しいか)
-
assertRedirect($uri)(HTTP テスト : $uri にリダイレクトされたか)
-
assertSessionHas($key, $value=null)(HTTP テスト : セッションの $key が $value に等しいか)
-
assertSessionMissing($key)(HTTP テスト : セッションの $key が存在しないか)
解答例
続きを読む
実行環境:
- Laravel v13.9.0
- PHP 8.4
- PHPUnit
Red
テストを作成します。
bash:
php artisan make:test NumberEightTest
tests/Feature/NumberEightTest.php:
<?php
namespace Tests\Feature;
use Tests\TestCase;
class NumberEightTest extends TestCase
{
public function test_正常なスタートページ(): void
{
$response = $this->get('/page/1');
$response->assertStatus(200)
->assertSessionHas('next_page', 2);
}
public function test_不正なスタートページ(): void
{
$response = $this->get('/page/3');
$response->assertRedirect('/page/1')
->assertSessionMissing('next_page');
$response = $this->get('/page/8');
$response->assertRedirect('/page/1')
->assertSessionMissing('next_page');
}
public function test_正常な途中ページ(): void
{
$response = $this->withSession([
'next_page' => 5,
])->get('/page/5');
$response->assertStatus(200);
$is_normal = (session('is_normal') === 'true');
if($is_normal) {
$response->assertSessionHas('next_page', 6);
}else{
$response->assertSessionHas('next_page', 5);
}
}
public function test_異変な途中ページ(): void
{
$response = $this->withSession([
'next_page' => 5,
])->get('/page/6');
$response->assertRedirect('/page/1')
->assertSessionMissing('next_page');
}
public function test_不正な途中ページ(): void
{
$response = $this->withSession([
'next_page' => 2,
])->get('/page/7');
$response->assertRedirect('/page/1')
->assertSessionMissing('next_page');
}
public function test_正常なクリアページ(): void
{
$response = $this->withSession([
'next_page' => 8,
])->get('/page/8');
$response->assertStatus(200)
->assertSeeText('クリア')
->assertSessionMissing('next_page');
}
}
たくさんのテストを一度に実行しますが、コメントアウトして、小分けにしても OK です。
テストの失敗を確認します。
Green
ルーティングを作成します。
routes/web.php:
use App\Http\Controllers\NumberEightController;
Route::get('/page/{number}', [NumberEightController::class, 'show'])->where('number', '[1-8]');
コントローラーを作成します。
bash:
php artisan make:controller NumberEightController
app/Http/Controllers/NumberEightController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
class NumberEightController extends Controller
{
public function show(Request $request, string $number)
{
$current_page = (int) $number;
$session_page = (int) $request->session()->get('next_page', 1);
if($current_page === 1) {
$request->session()->put('next_page', 2);
return "スタートページの view: <a href='/page/2'>先に進む</a>";
}
if($current_page !== $session_page) {
$request->session()->flush();
return redirect('/page/1');
}
if($current_page === 8) {
$request->session()->flush();
return 'クリアページの view';
}
$is_normal = (rand(0, 99) < 75); // 75% が正常
$next_page = $current_page + 1;
if($is_normal) {
$request->session()->put('next_page', $next_page);
$request->session()->put('is_normal', 'true');
return "正常ページの view: <a href='/page/{$current_page}'>元に戻る</a> <a href='/page/{$next_page}'>先に進む</a>";
}else{
$request->session()->put('next_page', $current_page);
$request->session()->put('is_normal', 'false');
return "異変ページの view: <a href='/page/{$current_page}'>元に戻る</a> <a href='/page/{$next_page}'>先に進む</a>";
}
}
}
テストを実行し、成功を確認してください。
Refactor
特にありません
解説
続きを読む
Session について
ざっくりいうと、ステートレスな HTTP リクエストを、ステートフルにしてくれる仕組みです。
Laravel では、 Request オブジェクトの session メソッドを利用して、安全に扱えます。
今回のように、簡単なゲームの状態をサーバー側で持つこともできますし、ログイン状態を持つような場合にも使われます。
ただし、ログイン認証については、 Auth ミドルウェアを利用することがほとんどだと思いますので、直接 session を触る機会は、それほど多くはないかもしれません。
<= 問題を読んだ・解いた・理解したなどのチェックにご利用ください。クリックすると、チェックが変化します。