0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【サーバー】無料でクライアントサーバー型のゲームを制作する3

Last updated at Posted at 2022-08-01

はじめに

前回は環境構築をしました。
今回はサーバーサイドの実装を進めます。
こちらのようなLaravel入門記事が参考になります。

仕様

以下の画像のようなランキング機能を実装します。
ランキングモーダル.jpg
ランキングにはユーザー名とスコアを表示します。
ゲーム開始前にユーザー登録はなく、ランキング登録時のみユーザー登録できるようになっています。
ランキング登録時に、新規ユーザーの場合はスコア登録と同時にユーザー登録も行い、既存ユーザーの場合はハイスコアなら更新します。

DB設計

ユーザー情報テーブル

user_profiles

Field Type Length Allow Null Kety Default Extra
id INT 10 × PRI auto_increment
uuid VARCHAR 255 × UNI None
user_name VARCHAR 255 × None
created_at TIMESTAMP NULL None
updated_at TIMESTAMP NULL None

ユーザーランキングテーブル

user_rankings

Field Type Length Allow Null Kety Default Extra
id INT 10 × PRI auto_increment
user_profile_id BIGINT 20 × None
score INT 11 × None
created_at TIMESTAMP NULL None
updated_at TIMESTAMP NULL None

API設計

ランキング登録API

add_user_ranking

  • Params
name Type memo
uuid string ユーザーを識別するID
user_name string ユーザー名
score int 取得したスコア
  • Responses(新規ユーザーの場合)
name Type memo
uuid string ユーザーを識別するID
  • Responses(既存ユーザーの場合)
    なし

ランキング情報取得API

get_user_ranking

  • Params
    なし

  • Responses

name Type memo
responseParams.user_name string ユーザー名
responseParams.score int ハイスコア

ユーザー情報取得API

get_user_info

  • Params
name Type memo
uuid string ユーザーを識別するID
  • Responses
name Type memo
responseParams.user_name string ユーザー名
responseParams.high_score int ハイスコア

Laravelコマンド

Laravelではartisanコマンドで様々なファイルの作成を行います。
以降のコマンドの実行はLaravelをインストールしたディレクトリに移動し行ってください。
私はSample_Api/laravelに入れたので、以下のコマンドでこのディレクトリに移動しておきます。

cd Sample_Api/laravel

マイグレーション

テーブルの作成を行います。
まず以下のコマンドでuser_profilesテーブルのマイグレーションファイルを作成します。

php artisan make:migration create_user_profiles_table

laravel/database/migrationsディレクトリにマイグレーションファイルが作成されました。

DB設計に合わせて以下のように記述します。

    public function up()
    {
        Schema::create('user_profiles', function (Blueprint $table) {
          $table->increments('id');
          $table->string('uuid')->unique();
          $table->string('user_name');
          $table->timestamps();
        });
    }

次は同じようにuser_rankingsテーブルのマイグレーションファイルを作成します。
以下のコマンドを実行します。

php artisan make:migration create_user_rankings_table

以下のように記述します。

    public function up()
    {
        Schema::create('user_rankings', function (Blueprint $table) {
          $table->increments('id');
          $table->bigInteger('user_profile_id')->references('id')->on('user_profiles');
          $table->integer('score');
          $table->timestamps();
        });
    }

準備が完了しました。
マイグレーションを実行し、テーブルを作成します。
マイグレーション実行のコマンドは以下です。

php artisan migrate

Sequel proなどでDBを確認しましょう。
user_profilesテーブルとuser_rankingsテーブルが生成されていると思います。

モデル

テーブルに対応するようモデルを作成します。
モデルはテーブルと自動でマッピングされるようになっています。
マッピングさせるため、必ずテーブル名の単数形を名付けるようにしてください。

まずはuser_profilesテーブルとマッピングするモデルを作成します。
以下のコマンドでモデルファイルを作成できます。

php artisan make:model UserProfile

