Fa struktúra listázása MySQL-ből PHP-vel

Sziasztok!

Van egy MySQL táblám a következő struktúrával:

Id - Kategória azonosító
Name - Kategória neve
Parent - Melyik kategória alkategóriája(Egy másik kategória id-jét tartalmazza ez a mező)

Egy klasszikus problémát szeretnék megoldani, mégpedig egy fa struktúrát kilistáztatni ebből a táblából. Utána olvastam a dolognak és úgy vettem észre, hogy ez a táblaszerkezet nem igazán előnyös nagy adatmennyiségnél. Nekem csak kevés adattal kell dolgoznom, így ha megoldható a probléma így akkor nem térnék el ettől a struktúrától. Én rekurzívan (ó, jajj) álltam neki a probléma megoldásának úgy, hogy a legfelső szintektől indultam lefele, aztán próbálkoztam már vissza irányba is, de valahogy mindig egy logikai bukfenc lett a kódom vége... Így aki tudna erre nekem szép PHP-s megoldást azt megköszönném nagyon. Közben próbálkozok még majd én is este és linkelek akkor kódot is, hogy kb. merre indultam. Googleztam is sokat, de ilyen adatszerkezettel nem láttam megoldást a problémára, de lehet csak én vagyok vak.

Köszi!

Hozzászólások

Ilyesmi (rekurziv):


function treebranch($allnodes, $node) { // szint gyujtessel

        // visszater a gyokertol az adott node-ig tarto faaggal
        // elemek sorrendje is ez. gyoker->...->node
        
        if ( -1 == $node['parent'] ) {
                $node['treelevel'] = 0;
                return array($node['id'] => $node);
        }

        $parent = $allnodes[$node['parent']];
        
        $roottoparent = treebranch($allnodes, $parent);
        
        $node['treelevel'] = $roottoparent[$parent['id']]['treelevel'] + 1;
                
        return $roottoparent + array($node['id'] => $node);
}


function treefromleaves($allnodes, $needednodes) { // rendezessel

        // felepiti a fat a gyokertol az egyes node-okig terjedo agakbol.
        // array += array -nal a meglevo elemek a helyukon maradnak, igy a szulok mindig megelozik a
        // gyerekeket, ezert bekapcsolhato az opcio, amitol gyorsabb a javascriptes faepito
        // (bar utana ugyis rendezzuk, ugyhogy mindegy is lenne...)
        $tree = array();
        foreach ($needednodes as $needednode)
                $tree += treebranch($allnodes, $needednode);
                
        // kulcs osszerendelest meghagyva rendezi szint+sortorder+nev szerint
        uasort($tree, create_function(
                        '$a, $b',
                        'return strnatcmp(
                                $a[\'treelevel\'].\'_\'.$a[\'sortorder\'].\'_\'.$a[\'name\'],
                                $b[\'treelevel\'].\'_\'.$b[\'sortorder\'].\'_\'.$b[\'name\']
                                );'
                        )
        );
        
        return $tree;
        
}

Elmeletben az RDBMS-eket nem igazan arra talaltak ki, hogy fa strukturakat taroljanak, de persze a gyakorlatban megis ez szokott tortenni :)
Ket elterjedt megkozelites van: az adjacency list model es a modified preorder tree traversal (MPTT) algoritmus.
Guglizz rajuk.

Nem kell php sem, google closure tables.

ha valoban kis mennyisegu elemrol van szo, es nem akarsz a tablastrukturan valtoztatni, akkor az alabbi osztaly(ka) a segitsegedre lehet.
peldanyositod, es az addNode metodussal hozzaadsz elemeket


class Tree {
    protected $nodes = array();
    protected $edges = array();

    public function addNode(array $node) {
        $this->edges[$node['id']] = $node['parent'];
        $this->nodes[$node['id']] = $node['name'];
    }

    public function getChildren($id) {
        return array_keys($this->edges, $id);
    }

    public function getParent($id) {
        return $this->edges[$id];
    }

    public function getName($id) {
        return $this->nodes[$id];
    }
}
  • az addNode tombot var, es csak a nevet tarolja el az elen kivul, de lehet durvulni: objektumot var, es az egeszet letarolja nodes-ben (felteve, hogy tobb mezod is van a tablaban, es hasznalni is akarod oket), ez persze a memoria rovasara megy
  • ajanlatos az adatbazisbol kapott eredmenyt fetch-eleskor rogton beledobalni, es nem prefetch-elni, szinten memoriatakarekossagi okokbol
  • a getName es getParent metodusokba nem irtam ellenorzest, igy konnyen szorhatja a notice-okat! ajanlott kiegesziteni
  • a getChildren metodus az array_keys masodik 'search_value' parameterevel trukkozik, igeny szerint harmadik parameternek TRUE-t adva tipusszigoru lesz, bar ha nem flancos az adatbazis-reteg akkor ugyis minden string
  • 0, "" es NULL 'id'-ra a getChildren enyhe mellekhatassal dolgozik, de nem veszes (megintcsak ha ertelmes adatok jonnek a tablabol, akkor miert is lenne id=0?)

(szerk.: persze kodot ne kuldjek be tabokkal)

Plastiknak folottem: < code > helyett [code] es jo lesz. Direkt nem irtam rad, meg szerkesztheted.

Köszönöm a sok megoldást! A closure table dolog tűnt elsőre a legszimpatikusabbnak, most annak álltam neki, csak a táblámból kell csináljak egy nézet táblát, ahol az elvárt formában vannak az adatok letárolva. Aztán lehet az lesz a vége, hogy a script lesz módosítva ami dolgozik a kategóriákkal...