iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

0

筆者我很喜歡打橋牌,但目前為止還未找到一個覺得玩起來順手的橋牌APP,因此乾脆自己開出橋牌遊戲的api,再找工作室寫APP的夥伴來做畫面。
這遊戲看似很複雜,但其實應用到的技術不外乎是資料庫的CRUD而已,難度並不高,對Laravel初學者來講,可以拿來當作對資料庫操作的練習。

由於橋牌的遊戲邏輯較為複雜,落落長的程式碼其實不外乎是在判斷這局是否輪到這個玩家出牌、他出的牌是不是合法的等等,我在這邊就不一一解釋每一行程式碼在幹麻了,只分享這次學到的新寫法。
完整程式碼:點我

validate沒過可不可以不要讓他自己跳錯誤訊息?

$rules = $request->validate([
             'name' => ['exists:cards'],
             'trump' => ['required', 'integer', 'min:100', 'max:500'],
             'line' => ['required', 'integer', 'max:7']
             ]);

若照上面的寫法,一旦其中一個欄位驗證不符合規則,Laravel就會自動回傳它預設的錯誤訊息。
如果想要客製化自己的錯誤訊息,可以寫成這樣:

 $rules = [
              'name' => ['exists:cards'],
              'trump' => ['required', 'integer', 'min:100', 'max:500'],
              'line' => ['required', 'integer', 'max:7']
          ];
          $validator = validator::make($request->all(), $rules);
          if ($validator->fails()) { //若驗證失敗,則回傳以下錯誤訊息
              return $this->sendError(
                  "Player's name not found. Trump may not be greater than 500, or line may not be greater than 7",
                  3, 400);
          }

原來Eloquent可以這樣用

Eloquent是Laravel的ORM(物件關聯對映),它讓我們可以透過程式語言(PHP)去操作資料庫,妥善運用它可以讓資料庫的操作更加方便、程式碼更簡潔。

$data['card'] = Card::where('name', $request->name)->orderBy('color', 'ASC')->orderBy('card', 'ASC')->get()

這一段是讓玩家呼叫看牌api來看自己的手牌時,能讓牌的順序按照花色,再按照點數從小到大排序。
我這邊有踩過一個小小的坑:如果我的'color'和'card'這兩個欄位儲存的資料型態是integer的話,讓他由小至大排序就會是這樣:{2, 3, 4, 5, ....., 11, 12, 13};但如果是string的話,排序則會變成這樣:{12, 13, 14, 2, 3, 4, 5, ...},1開頭的十位數字會被排在各位數字之前。這是因為若要排序string型態的資料,是依照字元的ascii code來做排序的,因此字串'11'會比字串'2'還要小。要處理這個問題的話,DB的欄位就必須是integer。

要怎麼對回傳的Object內容動手腳?

應前端要求,我把所有關於遊戲的資訊都放在card這支API,response內容其中一個欄位是遊戲房間的玩家資料,原本長這樣:

"room": [
        {
            "id": 1,
            "name": "lulu",
            "goal": 7,
            "trick": 0,
        },
        {
            "id": 2,
            "name": "阿寶",
            "goal": 7,
            "trick": 0,
        }
    ],

當時前端的夥伴要求我在room裡再加一個欄位'me'來分辨哪位是client端的玩家名稱:若是該位玩家,value為true,否則為false。
我第一時間想到最簡單粗暴的方法:就是先把 $data['room'] 轉成array,取出玩家名稱後,比對其和client端送來的名字是否相同,在他底下加入'me'這個欄位後,再把剛才比對結果放進去。原本寫法如下:

$data['room'] = Player::all()->toArray();
$ $data['room']['me'] = $data['room']['name'] == $request->name;

後來才知道,原來上述步驟可以這樣寫:

 $data['room'] = Player::all()->toArray();
 $data['room'] = array_map(function ($player) use ($request) { //use 後面括號放的是需要在這個closure中用到的參數
        $player['me'] = $player['name'] == $request->name;
        return $player;
  }, $data['room']); 

array_map()放的第一個參數是closure,也就是一個anonymous的function;第二個參數是你主要要對他動手腳的變數。
closure括號裡對應到的參數就是array_map()的第二個參數($player就是$data['room'])。
增加上面這段後,'room'就會變成這樣:

 "room": [
        {
            "id": 1,
            "name": "lulu",
            "goal": 7,
            "trick": 0,
            "me": false
        },
        {
            "id": 2,
            "name": "阿寶",
            "goal": 7,
            "trick": 0,
            "me": true
        }
    ],

取出資料的順序竟然不是按照id排序?!

APP做好後,某一次測試發現一個很奇怪的問題:

Compare::latest()->first()

拿到的竟然不是資料表中所看到的最後一筆資料,而是倒數第二筆。
後來才發現,原來Laravel ORM的latest()是將資料表中的資料按照"created_at",由最新到最舊排序,也就是說,latest()預設是以寫入時間來作為排序依據的。
由於兩個玩家出牌時間可能很接近,進到後端儲存出牌資料時所紀錄下的時間有可能是一樣的。
假設若最後三筆資料的寫入時間是相同的,那麼將資料撈出來的時候,這三筆資料的順序會是隨機的。
如果想讓資料依照id大小排序,那麼要在latest()中加入參數:

Compare::latest('id')->first();

這樣才能確保資料是按照id的順序來查找的。

部署到遠端後無法migrate?

在遠端migrate的時候爆出以下訊息:

boa1417@instance0205:~/spoiler$ php artisan migrate

   Illuminate\Database\QueryException  : could not find driver (SQL: select * from information_schema.tables where table_schema = spoiler and table_name = migrations and table_type = 'BASE TABLE')

  at /home/boa1417/spoiler/vendor/laravel/framework/src/Illuminate/Database/Connection.php:669
    665|         // If an exception occurs when attempting to run a query, we'll format the error
    666|         // message to include the bindings with SQL, which will make this exception a
    667|         // lot more helpful to the developer instead of just the database's errors.
    668|         catch (Exception $e) {
  > 669|             throw new QueryException(
    670|                 $query, $this->prepareBindings($bindings), $e
    671|             );
    672|         }
    673|

  Exception trace:

  1   PDOException::("could not find driver")
      /home/boa1417/spoiler/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  2   PDO::__construct()
      /home/boa1417/spoiler/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  Please use the argument -v to see more details.

可能原因:少了讓php和mysql連街的PDO
Solution:

sudo apt-get install php版本號-mysql

上一篇
Day 32 淺談資料庫正規化
下一篇
Day 34 Laravel Factory:填充假資料
系列文
後端基礎PHP+Mysql & Laravel 30日養成計畫36

尚未有邦友留言

立即登入留言