次にuser_rankingsテーブルとマッピングするモデルを作成します。
以下のコマンドを実行します。

php artisan make:model UserRanking

モデルが作成できました。

コントローラー

コントローラーにAPIの実装を書いていきます。
ユーザーランキングのAPIを作成するので、UserRankingControllerと名付けることにします。
コントローラーは以下のコマンドで作成できます。

php artisan make:controller UserRankingController

今回は3つのAPIを作成します。
①ランキング登録API
②ランキング情報取得API
③ユーザー情報取得API

①はadd_user_rankingメソッドに、②はget_user_rankingメソッドに、③はget_user_infoメソッドに記述することにします。
詳しい実装は後で書きます。

ルーティング

リクエストを渡すメソッドを設定します。
APIのルーティングはlaravel/routes/app.phpに記述します。
Laravel 8.xでは以下のように書きます。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserRankingController;

Route::group(['middleware' => ['api']], function(){
  Route::post('/user/ranking/add', [UserRankingController::class, 'add_user_ranking']);
  Route::get('/user/ranking/get', [UserRankingController::class, 'get_user_ranking']);
  Route::post('/user/info/get', [UserRankingController::class, 'get_user_info']);
});

1つ目が①ランキング登録API、2つ目が②ランキング情報取得API、3つ目が③ユーザー情報取得APIです。
コントローラーのパスをuseするのを忘れないようにしましょう。
また、/user/ranking/addのように頭に/を必ずつける必要があります。

POSTMANなどで接続の確認をしましょう。
例えばhttp://localhost:8002/laravel/public/api/user/ranking/addのリンクで1つ目のAPIに接続できます。

ランキング情報取得APIの実装

ランキングを表示するときに、ランキング情報を取得するAPIを実装します。
リクエストはなし、レスポンスにはユーザー名とスコアを返します。

SQLで書くと以下のようになります。

SELECT up.user_name, ur.score
FROM user_profiles up
INNER JOIN user_rankings ur ON up.id = ur.user_profile_id
ORDER BY ur.score DESC, ur.updated_at ASC

順位付けの優先度は①スコアが高い②登録日が早いにしました。
これをLaravelのクエリビルダ記法で書くと以下のようになります。

$user_scores = DB::table('user_profiles')
      ->join('user_rankings', 'user_profiles.id', '=', 'user_rankings.user_profile_id')
      ->select('user_profiles.user_name', 'user_rankings.score')
      ->orderBy('user_rankings.score', 'desc')
      ->orderBy('user_rankings.updated_at', 'asc')
      ->get();

クエリビルダにはDBクラスを使います。
useするのを忘れないようにしましょう。

use Illuminate\Support\Facades\DB;

取得したデータをレスポンスで返します。

return ["responseParams" => $user_scores];

これで完成です。

use Illuminate\Support\Facades\DB;

class UserRankingController extends Controller
{
    public function get_user_ranking()
    {
      $user_scores = DB::table('user_profiles')
            ->join('user_rankings', 'user_profiles.id', '=', 'user_rankings.user_profile_id')
            ->select('user_profiles.user_name', 'user_rankings.score')
            ->orderBy('user_rankings.score', 'desc')
            ->orderBy('user_rankings.updated_at', 'asc')
            ->get();
      return ["responseParams" => $user_scores];
    }
}

ランキング登録APIの実装

次はランキング登録するAPIを実装します。
新規ユーザーの場合は同時にユーザー登録も行い、既存ユーザーの場合はハイスコアのときのみスコアを更新します。
リクエストはuuid、ユーザー名、スコアで、新規ユーザーの場合は作成したuuidをレスポンスで返します。また、リクエストに応じてDBに変更を加えます。

まずはリクエストを受け取ります。

$uuid      = (string)$request->input('uuid');
$user_name = (string)$request->input('user_name');
$score     = (int)$request->input('score');

キャストはしておいた方が良いです。
キャストしなければintがstringになってしまいます。

