Contact Form DB のテーブルについて

Contact Form 7 というアンケートフォームのプラグインは、メールを送信するだけなので、DBへデータを格納するようにするプラグインが Contact Form DB です。

両プラグインをWordPressに入れることで、手軽に入力フォームの内容をDBに格納できます。

しかし、この Contact Form DB で作られるテーブルが曲者でした。

WordPressの管理画面から Contact Form DB の管理画面を表示すると、入力フォームから入力されたデータの一覧を表示でき、それを見るとあたかも一つのフォームからの投稿が一つのレコードとして格納されているように見えます。 しかし、実際のテーブルには、入力フォームの input 一つ一つがそれぞれ1レコードとして登録されます。

入力項目が3つのフォームだと、1回の投稿で3つレコードが登録されるわけで、それぞれのレコードには、それぞれの入力項目の内容のみが、投稿時間と入力フォーム名と入力項目名をキーに記録されるのです。

ちなみにテーブル構造は下記の通り。

mysql> DESC wp_cf7dbplugin_submits;
+-------------+---------------+------+-----+---------+-------+
| Field       | Type          | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| submit_time | decimal(16,4) | NO   | MUL | NULL    |       |
| form_name   | varchar(127)  | YES  | MUL | NULL    |       |
| field_name  | varchar(127)  | YES  | MUL | NULL    |       |
| field_value | longtext      | YES  |     | NULL    |       |
| field_order | int(11)       | YES  |     | NULL    |       |
| file        | longblob      | YES  |     | NULL    |       |
+-------------+---------------+------+-----+---------+-------+
6 rows in set (0.03 sec)

インデックス一覧は下記の通り。

mysql> SHOW INDEX FROM wp_cf7dbplugin_submits;
+------------------------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table                  | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| wp_cf7dbplugin_submits |          1 | submit_time_idx |            1 | submit_time | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
| wp_cf7dbplugin_submits |          1 | form_name_idx   |            1 | form_name   | A         |           1 |     NULL | NULL   | YES  | BTREE      |         |               |
| wp_cf7dbplugin_submits |          1 | field_name_idx  |            1 | field_name  | A         |          26 |     NULL | NULL   | YES  | BTREE      |         |               |
+------------------------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

ちなみに、時間が同じで投稿内容が違うレコードを insert してみたところ、登録できてしまい、その状態で管理画面からデータを参照すると、時間が同じ2つの投稿がごちゃごちゃに一つのレコードとして表示されてしまいます。 (まぁ、当然ですが)

同じ時間になるのはミリ秒単位で同一の場合なので、そんなに気にしないWebサイトなら問題無いかもしれないですが、特に個人情報を扱うようなWebサイトでは気にするべきでしょう。

で、対応策としては、テーブル構造を変えてしまいます。

まずインデックス削除。

ALTER TABLE wp_cf7dbplugin_submits DROP INDEX submit_time_idx;
ALTER TABLE wp_cf7dbplugin_submits DROP INDEX form_name_idx;
ALTER TABLE wp_cf7dbplugin_submits DROP INDEX field_name_idx;

続いて、複合キーで設定し直します。

alter table wp_cf7dbplugin_submits add constraint primary key(submit_time, form_name, field_name);

結果はこんな感じ。

mysql> SHOW INDEX FROM wp_cf7dbplugin_submits;
+------------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table                  | Non_unique | Key_name | Seq_in_index | Column_name |Collation  | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| wp_cf7dbplugin_submits |          0 | PRIMARY  |            1 | submit_time |A          |         173 |     NULL | NULL   |      | BTREE      |         |               |
| wp_cf7dbplugin_submits |          0 | PRIMARY  |            2 | form_name   |A          |         173 |     NULL | NULL   |      | BTREE      |         |               |
| wp_cf7dbplugin_submits |          0 | PRIMARY  |            3 | field_name  |A          |        1038 |     NULL | NULL   |      | BTREE      |         |               |
+------------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

ちなみに、MySQLのバージョンによっては複合キーは設定できないようですが、最近のものはできます。

これで、仮に完全同一タイミングで投稿があっても、どちらか片方は登録されませんので、データの整合性は保てます。