设置PHP数组与从Json对象创建的下拉列表一起工作



我已经被这个问题困扰了好几天了,需要一些专家的建议!

所以,作为个人/学习项目,我正在为我朋友的《龙与地下城》游戏制作一个有趣的小"角色关系追踪器"。有了这个,我就有了一个虚拟地下城主人的DB表,以及他们为了测试代码而运行的游戏。我试图创建一个从PHP和编码JSON的混合生成的级联下拉列表作为提交新字符形式的一部分。它的工作原理!除了,在包含选项信息的数据库中,有些DM列表中只有一个游戏,而另一些DM列表中有多个游戏。对于这些单打,JS列出了每个字母作为不同的选择,而不仅仅是一个完整的选项。我已经尝试重新制作PHP两次,试图解决这个问题,并通过一些JS的尝试,但无济于事。这些更改要么完全破坏了代码,要么没有导致任何更改。

这提琴是我尽最大努力的结果

对应于实际代码:

HTML

<div class="selectdiv">
<select name="chargm" id="charadm" onChange="chgm(this.value);" required>
<option value="" selected="true" disabled>Game Master</option>
<?php foreach ($gameMasters as $masterName) { echo "<option value='". $masterName . "'>" . $masterName . "</option>"; } ?>
</select>
</div>
<div class="selectdiv">
<select name="chargame" id="chargm" required>
<option value="" selected="true" disabled>Game Name</option>
</select>
</div>

这是PHP(我知道这是超级混乱和冗余的,但我不能让它以任何其他方式工作,因为某些原因,它做它的工作?)

//database connection stuff
$sqls = "SELECT * FROM datgames;";
$stmts = mysqli_query($conn, $sqls);
$resultcheck = mysqli_num_rows($stmts);
$gameMasters = array(); //create empty array of game masters.
$gameData = array(); //set up game list data array
//check if db can be read before contiuning.
if($resultcheck > 0){
while($row = mysqli_fetch_assoc($stmts)){ //while there are rows, add to array
$gameMasters[] = $row["datgamDM"]; //fill the gameMasters array with all gm's
$gmdm = $row["datgamDM"]; //define gmdm as the game master of the row
//copy existing info in gameData to preserve data
$anotm = $gameData;
//clear game data to reset it to avoid repeats
unset($gameData);
//create the key => value pair
$tmpar[$gmdm] = $row["datgamName"];
//merge the temp arrays and apply them to the global array
$gameData = array_merge_recursive($anotm, $tmpar);
//clear the temporary arrays to avoid repeats
unset($anotm);
unset($tmpar);
}
}else{ exit(); } //if db can't be reached, break the code.
$gameMasters = array_unique($gameMasters);
//print_r($gameData); //making sure the array is right. this line is removed once this is working

和PHP当前使用该循环

的确切JSON输出
{
"Reid":[
"Curse of Strahd",
"ufkck"],
"bob":[
"Curse of Strahd",
"fffs"]
,"jomama":"blaal",
"taco":"salff"
};

和JS改编自divy3993的回答这里

var list = <?php echo json_encode($gameData); ?>;
function chgm(value) {
if (value.length == 0) document.getElementById("chargm").innerHTML = "<option></option>";
else {
var games = "";
for (categoryId in list[value]) {
games += "<option>" + list[value][categoryId] + "</option>";
}
document.getElementById("chargm").innerHTML = games;
}
}

问题简而言之:我在PHP(最有可能的原因)或Javascript中做错了什么,导致单对象组中的单词分裂成字母,而不是显示完整的单词作为第二个下拉选项的唯一选项?

或者更确切地说,我如何让PHP使单条目显示为多维数组,同时保持键,使其显示为JSON对象中的数组?

使用array_merge_recursive()的问题是,它可以产生不一致的结构,因为它创建深度。

例如,如果有多个元素,则第一级键包含一个已索引的子数组,但如果只有一个元素存在,则在第一级键创建一个关联数组。我在这里解释这一点,并提供一个简单的演示。

