login  Naam:   Wachtwoord: 
Registreer je!
 Scripts:

Scripts > PHP > Navigatie systemen > Array2Nested v1.1.2

Array2Nested v1.1.2

Auteur: Roland - 27 december 2008 - 15:18 - Gekeurd door: Stijn - Hits: 2886 - Aantal punten: 4.50 (4 stemmen)




Ik las op PHPFreakz onlangs een topic waarin gevraagd werd hoe je data kan omzetten naar geneste data zodat je bv categorieën eindeloos kunt nesten in een forum, webshop etc.

Met dat idee en te veel vrije tijd tijdens de kerstdagen ben ik begonnen aan een class/module om het hele recursieve nesten ietwat te automatiseren.

Het nesten gebeurd zoals genoemd recursief a.d.h.v. een id/parent combinatie binnen een array.

Ondanks dat ik dat ik tijdens het maken van deze class pas op de hoogte werd gesteld van de left/right methode (http://www.sitepoint.com/article/hierarchical-data-database/2/), heb ik toch besloten om aan de recursieve methode vast te houden. Dit maakt het werken met objecten van _directe_ kinderen veel eenvoudiger. Daarnaast is het in de praktijk onwaarschijnlijk dat je tot diepte 1000+ gaat nesten.

Uiteraard mogen mensen zich melden die zich interesseren voor het implementeren van een layer optie in deze module. Iets als;
abstract Nested_Nest, Nested_Nest_Recursive, Nested_Nest_LeftRight
Het is uiteraard wel essentieel dat de recursieve werking van de module blijft werken. (Nested::getRoot()->firstChild()->firstChild()[->...])

Getest op:
PHP 5.2.4

Versie:
1.1.2

Bestanden:
Nested_Example.php, Nested.php, Nested_Child.php, Nested_Exception.php

Output voorbeeld:
http://www.deva...xample.png

Geplaatst op:
PHPFreakz, PHPHulp, Sitemasters

Changelog:
v1.1.2 [30-12-08]
* Nested::sortAll toegevoegd om de kinderen van elk item tegelijk hetzelfde te sorteren
* Nested_Child::getIndex() toegevoegd
* Nested_Child::randomChild/childById/childByIndex toegevoegd
* Nested_Child::isChild/getChild verwijderd
* Nested_Output verwijderd; HTML implementatie is niet te globaliseren - opnieuw voorbeeld toegevoegd voor geneste HTML list

v1.1.1 [28-12-08]
* Nested::getFlat() aangepast; Nested::getFlat() -> array(id => Nested_Child); Nested::getFlat(true) -> array(id => array(row_data))
* Nested::MAX_DEPTH ident toegevoegd; elk kind lager als n bepaalde diepte wordt kind van de vorige parent (0 = unlimited)
* Performance update: diepte wordt niet meer berekend door de 'ancestors' te tellen maar direct bij het nesten
* Nested_Child::isMaxDepth() toegevoegd om te controleren of een kind zich in de maximale diepte bevindt
* Kinderen sorteren gebeurd nu via het 'natural ordering algorithm', zodat 'Titel 10' na 'Titel 9' komt (ASC)

v1.1.0 [27-12-08]
* Nested_Child::FLAG_LIMIT toegvoegd om het aantal kinderen via Nested_Child::getChildren() te limieten
* Nested::__toString/toHtml, Nested_Child::__toString/toHtml en Nested_Output toegvoegd (zie voorbeeld)

v1.0.0 [26-12-08]
* dump

Code:
Nested_Example.php
  1. <?php
  2. require 'Nested.php';
  3.  
  4. // forum data whatever
  5. $data = array(
  6. array('myID' => 1, 'myParent' => 0, 'title' => 'Titel #1'), // level 1
  7. array('myID' => 2, 'myParent' => 1, 'title' => 'Titel #2'), // level 1.1
  8. array('myID' => 3, 'myParent' => 1, 'title' => 'Titel #3'), // level 1.2
  9. array('myID' => 4, 'myParent' => 3, 'title' => 'Titel #4'), // level 1.2.1
  10. array('myID' => 5, 'myParent' => 3, 'title' => 'Titel #5'), // level 1.2.2
  11. array('myID' => 6, 'myParent' => 3, 'title' => 'Titel #6'), // level 1.2.3
  12. array('myID' => 7, 'myParent' => 6, 'title' => 'Titel #7'), // level 1.2.3.1
  13. array('myID' => 8, 'myParent' => 7, 'title' => 'Titel #8'), // level 1.2.3.1.1
  14. array('myID' => 9, 'myParent' => 8, 'title' => 'Titel #9'), // level 1.2.3.1.1.1
  15. array('myID' => 10, 'myParent' => 9, 'title' => 'Titel #10') // level 1.2.3.1.1.1.1
  16. );
  17.  
  18. try {
  19. // object aanmaken, maar nog niet nesten
  20. $nested = new Nested($data, false);
  21.  
  22. // identificatie steutels instellen
  23. $nested->setIdent(array(
  24. Nested::KEY_ID => 'myID',
  25. Nested::KEY_PARENT => 'myParent'
  26. ));
  27.  
  28. // nu nesten!
  29. $nested->nest();
  30.  
  31. // tijdsduur
  32. echo 'Nesten duurde ' . number_format($nested->getParsetime(), 6);
  33.  
  34. // haal alle items op
  35. $flat_object = $nested->getFlat(); // array(id => Nested_Child)
  36. $flat_array = $nested->getFlat(true); // array(id => array('myId' => .., [..]))
  37.  
  38. // alles sorteren, zet kinderen met meeste sub kinderen bovenaan
  39. $nested->sortAll(Nested_Child::SORT_CHILDNUM, Nested_Child::SORT_DESC);
  40.  
  41. // bouw een simpele app
  42. $child = isset($_GET['id']) && $nested->isValidId($_GET['id'])
  43. ? $nested->getById($_GET['id'])
  44. : $nested->getRoot();
  45. echo '<h1>' . ($child->isRoot() ? 'Root' : $child->title) . '</h1>';
  46. if(!$child->isRoot()) {
  47. $path = array();
  48. foreach($child->getAncestors(true/*reverse*/) as $ancestor) {
  49. $path[] = ($ancestor->isRoot() ? 'Root' : $ancestor->title);
  50. }
  51. echo '<strong>Pad:</strong> ' . implode(' &raquo; ', $path) . '<br />';
  52. }
  53. echo '<strong>Diepte:</strong> ' . $child->getDepth() . '<br />';
  54. if(!$child->hasChildren()) {
  55. echo 'Dit kind heeft geen sub kinderen.';
  56. } else {
  57. echo '<ul>';
  58. // kinderen zijn al gesorteerd volgens SORT_CHILDNUM, SORT_DESC
  59. foreach($child->getChildren() as $subchild) {
  60. echo '<li><a href="?id=' . $subchild->getId() . '">' . $subchild->title . '</a> (' . $subchild->numChildren() . ')</li>';
  61. }
  62. echo '</ul>';
  63. }
  64.  
  65. // een complete structuur tonen
  66. echo '<h1>Boom structuur</h1>';
  67. function output_nested($child, $current_id) {
  68. $html = '<ul>';
  69. if($child->hasChildren()) {
  70. foreach($child->getChildren() as $subchild) {
  71. $title = $subchild->title;
  72. $title = $current_id == $subchild->getId()
  73. ? '<strong>' . $title . '</strong>'
  74. : '<a href="?id=' . $subchild->getId() . '">' . $title . '</a>';
  75. $title .= ' (' . $subchild->numChildren() . ')';
  76. $html .= '<li>' . $title . output_nested($subchild, $current_id) . '</li>';
  77. }
  78. }
  79. return $html . '</ul>';
  80. }
  81. echo output_nested(
  82. $nested->getRoot(), // bij de root beginnen
  83. $child->getId() // huidige id opvragen
  84. );
  85.  
  86. // getChildren & Flags
  87. if($child->hasChildren()) {
  88. $flags = array();
  89.  
  90. // exclude specifieke id's
  91. $flags[Nested_Child::FLAG_EXCLUDE] = 1; // alle kinderen behalve id 1
  92. // variant: $flags[Nested_Child::FLAG_EXCLUDE] = array(1,2); // alle kinderen behalve id 1 en 2
  93.  
  94. // _OF_: include specifieke id's
  95. $flags[Nested_Child::FLAG_INCLUDE] = 1; // alleen kind ophalen als id 1 is
  96. // variant: $flags[Nested_Child::FLAG_INCLUDE] = array(2,3); // alleen kind ophalen als id 1 of 2 is
  97.  
  98. // return enkel kind eigenschap/methode
  99. $flags[Nested_Child::FLAG_RETURN] = 'title'; // eigenschap kind returnen
  100. // variant: $flags[Nested_Child::FLAG_RETURN] = 'numChildren'; // methode kind returnen
  101.  
  102. // gebruikers callback toepassen
  103. function test($child) {
  104. // waarde $child afhankelink van return flag, standaard Nested_Child object
  105. // in dit voorbeeld bevat $child dus de titel
  106. return 'De titel: ' . $child;
  107. }
  108. $flags[Nested_Child::FLAG_CALLBACK] = 'test'; // gebruikersfunctie toepassen
  109. // variant: $flags[Nested_Child::FLAG_CALLBACK] = array($object, 'methode'); // methode toepassen
  110.  
  111. // kinderen door elkaar schudden
  112. $flags[Nested_Child::FLAG_SHUFFLE] = true; // zet de kinderen in random volgorde
  113.  
  114. // kinderen limieten
  115. $flags[Nested_Child::FLAG_LIMIT] = 2; // eerste 2 kinderen teruggeven
  116.  
  117. // flags toepassen
  118. foreach($child->getChildren($flags) as $child_or_return_or_callback_value) {
  119. // where the REAL magic happens ;]
  120. }
  121. }
  122.  
  123. // firstChild, lastChild, randomChild, childByIndex, childById
  124. if(false !== ($first_child = $child->firstChild())) {
  125. echo 'Eerste kind: ' . $first_child->title . '<br />';
  126. }
  127. if(false !== ($last_child = $child->lastChild())) {
  128. echo 'Laatste kind: ' . $last_child->title . '<br />';
  129. }
  130. if(false !== ($random_child = $child->randomChild())) {
  131. echo 'Random kind: ' . $random_child->title . '<br />';
  132. }
  133. if(false !== ($second_child = $child->childByIndex(1))) {
  134. echo 'Tweede kind: ' . $second_child->title . '<br />';
  135. }
  136. if(false !== ($id_2_child = $child->childById(2))) {
  137. echo 'ID #2 kind: ' . $id_2_child->title . '<br />';
  138. }
  139.  
  140. // sortAll, sortChildren
  141. $nested->sortAll('title'/*, Nested_Child::SORT_DESC*/); // alle kinderen van elk item in de geneste data zijn gesorteerd op titel
  142. $child->sortChildren('title'/*, Nested_Child::SORT_DESC*/); // de kinderen van het huidige item zijn gesorteerd volgens titel
  143. // ga verder met Nested_Child::getChildren e.d., de ingestelde volgorde blijft van toepassing
  144. } catch(Nested_Exception $e) {
  145. echo $e->getMessage() . ' (' . $e->getFile() . ', line ' . $e->getLine() . ')';
  146. }
  147. ?>


Nested.php
  1. <?php
  2. require 'Nested_Exception.php';
  3. require 'Nested_Child.php';
  4.  
  5. /**
  6.  * Nested
  7.  *
  8.  * @version 1.1.2
  9.  * @author Roland Franssen
  10.  * @email franssen[dot]roland[at]gmail[dot]com
  11.  */
  12.  
  13. /**
  14.  * Nested
  15.  *
  16.  * Object die een geneste set vasthoudt van alle kinderen
  17.  *
  18.  * @package Nested
  19.  */
  20. class Nested {
  21. /**
  22. * Ident codes
  23. *
  24. * @define KEY_ID Identificeer ID sleutel
  25. * @define KEY_PARENT Identificeer moeder sleutel
  26. * @define ROOT_ID Identificeer root ID
  27. * @define MAX_DEPTH Identificeer maximale diepte
  28. */
  29. const KEY_ID = 0;
  30. const KEY_PARENT = 1;
  31. const ROOT_ID = 2;
  32. const MAX_DEPTH = 3;
  33.  
  34. /**
  35. * Geneste kinderen
  36. *
  37. * @var null|array
  38. */
  39. static protected $rows;
  40. /**
  41. * Identificatie waardes volgens code
  42. *
  43. * @var array
  44. */
  45. static protected $idents = array(
  46. self::KEY_ID => 'id',
  47. self::KEY_PARENT => 'parent',
  48. self::ROOT_ID => 0,
  49. self::MAX_DEPTH => 0
  50. );
  51.  
  52. /**
  53. * Rauwe input data
  54. *
  55. * @var null|array
  56. */
  57. private $_data;
  58. /**
  59. * Parse tijd van het nesten
  60. *
  61. * @var null|float
  62. */
  63. private $_time;
  64.  
  65. /**
  66. * Initialiseer een nieuwe geneste set
  67. *
  68. * @param $data array Rauwe data volgends id/parent combinatie
  69. * @param $auto_nest boolean De data direct nesten
  70. * @return instance
  71. */
  72. public function __construct(array $data, $auto_nest = true) {
  73. $this->_data = $data;
  74. if($auto_nest === true) {
  75. $this->nest();
  76. }
  77. }
  78.  
  79. /**
  80. * Stel identificatie in
  81. *
  82. * @param $ident mixed Waarde van identificatie(s)
  83. * @param $type {@link Nested::KEY_ID}
  84. * {@link Nested::KEY_PARENT}
  85. * {@link Nested::ROOT_ID}
  86. * @return Nested
  87. */
  88. public function setIdent($ident, $type = null) {
  89. $ident = !is_array($ident) ? array($type => $ident) : $ident;
  90. foreach($ident as $type => &$value) {
  91. if(!isset(self::$idents[$type])) {
  92. throw new Nested_Exception('Invalid ident type. (' . (string) $type . ')');
  93. }
  94. if(ctype_digit($value)) {
  95. $value = (int) $value;
  96. }
  97. self::$idents[$type] = $value;
  98. }
  99. unset($type, $value);
  100. return $this;
  101. }
  102.  
  103. /**
  104. * Verkrijg de parse tijd van het nesten
  105. *
  106. * @return float
  107. */
  108. public function getParsetime() {
  109. $this->_isNested();
  110. return $this->_time;
  111. }
  112.  
  113. /**
  114. * Verkrijg een platte structuur van de data array(id => Nested_Child)
  115. *
  116. * @return array
  117. */
  118. public function getFlat($to_array = false) {
  119. $this->_isNested();
  120. $rows = self::$rows;
  121. unset($rows[self::$idents[self::ROOT_ID]]);
  122. if($to_array) {
  123. foreach($rows as &$row) {
  124. $row = $row->getRow();
  125. }
  126. unset($row);
  127. }
  128. return $rows;
  129. }
  130.  
  131. /**
  132. * Controleer of het ID bestaat in de structuur
  133. *
  134. * @param $id mixed ID van kind
  135. * @return boolean
  136. */
  137. public function isValidId($id) {
  138. $this->_isNested();
  139. return $id != self::$idents[self::ROOT_ID] && isset(self::$rows[$id]);
  140. }
  141.  
  142. /**
  143. * Verkrijg een kind middels ID
  144. * getById(id[, id, [..]]) | getById(array(id[, id, [..]]))
  145. *
  146. * @return false|array|Nested_Child
  147. */
  148. public function getById() {
  149. $this->_isNested();
  150. $args = func_get_args();
  151. if(!isset($args[0])) {
  152. return false;
  153. }
  154. if(!is_array($args[0]) && !isset($args[1])) {
  155. return $this->_getById($args[0]);
  156. }
  157. return array_map(array($this, '_getById'), (is_array($args[0]) ? $args[0] : $args));
  158. }
  159.  
  160. /**
  161. * Verkrijg het root kind
  162. *
  163. * @param $children boolean Return direct de kinderen
  164. * @return false|array|Nested_Child
  165. */
  166. public function getRoot($children = false) {
  167. $this->_isNested();
  168. $root_id = self::$idents[self::ROOT_ID];
  169. if(isset(self::$rows[$root_id])) {
  170. return ($root = self::$rows[$root_id]) && $children !== false ? $root->getChildren() : $root;
  171. }
  172. return false;
  173. }
  174.  
  175. /**
  176. * Sorteer kinderen van elk item
  177. *
  178. * @param $key mixed Sleutel uit originele data of {@link Nested_Child::SORT_CHILDNUM}
  179. * @param $mode {@link Nested_Child::SORT_ASC}
  180. * {@link Nested_Child::SORT_DESC}
  181. * @return Nested
  182. */
  183. public function sortAll($key, $mode = Nested_Child::SORT_ASC) {
  184. $this->_isNested();
  185. foreach(self::$rows as $child) {
  186. $child->sortChildren($key, $mode);
  187. }
  188. return $this;
  189. }
  190.  
  191. /**
  192. * Maak van rauwe data een geneste set
  193. *
  194. * @return Nested
  195. */
  196. public function nest() {
  197. $this->_time = microtime(false);
  198. self::$rows = array();
  199. list($key_id, $key_parent, $root_id, $max_depth) = array_values(self::$idents);
  200. foreach($this->_data as $index => &$row) {
  201. if(!is_array($row)) {
  202. throw new Nested_Exception('Array index (' . $index . ') is not an array.');
  203. }
  204. if(!isset($row[$key_id]) || !isset($row[$key_parent])) {
  205. throw new Nested_Exception('Key ' . $key_id . ' or ' . $key_parent . ' does not exists in index ' . $index . '.');
  206. }
  207. $id = &$row[$key_id];
  208. $parent = &$row[$key_parent];
  209. if(ctype_digit($id)) {
  210. $id = (int) $id;
  211. }
  212. if(ctype_digit($parent)) {
  213. $parent = (int) $parent;
  214. }
  215. if($id === $parent) {
  216. throw new Nested_Exception('Key ' . $key_id . ' cannot be the same as ' . $key_parent . ' in index ' . $index . '.');
  217. }
  218. if(isset(self::$rows[$id])) {
  219. throw new Nested_Exception('Key ' . $key_id . ' should be unique in index ' . $index . '.');
  220. }
  221. if(!isset(self::$rows[$parent])) {
  222. self::$rows[$parent] = new Nested_Child($parent);
  223. }
  224. $depth = (self::$rows[$parent]->getDepth() + 1);
  225. if(is_int($max_depth) && $max_depth > 0 && $depth > $max_depth) {
  226. $depth = $max_depth;
  227. $parent = self::$rows[$parent]->getParent()->getId();
  228. }
  229. self::$rows[$id] = new Nested_Child($id, $parent, $row);
  230. self::$rows[$id]->setDepth($depth);
  231. self::$rows[$parent]->addChild($id, self::$rows[$id]);
  232. }
  233. if(!isset(self::$rows[$root_id])) {
  234. throw new Nested_Exception('Root key missing. (' . $root_id . ').');
  235. }
  236. $this->_time = (microtime(false) - $this->_time);
  237. unset($this->_data, $index, $row, $id, $parent, $depth);
  238. return $this;
  239. }
  240.  
  241. /**
  242. * Methode voor ID transformatie
  243. *
  244. * @param $id mixed ID van kind
  245. * @return false|Nested_Child
  246. */
  247. private function _getById($id) {
  248. if($id instanceof Nested_Child) {
  249. return $id;
  250. }
  251. if($id == self::$idents[self::ROOT_ID]) {
  252. return false;
  253. }
  254. $rows = self::$rows;
  255. return isset($rows[$id]) ? $rows[$id] : false;
  256. }
  257.  
  258. /**
  259. * Controleer of rauwe data genest is
  260. *
  261. * @return boolean
  262. */
  263. private function _isNested() {
  264. if(!is_array(self::$rows)) {
  265. throw new Nested_Exception('Data is not nested.');
  266. }
  267. return true;
  268. }
  269. }
  270. ?>


Nested_Child.php
  1. <?php
  2. /**
  3.  * Nested_Child
  4.  *
  5.  * Uniek kind object uit de geneste set
  6.  *
  7.  * @package Nested
  8.  */
  9. class Nested_Child extends Nested {
  10. /**
  11. * Sorteer codes voor sortChildren
  12. *
  13. * @define SORT_ASC Sorteer kinderen aflopend
  14. * @define SORT_DESC Sorteer kinderen oplopend
  15. * @define SORT_CHILDNUM Sorteer kinderen op aantal sub kinderen
  16. */
  17. const SORT_ASC = 10;
  18. const SORT_DESC = 11;
  19. const SORT_CHILDNUM = 12;
  20.  
  21. /**
  22. * Flag codes voor getChildren
  23. *
  24. * @define FLAG_EXCLUDE Filter kind uit resultaat
  25. * @define FLAG_INCLUDE Filter kind in resultaat
  26. * @define FLAG_RETURN Filter kind eigenschap/methode in resultaat
  27. * @define FLAG_CALLBACK Filter user callback in resultaat
  28. * @define FLAG_SHUFFLE Verkrijg kinderen in random volgorde
  29. * @define FLAG_LIMIT Limiet het aantal kinderen
  30. */
  31. const FLAG_EXCLUDE = 30;
  32. const FLAG_INCLUDE = 31;
  33. const FLAG_RETURN = 32;
  34. const FLAG_CALLBACK = 33;
  35. const FLAG_SHUFFLE = 34;
  36. const FLAG_LIMIT = 35;
  37.  
  38. /**
  39. * Uniek ID van kind
  40. *
  41. * @var mixed
  42. */
  43. private $_id;
  44. /**
  45. * Moeder ID van kind
  46. *
  47. * @var mixed
  48. */
  49. private $_parent;
  50. /**
  51. * Originele data van kind
  52. *
  53. * @var null|false|array
  54. */
  55. private $_row;
  56. /**
  57. * Sorteer sleutel voor data
  58. *
  59. * @var mixed
  60. */
  61. private $_sort;
  62. /**
  63. * Aantal sub kinderen van kind
  64. *
  65. * @var integer
  66. */
  67. private $_count = 0;
  68. /**
  69. * Sub kinderen van kind (id => kind)
  70. *
  71. * @var array
  72. */
  73. private $_children = array();
  74. /**
  75. * Sub kinderen volgens index (index => id)
  76. *
  77. * @var array
  78. */
  79. private $_indexed = array();
  80. /**
  81. * De ouders van het kind tot aan de root
  82. *
  83. * @var null|array
  84. */
  85. private $_ancestors;
  86. /**
  87. * De naaste kinderen exclusief het huidige kind
  88. *
  89. * @var null|array
  90. */
  91. private $_siblings;
  92. /**
  93. * Diepte van het kind t.o.v. de root
  94. *
  95. * @var integer
  96. */
  97. private $_depth = -1;
  98.  
  99. /**
  100. * Initialiseer het kind met basis eigenschappen waarbij de root slechts een ID heeft
  101. *
  102. * @param $id mixed Uniek ID
  103. * @param $parent mixed Moeder ID
  104. * @param $row mixed Originele data
  105. * @return instance
  106. */
  107. public final function __construct($id, $parent = false, $row = false) {
  108. $this->_id = $id;
  109. $this->_parent = $parent;
  110. $this->_row = $row;
  111. }
  112.  
  113. /**
  114. * Wanneer property van kind gevraagd wordt, zoeken in originele data
  115. * Nested_Child->title => Nested_Child->$_row[title]
  116. *
  117. * @param $key string Eigenschap die gevraagd wordt
  118. * @return mixed
  119. */
  120. public function __get($key) {
  121. if($this->_row === false || !isset($this->_row[$key])) {
  122. throw new Nested_Exception('Invalid property (' . (string) $key . ') in ' . __CLASS__ . '.');
  123. }
  124. return $this->_row[$key];
  125. }
  126.  
  127. /**
  128. * Voeg sub kind toe aan het huidige kind
  129. *
  130. * @param $id mixed Uniek ID
  131. * @param $child Nested_Child Object van kind middels referentie
  132. * @return Nested_Child
  133. */
  134. protected function addChild($id, Nested_Child &$child) {
  135. $this->_children[$this->_count] = $child;
  136. $this->_indexed[$id] = $this->_count;
  137. ++$this->_count;
  138. return $this;
  139. }
  140.  
  141. /**
  142. * Stel diepte in van kind
  143. *
  144. * @param integer $depth Diepte van kind
  145. * @return Nested_Child
  146. */
  147. protected function setDepth($depth) {
  148. $this->_depth = (int) $depth;
  149. return $this;
  150. }
  151.  
  152. /**
  153. * Verkrijg diepte van kind t.o.v. de root waarbij de root zelf -1 is (eerste kind dus 0)
  154. *
  155. * @return integer
  156. */
  157. public function getDepth() {
  158. return $this->_depth;
  159. }
  160.  
  161. /**
  162. * Controleer of kind zich in de maximale diepte bevindt
  163. *
  164. * @return boolean
  165. */
  166. public function isMaxDepth() {
  167. $depth = $this->getDepth();
  168. return $depth > 0 && $depth === (int) parent::$idents[Nested::MAX_DEPTH];
  169. }
  170.  
  171. /**
  172. * Controleer of het huidige kind de root is
  173. *
  174. * @return boolean
  175. */
  176. public function isRoot() {
  177. return $this->getId() === parent::$idents[Nested::ROOT_ID];
  178. }
  179.  
  180. /**
  181. * Verkrijg ID van kind
  182. *
  183. * @return mixed
  184. */
  185. public function getId() {
  186. return $this->_id;
  187. }
  188.  
  189. /**
  190. * Verkrijg moeder ID van kind
  191. *
  192. * @return mixed
  193. */
  194. public function getParentId() {
  195. return $this->_parent;
  196. }
  197.  
  198. /**
  199. * verkrijg index van kind t.o.v. van overige kinderen
  200. *
  201. * @return false|integer
  202. */
  203. public function getIndex() {
  204. if(false !== ($parent = $this->getParent())) {
  205. return $parent->_indexed[$this->getId()];
  206. }
  207. return false;
  208. }
  209.  
  210. /**
  211. * Verkrijg originele data van kind
  212. *
  213. * @return false|array
  214. */
  215. public function getRow() {
  216. return $this->_row;
  217. }
  218.  
  219. /**
  220. * Verkrijg moeder object van kind
  221. *
  222. * @return false|Nested_Child
  223. */
  224. public function getParent() {
  225. if($this->isRoot()) {
  226. return false;
  227. }
  228. if($this->getParentId() === parent::$idents[Nested::ROOT_ID]) {
  229. return parent::getRoot();
  230. }
  231. return parent::getById($this->getParentId());
  232. }
  233.  
  234. /**
  235. * Controleer of het kind sub kinderen heeft
  236. *
  237. * @return boolean
  238. */
  239. public function hasChildren() {
  240. return $this->_count > 0;
  241. }
  242.  
  243. /**
  244. * Verkrijg het aantal sub kinderen
  245. *
  246. * @return integer
  247. */
  248. public function numChildren() {
  249. return $this->_count;
  250. }
  251.  
  252. /**
  253. * Verkrijg een kind middels ID
  254. *
  255. * @param $id mixed Uniek ID van kind
  256. * @return false|Nested_Child
  257. */
  258. public function childById($id) {
  259. return isset($this->_indexed[$id]) ? $this->_children[$this->_indexed[$id]] : false;
  260. }
  261.  
  262. /**
  263. * Verkrijg een kind middels index
  264. *
  265. * @param $index integer Index van kind
  266. * @return false|Nested_Child
  267. */
  268. public function childByIndex($index) {
  269. return isset($this->_children[$index]) ? $this->_children[$index] : false;
  270. }
  271.  
  272. /**
  273. * Verkrijg het eerste sub kind
  274. *
  275. * @return false|Nested_Child
  276. */
  277. public function firstChild() {
  278. return $this->childByIndex(0);
  279. }
  280.  
  281. /**
  282. * Verkrijg het laatste sub kind
  283. *
  284. * @return false|Nested_Child
  285. */
  286. public function lastChild() {
  287. return $this->childByIndex($this->numChildren() - 1);
  288. }
  289.  
  290. /**
  291. * Verkrijg random kind
  292. *
  293. * @return false|Nested_Child
  294. */
  295. public function randomChild() {
  296. return $this->hasChildren()
  297. ? ($this->numChildren() === 1
  298. ? $this->firstChild()
  299. : $this->childByIndex(mt_rand(0, ($this->numChildren() - 1))))
  300. : false;
  301. }
  302.  
  303. /**
  304. * Verkrijg sub kinderen
  305. *
  306. * @param $flags array Array met flags volgens array({@link Nested_Child::FLAG_*} => waarde)
  307. * @return array
  308. */
  309. public function getChildren($flags = null) {
  310. if(!$this->hasChildren()) {
  311. return array();
  312. }
  313. if(!isset($flags)) {
  314. return $this->_children;
  315. }
  316. for($i = 0, $result = array(); isset($this->_children[$i]); ++$i) {
  317. $child = $this->_children[$i];
  318. $id = $child->getId();
  319. if(isset($flags[self::FLAG_INCLUDE])) {
  320. $flag = $flags[self::FLAG_INCLUDE];
  321. if((!is_array($flag) && $id != $flag) || (is_array($flag) && !in_array($id, $flag))) {
  322. continue;
  323. }
  324. } elseif(isset($flags[self::FLAG_EXCLUDE])) {
  325. $flag = $flags[self::FLAG_EXCLUDE];
  326. if((!is_array($flag) && $id == $flag) || (is_array($flag) && in_array($id, $flag))) {
  327. continue;
  328. }
  329. }
  330. $return = $child;
  331. if(isset($flags[self::FLAG_RETURN])) {
  332. $method = strtolower(trim((string) $flags[self::FLAG_RETURN]));
  333. if($method <> '') {
  334. if(!method_exists($child, $method)) {
  335. $return = $child->{$method};
  336. } else {
  337. switch($method) {
  338. case 'getid': case 'getparentid': case 'getindex':
  339. case 'getdepth': case 'getrow': case 'getparent':
  340. case 'firstchild': case 'lastchild': case 'randomchild':
  341. case 'haschildren': case 'numchildren':
  342. $return = call_user_func(array($child, $method));
  343. break;
  344. }
  345. }
  346. }
  347. }
  348. if(isset($flags[self::FLAG_CALLBACK]) && is_callable($flags[self::FLAG_CALLBACK])) {
  349. $return = call_user_func($flags[self::FLAG_CALLBACK], $return);
  350. }
  351. $result[] = $return;
  352. }
  353. unset($return);
  354. $count = count($result);
  355. if(isset($flags[self::FLAG_SHUFFLE]) && $count > 1) {
  356. shuffle($result);
  357. }
  358. if(isset($flags[self::FLAG_LIMIT])) {
  359. $limit = $flags[self::FLAG_LIMIT];
  360. if(is_int($limit) && $limit < $count) {
  361. $result = array_slice($result, 0, $limit);
  362. }
  363. }
  364. return $result;
  365. }
  366.  
  367. /**
  368. * Sorteer kinderen
  369. *
  370. * @param $key mixed Sleutel uit originele data of {@link Nested_Child::SORT_CHILDNUM}
  371. * @param $mode {@link Nested_Child::SORT_ASC}
  372. * {@link Nested_Child::SORT_DESC}
  373. * @return Nested_Child
  374. */
  375. public function sortChildren($key, $mode = self::SORT_ASC) {
  376. if($this->hasChildren()) {
  377. $this->_sort = $key;
  378. usort($this->_children, array($this, '_sortCmp'));
  379. $this->_resetIndexation();
  380. for($i = 0; isset($this->_children[$i]); $this->_indexed[$this->_children[$i]->getId()] = $i, ++$i);
  381. }
  382. if($mode === self::SORT_DESC) {
  383. $this->_children = array_reverse($this->_children);
  384. $this->_resetIndexation();
  385. }
  386. return $this;
  387. }
  388.  
  389. /**
  390. * Verkrijg naaste kinderen behoudens het huidige kind
  391. *
  392. * @return false|array
  393. */
  394. public function getSiblings() {
  395. if($this->isRoot()) {
  396. return false;
  397. }
  398. if(!isset($this->_siblings)) {
  399. $this->_siblings = array();
  400. $parent = $this->getParent();
  401. if($parent->numChildren() > 1) {
  402. $this->_siblings = $parent->getChildren(array(
  403. self::FLAG_RETURN => 'getId',
  404. self::FLAG_EXCLUDE => $this->getId()
  405. ));
  406. }
  407. }
  408. if(!isset($this->_siblings[0])) {
  409. return array();
  410. }
  411. return parent::getById($this->_siblings);
  412. }
  413.  
  414. /**
  415. * Verkrijg ouders tot aan de root
  416. *
  417. * @param $reverse boolean Zet eerste ouder vooraan
  418. * @param $root boolean Voeg root toe
  419. * @return false|array
  420. */
  421. public function getAncestors($reverse = false, $root = true) {
  422. if($this->isRoot()) {
  423. return false;
  424. }
  425. if(!isset($this->_ancestors)) {
  426. for(
  427. $scope = $this, $this->_ancestors = array();
  428. ($parent = $scope->getParent()) !== false && !$parent->isRoot();
  429. $scope = $parent, $this->_ancestors[] = $scope->getId()
  430. );
  431. unset($scope, $parent);
  432. }
  433. if($root) {
  434. $this->_ancestors[] = parent::getRoot();
  435. }
  436. return parent::getById(($reverse ? array_reverse($this->_ancestors) : $this->_ancestors));
  437. }
  438.  
  439. /**
  440. * Vergelijkings methode voor sorteren
  441. *
  442. * @param $a Nested_Child
  443. * @param $b Nested_Child
  444. * @return integer
  445. */
  446. private function _sortCmp($a, $b) {
  447. $sort = $this->_sort;
  448. if($sort === self::SORT_CHILDNUM) {
  449. $a = $a->numChildren();
  450. $b = $b->numChildren();
  451. } else {
  452. $a = $a->{$sort};
  453. $b = $b->{$sort};
  454. }
  455. if(!is_int($a) || !is_int($b)) {
  456. return strnatcmp((string) $a, (string) $b);
  457. }
  458. return $a === $b ? 0 : ($a < $b ? -1 : 1);
  459. }
  460.  
  461. /**
  462. * Reset de id naar index relatie
  463. *
  464. * @return void
  465. */
  466. private function _resetIndexation() {
  467. for(
  468. $i = 0;
  469. isset($this->_children[$i]);
  470. $this->_indexed[$this->_children[$i]->getId()] = $i, ++$i
  471. );
  472. return;
  473. }
  474. }
  475. ?>


Nested_Exception.php
  1. <?php
  2. /**
  3.  * Nested_Exception
  4.  *
  5.  * Vang excepties op van deze module
  6.  *
  7.  * @package Nested
  8.  */
  9. class Nested_Exception extends Exception { }
  10. ?>
Download code! Download code (.txt)

 Bekijk een voorbeeld van dit script!
 Stemmen
Niet ingelogd.

 Reacties
Post een reactie
Lees de reacties (6)
© 2002-2024 Sitemasters.be - Regels - Laadtijd: 0.106s