Friction River Software

  • お問い合わせ

CakePHP5入門【WebAPI編⑯】当選番号検索

A子

ふと思いついた数字(3桁または4桁)があってさ

それが過去何回出現しているか…とか
直前の出現がいつだったか…なんて調べたいときがあるよね?

C菜

自車の目の前を走っている車のナンバープレートが気になったりもしますよね~

尾行してるんじゃないか?…って相手から疑われるくらいの時間、ずっと後ろを走ってるときもありますし~(笑)

B美

データベースを直接(SQLで)検索すれば良いじゃない

A子

身もふたもねぇ!

いや、WebAPIにしたいってことだから…(苦笑)

C菜

ユーザがフォーム送信した値で検索して結果を表示するなんてことが、WebAPIを使ってできるんでしょうか~?

B美

もちろん可能よ
(年月日で検索する「getYmd1」メソッドや「getYmd2」メソッドだって、そうだったじゃない)

あ、ただし、(フォームからの)POST送信はやめておいたほうが良いかも…
(やるならGETパラメータとして渡す方法ね)

C菜

なぜですか~?

B美

CakePHPではPOST送信の場合、CSRF対策として(隠しパラメータとしての)トークンを渡さないといけない

GETだったら、それは不要ってわけ

A子

あれ?

それ(トークン送信)ってこれまでのPOST送信でもやってたの?
(全然記憶に無いんだけど…)

B美

フレームワーク(CakePHP)が裏で勝手にやってたからね(苦笑)

これまで作ったWebアプリのフォーム画面上で右クリックしてから「ページのソース表示」ってやってみれば分かるわよ
(ブラウザが「Microsoft Edge」の場合)

A子

てか、そもそもCSRFって何なのよ

聞いたことが無いんだけど…

C菜

たしか「クロスサイト・リクエスト・フォージェリ」の頭文字をとったものだったと思いますよ~

B美

C菜正解!

Cross-Site Request Forgeries」とは、ログインした正規ユーザがそのサイト(A)にアクセスする際、悪意ある別サイト(B)からの攻撃によってログインサイト(A)を不正操作されちゃうって攻撃手法の一つなの

A子

前回(【補足⑤】)のCORSポリシーがあるのに、そういうこともできるんだ…

それって、悪意ある人がハッカーみたいに攻撃してくるってことなの?

B美

違う違う
悪意あるサイト(B)はあらかじめ罠として仕掛けておくの

サイト(A)にログインしたユーザが(ログイン状態を維持したまま)罠サイト(B)にアクセスすると、サイト(A)が不正操作されてしまうって仕組みね

C菜

複数のサイト(Webサーバ)を横断的に使うから「クロスサイト」なんですね~

B美

そういうこと

んで、CakePHPのビューファイルでは、POST送信において(CSRF対策として)隠しhiddenパラメータをフォームに(勝手に)埋め込んでいる
そのパラメータの値が正しければOKで、間違っていればNGって仕組みね

A子

あぁ
だったら、WebAPIを使う人がそのあたりを用意するのは現実的じゃないね

分かった
GET送信でいこう

C菜

(前回の)CORS設定でも(異なるオリジンからの)「POST」の許可をしてませんからね~

B美

それじゃ、次は「どういう風に検索値を渡すか」よ
「メソッド名/パラメータ」という形で渡して、メソッド側で引数として受け取るやり方は、すでに「getYmd1」メソッドや「getYmd2」メソッドで実装したからね

今回は別のやり方でやってみましょう

A子

普通にフォーム送信した値を受け取るなら

if ($this->request->is('post')) {
    $textfield = $this->request->getData('textfield');
}

…って感じかな?
(「textfield」はフォーム部品の名前ね)

C菜

それでは「POST」のやり方になるので、「is('post')」の箇所を「is('get')」に変更すべきだと思います~

B美

いえ、それだけでは不十分よ

「POST」では「getData」メソッドを使うんだけど、「GET」では「getQuery」メソッドになるの

A子

ふむ だったら

if ($this->request->is('get')) {
    $textfield = $this->request->getQuery('textfield');
}

…ってこと?

B美

そういうこと

C菜

ナンバーズ3の当選番号検索を行うメソッドを「num3Search」として書いてみました~

