Nested Sets Klasse

B

Bgag

Morgen!

Ich habe bis vor kurzen bei Baumstrukturen in einer Datenbank immer mit dem Parentid-System gearbeitet, doch vor kurzem bin ich auch die Nested sets gestoßen.
Sie haben den Vorteil, das die Abfragen aus der Datenbank bis um das 70fache schneller sind, da man zur Abfrage keine rekursiv durchlaufenden Funktionen benötigt. Zudem kann man eine genaue Reihenfolge der Unterpunkte festlegen und relativ einfach abfragen. Da das ganze so praktisch ist und bei mir recht häufig eine Anwendung findet, habe ich beschlossen eine Nested Sets Klasse zu schreiben.

Für Nested Sets gilt im allgemeinem:
* Die Wurzel hat grundsätzlich 1 als LFT-Wert
* Die Sortierung der Knoten ergibt sich aus den LFT-Werten
* Die Anzahl der Nachfahren eines Knotens errechnet sich durch (RGT - LFT - 1) / 2.
* Entsprechend gilt für alle Knoten ohne Kinder: RGT - LFT = 1
* LFT und RGT aller Nachfahren liegen zwischen den LFT- und RGT-Werten der Vorfahren

Ich arbeite hier mit einer erweiterten Version der Nested Sets.

Die Struktur meiner zugehörigen MySQL Tabelle sieht wie folgt aus:
Code:
ID | root_id | level | lft | rgt | name
Der zugehörige SQL-Code so:
Code:
CREATE TABLE `tablename` (
  `ID` int(12) unsigned NOT NULL auto_increment COMMENT 'The unique ID of the section',
  `root_id` int(12) unsigned NOT NULL default '0',
  `level` int(12) unsigned NOT NULL default '0',
  `lft` int(12) unsigned NOT NULL default '0',
  `rgt` int(12) unsigned NOT NULL default '0',
  `name` varchar(50) default NULL COMMENT 'The section name',
  PRIMARY KEY  (`ID`)
);

Die vorläufige Struktur der Klasse sieht so aus:
PHP:
<?php
	/* Require the classes */
	require_once('includes/DbConnector.php');
	require_once('includes/Validator.php');
	
	/* The NestedSet class */
	class NestedSet extends DbConnector
	{
		
		/* VARIABLES */
		var $db;		
		
		/* GET SECTIONS */
		function getSections($root=null) {
		}
		
		/* GET SECTION */
		function getSection($id=NULL) {
		}
			
		/* GET PATH */
		function getPath($id=NULL) {
		}
	
		/* SET SECTION */
		function setSection() {
		}
	
		/* ADD SECTION */
		function addSection() {
		}
		
		/* DELETE SECTIONS */
		function deleteSection() {
		}
	
		/* MOVE SECTION */
		function moveSection() {
		}
		
	}
?>

Hat jemand Vorschläge oder anregungen, wie ich das ganze Ausbauen könnte? Oder ist jemand daran interessiert mir bei der Entwicklung dieser Klasse zu helfen, da er sie gerne auch verwenden möchte?

MfG, Andy
 
Zuletzt bearbeitet von einem Moderator:
Hallo,

habe mich mit dem Thema nicht direkt beschäftigt, kann daher also keinen persönlichen Rat geben. Da es ja derzeit noch um das "wie am besten" geht, habe ich mal zwei Klassen rausgesucht, die Nested Sets als Thema haben.
Umsetzung und Qualität kann ich nichts zu sagen, da mir das Thema zu weit weg ist, als das ich da eine Beurteilung fällen kann.

Die Klassen wären die hier:

http://www.phpclasses.org/browse/package/2547.html
http://www.phpclasses.org/browse/package/1374.html

Vielleicht hilft es dir.

Gruss
 
Abend!
Vielen dank die Seite kannte ich noch garnicht hilft mir ja vielleicht etwas weiter mit meiner Klasse. Schau es mir gleich mal an und versuche ein paar Ideen für mich zu gewinnen.
MfG, Andy
 
Hallo!

Lang lang ist's her. Ich habe mich vor ewigkeiten mal an diese Klasse gemacht und bekam auch hier aus dem Forum Unterstützung durch Mau. Leider ist das ganze erst durch mein Abi und später durch den Beginn meines Studiums etwas außer Sicht geraten. Heute bin ich mehr durch Zufall über diese Klasse gestolpert und habe mich etwas geärgert, weil sie noch nicht wirklich fertig ist und ich es nie geschafft habe sie fertig zu stellen. habe mich deshalb mal rangesetzt und ein paar Änderungen vorgenommen. Leider funktioniert die Klasse noch nicht wirklich. Ich würde mich darüber freuen, wenn sich einige diese Klasse mal anschauen und mir ein paar Tipps zur Verbesserung und Erweiterung geben könnten. Habe sowohl die Klasse als auch einen beispielhaften Aufruf und die Datenabnk-Struktur mal hochgeladen. Freue mich auf eure Anmerkungen und Anregungen. Für Fragen bin ich selbstverständlich auch offen.

MfG, Andy
 

Anhänge

Hmm,das ist lustig.
Irgendwann in den letzten 4 Wochen habe ich auch eine NestedSets implementierung gesucht und da ich keine gute gefunden habe, bzw mein Wissen über NestedSets weiterhin minimal war, habe ich selber etwas geschrieben.
Dabei bin ich auch auf diesen Thread bei der Recherche gestossen, hab meine eigenen Links angesehen und gedacht "Schade, warum kein komplettes Beispiel" :)

