親子関係にある配列をネスト(入れ子)してリスト表示する
社員リストやサイトマップなど、データベースにバラバラに登録されているデータを部署ごとに並べ替えかつ、親子関係になるよう多重連想配列にしたいケースが有ります。
sqlのみだとlpadでスペースを入れて表現する方法であったり、WEB上でデザインするにはちょっと微妙な方法しか無いようです。
サンプルはSqliteで作った簡単なスタッフリストです。
DROP TABLE IF EXISTS "staff";
CREATE TABLE "staff" ("id" INTEGER PRIMARY KEY NOT NULL ,"parent" INTEGER,"sort" INTEGER DEFAULT (null) ,"name" TEXT);
INSERT INTO "staff" VALUES(1,NULL,1,'山田一郎');
INSERT INTO "staff" VALUES(2,1,2,'山田二郎');
INSERT INTO "staff" VALUES(3,1,3,'山田三郎');
INSERT INTO "staff" VALUES(4,2,4,'山田四郎');
INSERT INTO "staff" VALUES(5,NULL,1,'田中一郎');
INSERT INTO "staff" VALUES(6,5,2,'田中二郎');
INSERT INTO "staff" VALUES(7,NULL,1,'佐藤一郎');
INSERT INTO "staff" VALUES(8,7,2,'佐藤二郎');
INSERT INTO "staff" VALUES(9,8,3,'佐藤三郎');
INSERT INTO "staff" VALUES(10,9,4,'佐藤四郎');
INSERT INTO "staff" VALUES(11,NULL,1,'鈴木一郎');
parentは親となる列のidをインサートします。一応sortも含める事で表示順序をソートしています。後は名前。
データを呼び出す
$stmt = $db->prepare("SELECT * FROM staff ORDER BY sort");
$stmt->execute();
$array = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT )) {
$array[$row['id']] = $row;
}
結果をループし、自分のIDをキーとした配列に組み替えます。
親子構造の配列にする
$nests = array();
foreach($array as $row) {
//親idがあれば親の配列にchildrenというキーで加える
if($row['parent']) {
$array[$row['parent']]['children'][] = &$array[$row['id']];
}
//親の場合はそのまま
else{
$nests[$row['id']] = &$array[$row['id']];
}
}
parentに数値があれば、そのIDの配下にchildrenというキーで格納していきます。
この段階で階層構造で配列化されます。
配列をLIでリスト化する
/*
* 配列をリストにする
*/
$list = '';
if($nests) {
$list .= "<ul>";
$list .= nest($nests);
$list .= "</ul>";
}
echo $list; exit;
/*
* 再帰処理
*/
function nest($nest) {
global $li;
foreach($nest as $ns) {
if(!empty($ns['children'])) {
$li .= "<li>{$ns['name']}";
$li .= "<ul>";
nest($ns['children']);
$li .= "</ul>";
$li .= "</li>";
}else{
$li .= "<li>{$ns['name']}</li>";
}
}
return $li;
}
↑※Cryonはどうもタグを消したり微妙なのでクリックしてソース見て下さい。
関数nestを呼び出し、children(子配列)がある場合は親の下にULをセットしながら再帰します。
リスト化出来てしまえば、後はCSSレベルでデザインするだけ。
但し、SQLでループ、ネストする為にループ、リスト化する為にまたループ、とあまりスマートでは有りません。
array_walkを使ってどうにかシンプルに纏められないかと考えましたが、良い方法が見つかりませんでした。ループ処理が多いので、大量のデータ処理だと遅くなるかもしれませんが、300人程度の社員リストを処理しても殆ど一瞬でした。そもそもこの手のリストを作る際に、数千なんてレベルのデータを処理する事はあまり無いでしょうし、問題無いかと。
そうそう変化の無いデータであれば、キャッシュしてしまえば良いですし。




