記事作成の背景
Ajaxって聞くけど、、、なんだ?っていうのと、個人的に動的セレクトボックスの
ヒントを残しておきたくて作成しました。
なんかパッとする記事が見つけられなかったので、これが少しでも困ってる方の助けになれば幸いです。
環境
CakePHP4 (最新)
jQuery
MySQL 8.0
全体のコード
とりあえずコード見てみるか・・・って方向けに最初に記載しておきます。
勉強会向けに色々書いてありますが、こちらの記事では必要箇所のみ抜粋して記述いたします。
https://github.com/Kurogoma939/cake_ajax_sample.git
テストデータ
今回、検証環境はMAMPを用いて開発しました。
そのため、エクスポートしたSQLは以下の通りです。
保存場所 : app/SQL/create_tabel
、app/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='トランプテーブル';
解説
全体の処理の流れ
① セレクトボックスを選択
② 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));
}
}
動作確認
親のセレクトボックスの中身
子のセレクトボックスの中身
親のセレクトボックスを選択した状態(動的セレクトボックス実行後)
以上です!
P.S.
最近のPHPのFWはしっかりとdeclare(strict_types=1);
がデフォルトで付くんですね