紙ベースで実施されていたフットサルリーグ戦の運用をGCE(Google Compute Engine)+WordPressでシステム化したお話

#はじめに
　２年前から私たちのチームは「フットサルのリーグ戦」にが参加させて頂いています。
　このリーグ戦は15年続いているそうです！
　
　**リーグ戦の概要**
　**・毎月1回開催（年間12回）**
　**・各回、4チームの総当り戦を2セット行う**
　**・年間のチームの勝点で順位を決める**
　**・年間の得点王を決める**
　
　
◆2019年1月に行われた試合で使用した対戦表
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/515b5c08-82bd-7b5e-85f2-5fa644e43381.png)


この紙に各試合の審判が得点、得点者を入力。
その後、主催者が毎回チームの勝点と得点者の得点を集計されています。
　（よく見ると勝点でおかしい箇所があるような気がします）




##作成した画面
成績の参照は見たい情報が見れるようにしました。
得点入力画面は簡単なボタンクリックのみでできる画面にしました。
<BR>

**成績の参照（参加者がみる画面）**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/c52a3935-812e-b052-7b9b-2e5cfb79d3f2.png)


**管理者（得点入力、メンバー登録、集計処理）**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/fb9afcd1-e4f7-5f2c-f34b-bd580e9a75b5.png)


本投稿は
GCE(Google Compute Engine) + WordPressでシステム化した内容を書いております。

#システム化しようとした背景

**・紙ベースの運用されていて、主催者がデータ集計をするのが大変だと思ったから**
**・チーム年間順位・得点王、試合結果をいつでも参照できるようにしたいと思ったから**
**・集計を正しくしたいと思ったから**


上記内容を対応するために
「得点入力画面で得点をDBに格納して、試合終了後、結果を集計する」ことで
主催者の負荷を下げ、参加者はいつでも結果をみることができると思い、簡易システムを作成しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/59ec4dba-dc50-deaa-bbcc-42e2a066fb41.png)


**もう一つ理由があり**
**12月末から1月末まで骨折していたので、フットサルできなくて暇だったのがあります。。**

#実現方法

実現するにあたり、以下の３つを検討
　**１．どんな情報が見たいか、得点入力画面の検討**
　**２．１．でやりたい事のデータの持たせ方の検討（DB設計）**
　**３．環境の検討（サーバ、アプリの検討）**

##１．どんな情報が見たいか、得点入力画面の検討
まずは、Excelでイメージを書いてみる

**一番欲しい情報は現在のチーム順位表と得点王**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/c9be39a4-936b-619c-4d65-4b3f82f17bf5.png)


**各節の試合結果と得点者**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/d9658372-ea26-3e71-03a7-77ff7fc4563a.png)

**得点入力画面**
得点入力は、入力させずに「＋」、「－」ボタンで得点がUP、DOWNするようにする。
※入力チェック処理しなくていいし、また、ボタンだけの操作なので、システム化して得点入力が面倒くさくなったと感じさせないためです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/c8006993-ccac-5bc3-682c-2b107535925c.png)


##２．１．でやりたい事のデータの持たせ方の検討（DB設計）
大きく分けて、「マスタ」と「データ入力用」と「データ参照用」とのテーブルを作成。
※年間順位、年間得点王は、各回の試合結果入力後に集計させ、参照させる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/b2d3504f-3e89-1e41-bcb1-1683349aa143.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/b1bac767-af6f-cb98-cdfb-c35af6fccede.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/5986a5c5-c2e7-57a4-077a-d004cd6acabd.png)


各試合終了後、得点集計処理を実施して、データ参照用テーブルを更新。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/949b617b-a49c-c800-7d42-ac56c07a56b0.png)

※実際のテーブル名、フィールド名は英語にしてます

##３．環境の検討（サーバ、アプリの検討）
無料でサーバを使いたいので、無料枠 の GCE(Google Compute Engine) を使用
　　https://cloud.google.com/free/?hl=ja
　　　　　
また、リーグ戦の参加者は、PC、スマホ(大部分はスマホ)で結果をみると思うので、
PC、スマホと勝手に見せ方を調整してくれるWordPressを選択。
データ格納DBは無料のmariaDB、phpはWordPressを動かす上で必要となります。

###３-１　GCE(Google Compute Engine)の構築
以下の記事を参考にさせて頂きました。ありがとうございました。


