LoginSignup
13
22

More than 5 years have passed since last update.

MySQL, PHP でアンケート入力フォームを生成する

Posted at

はじめに

以下の質問が teratail に。

PHP 登録状況で変わる選択肢の状態にあわせてforeach?などで書き出したい

たびたびお世話になっています。
アンケートに関するPHPのページを作成しようと思っています。

1.行いたいことは、アンケートの本文と、選択枝のMySQLへの格納。
2.アンケート画面で、データベースから抽出して、選択肢を表示
3.チェックボックスやラジオボタンの状態を、DBへ書き込み

この中の2をどのようにできるかによって1の画面を作りたいと思っています。

下記のような配列のテスト画面を作りました。

//test.php
<?php
$survey= array(
    "sur1"=> array("紹介",
                   "看板",
                   "チラシ",
                   "フリーペーパー" => array ("雑誌A",
                                        "雑誌B",
                                         "雑誌C",
                                         "雑誌D"),
                   "ネット" => array ("ホームページ",
                                     "EPARK",
                                     "エキテン",
                                     "LINE",
                                     "Facebook",
                                     "Instagram",
                                     "その他")
                   ),
     "sur2"=> array("A市","B市","D市","E郡","その他のA県内","B県","その他の県"),
     "sur3"=> array("車で5分以内","車で10分以内","車で20分以内","車で30分以内","それ以上")
);
var_dump($survey);  
print '<br><br><br>';
var_dump($survey['sur1']);    
print '<br><br><br>'; 
var_dump($survey['sur1']['フリーペーパー']);
print '<br><br><br>';
//試してみてうまくいかなかったコードです。
//このようにすれば、フリーペーパーの中身は表示されますが、sur1やフリーペーパーをあらかじめ入力するのではなく、変数で入れたいのですが、その変数の取得方法が分かりませんでした。
foreach( $survey['sur1']['フリーペーパー'] as $value ){
  echo $value."<br>"; 
}
?>

上記のPHPに対してブラウザに表示された内容のコピペです。

array(3) {
  ["sur1"]=>
  array(5) {
    [0]=>
    string(6) "紹介"
    [1]=>
    string(6) "看板"
    [2]=>
    string(9) "チラシ"
    ["フリーペーパー"]=>
    array(4) {
      [0]=>
      string(7) "雑誌A"
      [1]=>
      string(7) "雑誌B"
      [2]=>
      string(7) "雑誌C"
      [3]=>
      string(7) "雑誌D"
    }
    ["ネット"]=>
    array(7) {
      [0]=>
      string(18) "ホームページ"
      [1]=>
      string(5) "EPARK"
      [2]=>
      string(12) "エキテン"
      [3]=>
      string(4) "LINE"
      [4]=>
      string(8) "Facebook"
      [5]=>
      string(9) "Instagram"
      [6]=>
      string(9) "その他"
    }
  }
  ["sur2"]=>
  array(7) {
    [0]=>
    string(4) "A市"
    [1]=>
    string(4) "B市"
    [2]=>
    string(4) "D市"
    [3]=>
    string(4) "E郡"
    [4]=>
    string(19) "その他のA県内"
    [5]=>
    string(4) "B県"
    [6]=>
    string(15) "その他の県"
  }
  ["sur3"]=>
  array(5) {
    [0]=>
    string(16) "車で5分以内"
    [1]=>
    string(17) "車で10分以内"
    [2]=>
    string(17) "車で20分以内"
    [3]=>
    string(17) "車で30分以内"
    [4]=>
    string(12) "それ以上"
  }
}
<br><br><br>array(5) {
  [0]=>
  string(6) "紹介"
  [1]=>
  string(6) "看板"
  [2]=>
  string(9) "チラシ"
  ["フリーペーパー"]=>
  array(4) {
    [0]=>
    string(7) "雑誌A"
    [1]=>
    string(7) "雑誌B"
    [2]=>
    string(7) "雑誌C"
    [3]=>
    string(7) "雑誌D"
  }
  ["ネット"]=>
  array(7) {
    [0]=>
    string(18) "ホームページ"
    [1]=>
    string(5) "EPARK"
    [2]=>
    string(12) "エキテン"
    [3]=>
    string(4) "LINE"
    [4]=>
    string(8) "Facebook"
    [5]=>
    string(9) "Instagram"
    [6]=>
    string(9) "その他"
  }
}
<br><br><br>array(4) {
  [0]=>
  string(7) "雑誌A"
  [1]=>
  string(7) "雑誌B"
  [2]=>
  string(7) "雑誌C"
  [3]=>
  string(7) "雑誌D"
}
<br><br><br><br><br><br>雑誌A<br>雑誌B<br>雑誌C<br>雑誌D<br>      

