7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ajaxを用いて動的セレクトボックスを作成する(CakePHP)

Posted at

記事作成の背景

Ajaxって聞くけど、、、なんだ?っていうのと、個人的に動的セレクトボックスの
ヒントを残しておきたくて作成しました。
なんかパッとする記事が見つけられなかったので、これが少しでも困ってる方の助けになれば幸いです。

環境

CakePHP4 (最新)
jQuery
MySQL 8.0

全体のコード

とりあえずコード見てみるか・・・って方向けに最初に記載しておきます。
勉強会向けに色々書いてありますが、こちらの記事では必要箇所のみ抜粋して記述いたします。
https://github.com/Kurogoma939/cake_ajax_sample.git

テストデータ

今回、検証環境はMAMPを用いて開発しました。
そのため、エクスポートしたSQLは以下の通りです。
保存場所 : app/SQL/create_tabelapp/SQL/insert_table

CREATE TABLE `marks` (
    `id` int(4) NOT NULL COMMENT 'ID',
    `name` varchar(10) NOT NULL COMMENT 'マーク名',
    `created` date NOT NULL COMMENT '作成日時'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='マークテーブル';

CREATE TABLE `trumps` (
     `id` int(100) NOT NULL COMMENT 'ID',
     `mark_id` int(4) NOT NULL COMMENT 'マークID',
     `number` int(100) NOT NULL COMMENT 'カードNo',
     `created` date NOT NULL COMMENT '登録日時',
     `name` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='トランプテーブル';

解説

全体の処理の流れ

cakeajax.png

① セレクトボックスを選択
② jQueryで①でイベント着火
③ AjaxでデータのPOST+メソッド呼び出し
④ 絞り込みクエリの実行+レスポンス(JavaScriptに返す)
⑤ ④の値を用いてセレクトボックスの値を作り直す

コード

template(HTML)ファイル(全体)

template/Marks/index.php
<div class="marks index content">
    <?= $this->Form->create(null, ['url' =>['controller' => 'Marks', 'action' => 'buttonAction', 'id' => 'mark-form']]) ?>
        <div><h1>マークのリスト</h1></div>
        <?= $this->Form->label('marks','トランプのマーク') ?>
        <?=
            $this->Form->select('marks', $markList,[
                'class' => 'markList', 'id' => 'mark', 'empty' => true,
            ])
        ?>
        <div><h1>トランプのリスト</h1></div>
        <?= $this->Form->label('trump', 'トランプの番号とマーク') ?>
        <?= $this->Form->select('trump', $trumpList,[
            'class' => '', 'id' => 'trump', 'empty' => true,
        ])
        ?>
    <?= $this->Form->end() ?>
</div>

JavaScript(Ajax)の処理

$.ajax({...})がJavaScript => PHPの処理で
.done((data) => {})がPHP => JavaScriptの処理になります。

Main.js
$(document).ready(function () {
    $('#mark').on('change', function () {
        let markId = $(this).val(); // セレクトのvalueを受け取り
        // POST処理する場合、CSRFは手動で送信
        const _csrfToken = $('input[name="_csrfToken"]').val();

        $.ajax({
            type: 'POST', // GET
            datatype: 'json',
            // ↓ MarksControllerのchangeMarkメソッドを呼び出し
            url: "/marks/changeMark" // URLでメソッドを呼び出す URLはドメイン以降
            data: {
                markId: markId,
                _csrfToken: _csrfToken
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("X-CSRF-Token", _csrfToken);
            },
        })
            .done((data) => {
                let objectData = JSON.parse(data); // JSON型をObject型へ変換
                let trumpList = objectData.trumpList; // $response[]で指定した値

                // トランプのセレクト要素
                let trumpSelect = $('#trump');
                let selectOptions = '<option value="0">該当するカードがありません</option>';
                // 中身を一度全てリセット
                trumpSelect.children().remove();
                // データがあった場合
                if (data) {
                    selectOptions = '';
                    // foreach($trumpList as $key => $val) みたいな処理の実装
                    for (const key in trumpList) {
                        var val = trumpList[key];
                        selectOptions += `<option value = '${key}'> ${val} </option>`;
                    }
                }
                trumpSelect.append(selectOptions);
            })
            .fail((jqXHR, textStatus, errorThrown) => {
                // JavaScriptの例外処理
                console.log(errorThrown);
            });
        return false;
    })
});

PHP(CakePHP側の処理)

app/Controller/MarksController.php
<?php
declare(strict_types=1);

namespace App\Controller;

use Cake\ORM\TableRegistry;

/**
 * Marks Controller
 */
class MarksController extends AppController
{
    public function initialize(): void
    {
        parent::initialize();
        $this->Trumps = TableRegistry::getTableLocator()->get('Trumps');
    }
    /**
     * Index method
     */
    public function index()
    {
        /* マークのセレクトボックス用データ */
        $this->set('markList', $this->Marks->find('list',
            [
                'keyField' => 'id', # セレクトボックスのvalue(見えない部分:Postされるデータ)
                'valueField' => 'name', # セレクトボックスの見える部分
            ]
        )
            ->distinct('id')); # 重複排除できる。Groupより確実

        /* トランプのセレクトボックス用データ */
        $this->set('trumpList', $this->Trumps->find('list',
            [
                'keyField' => 'id', # セレクトボックスのvalue(見えない部分:Postされるデータ)
                'valueField' => 'name', # セレクトボックスの見える部分
            ]
        ));
    }


    public function changeMark()
    {
        // AjaxからPOSTされたデータの受け取り
        $request = $this->request->getData();
        $markId = $request['markId'];

        // トランプリストを、取得したmark_idで絞り込み
        $trumpList = $this->Trumps->find('list',
            [
                'keyField' => 'id', # セレクトボックスのvalue(見えない部分:Postされるデータ)
                'valueField' => 'name', # セレクトボックスの見える部分
            ]
        )
            ->where(['mark_id' => $markId])
            ->order(['id' => 'ASC']);

        // JavaScriptに返す値(key部分で参照する)
        $response = [
            'trumpList' => $trumpList
        ];
        // Json形式にして返す
        return $this->getResponse()->withStringBody(json_encode($response));
    }
}

動作確認

親のセレクトボックスの中身

sssss.png

子のセレクトボックスの中身

casa.png

親のセレクトボックスを選択した状態(動的セレクトボックス実行後)

sadasa.png

以上です!

P.S.
最近のPHPのFWはしっかりとdeclare(strict_types=1);がデフォルトで付くんですね

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?