2014-02-14 72 views
1

我有一個問題,當我想在我的數據來創建樹結構,這是我對樣本數據如何從父類別的子項目製作樹?

$categories = array(
    array(
     'id' => 2 , 
     'name' => 'banana', 
     'parent' => 1 
    ), 

    array(
     'id' => 1 , 
     'name' => 'fruit', 
     'parent' => 0 
    ), 

    array(
     'id' => 3 , 
     'name' => 'Monkey', 
     'parent' => 4 
    ), 

    array(
     'id' => 4 , 
     'name' => 'Animal', 
     'parent' => 0 
    ), 

    array(
     'id' => 5 , 
     'name' => 'apple', 
     'parent' => 1 
    ), 

    array(
     'id' => 6 , 
     'name' => 'Cat', 
     'parent' => 4 
    ), 

    array(
     'id' => 7 , 
     'name' => 'Animal Water', 
     'parent' => 1 
    ), 

    array(
     'id' => 8 , 
     'name' => 'fish', 
     'parent' => 7 
    ), 
    array(
     'id' => 9 , 
     'name' => 'frog', 
     'parent' => 7 
    ) 
); 

那麼我想重組該數組是這樣

$fix = array(

    array(
     'id' => 4 , 
     'name' => 'Animal', 
     'parent' => 0, 
     'child' => array(
      array(
       'id' => 7 , 
       'name' => 'Animal Water', 
       'parent' => 1, 
       'child' => array(
        array(
         'id' => 8 , 
         'name' => 'fish', 
         'parent' => 7 
        ), 
        array(
         'id' => 9 , 
         'name' => 'frog', 
         'parent' => 7 
        ) 
       ) 
      ), 

      array(
       'id' => 3 , 
       'name' => 'Monkey', 
       'parent' => 4 
      ), 

      array(
       'id' => 6 , 
       'name' => 'Cat', 
       'parent' => 4 
      ), 
     ) 
    ), 

    array(
     'id' => 1 , 
     'name' => 'fruit', 
     'parent' => 0, 
     'child' => array(
      array(
       'id' => 2 , 
       'name' => 'banana', 
       'parent' => 1 
      ), 



      array(
       'id' => 5 , 
       'name' => 'apple', 
       'parent' => 1 
      ) 
     ) 
    ) 

); 

所以我嘗試使用這個功能,但沒有幫助我。

$GLOBALS['viyancs'] = array(); 
$GLOBALS['i'] = 0; 
function finding_parent($categories) { 

    foreach ($categories as $value) { 

     //finding parent 
     if ($value['parent'] === 0) { 
      $GLOBALS['viyancs'][$GLOBALS['i']][$value['id']]= $value; 
      $GLOBALS['i']++; 
      continue; 
     } 

     if (isset($GLOBALS['viyancs'][$GLOBALS['i']][$value['parent']])) { 
      $GLOBALS['viyancs'][$GLOBALS['i']][$value['parent']]['child']= $value; 
     } 
     $GLOBALS['i']++; 
    } 
    return $GLOBALS['viyancs']; 
} 

var_dump(finding_parent($categories)); 

還有其他建議嗎?

+0

看起來好像你基本上構建了一個嵌套的導航系統......我發現要做到這一點的最好方式是將其解析爲XML(使用DOMDocument),然後可以將id分配給節點(如'id =「 c {$ iCatId}')並通過標準的DOM操作將子節點附加到父母身上。最大的優點是可以使用XPath在樹的底部進行導航或開始,並且可以非常輕鬆地進行工作......當然,缺點是必須將所有內容解析爲XML - 但這比遞歸循環我想。 – CD001

+0

hii,看起來我聽到了另一個新的解決方案,你能解釋一下嗎?或者給我參考一下你的建議? – viyancs

+0

我在幾年前的博客上寫了一篇關於它的小文章......我將發佈它的一個版本作爲答案。 – CD001

回答

1

這裏的問題:

你有,你希望用戶能夠通過下鑽深度不確定,所以像產品»電子»音響»MP3播放器的導航系統。所以,你建立了一個數據庫,導航系統,並保持它非常乾淨,你可以有效地它儘量減少類似:

Table: nav 

id parent_id  name 
---------------------------------------------------------- 
1  0   'Products' 
2  1   'Electronics' 
3  2   'Audio' 
4  3   'MP3 Players' 

