рекурсивное дерево алгоритма php

#php #algorithm #recursion #tree

#php #алгоритм #рекурсия #дерево

Вопрос:

Я модифицирую скрипт с открытым исходным кодом, чтобы показать генеалогию через стиль организационной диаграммы.

наличие таблицы пользователей с полем id и parentid.

когда я открываю / viewOrgChart?user_id=1 или числа не в 2 3 4 5, это работает хорошо!

НО когда я открываю с user_id 2 3 4 5, он выдает ОШИБКУ: попытка получить свойство ‘parentid’ не-объекта $ us-> parentid имеет внутри массив другого уровня… Ошибка возникает только для этих нескольких идентификаторов пользователя…

 /viewOrgChart?user_id=2
/viewOrgChart?user_id=3
/viewOrgChart?user_id=4
/viewOrgChart?user_id=5
 

не удалось понять, что здесь не так.

А также я хочу ограничить количество отображаемых уровней на основе переданного в user_id . Допустим, я передаю с user_id=1, и я хочу отобразить все только до 4 уровня вниз

(в этом случае он должен отображаться до уровня firstname=eng, ниже уровня eng все показывать не нужно) Но… куда ..

Любой эксперт по алгоритмам?

 <?php

    function wpmlm_get_all_user_details_join() {
        $sql = "SELECT * FROM users";
        $results = DB::select($sql);
        return $results;
    }

    function wpmlm_get_user_details_by_id_join($user_id) {
        $sql = "SELECT * FROM users where id = '" . $user_id . "'";
        $results = DB::select($sql);
        return $results;
    }

    function wpmlm_makeNested($source) {
        $nested = array();
    
        foreach ($source as amp;$s) {
            if (is_null($s['parent_id'])) {
                // no parent_id so we put it in the root of the array
                $nested[] = amp;$s;
            } else {
                $pid = $s['parent_id'];
                if (isset($source[$pid])) {
    
                    if (!isset($source[$pid]['children'])) {
                        $source[$pid]['children'] = array();
                    }
    
                    $source[$pid]['children'][] = amp;$s;
                }
            }
        }
        return $nested;
    }    

    function wpmlm_buildTree(array $elements, $parentId) {
        static $counter = 0;
          $counter;
        $tree = array();
        foreach ($elements as $element) {
            if ($element->id == $parentId) {
                if ($counter == 1) {
                    $tree[] = $this->wpmlm_get_user_details_by_id_join($parentId);
                }
            }
            if ($element->parentid == $parentId) {
                $children = $this->wpmlm_buildTree($elements, $element->id);
                if ($children) {
                    $tree[] = $children;
                }
    
                $tree[] = $element;
            }
        }
        return $tree;
    }  

    public function flattenArray($arr) 
    {
        for ($i = 0; $i < count($arr); $i  ) {
            if (is_array($arr[$i])) {
                array_splice($arr, $i, 1, $arr[$i]);
            }
        }
        return $arr;
    }

    public function viewOrgChart(Request $request)
    {
        $user_id = $request->user_id;

        $user_details = $this->wpmlm_get_all_user_details_join();
    
        //$tree = $this->wpmlm_buildTree(elements, parentId);        
        $tree = $this->wpmlm_buildTree($user_details, $user_id);
        
        foreach ($tree as $key => $data) 
        {
            $test = $key; 
            
            if (is_array($data)) {
                foreach ($data as $sub_data) {
                    $tree = $this->flattenArray($tree);
                }
            }
        }
        $arr = array();
        $count = 0;
        foreach ($tree as $us) 
        {
            $count  ;
            if ($count == 1) {
                $parent_id = null;
            } else {
                $parent_id = $us->parentid; //fail at here ERROR
            }
            $arr[$us->id] = Array(
                'name' => $us->firstname,
                'user_id' => $us->id,
                'user_code' => $us->usercode,          
                'parent_id' => $parent_id,
                'email' => $us->email,
            );
        }
        
        $uniLevelTree = $this->wpmlm_makeNested($arr);
    
        $treeJson = json_encode($uniLevelTree[0]);  
        
    }

?>

       <div id="unilevel-tree">
          <div class="panel-border">            
              <div id="chart-container"></div>
              <div id="test"></div>
          </div>
      </div>

