動的にテキストボックスを追加+配列をDBに追加

作っていたクイズアプリの正解がずっと1つだったので、テキストボックスは1つでよかったけれども、

正解が複数あるものが出てきてしまったので、動的にテキストボックスを追加削除して、

サブテーブル(正解テーブル)にレコード追加しなくてはならなくなった時のメモ。

メインテーブル:main_table、サブテーブル:sub_table があると仮定してお読みください。

目次

<input>要素を配列にする

大体は正解は1つなのでこうしたくはなかったけれど、他に方法が思いつかないので

<input>要素を配列にすることにしました。

HTML(View)
<div id="answers-area">
  <div class="answer-row">
    <input type="text" name="answers[]" placeholder="正解を入力">
  </div>
</div>

<button type="button" onclick="addAnswer()">+追加</button>
<button type="button" onclick="removeAnswer()">-削除</button>

別の記事でもお話ししましたが、 name=”〇〇[]” と書くだけで<input>要素が配列になります。

下準備はこれだけでOKです。

onclick の部分は後述します。

JavaScript でテキストボックスを追加 / 削除

テキストボックスを追加

先述したonClickに設定されているaddAnswer関数を作ります。

answers-area内に作ることを前提に設計しています。

JavaScript
function addAnswer() {
  const area = document.getElementById('answers-area');

  const div = document.createElement('div');
  div.className = 'answer-row';

  div.innerHTML = `
    <input type="text" name="answers[]" placeholder="正解を入力">`;

  area.appendChild(div);
}

①divブロックを作る

②①で作った<div>タグに「answer-row」クラスを書きこむ

③①で作った<div>タグの中に<input>タグを作る

④①で作った<div>タグをidがanswers-areaの<div>タグに追加する

③の時、name=”〇〇[]” を書き忘れないように注意してくださいね!

これで「+追加」ボタンを押した分だけテキストボックスが追加されます。

テキストボックスを削除

こちらは、removeAnswer()関数を作ります。

JavaScript
function removeAnswer() {
  const area = document.getElementById('answers-area');
  if (area.children.length > 1) {
    area.removeChild(area.lastElementChild);
  }
}

最初の行のテキストボックスまで消されないように、if (area.children.length > 1) でガードしています。

最初の行でなければ、lastElementChildプロパティを指定して、

(この場合はanswers-areaのchildren = class=”answers-row”が記述されている<div>タグ)

removeChild で要素を削除しています。

(childNodes プロパティを使おうかと思ったけれど、バグを出しそうでやめました(汗))

これで、「-」ボタンを押すと最初の1行以外は、テキストボックスが最後の行から削除されていきます。

テキストボックス追加ボタン/削除ボタンを合わせるとこんな感じになります。

See the Pen Untitled by gulf_stream (@Hitomi-Kuge) on CodePen.

最初の行が消されないようにしておくと安心感が増しますよね。

メインテーブルの主キーを取得する

メインテーブルにデータを入れたら、サブテーブルにデータを入れます。

サブテーブルにはメインテーブルの主キーを入れることとします。

但し、メインテーブルの主キーはAUTO INCREMENTされたものです。

CREATEする時

メインテーブルにデータを入れて初めて、メインテーブルの主キーがわかります。

なので、その場合はPDOにあるlastInsertId()関数という便利な機能を使ってid を取得します。

PHP(Controller)※Viewでも可
// メインテーブルにデータを入れる
$stmt = $pdo->prepare("INSERT INTO main_table (main_text) VALUES (?)");
$stmt->execute([$text]);

// メインテーブルの主キーを取得
$main_id = $pdo->lastInsertId();

但し、この方法が使えるのはメインテーブルの主キーがAUTO INCREMENTされているものだけです。

UPDATEする時

UPDATEするページには主キーとなる id をGETパラメータで渡します。

(URLでいうhttps://localhost/?〇〇の〇〇の部分)

HTML(受け渡し元のView)
<?php
$stmt = $pdo->query("SELECT id FROM main_table");
$keys = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>

<html>
...
<?php foreach ($keys as $k): ?>
<a href="update.php?id=<?= $k['id'] ?>">UPDATE_<?=$k</a>
<? endforeach ?>
...
</html
PHP(Controller)※Viewでも可
$main_id = $_GET['id'];

サブテーブルの該当 id のデータを全削除(UPDATEのみ)

UPDATEの時のみ、サブテーブルに入っているメインテーブルと同じ id のデータを全削除しておきます。

テキストボックスを動的に追加/削除できるようにしてしまったので、行を変えられているかもしれないし、

UPDATEされているかもしれないし、はたまたDELETEされているかもしれません。

なので、サブテーブルに入っている取得した id と同じデータを一旦削除します。

PHP(Controller)※Viewでも可
$stmt = $pdo->prepare("DELETE FROM sub_table WHERE main_id = ?";);
$stmt->execute([$main_id]);

配列になったデータを1行ずつDBにデータを入れる

JSONを使って配列自体をカラムに入れる方法もあるらしいけれども、

ここは安全策を取って、配列を1行ずつ読みだしてカラムに入れる方法で実行することに。

念のため、空白行はスキップするようにしています。

PHP(Controller)※Viewでも可
foreach ($_POST['answers'] as $answer) {
  if (trim($answer) !== '') {
      $stmt = $pdo->prepare(
        "INSERT INTO sub_table (main_id, answer) VALUES (?, ?);"
      );
      $stmt->execute([$main_id, $answer]);
  }
}

おわりに

なんだ、配列をそのままカラムに入れるんじゃないのかとがっかりされたかもしれませんが、安全が一番です!

個人的にはforeach 様様です!

一例として、参考にしていただけると嬉しいです。

  • URLをコピーしました!
目次