　[GCP上でWordPressを無料で構築したい](https://qiita.com/mekabuko/items/d0ef4492f60e40f84f84)
　[GCP(Google Cloud Platform)での無料GCE(Google Compute Engine)インスタンス作成](https://qiita.com/FukuharaYohei/items/0614354d6a4eda32d521)
　[GCE の無料枠のサーバを立るときに、初見でハマりそうなところ](https://qiita.com/ndxbn/items/7ef0a96e409a5b5837bd)
　[無料のドメインを取得する](https://qiita.com/teekay/items/135dc67e39f24997019e)
　[Google Cloud DNSでIPアドレスとドメイン名を紐付ける](https://qiita.com/NagaokaKenichi/items/95052742d40392f3215e)


**WordPressのバージョン、PHP、Jqueryを最新にしないとGoogle Cloud Platformから警告がくる可能性がありますので詳細は以下の記事を参照ください**
[Google Cloud Platform から GCE(Google Compute Engine)+WordPress環境について「ご対応ください: 重大な問題」のメールが来た](https://qiita.com/kngsym2018/items/ed822ffc13cf8889f736)


GCE(Google Compute Engine)の無料枠だとメモリが少ないので WordPressの画面を開くと、mariaDBがメモリが確保できないためエラーとなってしまい、WordPressの画面がみれない状態になってしまいました。

追加の設定としてSWAPを設定しました。
設定後、mariaDBのエラーは発生してません。

```
$ sudo dd if=/dev/zero of=/swapfile bs=1M count=1024
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
```
インスタンスを再起動した時にswapが自動マウントされるようにfstabに設定をします。
※ /etc/fstab 以下の設定を追加

```
/swapfile none swap sw 0 0
```


###３－２　WordPressでの開発（はまったところのメモ）
ちょっと躓いたところをメモとして残します。
結局自分でHTMLを書いたので、なにもレイアウトの設定がない「固定ページ」で全て書いています

####その１. WordPressで自作のphpコードを実行する場合、WordPress固有の設定が必要

　これGCEにWordPress環境構築してから気がつきました。。

　WordPressで自分で書いたPHPを実行する場合、
　ショートコードというものを使用する必要があります。
　　[ショートコード API](https://wpdocs.osdn.jp/%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89_API)

　**◆ショートコード記載場所**
　　WordPressインストールディレクトリの
　 「/wp-content/themes/テーマ名」直下の**functions.php**がショートコードを記載するファイルとなります。

　**◆ショートコードを書いてみる**
　 functions.php を編集
　 サンプルでは、phpのファンクション「sampletest」を定義して
　 そのファンクション「sampletest」をショートコード「f_sampletest」と紐付けています
　　※sudo vi functions.php で更新です

```php:functions.php
/* ショートコードサンプル */
function sampletest() {
    return "Hello";
}
add_shortcode('f_sampletest', 'sampletest');
```

　 **注意点はこのfunctions.phpはWordPressのページ表示/管理画面の表示でコールされます。**
　 **functions.phpで構文エラー(;がない等)の場合、WordPressがみれなくなりますので**
　 **編集する前にバックアップを取得しておくことをお勧めします。**

**◆WordPressからショートコードを呼び出す**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/5a741d10-3fdd-d9ac-76d1-928d1913eba3.png)

画面が表示される際にショートコード(f_sampletest)が実行され、ショートコードで設定した文字が表示される
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/a5197809-d79d-205d-b0f2-06ccbd0a6809.png)


**◆ショートコードに引数を渡す場合**

```php:functions.php
/* ショートコード引数あり */
function sampletest_argv($attr) {
    $msg = $attr[0];
    return $msg;
}
add_shortcode('f_sampletest_argv', 'sampletest_argv');

```

[ショートコード　引数]　を設定⇒[更新]クリック⇒[固定ページを表示]クリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/f812907b-0769-c9cd-4dbd-3d4d2965a259.png)
画面が表示される際にショートコード(f_sampletest_argv)が実行され、ショートコード実行時に設定した引数の文字が表示される
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/a7264974-d6e0-55cf-aa68-a26d4497a9e5.png)

**◆実際のアプリで使用したショートコード抜粋**

```php:functions.php

function view_top($attr) {

    global $wpdb;
    
    //引数（年度）を取得
    $yyyy = $attr[0];
    
    $listresult  = "</tbody></table>";

    $listresult .= "<br><strong>◆得点ランキング(上位3まで)</strong><br>";
   
    //得点王取得SQL
    $strsql  ="select ";
    $strsql .="    r.rank,t.teamname,p.playername ,r.score";
    $strsql .="    from ";
    $strsql .="       topscore r, ";
    $strsql .="       team t, ";
    $strsql .="       player p ";
    $strsql .="    where ";
    $strsql .="        r.yyyy = %d  ";
    $strsql .="    and r.teamno = t.teamno ";
    $strsql .="    and r.playerid = p.playerid ";
    $strsql .="    and rank < 4 ";
    $strsql .="    order by r.rank,p.playername ";

    $sql = $wpdb->prepare($strsql, $yyyy);
    $rows = $wpdb->get_results($sql);

    //table ヘッダ情報
    $listresult .= "<table class=\"wp-table-yoko\"><thead><tr><th>順位</th><th>氏名</th><th>チーム名</th><th>得点</th></tr></thead><tbody>";
    
    //テーブルレコード設定
    foreach($rows as $row){
        $listresult .= "<tr><td>". $row->rank . "</td><td>" . $row->playername . "</td><td style =\"text-align: left;\">" . $row->teamname . "</td><td>" . $row->score . "</td></tr>";
    }

    $listresult .= "</tbody></table>";
    
    //画面表示
    return $listresult;

}
add_shortcode('f_viewtop', 'view_top');

```

ショートコード(f_viewtop)を設定⇒[固定ページを表示]クリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/cc5c71c7-6c70-f621-bf84-80044bdfd454.png)


ショートコード(f_viewtop)に引数を設定(2019)して実行した結果。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/cbf1b719-0e88-60bc-3834-06762e0361fd.png)


####その２． 自作のJavaScriptを使用する場合、WordPress固有の設定が必要
　これもWordPressの設定が必要です。

　得点入力画面で「＋」、「－」ボタンのみで得点入力するようにしました。
　そのため、「＋」、「－」ボタンが押されたタイミングで、JavaScriptで得点表示を
　変更する必要がありました。

　（赤枠の「＋」、「－」ボタンをクリックすると対応する得点が加算、減算されます）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/d727dff2-4e56-0366-09ef-0be2aa51436a.png)

