PHPとSqliteとJavaScriptを使ってランキング機能を実装してみる1

スポンサーリンク

サンプルのソースコードはgithubにあります。

スポンサーリンク

概要

過去に制作したゲームの一部に上位3位までを表示するランキング機能を実装してみました。↓のゲームは3秒間にクリックした回数を競うゲームです。 3位までに入るとランキングに登録することが出来ます。クリックするとゲームが開始されます。

ランキング機能実装した過去のゲーム↓

Visual Studio Codeの拡張機能SQliteを使用してデータベースを作成する

データベースの作成にはVisual Studio Codeの拡張機能SQliteを使用しました。

テーブル名はtestにしました。テーブルの中には二つのカラムがあり、一つ目のnameカラムには名前を保持して二つ目のscoreカラムには得点や記録などを保持します。テーブルには最下位になるデータを一件追加しています。

CREATE TABLE test(name, score);

INSERT INTO test VALUES('名無しさん', '0');

PHPのPDOを使ってデータベースに接続してデータを取得する

PDOクラスをインスタンス化する。

function getPdoInstance()
{
try{
    $pdo = new PDO('sqlite:./test.db'); //  PDOの引数に(sqlite:データベースのパス)で指定する
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // エラーが起きた時例外を投げる
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); // 連想配列形式でデータを取得する
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // 指定した型に合わせる

    return $pdo;
    
    }catch(PDOException $e){
    //echo $e->getMessage();
    exit('エラーが発生しました');
    }
}
$pdo = getPdoInstance();

PDOクラスの引数にデータベースファイルが置いてあるパスを指定します。これでデータベースの接続は完了です。必要に応じてsetAttributeメソッドで属性をセットします。今回は3つ属性をセットしました。

queryメソッドを使用して必要なデータを取得する。

function getDb($pdo)
{
    $stmt = $pdo->query("SELECT 
                            i1.name 
                            ,i1.score
                            ,(SELECT count(i2.score)
                                FROM test i2
                                WHERE i1.score < i2.score) + 1 AS rank
                        FROM 
                            test i1
                        WHERE 
                            rank <= 3 
                        ORDER BY 
                            rank");
    $stmts = $stmt->fetchAll();
    return $stmts;
}
$json = getDb($pdo);
$data=json_encode($json);
echo $data;

相関サブクエリを使い順位を付けて並べ替え上位3位までをfetchAllメソッドでデータを取得しています。取得したデータはjson_encodeメソッドでjson形式にエンコードしています。

JavaScriptのFetch APIを使ってサーバーにリクエストする

先ほどPHPで作成したjsonをJavaScriptのFetchAPIを使用して取得します。

let lastScore; // 3位のスコアを保持する変数

function fetchRanking() {
  return fetch("./ranking.php")
    .then((response) => response.json())
    .then((data) => {
      if (Object.keys(data).length === 0) {
        lastScore = 0;
      } else {
        lastScore = data[Object.keys(data).sort().pop()].score;
      }
      return data;
    })
    .catch((error) => {
      console.log(error);
      return;
    });
}

変数lastScoreに取得した上位3位までのデータ中一番低いスコアを保持して、ゲーム終了時のスコア比較に使用します。

ランキング入賞時の処理

スコアを比較して上位3位に入っていた時のみサーバーへリクエストするようにしました。

function sendRanking(score) {
  if(isNaN(score)) return;
  if (score <= lastScore) {
    const fd = new FormData();
    const name = prompt(
      "おめでとうございます!TOP3にランクインしました!",
      "名前を入力してください(8文字以内・省略可)"
    );
    if (!name) return;
    fd.append("name", name);
    fd.append("score", score);
    fetch("./ranking.php", {
      method: "POST",
      body: fd,
    }).catch((error) => {
      console.log(error);
    });
  }
}

PHPのPDOを使ってデータを追加する

JavaScriptからリクエストされたデータをprepareメソッドを使いクエリを送信して追加します。

if($_SERVER['REQUEST_METHOD'] === 'POST')
{
    $score = filter_input(INPUT_POST, 'score');
    $name = trim(filter_input(INPUT_POST, 'name'));
    if($name === '' || $name === 'null' || mb_strlen($name) > 8){
      $name = '名無しさん';
    }
    $stmt = $pdo->prepare("INSERT INTO test (name, score) VALUES (:name, :score)");
    $stmt->bindValue('name', $name, PDO::PARAM_STR);
    $stmt->bindValue('score', $score, PDO::PARAM_STR);
    $stmt->execute();

    exit;
}

クライアント側でスコアを保持している為の不具合

この記事のランキング実装方法ではブラウザの開発者ツール等を使いランキングに入賞できてしまうことが解かりました。続きは↓の記事になります。

最後までお読みいただきありがとうございました。
サンプルのソースコードはgithubにあります。

タイトルとURLをコピーしました