次に、新規ユーザーか既存ユーザー判定し、それぞれ処理を書いています。
uuidがないなら新規、あるなら既存ユーザーですので、以下のように場合分けできます。

if($uuid == null) // 新規ユーザー
{
    ...
}
else // 既存ユーザー
{
    ...
}

まずは新規ユーザーの処理を書いていきます。
新規ユーザーなので、ユーザー登録をする必要があります。
uuidを生成してあげましょう。
こちらを参考にしました。
Laravelがメソッドを用意してくれているので簡単です。

$new_uuid = (string)Str::uuid();

これだけです。
Strクラスをuseする必要があります。

use Illuminate\Support\Str;

これだけでも良いのですが、万が一生成されるuuidが被ったときのために、被ったら生成し直す処理を書きます。

// uuidの被り判定
$same_flg = true;

// 被りのないものができるまでuuid生成
while($same_flg == true)
{
  $new_uuid  = (string)Str::uuid();
  $same_uuid = UserProfile::where('uuid', '=', $new_uuid)
        ->first();
  if(empty($same_uuid))
  {
    $same_flg = false;
  }
}

これでuuidが生成できました。
生成したuuidをリクエストで得たuser_nameと一緒にユーザー登録します。

UserProfile::create([
    'uuid'      => $new_uuid,
    'user_name' => $user_name
]);

LaravelのEloquentを使っています。
これはすべてのモデルを継承しています。
使用するモデルをuseするようにしましょう。

use App\Models\UserProfile;

作成したuuidを最後にレスポンスで返します。

return $new_uuid;

次に作成したユーザーでランキング登録をします。
ユーザー情報テーブルとユーザーランキングテーブルはユーザー情報テーブルのidで紐づいています。
なので、生成したuuidからユーザー情報テーブルのidを取得し、そのidとリクエストで受け取ったスコアをユーザーランキングテーブルに登録するという手順になります。
まずは生成したuuidからユーザー情報テーブルのidを取得します。

$array_user_profile_id = UserProfile::where('uuid', '=', $new_uuid)
   ->first(['id']);
$user_profile_id = $array_user_profile_id['id'];

idが配列となって抽出されるので、その後、値だけを抜き出すようにしています。
次にランキング登録をします。

UserRanking::create([
    'user_profile_id' => $user_profile_id,
    'score'           => $score
]);

使用するモデルのuseは忘れないようにしましょう。

use App\Models\UserRanking;

これで新規ユーザーの処理はできました。
次に既存ユーザーの処理です。
リクエストで得たuuidに紐づくユーザーランキングテーブルのuser_profile_idとscoreを取得し、そのscoreをリクエストで得たscoreが超えていれば、そのuser_profile_idのscoreを更新するという手順になります。
まずはリクエストで得たuuidに紐づくユーザーランキングテーブルのuser_profile_idとscoreを取得します。
クエリビルダで書きました。

$array_user_high_score = DB::table('user_rankings')
     ->join('user_profiles', 'user_rankings.user_profile_id', '=', 'user_profiles.id')
   ->select('user_rankings.user_profile_id', 'user_rankings.score')
     ->where('user_profiles.uuid', '=', $uuid)
     ->first();
$user_profile_id = $array_user_high_score->user_profile_id;
$high_score      = $array_user_high_score->score;

これも先ほどと同じように配列で抽出されるため、その後、値だけを抜き出すようにしています。
値を抜き出す記法が先ほどと違いますが、クエリビルダの場合はこう書くようです。
参考記事はこちらです。
次に、取得したscoreがリクエストで得たscoreを超えていたとき、そのユーザーののscoreを更新する処理を書きます。

if($score > $high_score)
{
  UserRanking::where('user_profile_id', '=', $user_profile_id)
        ->update([
          'score' => $score
        ]);
}

以上で既存ユーザーの処理もできました。
これで完成です。

use Illuminate\Http\Request;
use App\Models\UserRanking;
use App\Models\UserProfile;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;