上記を元に、ブラウザに下記のように出力したいと思っていますが、上記のような配列をもとに、選択肢の数だけ出力するのは可能でしょうか?

アンケート
ここにアンケート本文

〇紹介 〇看板 〇チラシ フリーペーパー【 〇雑誌A 〇雑誌B 〇雑誌C 〇雑誌D 】 ネット 【 〇ホームページ 〇EPARK 〇エキテン 〇LINE 〇Facebook 〇Instagram 〇その他 】

ここにアンケート本文
〇A市 〇B市 〇D市 〇E郡 〇その他のA県内 〇B県 〇その他の県

ここにアンケート本文
〇車で5分以内 〇車で10分以内 〇車で20分以内 〇車で30分以内 〇それ以上

上記のように書き出すことが可能であれば、この選択肢を登録する画面や、DBへの書き込み、DBからの書き出しを検討したいと思っています。難解すぎたり、非効率であれば、別の方法を使いたいとも思っていますが、DBへの負荷?などを考えると上記のようにした方がいいような気がしています。
foreachなどをいくつか試しましたが、うまくいきませんでした。

実際には、登録状況で変わる選択肢の状態にあわせてforeachで取り出したいのですが、なかなかうまくいきません。上記のように、配列と配列じゃないものが混ざっているのはNGでしょうか?上記のような配列を作るのは、問題があるのか、また書き出し方(チェックボックスやHTMLタグは不要ですが、条件分岐やループの回し方を知りたいです)等、お知恵をお借りできればと思っています。

お手数おかけしますが、よろしくお願いします。

要件

上記の案件について、要件をまとめると、
問題となっているのは、画面の出力部分なので、DBテーブル設計が肝となります。

必要なテーブル

  1. 設問テーブル
  2. 選択肢テーブル

上記の2点となる。
選択肢テーブルはツリー構造となるため、parentId カラムを持って、上下関係を持つ必要があります。

解説は後日書き足すとして、テーブル定義、ファイルを上げておきます。

テーブル定義

-- 設問テーブル
CREATE TABLE `question` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `sentence` varchar(256) NOT NULL DEFAULT '' COMMENT '質問文',
  `orderNumber` int(11) DEFAULT NULL COMMENT '表示順',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='設問';
-- 選択肢テーブル
CREATE TABLE `choice` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `parentId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '親ID',
  `questionId` int(10) unsigned DEFAULT NULL COMMENT '設問ID',
  `sentence` varchar(1000) DEFAULT NULL COMMENT '選択肢文言',
  `orderNumber` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '表示順',
  PRIMARY KEY (`id`),
  KEY `parentId` (`parentId`),
  KEY `questionId` (`questionId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='選択肢';

サンプルデータ

INSERT INTO `question` (`id`, `sentence`, `orderNumber`)
VALUES
    (1,'職場までの通勤時間は?',2),
    (2,'この案件を何で知りましたか?',1);
INSERT INTO `choice` (`id`, `parentId`, `questionId`, `sentence`, `orderNumber`)
VALUES
    (1,0,1,'車で5分以内',1),
    (2,0,1,'車で10分以内',2),
    (3,0,1,'車で20分以内',3),
    (4,0,1,'車で30分以内',4),
    (5,0,1,'それ以上',5),
    (6,0,2,'紹介',1),
    (7,0,2,'看板',2),
    (8,0,2,'チラシ',3),
    (9,0,2,'フリーペーパー',4),
    (10,9,2,'雑誌A',5),
    (11,9,2,'雑誌B',6),
    (12,9,2,'雑誌C',7),
    (13,9,2,'雑誌D',8),
    (14,0,2,'ネット',9),
    (15,14,2,'ホームページ',10),
    (16,14,2,'EPARK',11),
    (17,14,2,'エキテン',12),
    (18,14,2,'LINE',13),
    (19,14,2,'Facebook',14),
    (20,14,2,'Instagram',15),
    (21,14,2,'その他',16);

