⬆️ ⬇️

My view of trees in PHP

Hi, actually, I decided to write this post in reply to this message from the sandbox.

I do not consider myself a pro in programming, although sometimes I really want to, but still, I think it’s wrong to build trees in this way).

I will not build a comment tree, but a menu tree. My way of building a menu is easy to modify and to build a comment tree. A menu can have any number of submenus nested. My menu table structure is:



m_Id

m_Position

m_LineId

m_ParentId

m_Type

m_Title

m_Description

m_Data





When I was puzzled by the construction of a tree, I was too lazy to think for myself, I decided to look for ready-made solutions for these your tyrnets. Ready solutions are kind of like, someone suggested, but these ready solutions were implemented in frameworks that did not suit me, the rest, the trees were built with a bunch of queries to the database, but I believed that there was a way to build an arbitrarily huge tree in just 1 request , moreover, without any perversions with all kinds of COUNT () and other Labuda. I will not take the time, I will give the code of my class that builds the menu tree:

 class MenuFactory extends BaseClass { /** * * var mixed        */ public $MenuTree; /** * * @var mixed       , .. ,      */ public $MenuList; private $level=0; /** * * @param int Id     * @param int/bool  bool==true -      ,     .  ,          * @return mixed */ public function GetMenu($menu_id, $return_as_list = false) { //         $this->sql->Query("SELECT * FROM `menus` WHERE `m_LineId`=?d ORDER BY m_Position, m_ParentId", $menu_id); // $this->MenuList = array(); $ml = array(); while($r = $this->sql->Assoc()) { //    MenuItem    $m = CommonUtils::ObjectFromArray('MenuItem', CommonUtils::DelPrefix($r,'m_')); //       $this->MenuList[] = $m; //      ,        $ml[] = clone $m; } //  $return_as_list == true       if(is_bool($return_as_list) && $return_as_list) { return $this->MenuList; } // ,  $return_as_list == false    else if(!$return_as_list) { $this->MenuTree = array(); $this->MenuTree = $this->GetMenuChilds(0,$ml); return $this->MenuTree; } // ,  $return_as_list == ,      ,    else if(is_numeric($return_as_list)) { $this->MenuTree = array(); $this->MenuTree = $this->GetMenuChilds(0,$ml, $return_as_list); return $this->MenuTree; } //print_r($this->menuTree); } /** * * @param int $p_id Id    * @param mixed $menuList   ,        * @param int $levels   * @return mixed      */ private function GetMenuChilds($p_id, $menuList, $levels = null) { //     ,     $a = array(); //     foreach($menuList as $item) { //TODO: Optimize code //  ParentId       id, // ,      .    if($item->ParentId == $p_id) { $this->level++; //  $levels ,     $this->levels, //        ,  $levels if($levels != null && $this->level <= $levels) { //       .        // $this->MenuList   , ParentId   id   , // ,       $item->ChildMenus = $this->GetMenuChilds($item->Id, $menuList, $levels); //          $this->level $item->Level = $this->level; //      $a[] = $item; } else { //           $this->MenuList $item->ChildMenus = $this->GetMenuChilds($item->Id, $menuList); $item->Level = $this->level; } $this->level--; // $levels  ,  ParentId     ,   //  ,        if($levels == null) { $a[] = $item; } } } //      return $a; } /** * * @return mixed      */ public function GetMenuList() { return $this->MenuList; } /** * * @param int $menu_id Id  ,        * @return mixed    */ public function GetMenuPathTo($menu_id) { //      ,      //  ,     .    $a = array(); for($i=0; $i<count($this->MenuList); $i++) { //      id  id   if($this->MenuList[$i]->Id == $menu_id) { //     $a[] = $this->MenuList[$i]; //      ,     , //    $menu_id  ,  id    $menu_id = $this->MenuList[$i]->ParentId; // ,  ) $i=-1; } } //        -> , //  ,         :  ->  return array_reverse($a); } /** * * @param int $menu_id Id ,     * @return MenuItem   */ public function GetMenuById($menu_id) { $this->sql->Query("SELECT * FROM `menus` WHERE `m_Id`=?d LIMIT 1", $menu_id); $record = $this->sql->Assoc(); if($record) { $record = CommonUtils::DelPrefix($record,'m_'); return CommonUtils::ObjectFromArray('MenuItem', $record); } return null; } } 




The code of the class that represents the menu item:

 class MenuItem { public $Id; public $Position; public $ParentId; public $LineId; public $Type; public $Title; public $Description; public $Data; public $Level; public $ChildMenus; } 


')

I do not know how to highlight the php code, but not about that.

In principle, I think it is clear that the GetMenu () method allows you to get both the menu tree and the menu as a list. In addition, if you specify a number from 0 as the second argument to infinity, the menu tree will load with a depth equal to that number.



the GetMenuPathTo () method searches for a path to the menu item whose id is passed as a parameter; this method was implemented to organize bread crumbs on the site



CommonUtils :: DelPrefix () allows you to remove prefixes in associative arrays, allowing you to get the actual names of the properties of menu items.

CommonUtils :: ObjectFromArray () returns an object of the class specified to it, which will be filled with values ​​from the associative array passed in the second parameter.



I do not argue, perhaps, someone may not like this code, but it does an excellent job with building menu trees, in just 1 database query.



All success.



upd : // to all who leave kamenty in style: “what didn’t suit the% methodname% for building trees”, or “why not% methodname%?” solemnly declare: There are a lot of ways to build trees. I brought my implementation, because most likely did not know about your% methodname%. Anyways, your comments are important to me and will not be left without proper attention.

Source: https://habr.com/ru/post/114573/



All Articles