class UserRankingController extends Controller
{
    // ランキング登録
    public function add_user_ranking(Request $request)
    {
      // リクエストパラメータを受け取る
      $uuid      = (int)$request->input('uuid');
      $user_name = (string)$request->input('user_name');
      $score     = (int)$request->input('score');

      if($uuid == null) // 新規ユーザー
      {
        // uuidを生成する
        $new_uuid = self::_create_uuid();

        // ユーザー登録
        UserProfile::create([
          'uuid'      => $new_uuid,
          'user_name' => $user_name
        ]);

        // ランキング登録
        $array_user_profile_id = UserProfile::where('uuid', '=', $new_uuid)
              ->first(['id']);
        $user_profile_id = $array_user_profile_id['id'];
        UserRanking::create([
          'user_profile_id' => $user_profile_id,
          'score'           => $score
        ]);

        return $new_uuid;
      }
      else // 既存ユーザー
      {
        // uuidに紐づくユーザーのスコアを取得する
        $array_user_high_score = DB::table('user_rankings')
              ->join('user_profiles', 'user_rankings.user_profile_id', '=', 'user_profiles.id')
              ->select('user_rankings.user_profile_id', 'user_rankings.score')
              ->where('user_profiles.uuid', '=', $uuid)
              ->first();
        $user_profile_id = $array_user_high_score->user_profile_id;
        $high_score      = $array_user_high_score->score;

        // ハイスコアならランキングテーブルを更新する
        if($score > $high_score)
        {
          UserRanking::where('user_profile_id', '=', $user_profile_id)
                ->update([
                  'score' => $score
                ]);
        }
      }
    }

    // uuidを生成する
    private function _create_uuid()
    {
      // uuidの被り判定
      $same_flg = true;

      // 被りのないものができるまでuuid生成
      while($same_flg == true)
      {
        $new_uuid  = (string)Str::uuid();
        $same_uuid = UserProfile::where('uuid', '=', $new_uuid)
              ->first();
        if(empty($same_uuid))
        {
          $same_flg = false;
        }
      }
      return $new_uuid;
    }
}

uuidの生成は別のメソッドに分けました。
処理ごとにメソッドを分けておくと、使いまわせて便利です。

ユーザー情報取得APIの実装

最後にユーザー情報を取得するAPIを実装します。
リクエストはuuid、で、ユーザー名とハイスコアをレスポンスで返します。

まずはリクエストを受け取ります。

$uuid = (string)$request->input('uuid');

次にこのuuidからユーザー情報テーブルのユーザー名と、ユーザーランキングテーブルのスコアを取得します。先ほどとほぼ同じです。

// uuidに紐づくユーザー情報を取得する
$array_user_data = DB::table('user_rankings')
      ->join('user_profiles', 'user_rankings.user_profile_id', '=', 'user_profiles.id')
      ->select('user_profiles.user_name', 'user_rankings.score')
      ->where('user_profiles.uuid', '=', $uuid)
      ->first();
$user_name  = $array_user_data->user_name;
$high_score = $array_user_data->score;

最後にレスポンスを返します。

return ["responseParams" => [[
          'user_name'  => $user_name,
          'high_score' => $high_score
        ]]];

これで完成です。

// ユーザー情報取得
public function get_user_info(Request $request)
{
  // リクエストパラメータを受け取る
  $uuid = (string)$request->input('uuid');

  // uuidに紐づくユーザー情報を取得する
  $array_user_data = DB::table('user_rankings')
        ->join('user_profiles', 'user_rankings.user_profile_id', '=', 'user_profiles.id')
        ->select('user_profiles.user_name', 'user_rankings.score')
        ->where('user_profiles.uuid', '=', $uuid)
        ->first();
  $user_name  = $array_user_data->user_name;
  $high_score = $array_user_data->score;

    return ["responseParams" => [[
      'user_name'  => $user_name,
      'high_score' => $high_score
    ]]];
}

さいごに

今回はサーバーサイドをやりました。
次回はクライアントサイドをやります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?