//ナンバーズ3の当選番号検索
public function num3Search()
{
    if ($this->request->is('get')) {
        $number = $this->request->getQuery('number');
        $search_result = $this->Numbers->find()->where(['num3_str' => $number])->toArray();

        $status = [
            'code' => 200,
            'message' => 'Success'
        ];
        $result = $search_result;

        //JSON化する前の連想配列
        $json = [
            'status' => $status,
            'result' => $result
        ];

        //JSONエンコードした結果を返す
        $this->autoRender = false;
        header("Content-Type: application/json");
        echo json_encode($json);
    }
}

B美

ふむ
大枠としては十分ね

あとは細かいエラーチェックが必要よ
(検索結果が空のときとか…)

A子

あー

1.空文字列が送信されたとき
2.3けたの数字以外が送信されたとき
3.結果が空だったとき

なんかが思いついたエラーだね

C菜

エラーチェックを追加してみました~

//ナンバーズ3の当選番号検索
public function num3Search()
{
    $error_code = 0;

    if ($this->request->is('get')) {
        $number = $this->request->getQuery('number');

        //numberが無い場合はエラー
        if ($number === null) {
            $error_code = 1;
        }

        //3桁の数字でなければエラー
        if ($error_code == 0) {
            if (!preg_match('/^\d{3}$/', $number)) {
                $error_code = 2;
            }
        }


        if ($error_code == 0) {
            $search_result = $this->Numbers->find()->where(['num3_str' => $number])->toArray();

            if (empty($search_result)) {
                //結果が空ならエラー
                $error_code = 3;
            }
        }


        if ($error_code == 1) {
            //NG1
            $status = [
                'code' => 601,
                'message' => 'No Parameter'
            ];
            $result = null;
        } elseif ($error_code == 2) {
            //NG2
            $status = [
                'code' => 602,
                'message' => 'Numeric Check NG'
            ];
            $result = null;
        } elseif ($error_code == 3) {
            //NG3
            $status = [
                'code' => 404,
                'message' => 'Not Found'
            ];
            $result = null;
        } else {

            //OK
            $status = [
                'code' => 200,
                'message' => 'Success'
            ];
            $result = $search_result;
        }

        //JSON化する前の連想配列
        $json = [
            'status' => $status,
            'result' => $result
        ];

        //JSONエンコードした結果を返す
        $this->autoRender = false;
        header("Content-Type: application/json");
        echo json_encode($json);
    }
}

エラー時のステータスコードですけど、勝手に600番台を作りましたよ~
(この世に存在しないコード番号ですけど~)



B美

それは別に問題ないわ

API仕様書に明記しておけば良いだけだから…

A子

対応するHTMLファイル(apitest05.html)は以下の通りね

てか、ChatGPT先生に教えてもらった通りなんだけど…(苦笑)
(意味不明な個所が多々有り(笑))





C菜

実行してみました~

http://192.168.1.205/apitest05.html

↓(「123」を検索)

A子

おぉ!
ばっちりじゃん

B美

ん?
結果として「実施回」「抽選日」「曜日」「六曜」のみを取得できれば良いのなら、データベース検索時に射影プロジェクションすべきね

$search_result = $this->Numbers->find()
    ->select(['lottery_time', 'lottery_date_str', 'lottery_week_str1', 'lottery_week_str2', 'lottery_rokuyo_str'])
    ->where(['num3_str' => $number])
    ->toArray();

あ、「lottery_week_str2」は不要かもしれないけど、念のため…

C菜

なるほどですねぇ~
それでは、ついでに「ナンバーズ4」のための「num4Search」メソッドと、「apitest06.html」も作っておきましょう~

「num3Search」メソッドと「apitest05.html」の内容とほとんど同じなので、コードは省略するです~
実行結果だけを表示しておきますね~

http://192.168.1.205/apitest06.html

↓(「1234」を検索)

A子

良いね
完璧じゃん

B美

API仕様書(templates/Top/index.php)については、C菜にお願いするとして…

それが完成次第、私のほうで本番環境(VPSサーバ)への設置デプロイをやっておくわね

C菜

了解です~

B美部長も色々と大変ですね~
お疲れ様です~
(主に、A子社長の無茶ぶりに対処するという意味で…(苦笑))

A子

あ、そうだ!

このアプリのバージョンを「1.0.0」にしよう
(そろそろリリース版にしても良いんじゃない?)

B美

・・・

C菜

・・・

A子

なっ、何?

そのジト目は…