這給你一個很好的結構,其中所有的導航項目有父母(或不若他們是最高級別的),只要你深入研究就可以了......當你想「鑽取」時,問題就來了!

請參閱爲了保持URI清潔,您只需確保傳入導航標識並使用它來構建頁面。所以如果你是傳遞像?nav=4這樣的變量的URI,那麼當nav=4時系統必須知道哪些導航項目是可見的;例如nav 4的父級(和同級),該nav的父級和同級級別...等等,直到樹根到達根級項目。

我已經看到這在十幾種不同的方式完成 - 大多數涉及可怕的遞歸循環,我一直認爲必須有更好的方法。

一種替代方法是在數據庫中爲每個導航項目存儲「路徑」。它非常高效,只需添加一個VARCHAR字段並將導航條目的路徑作爲序列化字符串存儲......就像「1; 2; 3; 4」。

但是,這從來沒有「與我坐在一起」。它似乎錯誤以已經存在的方式存儲數據的結構 - 以parent_id的形式。它打破了你的數據的關係方面,因爲如果你移動了一個導航項目,你不僅需要重新分配parent_id,而且還要重建該路徑 - 比如說你想放棄音頻子類別並且將MP3播放器移動到電子類別中。使用基本的數據庫結構,您只需放下nav.id = 3,重新分配parent_id和jobsagoodun。如果您正在存儲路徑,但您還需要更新該路徑,以便取代「1; 2; 3; 4」,現在它讀取「1; 2; 4」。

從維護數據的關係完整性的角度來看,這對我來說似乎總是一個壞主意。


解決方案

因爲我們希望將數據存儲在一個結構化的方式 - 爲什麼不將其轉換爲結構化的方式? XML。

首先我們將整個導航系統映射到XML - 所以我們需要先選擇它。

SELECT nav.id, nav.parent_id, nav.name FROM nav 

現在,我們需要遍歷數據並將其映射到一個XML結構 - 我使用一個數據庫抽象對象,假設一個讀方法執行對數據庫的SELECT查詢並返回一個多維數組像:

array ( 
    [0] => array ('id' => 1, 'parent_id' => 0, 'name' => "Products"), 
    [1] => array ('id' => 2, 'parent_id' => 1, 'name' => "Electronics"), 
    ... and so on 
); 

所以,如果你是陣列上的循環做了,每個實例將是一個關聯數組持有的導航項目的數據......現在來構建它。

$xml = new DOMDocument(); 
$xmlRoot = $xml->createElement('root'); 
$xml->appendChild($xmlRoot); 

//order by the parent ID so that when attaching nodes the parents will exist first 
$aNavList = $db->read("SELECT nav.id, nav.parent_id, nav.name FROM nav ORDER BY nav.parent_id ASC"); 

foreach($aNavList as $aNavItem) { 
    $iParentID = (int) $aNavItem['parent_id']; 

    //create the 'nav' node containing the text: e.g. <nav>Products</nav> 
    $xmlNav = $xml->createElement('nav', $aNavItem['name']); 

    //assign attributes to the nav node to store the data 
    // XML IDs can't start with numbers 
    $xmlNav->setAttribute('id', "nav_{$aNavItem['id']}"); 
    $xmlNav->setIdAttribute('id', true); 

    // the pure database ID 
    $xmlNav->setAttribute('db_id', $aNavItem['id']); 

    //Attach the nodes to the XML document 
    // If the parent ID of this node is 0, attach to the 'root' element 
    // Otherwise attach to the element with the ID "nav_{this parent id}" 
    $xmlParentNode = $iParentID == 0 ? 
     $xmlRoot : 
     $xml->getElementById("nav_{$aNavItem['parent_id']}"); 

    $xmlParentNode->appendChild($xmlNav); 
} 

的$ xml變量現在應該把你的導航數據的結構化版本,即:

<root> 
    <nav id="nav_1" db_id="1" page_id="1">Products 
     <nav id="nav_2" db_id="2" page_id="2">Electronics 
      <nav id="nav_3" db_id="3" page_id="3">Audio 
       <nav id="nav_4" db_id="4" page_id="4">MP3 Players</nav> 
      </nav> 
     </nav> 
    </nav> 
</root> 

我承認,這不是非常令人興奮的,但它確實意味着你可以使用標準的DOM導航技術來運行你的導航系統。請記住,我們只有導航樹分支中的最後一個節點的ID,但現在該節點嵌套在其所有祖先中。現在我們可以向上導航數據了!