<link rel='stylesheet' id='wp-mlm-bootstrap-css-css'  href='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/css/bootstrap.min.css?ver=5.2.1' type='text/css' media='all' />
<link rel='stylesheet' id='wp-mlm-font-awesome-css-css'  href='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/css/font-awesome.min.css?ver=5.2.1' type='text/css' media='all' />
<link rel='stylesheet' id='wp-mlm-orgchart-style-css-css'  href='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/css/orgchart-style.css?ver=5.2.1' type='text/css' media='all' />
<link rel='stylesheet' id='orgchart-css-css'  href='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/css/jquery.orgchart.css?ver=5.2.1' type='text/css' media='all' />
<link rel='stylesheet' id='admin-wp-mlm-style-css'  href='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/css/style.css?ver=5.2.1' type='text/css' media='all' />

<script type='text/javascript' src='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-admin/load-scripts.php?c=0amp;amp;load%5B%5D=jquery-core,jquery-migrate,utilsamp;amp;ver=5.2.1'></script>
<script type='text/javascript' src='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/js/bootstrap.min.js?ver=5.2.1'></script>
<script type='text/javascript' src='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/js/bootstrap-datepicker.js?ver=5.2.1'></script>

<script type='text/javascript' src='https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/js/jquery.orgchart.js?ver=5.2.1'></script> 



<script type="text/javascript">

jQuery( document ).ready( function( $ ) {

    var datasource =<?php echo $treeJson; ?>

    var nodeTemplate = function(data) {
      return `
      <span class = "user-image d-flex justify-content-center" >
        <img src = "https://wpmlmsoftware.com/unilevel-mlm-demo/wp-content/plugins/wp-mlm/images/user.png" > 
      </span>
      <div class = "title" > ${data.name} </div>
      <div class = "" > ${data.email} </div>
      <div class = "" > ${data.user_id} </div>   
      `;

      };

      var oc = $('#chart-container').orgchart({
          'data' : datasource,
          'nodeTemplate': nodeTemplate
      });
 });
    
</script>
 