**手順１ JavaScriptを作成して格納**
「/wp-content/themes/テーマ名/js」に 作成したJavaScript(score.js)を格納します

```javascript:score.js
function incValue(idname){
  // value値を取得する
  var obj = document.getElementById(idname);

  //valueに値をセットする
  obj.value = parseInt(obj.value)  + parseInt(1);

}
function decValue(idname){
  // value値を取得する
  var obj = document.getElementById(idname);

  score = parseInt(obj.value)

  if (score > 0){
  //valueに値をセットする
      obj.value = parseInt(obj.value)  - parseInt(1);
  }

}

```



**手順２ functions.php にjavascriptに関する記載を書く**

```php:functions.php
function score_scripts() {
  wp_enqueue_script( 'score-script', get_template_directory_uri().'/js/score.js' , array(), date('U'));
}
add_action( 'wp_enqueue_scripts', 'score_scripts' );
```

この設定で自前のJavaScriptが動くようになります

<br>

####その３．データ登録方法(POST送信)
　データ登録方法は、「データ入力画面」からPOST送信して、
　送信された側の「データ登録終了画面」に設定したショートコードでデータ登録させる方法にしました。
　※実際のアプリで使用したデータ登録をサンプルとして残します。

　**手順１ POST送信する固定ページの設定**
　　POST送信する固定ページにformタグを設定して、POST送信するように記載。
　　formタグの中でショートコードをコールするようにします。
　　（ショートコードの中で、submitボタンを作成してます）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/0847df90-b806-33d0-a01d-286408b691c3.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/f6abfb41-477b-5230-f8e9-22087aae97e1.png)

　**手順２ POST受信する固定ページの設定**
　　ショートコード(f_postdata)のみ記載
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/b5413d95-330a-336f-7254-c5c2d6fccc62.png)

f_postdataのショートコード（抜粋）

```php:functions.php
function postdata() {

    global $wpdb;

    //POSTされたデータを受け取る
    $yyyy = $_POST['yyyy'];
    $mm = $_POST['mm'];
    $seq = $_POST['seq'];
    $team1 = $_POST['team1'];
    $team2 = $_POST['team2'];
    $score1 = $_POST['score1'];
    $score2 = $_POST['score2'];

<処理省略>    
    //得点を登録
    $strsql  = "update matchresults ";
    $strsql .= " set score1 = %d ,";
    $strsql .= "     score2 = %d  ";
    $strsql .=" where ";
    $strsql .="        yyyy= %d ";
    $strsql .="    and mm= %d ";
    $strsql .="    and seq= %d ";

    $sql = $wpdb->prepare($strsql,$score1,$score2, $yyyy,$mm,$seq);
    $rows = $wpdb->get_results($sql);

<処理省略　この後、登録したデータを表示する処理>
 
}
add_shortcode('f_postdata', 'postdata');
```
POST送信⇒POST受信した固定ページでショートコード(f_postdata)を実行した結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/d232446e-316b-3608-05bd-b8ed3090666f.png)