假設我們有一個巨大的,嵌套的導航系統,我們想要創建一個麪包屑路徑,我們需要的是我們正在尋找的節點以及該節點的任何直接祖先 - 以便最終得到類似前述:

產品»電子»音頻»MP3播放器

現在,我們可以做到這一點沒有存儲在$ XML是一個未知數或使用DOMDocument遞歸循環,並從地址導航變量 - 例如, products.php?NAV = 4

$iNavSoughtID = (int) $_GET['nav']; 
$htmlBreadcrumb = ""; 
$xmlNodeSought = $xml->getElementById("nav_{$iNavSoughtID}"); 

while($xmlNodeSought->nodeName != 'root') { 
    $htmlBreadcrumb .= '<a href="/products.php?nav=' 
        . $xmlNodeSought->getAttribute('db_id') . '">' 
        . $xmlNodeSought->firstChild->nodeValue 
        . '</a>'; 

    $xmlNodeSought = $xmlNodeSought->parentNode; 
} 

這是一個非常簡單的例子,沒有任何安全性或完整性檢查的,但它確實給你,你纔剛剛開始,在該地址標識的節點的工作面包屑和工作沿着xml樹向下走,直到找到根 - 沒有任何可怕的遞歸循環或數據庫中可疑的路徑數據。

如果以XML格式存儲導航系統,則只需在開始時執行單個數據庫查詢以構建文檔,然後通過簡單DOM操作即可執行所有導航視圖。您甚至可以讓CMS實際編寫(和更新)導航XML文檔,並使用JavaScript(AJaX)即時構建網站導航系統(甚至可以在CSS中使用:懸停和顯示,如果您解析了XML到HTML),那麼你只能在導航系統上執行數據庫查詢,並且當它改變時!

即使使用DOM操作,當然要比嵌套的SQL查詢或遞歸循環更有效率,尤其是對於大型導航系統。


這是基於一個博客文章我寫了幾年前和沒有具體要求相匹配的問題,但這個問題似乎偶爾會突然出現這是我想出的解決方案對我來說是正確的

雖然原理是一樣的 - 創建一個嵌套的導航系統,同時保持良好的關係數據庫結構。

我將此作爲社區wiki答案讓更多的人可以適應它,以便更好地適合問題的具體問題。

0

您TOU具有類別陣列CATEGORY_ID和PARENT_ID

陣列 ( [0] =>數組 ( [ID] => 1 [父] => 0 [名稱] =>男人 )

[1] => Array 
    (
     [id] => 2 
     [parent] => 0 
     [name] => Woman 
    ) 

[2] => Array 
    (
     [id] => 3 
     [parent] => 1 
     [name] => Tshirt 
    ) 

[3] => Array 
    (
     [id] => 4 
     [parent] => 1 
     [name] => Jeans 
    ) 

[4] => Array 
    (
     [id] => 5 
     [parent] => 2 
     [name] => woman jeans 
    ) 

[5] => Array 
    (
     [id] => 6 
     [parent] => 2 
     [name] => woman top 
    ) 

[6] => Array 
    (
     [id] => 7 
     [parent] => 3 
     [name] => branded tshirt 
    ) 

[7] => Array 
    (
     [id] => 8 
     [parent] => 4 
     [name] => branded jeans 
    ) 

0
function getCategory($category_array) 
{ 
foreach($category_array as $key => $category_data): 
$cat_option_array; 
if($category_data['parent']==0) 
{ 
    $cat_option_array .= "<option value=".$category_data['id'].">".$category_data['name']."</option>"; 
    foreach($category_array as $subcategory_key => $subcategory_data): 
    if($category_data['id']==$subcategory_data['parent']) 
    { 
     $cat_option_array .= "<option value=".$subcategory_data['id'].">--".$subcategory_data['name']."</option>"; 

     foreach($category_array as $level2categorykey => $level2category): 

     if($level2category['parent']==$subcategory_data['id']) 
     { 
      $cat_option_array .= "<option value=".$level2category['id'].">----".$level2category['name']."</option>"; 
     } 

     endforeach; 
    } 
    endforeach; 
} 
endforeach; 
echo $cat_option_array; 
} 
+0

請編輯您的格式化答案,並將兩個答案合併爲一個。 – alterfox

相關問題