SQL:

 CREATE TABLE `users` (
  `id` bigint(20) UNSIGNED NOT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `firstname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `parentid` int(10) UNSIGNED DEFAULT NULL
);

INSERT INTO `users` (`id`, `firstname`, `email`, `created_at`, `usercode`, `parentid`) VALUES
(1, 'andrew', 'test1@gmail.com', '2020-11-24 05:22:42', 'MB800001', 0),
(2, 'beta', 'test2@gmail.com', '2020-11-24 05:22:42', NULL, 1),
(3, 'cicak', 'test3@gmail.com', '2020-11-24 05:22:42', NULL, 2),
(4, 'dorae', 'test4@gmail.com', '2020-11-24 05:22:43', NULL, 3),
(5, 'eng', 'test5@gmail.com', '2020-11-24 05:22:43', NULL, 4),
(6, 'Fanny', 'test6@gmail.com', '2020-11-24 05:22:43', NULL, 5),
(7, 'Gary', 'test7@gmail.com', '2020-11-24 05:22:43', NULL, 6),
(8, 'Hafiz', 'test8@gmail.com', '2020-11-24 05:22:43', NULL, 7),
(9, 'Isaac', 'test9@gmail.com', '2020-11-24 05:22:43', NULL, 8),
(10, 'louis', 'test10@gmail.com', '2020-11-24 05:22:44', NULL, 1),
(11, 'shze', 'test11@gmail.com', '2020-11-24 05:22:44', NULL, 1),
(12, 'paul', 'test12@gmail.com', '2020-11-24 05:22:44', NULL, 10),
(13, 'eunice', 'test13@gmail.com', '2020-11-24 05:22:44', NULL, 10),
(14, 'shaun', 'test14@gmail.com', '2020-11-24 05:22:44', NULL, 11),
(15, 'xiao', 'test15@gmail.com', '2020-11-24 05:22:44', NULL, 11),
(16, 'hui', 'test16@gmail.com', '2020-11-24 05:22:44', NULL, 11),
(17, 'gen', 'test17@gmail.com', '2020-11-24 05:22:45', NULL, 16),
(18, 'hani', 'test18@gmail.com', '2020-11-24 05:22:45', NULL, 17),
(19, 'hola', 'test19@gmail.com', '2020-11-24 05:22:45', NULL, 18),
(20, 'hugo', 'test20@gmail.com', '2020-11-24 05:22:45', NULL, 19),
(21, 'lady', 'test21@gmail.com', '2020-11-24 05:22:45', NULL, 18);
 

Комментарии:

1. Какой конечный результат JSON вы ожидаете?

2. допустим, передайте user_id=2 и ограничьте отображение только 4 уровня для этого пользователя, тогда конечный json должен быть var datasource= {«name»:»beta»,»user_id»:2, «user_code»:null, «parent_id»:1, «email»: «test2@gmail.com»,»children»:[{«name»:»cicak»,»user_id»:3,»user_code»:null,»parent_id»:2,»email»:»test3@gmail.com»,»children»:[{«name»:»dorae»,»user_id»:4,»user_code»:null,»parent_id»:3,»email»:»test4@gmail.com»,»children»:[{«name»:»eng»,»user_id»:5,»user_code»:null,»parent_id»:4,»email»:»test5@gmail.com»,»children»:[{«name»:»Fanny»,»user_id»:6,»user_code»:null,»parent_id»:5,»email»:»test6@gmail.com»}]}]}]}]}

Ответ №1:

Вот класс, который я написал, который должен делать то, что вам нужно:

 <?php
class OrgTree {
    public $users;
    public function __construct($users) {
        $this->users = $users;
    }

    private function getUserById($userId) {
        return array_values(array_filter($this->users, function($user) use ($userId) {
            return $user['id'] === $userId;
        }))[0] ?? null;
    }

    private function getUsersByParentId($parentId) {
        return array_values(array_filter($this->users, function($user) use ($parentId) {
            return $user['parentid'] === $parentId;
        }));
    }
     

    private function buildTree($userDetails, $maxDepth, $currentDepth = 0) 
    {
        $info = [
            "name" => $userDetails['firstname'],
            "user_id" => $userDetails['id'],
            "user_code" => $userDetails['usercode'],
            "parent_id" => $userDetails['parentid'],
            "email" => $userDetails['email'],
        ];
        if ($maxDepth > $currentDepth) {
            $childUsers = $this->getUsersByParentId($userDetails['id']);
            $nextDepth =   $currentDepth;
            $info['children'] = array_map(function($childUser) use ($nextDepth, $maxDepth) {
                return $this->buildTree($childUser, $maxDepth, $nextDepth);
            }, $childUsers);
        }
        return $info;
    }
    
    public function createOrgTree($userId, $maxDepth = 4) {
        return $this->buildTree($this->getUserById($userId), $maxDepth);
    }
}
 

Вам просто нужно передать пользователей из вашей базы данных:

 public function viewOrgChart(Request $request) {
    $user_id = $request->user_id;
    $depth = 4; // get this from request if needed

    $user_details = $this->wpmlm_get_all_user_details_join();
           
    $orgTree = new OrgTree($user_details);
    $tree = $orgTree->createOrgTree($user_id, $depth);

    $treeJson = json_encode($tree);
}
 

Вы также можете изменить класс, чтобы извлекать пользователей из базы данных по мере необходимости, вместо того, чтобы получать полный список в начале, как если бы у вас было огромное количество пользователей, такой подход был бы более эффективным, особенно при низких $depth значениях.

Для этого вам просто нужно удалить:

 public $users;
public function __construct($users) {
    $this->users;
}
 

и изменять getUserById и getUsersByParentId выполнять SQL-запросы и возвращать данные.

Комментарии:

1. привет, Мэтт, он выдает ошибку при возврате $user[‘id’] === $userId; Ошибка Не может использовать объект типа stdClass в качестве массива

2. Как вы получаете пользователей из БД? Похоже, вам нужно получить их как ассоциативный массив, а не объект

3. я использую laravel, они больше не поддерживают db FETCH_ASSOC, попробовал toArray(); но не работает. Сейчас я думаю о том, чтобы вручную восстановить, подходит ли это? foreach($results как $row) { $id = $row->id; $firstname = $row->firstname; $parentid = $row->parentid; $email = $row->email; $resultsxx[$id] = array(‘id’ => $идентификатор, ‘firstname’ => $firstname, ‘parentid’ => $parentid, ’email’ => $email ); }

4. Извините, я этого не понял, в вашем исходном примере используется доступ к массиву, поэтому я предположил, что это сработает. Вероятно, самый простой способ — изменить любые обращения к массиву в классе OrgTree с доступа к массиву, например, $user['firstname'] на доступ к объекту, например, $user->firstname

5.я поставил точку останова в __construct и получил эту структуру, pasteboard.co/JC9HIPG.png pasteboard.co/JC9HTuS.png