Переглянути джерело

[tree] refactor children hierarchy function

gedi 13 роки тому
батько
коміт
ef09b50ea0
1 змінених файлів з 65 додано та 103 видалено
  1. 65 103
      lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php

+ 65 - 103
lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php

@@ -782,16 +782,23 @@ class NestedTreeRepository extends AbstractTreeRepository
     }
 
     /**
-    * Retrieves the nested array or the html output
-    *
-    * @throws \Gedmo\Exception\InvalidArgumentException
-    * @param object $node - from which node to start reordering the tree
-    * @param boolean $direct - true to take only direct children
-    * @param bool $html
-    * @param array|null $options
-    * @return array|string
-    */
-    public function childrenHierarchy($node = null, $direct = false, $html = false, array $options = null)
+     * Retrieves the nested array or the decorated output.
+     * Uses @options to handle decorations
+     *
+     * @throws \Gedmo\Exception\InvalidArgumentException
+     * @param object $node - from which node to start reordering the tree
+     * @param boolean $direct - true to take only direct children
+     * @param array $options :
+     *     decorate: boolean (true) - retrieves tree as UL->LI tree
+     *     nodeDecorator: Closure (null) - uses $node as argument and returns decorated item as string
+     *     rootOpen: string ('<ul>') - branch start
+     *     rootClose: string ('</ul>') - branch close
+     *     childStart: string ('<li>') - start of node
+     *     childClose: string ('</li>') - close of node
+     *
+     * @return array|string
+     */
+    public function childrenHierarchy($node = null, $direct = false, array $options = array())
     {
         $meta = $this->getClassMetadata();
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
@@ -814,7 +821,7 @@ class NestedTreeRepository extends AbstractTreeRepository
             'ASC'
         )->getArrayResult();
 
-        return $this->buildTree($nodes, $html, $options);
+        return $this->buildTree($nodes, $options);
     }
 
     /**
@@ -971,126 +978,81 @@ class NestedTreeRepository extends AbstractTreeRepository
     /**
      * Builds the tree
      *
-     * @param array $nodes
-     * @param bool $html
-     * @param array|null $options
+     * @param array $nodes - array result of ORM query
+     * @param array $options
      * @return array|string
      */
-    private function buildTree(array $nodes, $html = false, array $options = null)
+    public function buildTree(array $nodes, array &$options = array())
     {
         //process the nested tree into a nested array
-        $config = $this->listener->getConfiguration($this->_em, $this->getClassMetadata()->name);
-        $nestedTree = $this->processTree($nodes, $config);
-
-        // If you don't want any html output it will return the nested array
-        if (!$html) {
-            return $nestedTree;
-        }
-
-        //Defines html decorators and opcional options
-        if (!empty($options['root'])) {
-            $root_open  = $options['root']['open'];
-            $root_close = $options['root']['close'];
-        } else {
-            $root_open  = "<ul> ";
-            $root_close = " </ul>";
-        }
-        if (!empty($options['child'])) {
-            $child_open  = $options['child']['open'];
-            $child_close = $options['child']['close'];
-        } else {
-            $child_open  = "<li> ";
-            $child_close = " </li>";
-        }
-        $representationField = empty($options['representationField']) ?
-            'title' :
-            $options['representationField']
-        ;
-        if (!$this->getClassMetadata()->hasField($representationField) && $html) {
-            throw new InvalidArgumentException("There must be a representation field specified");
-        }
-
-        $html_decorator = array(
-            'root'  => array('open' => $root_open,  'close' => $root_close),
-            'child' => array('open' => $child_open, 'close' => $child_close),
-            'represents' => $representationField
-        );
-
-        $html_output = $this->processHtmlTree($nestedTree, $html_decorator, $html_output = null);
-
-        return $html_output;
-    }
-
-    /**
-     * Creates the nested array
-     *
-     * @static
-     * @param array $nodes
-     * @return array
-     */
-    private static function processTree(array $nodes, array $config)
-    {
-        // Trees mapped
-        $trees = array();
+        $meta = $this->getClassMetadata();
+        $config = $this->listener->getConfiguration($this->_em, $meta->name);
+        $nestedTree = array();
         $l = 0;
 
         if (count($nodes) > 0) {
             // Node Stack. Used to help building the hierarchy
             $stack = array();
-
             foreach ($nodes as $child) {
                 $item = $child;
-
-                $item['children'] = array();
-
+                $item['__children'] = array();
                 // Number of stack items
                 $l = count($stack);
-
                 // Check if we're dealing with different levels
-                while($l > 0 && $stack[$l - 1][$config['level']] >= $item[$config['level']]) {
+                while($l-- && $stack[$l - 1][$config['level']] >= $item[$config['level']]) {
                     array_pop($stack);
-                    $l--;
                 }
-
                 // Stack is empty (we are inspecting the root)
                 if ($l == 0) {
                     // Assigning the root child
-                    $i = count($trees);
-                    $trees[$i] = $item;
-                    $stack[] = & $trees[$i];
+                    $i = count($nestedTree);
+                    $nestedTree[$i] = $item;
+                    $stack[] = &$nestedTree[$i];
                 } else {
                     // Add child to parent
-                    $i = count($stack[$l - 1]['children']);
-                    $stack[$l - 1]['children'][$i] = $item;
-                    $stack[] = & $stack[$l - 1]['children'][$i];
+                    $i = count($stack[$l - 1]['__children']);
+                    $stack[$l - 1]['__children'][$i] = $item;
+                    $stack[] = &$stack[$l - 1]['__children'][$i];
                 }
             }
         }
-        return $trees;
-    }
 
-    /**
-     * Creates the html output of the nested tree
-     *
-     * @param $parent_node
-     * @param $html_decorator
-     * @param $html_output
-     * @return string
-     */
-    private function processHtmlTree($parent_node, $html_decorator, $html_output)
-    {
-        if (is_array($parent_node)) {
-            $html_output .= $html_decorator['root']['open'];
-            foreach ($parent_node as $item) {
-                 $html_output .= $html_decorator['child']['open'] . $item[$html_decorator['represents']];
-                if (count($item['children']) > 0) {
-                    $html_output = $this->processHtmlTree($item['children'], $html_decorator, $html_output);
+        $default = array(
+            'decorate' => true,
+            'rootOpen' => '<ul>',
+            'rootClose' => '</ul>',
+            'childOpen' => '<li>',
+            'childClose' => '</li>',
+            'nodeDecorator' => function ($node) use ($meta) {
+                // override and change it, guessing which field to use
+                if ($meta->hasField('title')) {
+                    $field = 'title';
+                } else if ($meta->hasField('name')) {
+                    $field = 'name';
+                } else {
+                    throw new InvalidArgumentException("Cannot find any representation field");
                 }
-                $html_output .= $html_decorator['child']['close'];
+                return $node[$field];
             }
-            $html_output .= $html_decorator['root']['close'];
+        );
+        $options = array_merge($default, $options);
+        // If you don't want any html output it will return the nested array
+        if ($options['decorate'] || !count($nestedTree)) {
+            return $nestedTree;
         }
 
-        return $html_output;
+        $build = function($tree) use (&$build, &$options) {
+            $output = $options['rootOpen'];
+            foreach ($tree as $node) {
+                $output .= $options['childOpen'] . $options['nodeDecorator']($node);
+                if (count($node['__children']) > 0) {
+                    $output .= $build($node['__children']);
+                }
+                $output .= $options['childClose'];
+            }
+            return $output . $options['rootClose'];
+        };
+
+        return $build($nestedTree);
     }
 }