PHPディレクトリ構造

ディレクトリ構造は以下の図のようになっています。

スクリーンショット 0029-01-16 20.11.06.png

Model

QuestionModel.php
<?php

/**
 * QuestionModel.php
 */
class QuestionModel
{

    private $id = null;
    private $sentence = null;
    private $orderNumber = null;

    public function getId()
    {
        return $this->id;
    }

    public function getSentence()
    {
        return $this->sentence;
    }

    public function getOrderNumber()
    {
        return $this->orderNumber;
    }

    public function setParams(array $row)
    {
        $this->setId($row['id'])
            ->setSentence($row['sentence'])
            ->setOrderNumber($row['orderNumber']);
        return $this;
    }

    public function setId($id)
    {
        $this->id = $id;
        return $this;
    }

    public function setSentence($sentence)
    {
        $this->sentence = $sentence;
        return $this;
    }

    public function setOrderNumber($orderNumber)
    {
        $this->orderNumber = $orderNumber;
        return $this;
    }

}
ChoiceModel.php
<?php

/**
 * ChoiceModel.php
 */
class ChoiceModel
{

    private $id = null;
    private $parentId = null;
    private $questionId = null;
    private $sentence = null;
    private $orderNumber = null;

    public function getId()
    {
        return $this->id;
    }

    public function getParentId()
    {
        return $this->parentId;
    }

    public function getQuestionId()
    {
        return $this->questionId;
    }

    public function getSentense()
    {
        return $this->sentence;
    }

    public function getOrderNumber()
    {
        return $this->orderNumber;
    }

    public function setParams(array $row)
    {
        $this->setId($row['id'])
            ->setParentId($row['parentId'])
            ->setQuestionId($row['questionId'])
            ->setSentense($row['sentence'])
            ->setOrderNumber($row['orderNumber']);
        return $this;
    }

    public function setId($id)
    {
        $this->id = $id;
        return $this;
    }

    public function setParentId($parentId)
    {
        $this->parentId = $parentId;
        return $this;
    }

    public function setQuestionId($questionId)
    {
        $this->questionId = $questionId;
        return $this;
    }

    public function setSentense($sentence)
    {
        $this->sentence = $sentence;
        return $this;
    }

    public function setOrderNumber($orderNumber)
    {
        $this->orderNumber = $orderNumber;
        return $this;
    }

}

Controller

QuestionController.php
<?php

/**
 * QuestionController.php
 */
class QuestionController
{

    /**
     * 設問を全て取得する
     */
    public static function all()
    {
        $sql = 'SELECT ';
        $sql .= 'id, sentence, orderNumber ';
        $sql .= 'FROM question ';
        $sql .= 'ORDER BY orderNumber ASC';
        $params = [];
        $rows = DB::select($sql, $params);

        $arrObjQuestionModels = [];
        foreach ($rows as $row) {
            $objQuestionModel = new QuestionModel();
            $objQuestionModel->setParams($row);
            $arrObjQuestionModels[] = $objQuestionModel;
        }
        return $arrObjQuestionModels;
    }

}
ChoiceController.php
<?php

/**
 * ChoiceController.php
 */
class ChoiceController
{

    private static $choiceGroup = [];

    /**
     * 子孫となる選択肢を取得
     */
    public static function getChildren($questionId, $id)
    {
        if (isset(self::$choiceGroup[$id])) {
            return self::$choiceGroup[$id];
        }
        $sql = 'SELECT ';
        $sql .= 'id';
        $sql .= ', parentId';
        $sql .= ', questionId';
        $sql .= ', sentence';
        $sql .= ', orderNumber ';
        $sql .= 'FROM choice ';
        $sql .= 'WHERE questionId = :questionId ';
        $sql .= 'AND parentId = :parentId ';
        $sql .= 'ORDER BY orderNumber ASC';
        $params = [];
        $params[':questionId'] = $questionId;
        $params[':parentId'] = $id;
        $rows = DB::select($sql, $params);
        foreach ($rows as $row) {
            $objCoiceModel = new ChoiceModel();
            $objCoiceModel->setParams($row);
            self::$choiceGroup[$row['parentId']][] = $objCoiceModel;
        }
        return (isset(self::$choiceGroup[$id])) ? self::$choiceGroup[$id] : [];
    }

