単一テーブルからの隣接ツリー
-
09-09-2019 - |
質問
ネストされたリストについて議論している人をたくさん読みましたが、PHP で隣接リスト/ツリーを反復処理する方法を疑問に思っていました。
次のようなテーブルがあります。id、タイトル、parent_id
そして、すべてのレコードを $pages という配列に選択しました。
次に、この php を使用します。
function makeList($pages, $used) {
if (count($pages)) {
echo "<ul>";
foreach ($pages as $page) {
echo "<li>".$page['pag_title'];
$par_id = $page['pag_id'];
$subsql("SELECT * FROM pages WHERE pag_parent = ".$par_id."");
// running the new sql through an abstraction layer
$childpages = $dbch->fetchAll();
makeList($childpages, $used, $lastused);
echo "</li>";
}
echo "</ul>";
}
}
この種の機能は機能しますが、サブメニューが繰り返されることになります。
- 家
- ニュース
- サブニュース
- 記事
- 記事
- ニュース
- ニュース
- サブニュース
- 記事
- 記事
- サブニュース
- 記事
関数を介して渡される配列に現在のIDを追加し、in_arrayを使用してそれがそこにあるかどうかを確認しようとしましたが、それを行うのは楽しいことではありませんでした。
助けていただければ幸いです。
ツリー全体を解析する必要があるため、親を 0 として選択することはできません
解決
は、最初の関数呼び出しの前に外にそれを行うには持っていけない。
function makeList($par_id = 0) {
//your sql code here
$subsql("SELECT * FROM pages WHERE pag_parent = $par_id");
$pages = $dbch->fetchAll();
if (count($pages)) {
echo '<ul>';
foreach ($pages as $page) {
echo '<li>', $page['pag_title'];
makeList($page['pag_id']);
echo '</li>';
}
echo '</ul>';
}
}
あなたのようなより多くの木が、このサイトで見たいと思うかもしれません、それを格納するために:保存データベースので階層データます。
他のヒント
あなたは親IDでグループ化されたページの配列を作成した場合、再帰的にリストを構築することは非常に簡単です。これが唯一のデータベースクエリが必要になります。
<?php
//example data
$items = array(
array('id'=>1, 'title'=>'Home', 'parent_id'=>0),
array('id'=>2, 'title'=>'News', 'parent_id'=>1),
array('id'=>3, 'title'=>'Sub News', 'parent_id'=>2),
array('id'=>4, 'title'=>'Articles', 'parent_id'=>0),
array('id'=>5, 'title'=>'Article', 'parent_id'=>4),
array('id'=>6, 'title'=>'Article2', 'parent_id'=>4)
);
//create new list grouped by parent id
$itemsByParent = array();
foreach ($items as $item) {
if (!isset($itemsByParent[$item['parent_id']])) {
$itemsByParent[$item['parent_id']] = array();
}
$itemsByParent[$item['parent_id']][] = $item;
}
//print list recursively
function printList($items, $parentId = 0) {
echo '<ul>';
foreach ($items[$parentId] as $item) {
echo '<li>';
echo $item['title'];
$curId = $item['id'];
//if there are children
if (!empty($items[$curId])) {
makeList($items, $curId);
}
echo '</li>';
}
echo '</ul>';
}
printList($itemsByParent);
ここで$ページから来ていますか?あなたはそれをエスケープするか、プリペアドステートメントを使用していない場合は、あなたのコード内のSQLインジェクションの脆弱性があるかもしれません。
また、forループ内のSELECTステートメントは、悪い習慣のようにジャンプします。表がその大きなない場合は、テーブル全体の内容を選択し、ツリーデータ構造を構築するためにPHPで結果セットを反復処理。これは、n個の*を取ることができた(n-1)のリンクリストされて、あなたの木の病的な場合は/ 2回の繰り返し。すべてのノードがツリーに追加された際に停止、または残りのノードの数は、次への一回の反復から同じまま - これは、残りのノードがルートノードの子ではないこと
。データベースが再帰SQLクエリをサポートしている場合あるいは、あなたはそれを使用することができ、そしてそれは、あなたの親ノードの子であるノードを選択します。あなたはまだPHPで自分をオブジェクトツリーを構築する必要があります。クエリの形のようなものになります:
WITH temptable(id, title, parent_id) AS (
SELECT id, title, parent_id FROM pages WHERE id = ?
UNION ALL
SELECT a.id, a.title, a.parent_id FROM pages a, temptable t
WHERE t.parent_id = a.id
) SELECT * FROM temptable
置き換えて '?'開始ページIDを持つ2行目。
最も簡単な修正はちょうどのようなWHERE句を追加し、あなたは(あなたが表示されません)$pages
を設定する最初の選択を行っている際に、だろう
WHERE pag_parent = 0
(またはあなたが "トップレベル" のページを保存しているかによって、NULL IS)。
あなたが最初にすべての子を選択しないであろうその方法。
そのテーブルが大きくなる場合には、再帰は扱いにくい得ることができます。私は、再帰レス方式についてのブログ記事を書いた:<のhref = "http://www.alandelevie.com/2008/07/12/recursion-less-storage-of-hierarchical-data-in-a-relational -database /」のrel = "nofollowをnoreferrer"> http://www.alandelevie.com/2008/07/12/recursion-less-storage-of-hierarchical-data-in-a-relational-database/
ノードの最上位の親、すべての親、およびすべての子の検索 (Tom Haigh の回答の拡張):
<?php
//sample data (can be pulled from mysql)
$items = array(
array('id'=>1, 'title'=>'Home', 'parent_id'=>0),
array('id'=>2, 'title'=>'News', 'parent_id'=>1),
array('id'=>3, 'title'=>'Sub News', 'parent_id'=>2),
array('id'=>4, 'title'=>'Articles', 'parent_id'=>0),
array('id'=>5, 'title'=>'Article', 'parent_id'=>4),
array('id'=>6, 'title'=>'Article2', 'parent_id'=>4)
);
//create new list grouped by parent id
$itemsByParent = array();
foreach ($items as $item) {
if (!isset($itemsByParent[$item['parent_id']])) {
$itemsByParent[$item['parent_id']] = array();
}
$itemsByParent[$item['parent_id']][] = $item;
}
//print list recursively
function printList($items, $parentId = 0) {
echo '<ul>';
foreach ($items[$parentId] as $item) {
echo '<li>';
echo $item['title'];
$curId = $item['id'];
//if there are children
if (!empty($items[$curId])) {
printList($items, $curId);
}
echo '</li>';
}
echo '</ul>';
}
printList($itemsByParent);
/***************Extra Functionality 1****************/
function findTopParent($id,$ibp){
foreach($ibp as $parentID=>$children){
foreach($children as $child){
if($child['id']==$id){
if($child['parent_id']!=0){
//echo $child['parent_id'];
return findTopParent($child['parent_id'],$ibp);
}else{ return $child['title'];}
}
}
}
}
$itemID=7;
$TopParent= findTopParent($itemID,$itemsByParent);
/***************Extra Functionality 2****************/
function getAllParents($id,$ibp){ //full path
foreach($ibp as $parentID=>$nodes){
foreach($nodes as $node){
if($node['id']==$id){
if($node['parent_id']!=0){
$a=getAllParents($node['parent_id'],$ibp);
array_push($a,$node['parent_id']);
return $a;
}else{
return array();
}
}
}
}
}
$FullPath= getAllParents(3,$itemsByParent);
print_r($FullPath);
/*
Array
(
[0] => 1
[1] => 2
)
*/
/***************Extra Functionality 3****************/
//this function gets all offspring(subnodes); children, grand children, etc...
function getAllDescendancy($id,$ibp){
if(array_key_exists($id,$ibp)){
$kids=array();
foreach($ibp[$id] as $child){
array_push($kids,$child['id']);
if(array_key_exists($child['id'],$ibp))
$kids=array_merge($kids,getAllDescendancy($child['id'],$ibp));
}
return $kids;
}else{
return array();//supplied $id has no kids
}
}
print_r(getAllDescendancy(1,$itemsByParent));
/*
Array
(
[0] => 2
[1] => 3
)
*/
print_r(getAllDescendancy(4,$itemsByParent));
/*
Array
(
[0] => 5
[1] => 6
)
*/
print_r(getAllDescendancy(0,$itemsByParent));
/*
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
)
*/
?>