####その４．ランク付け集計方法
mariaDBでOracleのRANK・DENSE_RANKを取得するSQL文使用できない
　https://docs.oracle.com/cd/E16338_01/server.112/b56299/functions052.htm

簡単に言うと、RANKはある列の値に順位をつけてくれるものになります。
DENSE_RANKは、順位をつけるのですが、同一値の場合に順位を飛ばさずに順位をつけます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/e3c8c8d2-c314-fbd2-10d4-873b46c2b419.png)
※本リーグ戦の運用では、得点王はDENSE_RANKで順位をつけています。


mariaDBでDENSE_RANKするSQLを記載
ダミーテーブルとサンプルデータとRank取得するSQL

```sql
--得点データ格納するテーブル
create table test_scoring(
    yyyy int  NOT NULL,
    mm int  NOT NULL,
    seq int NOT NULL,
    playerid  int NOT NULL,
    score int,
  PRIMARY KEY(yyyy,mm,seq,playerid)
);

--ダミーデータを入れる
insert into test_scoring values(2019,1,1,17,1);
insert into test_scoring values(2019,1,1,18,1);
insert into test_scoring values(2019,1,1,19,1);
insert into test_scoring values(2019,1,2,9,1);
insert into test_scoring values(2019,1,3,13,1);
insert into test_scoring values(2019,1,3,11,1);
insert into test_scoring values(2019,1,4,3,1);
insert into test_scoring values(2019,1,4,20,2);
insert into test_scoring values(2019,1,4,18,1);
insert into test_scoring values(2019,1,5,17,1);
insert into test_scoring values(2019,1,5,12,1);
insert into test_scoring values(2019,1,6,14,1);
insert into test_scoring values(2019,1,6,15,1);
insert into test_scoring values(2019,1,6,5,2);
insert into test_scoring values(2019,1,7,12,1);
insert into test_scoring values(2019,1,7,4,1);
insert into test_scoring values(2019,1,7,8,1);
insert into test_scoring values(2019,1,8,16,1);
insert into test_scoring values(2019,1,8,20,1);
insert into test_scoring values(2019,1,8,21,1);
insert into test_scoring values(2019,1,10,18,1);
insert into test_scoring values(2019,1,11,15,1);
insert into test_scoring values(2019,1,12,3,1);
insert into test_scoring values(2019,1,12,8,1);
insert into test_scoring values(2019,1,12,18,1);
insert into test_scoring values(2019,1,12,22,1);

--DENSE_RANKでランク付けするSQL
select
    CASE WHEN @before_sumscore = d.sumscore THEN @rank
    ELSE @rank:=@rank+1
    END AS rank,
    playerid,
    @before_sumscore := d.sumscore as sumscore 
from 
   (
    select 
        playerid  ,
        sum(score) as sumscore  
    from 
        test_scoring  
    where 
        yyyy=2019 
    group by 
        playerid
   ) d,
   (select @rank:=0, @before_sumscore:=0) s
order by 
   sumscore desc ;

```

**取得結果**
同一得点の場合、Rankは同じ順位に取得されています。
また、順位が飛ばされていません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/ceff5c87-e8ab-38d8-c570-0625854e798d.png)

#作成した画面と遷移図

**成績の参照（参加者がみる画面）**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/c52a3935-812e-b052-7b9b-2e5cfb79d3f2.png)


**管理者（得点入力、メンバー登録、集計処理）**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/fb9afcd1-e4f7-5f2c-f34b-bd580e9a75b5.png)


成績の参照は見たい情報が見れるようにしました。
得点入力画面は簡単なボタンクリックのみでできる画面にしました。

#2月リーグ戦で主催者への説明
タブレットを持っていき、主催者の前で得点入力、集計処理(得点・得点王)を行い説明をしました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261475/08988e0c-529a-8031-91cc-ff91ccb01a79.png)


主催者とお話して以下の予定で進めることになりました。
３月は紙ベース運用とシステムの平行運用
４月からはシステムでの運用

※2019年から運用して、2020年も使用しています。

#関連リンク
[Google Cloud Platform から GCE(Google Compute Engine)+WordPress環境について「ご対応ください: 重大な問題」のメールが来た](https://qiita.com/kngsym2018/items/ed822ffc13cf8889f736)