mysqli的query()的结果集可以使用foreach()立即遍历,所以我推荐使用简洁的技术,它可以建立直观的关联数组访问。

$result = [];
foreach ($conn->query("SELECT datgamDM, datgamName FROM datgames") as $row) {
$result[$row["datgamDM"]][] = $row["datgamName"];
}
exit(json_encode($result));

这样,您就有了一个一致的结构——一个索引数组的关联数组。换句话说:

{
"Reid":["Curse of Strahd","ufkck"],
"bob":["Curse of Strahd","fffs"],
"jomama":["blaal"],
"taco":["salff"]
}

生活只会变得更容易。您只需要像这样迭代:

for (index in list[value]) {

至于你用来生成选择/选项标记的技术——这不是我的方式,有多种方法可以做到这一点,StackOverflow上有大量的页面为你解释这些选项。

我通常不喜欢提供表单指令或标签作为字段顶部选项的UI。我建议您给您的表单字段<label>s,以便选项只包含真正的选项。


作为一个完全不同的选择,如果你不想每次用户做出选择更改时都修改DOM,你可以打印所有的次要选择字段,并预先加载它们的选项,然后"隐藏";他们所有人。然后,当用户更改主select字段时,只需"显示"带相关id的域。这当然会创建更多的html标记(这取决于您的数据量),但它大大降低了javascript代码的复杂性,因为所有的动态处理都是在页面加载时完成的。如果有一天,您想让您的主选择字段成为一个"多选择"字段,那么拥有可切换的辅助字段将很好地工作. ..."马儿为课程"&;等等。

别把我的话当回事//是你的旧代码///是我的评论

var list = {
"Reid": ["Curse of Strahd", "uuck"],
"bob": ["Curse of Strahd", "fffts"],
"jomama": "blaal",
"taco": "salff"
};

function chgm(value) {
// if (value.length == 0) document.getElementById("chargm").innerHTML = "<option></option>";
/// do not use == use ===
/// avoid innerHTML
var node = document.getElementById("chargm");
var option;
if (!value) {
node.appendChild( document.createElement('options') );
return;
} 
//else {
/// pls do not append to a string!
/// imagine you have 7000 values, the OS have everytime to get the string size AND 
/// (re-)allocate new memory!!
// var games = "";
/// var games = []; // use an array instead of!
/// use of instead of in, if you want to use in, you have to make sure if list.hasOwnProperty(value) is true
// for (gameMas in list[value]) {
/// for (var gameMas of Object.keys(list)) {
/// but we know already what we are looking for, so let's check for that:
if (list.hasOwnProperty(value)) {
// ok we have now other new values, but what's with the other ones?
// lets kill everything for lazyness
while(node.firstChild && node.removeChild(node.firstChild));
/// reset the header
node.appendChild( document.createElement('option') ).innerText = 'Game / RP Name';
// games += "<option>" + list[value][gameMas] + "</option>";
/// your example array is inconsistent, so we have to check for the type
if (!Array.isArray(list[value])) {
/// with an array do this:
/// games.push('<option>' + list[gameMas] + '</option>');

option = node.appendChild( document.createElement('option') );
option.innerText = list[value];
option.value = list[value];
return;
}
/// else
for (var v of list[value]) {
/// with an array do this:
/// games.push('<option>' + list[gameMas][value] + '</option>');
option = node.appendChild( document.createElement('option') );
option.innerText = v;
option.value = v;
} 
}
// document.getElementById("chargm").innerHTML = games;
/// with an array do this:
/// document.getElementById("chargm").innerHTML = games.join('');
}
<select name="chargm" id="charadm" onChange="chgm(this.value);">
<option value="" selected="true" disabled="">DM/Admin</option>
<option value="Reid">Reid</option>
<option value="bob">bob</option>
<option value="jomama">jomama</option>
<option value="taco">taco</option>
</select>
<select name="chargame" id="chargm">
<option value="" selected="true" disabled>Game / RP Name</option>
</select>