##はじめに
以下の質問が teratail に。
PHP 登録状況で変わる選択肢の状態にあわせてforeach?などで書き出したい
たびたびお世話になっています。
アンケートに関するPHPのページを作成しようと思っています。1.行いたいことは、アンケートの本文と、選択枝のMySQLへの格納。
2.アンケート画面で、データベースから抽出して、選択肢を表示
3.チェックボックスやラジオボタンの状態を、DBへ書き込みこの中の2をどのようにできるかによって1の画面を作りたいと思っています。
下記のような配列のテスト画面を作りました。
//test.php
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 ''; var_dump($survey['sur1']); print '
'; var_dump($survey['sur1']['フリーペーパー']); print '
'; //試してみてうまくいかなかったコードです。 //このようにすれば、フリーペーパーの中身は表示されますが、sur1やフリーペーパーをあらかじめ入力するのではなく、変数で入れたいのですが、その変数の取得方法が分かりませんでした。 foreach( $survey['sur1']['フリーペーパー'] as $value ){ echo $value."
"; } ?>
>
> 上記のPHPに対してブラウザに表示された内容のコピペです。
>
> ```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テーブル設計が肝となります。
###必要なテーブル
- 設問テーブル
- 選択肢テーブル
上記の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ディレクトリ構造
ディレクトリ構造は以下の図のようになっています。
##Model
<?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;
}
}
<?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
<?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;
}
}
<?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
<?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();
}
}
###その他
<?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');
<?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>
##スクリーンショット