Nun, kommen wir zum eigentlichen Thema.
Ich habe deine Klasse bisher nur überflogen und kann deine Aussage, das die Klasse nicht so wirklich funktioniert so nicht bestätigen oder die Ursache nennen, warum sie nicht funktioniert. Eine genauere Beschreibung des Fehlers wäre gut.

Zum Aufbau und Qualität des Codes:
Es macht einen sauberen Eindruck, dazu gehören für mich sinnige Variablennamen, OOP Arbeiten mit Exceptions, gute Struktur und Dokumentation. Ich weiss, das ist keine Aussage die wirklich etwas über die funktionalität aussagt, aber es ist ein Anfang. Ich werde die Tage versuchen es zu testen und ggf. so deine Probleme erkennen.
Bei MoveNode ist es gut, das du schon eine Logikprüfung machst. ParentNodes können keine Childs ihrer Childs werden.
Das verschieben nach links und rechts fehlt noch, sowie das "hochleveln" eines Knoten. Das ist auch nicht ohne weiteres umzusetzen, bei mir fehlt es ebenfalls noch, da man sich ja erstmal platz machen muss und dabei nicht alle knoten lft und rgt Valus anpacken darf (meine Lösung wird über eine tmp spalte in der Tabelle sein).

Zum Ende hin aber noch etwas konkretes.
Du arbeitests mit FieldArrays die dann nach einem Implode den SQL String aufwerten. Ich finde so eine Lösung persönlich unpraktisch, da man nicht direkt sehen kann, welche Felder im SQL angepackt werden und man erst ein paar Zeilen oberhalb suchen muss, welche Felder hier "Imploded" werden. Das ist aber denke ich geschmackssache.
Ein anderer Punkt, den ich einbauen würde wäre "Lock Tables" und "Rollbacks". Sollte ein SQL Query in schief laufen und es ist nicht gerade der erste, versaust du dir deine NestedSets Struktur und Logik, sprich der Tree wird unbrauchbar und unerwartete Ergebnisse treten auf.
Da du schon mit PDO und Exceptions arbeitest, ist die Implementierung in deinen Code mit 2-3 Zeilen Code pro SQL Block erledigt (Stichworte: beginTransaction/commit/rollback).

Nun da ich, wie ich oben geschrieben habe, mich vor ein paar Wochen auch an das Thema gewagt habe, so kann ich direkt vergleiche ziehen zu meiner Lösung.
Ich brauchte wie gesagt eine gute Implementierung und war mit allem was ich gefunden habe nicht wirklich zufrieden. Viele Lösungen im Netz die man findet reichen von "Insert/Delete/Select" - Node/Trees aber dann hapert es. Oft wird nur die reine SQL Syntax erläutert und die Umsetzung in PHP fehlt oft (okay, man soll ja auch selber noch was tun).
Meine Lösung weicht da von vielen ab, zumindest ist meine Einschätzung so. Natürlich ist das Insert/Delete Node irgendwo identisch, allerdings beim auslesen habe ich einen anderen Weg eingeschlagen, den ich persönlich einfacher und eleganter finde und PDO weiter ausnutzt.
Dazu besteht meine NestedSets implemtierung nicht aus 1 Klasse, sondern ich hab mir eine 2. Hilfsklasse geschrieben, welche für jeden Knoten im Baum genutzt wird. Bevor ich weitere Erklärungen anfange: Bei riesen Bäumen mit hunderten von Knoten kann die Lösung langsam werden, da ich für jeden Knoten im Baum eine Instanz mein "NestSetsNode" Klasse erzeuge.
Das auslesen des Baums gestalten sich aber simpel. Ein normaler SQL String für das Selektieren des Baums (so wie deiner) und dann mit den folgenden Zeilen die komplette Struktur in Objekten gepackt.

PHP:
        $result = $this->db->query(/* SQL Select String*/);
        $result->setFetchMode(PDO::FETCH_CLASS, 'nestedSetsNode');

        $tmp = array();
        $rootNode = NULL;
        $smallLeftValue = PHP_INT_MAX;

        FOREACH($result AS $obj) {
            $nodeId = $obj->getNodeId();
            $parentId = $obj->getParentId();
            $tmp[$nodeId] = $obj;
            IF(array_key_exists($parentId, $tmp)) {
                $tmp[$parentId]->addChildNode($obj);
            }

            IF($obj->getLeftValue() < $smallLeftValue) {
                $smallLeftValue = $obj->getLeftValue();
                $rootNode = $obj;
            }
        }
        

        RETURN $rootNode->toHTMLList();
        // oder RETURN $roodNode->toArray();
// zur Verdeutlichung: die NestedSetsNode Klasse hat wiederrum eine HTMLList() und toArray() Methode, 
// die bis zum letzten Node aufgerufen wird und das Ergebnis immer zum ParentNode zurückgegeben wird
Mit einem einfache "echo" habe ich dann einen kompletten Pfad als HTML Liste oder wenn ich (warum ich das eigentlich haben wollte) per Javascript und JSON arbeite, reicht ein "echo json_encode($roodNode->toArray())".

Das UI (bei mir soll es in JS sein), sowie eine komplette umsetzung von "moveNode" fehlt bei mir noch, aber mit dem Ansatz bin ich bisher sehr zufrieden.
Wenn du Bock hast und ich die Zeit finde, kann ich die nächsten Tage oder kommende Woche mal ein Beispiel zusammenbauen.

Gruss
 
Zurück