    /**
     * 設問ごとの選択肢を取得する
     * @param type $questionId
     */
    public static function getByQuestionId($questionId)
    {
        $sql = 'SELECT ';
        $sql .= 'id';
        $sql .= ', parentId';
        $sql .= ', questionId';
        $sql .= ', sentence';
        $sql .= ', orderNumber ';
        $sql .= 'FROM choice ';
        $sql .= 'WHERE questionId = :questionId ';
        $sql .= 'AND parentId = :parentId ';
        $sql .= 'ORDER BY orderNumber ASC';
        $params = [];
        $params[':questionId'] = $questionId;
        $params[':parentId'] = '0';
        $rows = DB::select($sql, $params);
        $arrObjCoiceModels = [];
        foreach ($rows as $row) {
            $objCoiceModel = new ChoiceModel();
            $objCoiceModel->setParams($row);
            $arrObjCoiceModels[] = $objCoiceModel;
        }
        return $arrObjCoiceModels;
    }

}

lib

DB.php
<?php

/**
 * DB.php
 */
class DB
{

    const DSN = 'mysql:host=localhost;dbname=questionnaire;charset=utf8;';
    const DB_USER = 'root';
    const DB_PASSWORD = 'password';

    private static function _connect()
    {
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ];
        $pdo = new PDO(self::DSN, self::DB_USER, self::DB_PASSWORD, $options);
        return $pdo;
    }

    /**
     *
     * @param string $sql
     * @param array $params
     * @return array
     */
    public static function select($sql, $params)
    {
        $pdo = self::_connect();
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll();
    }

}

その他

common.php
<?php

/**
 * common.php
 */
function h($string)
{
    return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
}

/**
 * autoload
 * @param type $class
 */
function autoload($class)
{
    if (preg_match('/\w+Controller/', $class)) {
        $file = sprintf('controller/%s.php', $class);
    } else if (preg_match('/\w+Model/', $class)) {
        $file = sprintf('model/%s.php', $class);
    } else if (preg_match('/\w+/', $class)) {
        $file = sprintf('lib/%s.php', $class);
    }
    if (file_exists($file)) {
        require $file;
    }
}

spl_autoload_register('autoload');
index.php
<?php
ini_set('display_errors', true);
error_reporting(E_ALL);

require 'common.php';
$questions = QuestionController::all();
?>
<!DOCTYPE HTML>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>アンケート</title>
        <style type="text/css">
            .sentence:before {
                content: 'Q.';
                margin-right: 0.5em;
            }
            .question ul {
                list-style: none;
            }
        </style>
    </head>
    <body>
        <div id="questionnaire">
            <form action="" method="post">
                <?php foreach ($questions as $question) : ?>
                    <div class="question">
                        <p class="sentence"><?= h($question->getSentence()); ?></p>
                        <ul class="choice">
                            <?php foreach (ChoiceController::getByQuestionId($question->getId()) as $choice) : ?>

                                <?php if (0 === count(ChoiceController::getChildren($question->getId(), $choice->getId()))) : ?>
                                    <li>
                                        <label>
                                            <input type="radio" name="question[<?= h($question->getId()); ?>]" value="<?= h($choice->getId()); ?>" />
                                            <?= h($choice->getSentense()); ?>
                                        </label>
                                    </li>
                                <?php else: ?>
                                    <li>
                                        <?= h($choice->getSentense()); ?>
                                    </li>
                                    <ul>
                                        <?php foreach (ChoiceController::getChildren($question->getId(), $choice->getId()) as $child) : ?>
                                            <li>
                                                <label>
                                                    <input type="radio" name="question[<?= h($question->getId()); ?>]" value="<?= h($child->getId()); ?>" />
                                                    <?= h($child->getSentense()); ?>
                                                </label>
                                            </li>
                                        <?php endforeach; ?>
                                    </ul>
                                <?php endif; ?>
                            <?php endforeach; ?>
                        </ul>
                    </div>
                <?php endforeach; ?>
            </form>
        </div>
    </body>
</html>

スクリーンショット

localhost_2017-01-16_20-23-45.png

13
22
2

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
13
22