Laravelで将棋アプリを作りたい（第三回）


[第一回：プロジェクト作成 ~ 盤面作成](https://qiita.com/motoshi_cocoa/items/4397885d12185b9adb5b)
[第二回：駒移動](https://qiita.com/motoshi_cocoa/items/2c7903299f9cd4f7c1ca)

今回はデータベース連携と玉将配置

#目次
[1.データベース作成](#1-データベース作成)
[2.駒の移動先をデータベースに保存](#2-駒の移動先をデータベースに保存)
[3.駒の位置をデータベースから取得](#3-駒の位置をデータベースから取得)
[4.先手後手の導入と玉将配置](#4-先手後手の導入と玉将配置)

#1. データベース作成

駒の移動履歴を記録するためにデータベースを使用する。
今回は以下のような`game_records`テーブルを作成したい。

|カラム  |データ型  |説明  |
|---|---|---|
|id  |bigIncrement  |  |
|turn  |integer  |手番（0:先手、1:後手）  |
|piece  |integer  |駒番号（0:王将）  |
|square  |varchar  |マス番号  | 

MySQLを起動してログインする。

```shell:ターミナル
$ mysql.server start
Starting MySQL
... SUCCESS! 
$ mysql -uroot -p
Enter password:
```

`shogi_app`というデータベースを新しく作成する。この中に、`game_records`テーブルが作成されるようにする。

```sell:ターミナル
mysql> create database shogi_app;
```

Laravelの.envファイルに記されているデータベース設定を必要に応じて修正する

```:.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shogi_app
DB_USERNAME=root
DB_PASSWORD={ログイン時に使用しているパスワード}
```

モデルを使ってデータベース操作を行う。
まず、アプリ配下で以下を実行してモデルとマイグレーションファイルを作成する。

```sell:ターミナル
$ php artisan make:model GameRecord --migration
``` 

作成されたマイグレーションファイルのupメソッドでテーブルを定義する。

```php:databases/migrations/2021_02_xx_xxxx_create_game_records_table.php
public function up()
    {
        Schema::create('game_records', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('turn');
            $table->integer('piece');
            $table->string('square');
            $table->timestamps();
        });
    }
```

マイグレーションを実行してテーブルを作成！

```shell:ターミナル
$ php artisan migrate
```

#2. 駒の移動先をデータベースに保存

indexメソッドにPOST送信されてくる移動先の駒情報をDBに保存する。
まず、コントローラにモデルの使用を宣言する。

```php:app/Http/Controllers/ShogiController.php
use Illuminate\Http\Request;
use App\Models\GameRecord; // 追加
```

POST送信された時の保存処理は以下のように書ける。
`new モデル名`でモデルのインスタンスを新規に作成して、操作・保存できる。

```php
$data = new GameRecord;
$data->turn = $request->input('turn'); // 手番
$data->piece = 0; // 王将
$data->square = $request->input('move'); // 移動先マス
$data->save();
```

#3. 駒の位置をデータベースから取得

モデルを使って簡単にデータを取得できる。

例：`$bLast`として直近の先手のデータを取得する場合
idの逆順に並べ【orderBy】、turnが0（先手)のデータを検索して【where】、一件だけを取得【first】で実現できる。

```php
$bLast = GameRecord::where('turn', 0)->orderBy('id', 'desc')->first();
```

これを使ってコントローラーでデータベースから駒の位置を取得して、それをビューに渡すようにする。

ここで、データがない場合のことを考えて、`config`フォルダに初期値を設定しておく。（配列で定義）

```php:config/const.php
<!-- 新規作成 -->
<?php
return [
    'start' => [
        'bKing' => [ // 先手の初期値
            'turn' => 0,
            'piece' => 0,
            'square' => 59,
        ],
        'wKing' => [ //後手の初期値
            'turn' => 1,
            'piece' => 0,
            'square' => 51,
        ],
    ]
];
```

config配下に記述した定数は以下のようにコントローラーから呼び出すことができる。

```php:例：先手の初期値を取得
config('const.start.bKing')
```

以上で
「（最初だけ）初期値を`const.php`から取得」→「移動したらデータベースに保存」→「直近のデータをデータベースから取得」
を繰り返して、駒の移動を実現する。

#4. 先手後手の導入と玉将配置

将棋は二人でやるものだと気づいたので、先手・後手の違いをつける。

indexメソッドとselectメソッド共に、先手「王将」と後手「玉将」それぞれのデータを取得し、`$bKing`、`$wKing`としてビューに渡す。

selectメソッドでは（初期値またはデータベースから）取得した「選択中の駒」の情報が渡されている。それを参考に先手と後手で処理を分けた際に`$turn`という変数を手番に合わせて定義して、ビューに渡す。

selectページでは受け取った`$turn`も以下のような隠しフォームでindexページにPOST送信するようにする。

```php
echo '<input type="hidden" value="' . $turn . '" name="turn"></input>';
```

ビューで後手の「玉将」を表示させる。

indexビューの修正箇所

```php:resources/views/shogi/index.blade.php
if ($square == $bKing['square']) {
    // 王将
    echo '<p class="piece row turnb square' . $square . '" id="square' . $square . '"><a style="text-decoration:none" href="' . action('App\Http\Controllers\ShogiController@select', $bKing['turn'] .  ':' . $bKing['piece'] . ':' . $r . ':' . $c) . '">王</a></p>';
} elseif ($square == $wKing['square']) {
    // 玉将
    echo '<p class="piece row turnw square' . $square . '" id="square' . $square . '"><a style="text-decoration:none" href="' . action('App\Http\Controllers\ShogiController@select', $wKing['turn'] .  ':' . $wKing['piece'] . ':' . $r . ':' . $c) . '">玉</a></p>';
}
```

selectビューの修正箇所

```php:resources/views/shogi/select.blade.php
if ($square == $bKing['square']) {
    // 駒マス「王将」
    echo '<p class="piece row turnb square' . $square . ' selected" id="square' . $square . '">王</p>';
} elseif ($square == $wKing['square']) {
    // 駒マス「玉将」
    echo '<p class="piece turnw row square' . $square . ' selected" id="square' . $square . '">玉</p>';
}
```

玉将の文字を反転するために追加したCSS

```css:public/css/style.css
.turnw {
    transform:rotate(180deg);
}
```

手番別の記述を追加したindexメソッド

```php:app/Http/Controllers/ShogiController.php
    public function index(Request $request)
    {
        if ($request->isMethod('post')) {
            // POST時の処理
            $data = new GameRecord;
            $data->turn = $request->input('turn'); // 手番
            $data->piece = 0; // 王将
            $data->square = $request->input('move'); // 移動先マス
            $data->save();
        }
        // 先手番の最新手を取得
        $bLast = GameRecord::where('turn', 0)->orderBy('id', 'desc')->first();
        $bKing = (!empty($bLast['square'])) ? $bLast : config('const.start.bKing');
        // 後手番の最新手を取得
        $wLast = GameRecord::where('turn', 1)->orderBy('id', 'desc')->first();
        $wKing = (!empty($wLast['square'])) ? $wLast : config('const.start.wKing');
        return view('shogi/index', compact('bKing', 'wKing'));
    }
```

手番別の記述を追加したselectメソッド

```php
    public function select($piece)
    {
        // $pieceには 0:0:5:9 のように手番、駒番、列、行が「:」でくっついた形が入る。
        $king = array();
        $select = explode(":", $piece);
        $king = array(
            'turn' => $select[0],
            'piece' => $select[1],
            'square' => $select[2] . $select[3],
        );
        // 移動可能マス配列としてselectページに渡す
        $ways = array();
        // 王将の移動可能マスを割り出して、$waysに追加する。
        for ($c = $select[3]-1; $c < $select[3]+2 ; $c++) {
            for ($r = $select[2]-1; $r < $select[2]+2 ; $r++) {
                $ways[] = $r . $c;
            }
        }
        $way = array_unique($ways);
        if ($select[0] == 0) {
            // 先手番の処理
            $bKing = $king;
            $wLast = GameRecord::where('turn', 1)->orderBy('id', 'desc')->first();
            $wKing = (!empty($wLast['square'])) ? $wLast : config('const.start.wKing');
            $turn = 0;
        } elseif ($select[0] == 1) {
            // 後手番の処理
            $wKing = $king;
            $bLast = GameRecord::where('turn', 0)->orderBy('id', 'desc')->first();
            $bKing = (!empty($bLast['square'])) ? $bLast : config('const.start.bKing');
            $turn= 1;
        }
        return view('shogi/select', compact('bKing', 'wKing', 'way', 'turn'));
    }
```

以上でデータベース連携 & 玉将配置までできた。今回はここまで。

![スクリーンショット 2021-02-28 17.56.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594194/baec75ae-6d52-b779-b762-0b306fcb57d2.png)

[Github](https://github.com/motoshifurugen/laravel-shogi)