{"id":143,"date":"2007-11-11T16:24:21","date_gmt":"2007-11-11T15:24:21","guid":{"rendered":"http:\/\/eriz.pcinside.pl\/weblog\/php-feat-mysql-sposob-na-drzewka-143.html"},"modified":"2007-11-11T16:35:27","modified_gmt":"2007-11-11T15:35:27","slug":"php-feat-mysql-sposob-na-drzewka","status":"publish","type":"post","link":"https:\/\/eriz.pcinside.pl\/weblog\/php-feat-mysql-sposob-na-drzewka-143.html","title":{"rendered":"PHP feat. MySQL: Spos\u00f3b na drzewka"},"content":{"rendered":"<p><em>aktualizacja<\/em><\/p>\n<p>W Sieci mo\u017cna natkn\u0105\u0107 si\u0119 na wiele przepis\u00f3w, w jakis spos\u00f3b stworzy\u0107 drzewka zapisywane w DB. Niekt\u00f3re maj\u0105 wi\u0119cej, niekt\u00f3re mniej wad&#8230; Ale poszukiwa\u0142em jakiego\u015b w miar\u0119 uniwersalnego rozwi\u0105zania, kt\u00f3re by najlepiej pasowa\u0142o do moich potrzeb.<\/p>\n<h3>Za\u0142o\u017cenia<\/h3>\n<p>Potrzebujemy drzewka kategorii. Ot, takie najprostsze, np:<\/p>\n<ul>\n<li>kategoria\n<ul>\n<li>podkategoria1<\/li>\n<li>podkategoria2<\/li>\n<\/ul>\n<\/li>\n<li>kategoria<\/li>\n<\/ul>\n<p><!--more--><\/p>\n<p>Poniewa\u017c zachodzi\u0142em w g\u0142ow\u0119, co by tu mo\u017cna by\u0142o zrobi\u0107, budowanie drzewka zachodzi\u0142o bez problem\u00f3w, kod si\u0119 walidowa\u0142, postanowi\u0142em &#8211; na dzie\u0144 dobry &#8211; po\u0107wiczy\u0107 na tablicach i metod\u0105 notatek\/pr\u00f3b i b\u0142\u0119d\u00f3w doj\u015b\u0107 do rozwi\u0105zania.<\/p>\n<h3>Przygotowania<\/h3>\n<p>Bazowa\u0142em na poni\u017cszej tablicy:<\/p>\n<p><code lang=\"php\">$d[] = array('asdasd1-', 1);<br \/>\n$d[] = array('asdasd2-', 1);<br \/>\n$d[] = array('asdasd3-', 2);<br \/>\n$d[] = array('asdasd4-', 2);<br \/>\n$d[] = array('asdasd5-', 1);<br \/>\n$d[] = array('asdasd6-', 2);<br \/>\n$d[] = array('asdasd7-', 2);<br \/>\n$d[] = array('asdasd8-', 1);<br \/>\n$d[] = array('asdasd9-', 2);<br \/>\n$d[] = array('asdasd10-', 1);<\/code><\/p>\n<p>Pierwsze pole w tablicy ka\u017cdego rekordu, to nazwa, a drugie &#8211; stopie\u0144 zag\u0142\u0119bienia w strukturze. O tym p\u00f3\u017aniej.<\/p>\n<p>Pocz\u0105tkowo, pr\u00f3bowa\u0142em obrabia\u0107 to za pomoc\u0105 zwyk\u0142ej p\u0119tli <em>for<\/em>. Errr, nie. Dlaczego? O ile w przypadku tablic nie b\u0119dzie raczej problem\u00f3w wydajno\u015bciowych przy liczeniu element\u00f3w tablicy, to podczas korzystania z bazy ju\u017c mog\u0105 si\u0119 pojawi\u0107 takie problemy. Wiem, \u017ce rzadko mog\u0105 si\u0119 zdarzy\u0107 takie duuuu\u017ce drzewa, ale&#8230; Ka\u017cda ms jest wa\u017cna. ;]<\/p>\n<p>Wi\u0119c co zrobi\u0107? Pozostaje ju\u017c tylko p\u0119tla <em>while<\/em>.<\/p>\n<p>Po co wi\u0119c nam drugi parametr w tablicy? Ot\u00f3\u017c &#8211; jak ju\u017c wcze\u015bniej wspomnia\u0142em &#8211; pos\u0142u\u017cy on nam do okre\u015blenia, jak g\u0142\u0119boko w hierarchicznej strukturze drzewa znajduje si\u0119 dany wpis. St\u0105d &#8211; aby poprawnie rozpoczyna\u0107 i ko\u0144czy\u0107 listy wyliczeniowe &#8211; musimy sprawdza\u0107, czy nast\u0119pny element jest wi\u0119kszy b\u0105d\u017a mniejszy od obecnie przetwarzanego. Upraszczaj\u0105c, je\u015bli liczba jest wi\u0119ksza od bie\u017c\u0105cej &#8211; rozpocznij now\u0105 podkategori\u0119, je\u015bli mniejsza &#8211; zamknij podkategori\u0119, je\u015bli taka sama &#8211; wypisz element.<\/p>\n<p>W przypadku p\u0119tli <em>for<\/em> tego problemu za bardzo nie ma, poniewa\u017c mo\u017cemy u\u017cy\u0107 konstrukcji <em>$d[$a+1]<\/em>, aby dosta\u0107 si\u0119 do nast\u0119pnego elementu. Przy <em>while<\/em> musimy sobie pom\u00f3c.<\/p>\n<p>Po prostu, b\u0119dziemy pobiera\u0107 dwa rekordy za jednym razem.<\/p>\n<h3>&#8230;i praktyka.<\/h3>\n<p>Aby nie pobiera\u0107 kilkukrotnie tego samego rekordu, utworzymy sobie dwa bufory.<\/p>\n<p><code lang=\"php\">$start = true;<br \/>\n$buff1 = null;<br \/>\n$buff2 = null;<\/code><\/p>\n<p>Z ka\u017cdym przej\u015bciem p\u0119tli b\u0119d\u0105 si\u0119 one przesuwa\u0142y cyklicznie o jeden (z drugiego do pierwszego; do pierwszego zapisany nowy element).<\/p>\n<p>Pocz\u0105tek p\u0119tli wygl\u0105da mniej wi\u0119cej tak:<\/p>\n<p><code lang=\"php\">while(list($key,$r) = each($d)){<\/p>\n<p>    if($start){<br \/>\n        $buff1 = $r;<br \/>\n        $buff2 = $d[1];next($d);<br \/>\n        $start = false;<br \/>\n    }else{<br \/>\n        $buff1 = $buff2;<br \/>\n        $buff2 = $r;<br \/>\n    }<\/code><\/p>\n<p>Zmienn\u0105 pomocnicz\u0105 jest tu <em>$start<\/em>, kt\u00f3ra s\u0142u\u017cy do sprawdzenia, czy jest to pierwszy przebieg p\u0119tli. Umo\u017cliwia to prawid\u0142ow\u0105 wymian\u0119 danych pomi\u0119dzy buforami.<\/p>\n<p>W celu zwi\u0119kszenia przejrzysto\u015bci kodu (dla cel\u00f3w tego wpisu!), tworzymy referencje do odpowiednich element\u00f3w tablicy, kt\u00f3re zawieraj\u0105 dane o stopniu zag\u0142\u0119bienia.<\/p>\n<p><code lang=\"php\">$curr = &$buff1[1];<br \/>\n$next = &$buff2[1];<\/code><\/p>\n<p>I teraz obrabiamy poszczeg\u00f3lne elementy.<\/p>\n<p><code lang=\"php\">\/\/rozpocznij element i wypisz warto\u015b\u0107<br \/>\necho '<\/p>\n<li>'.$buff1[0];\n<p>\/\/je\u015bli istnieje nast\u0119pny element...<br \/>\nif($next!==null){<br \/>\n    \/\/je\u015bli nast\u0119pny element jest g\u0142\u0119biej, utw\u00f3rz now\u0105 list\u0119<br \/>\n    if($next>$curr){<br \/>\n        echo '<\/p>\n<ul>';<br \/>\n    }<\/p>\n<p>    \/\/je\u015bli nast\u0119pny element jest p\u0142ycej, ko\u0144czymy list\u0119...<br \/>\n    if($next<$curr){\n        \/\/...odpowiedni\u0105 ilo\u015b\u0107 razy - przeliczanie w przypadku bardziej zag\u0142\u0119bionych list\n        for($x=0;$x<($curr-$next);$x++){\n            echo '<\/li>\n<\/ul>\n<p>';<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/je\u015bli nast\u0119pny element jest na takiej samej g\u0142\u0119boko\u015bci, uzupe\u0142nij element listy<br \/>\n    if($next==$curr){<br \/>\n        echo '<\/li>\n<p>';<br \/>\n    }<\/p>\n<p>}else{<br \/>\n    \/\/je\u015bli to ostatni...<br \/>\n    echo '<\/li>\n<p>';<br \/>\n}<\/code><\/p>\n<p>Oczywi\u015bcie, nie zapomnij o uj\u0119ciu ca\u0142ego kodu w znaczniki <em>&lt;ul&gt;&lt;\/ul&gt;<\/em>.<\/p>\n<h3>MySQL<\/h3>\n<p>W kodzie za bardzo si\u0119 r\u00f3\u017cni\u0107 nie b\u0119dzie w por\u00f3wnaniu to edycji tablicowej. Wystarczy edycja, nazwijmy to &#8222;nag\u0142\u00f3wka p\u0119tli&#8221;.<\/p>\n<p><code lang=\"php\">while($r = $q->getRow()){<\/p>\n<p>    if($start){<br \/>\n        $buff1 = $r;<br \/>\n        $buff2 = $q->getRow();<br \/>\n        $start = false;<br \/>\n    }else{<br \/>\n        $buff1 = $buff2;<br \/>\n        $buff2 = $r;<br \/>\n    }<\/code><\/p>\n<p>Oczywi\u015bcie, funkcj\u0119 <em>$q->getRow()<\/em> zamie\u0144 na odpowiedni\u0105, u\u017cywan\u0105 w Twoim skrypcie. Konieczna b\u0119dzie r\u00f3wnie\u017c zmiana odwo\u0142a\u0144 do poszczeg\u00f3lnych tablic (<em>$d[0]<\/em>, itp. zamie\u0144 na odpowiadaj\u0105ce Twojej tabeli w bazie).<\/p>\n<p>Sama tabela mo\u017ce wygl\u0105da\u0107 mniej wi\u0119cej tak:<\/p>\n<ul>\n<li><em>ID unsigned INT<\/em> &#8211; dla potrzeb zarz\u0105dzania<\/li>\n<li><em>depth unsigned INT<\/em> &#8211; g\u0142\u0119boko\u015b\u0107 w hierarchii<\/li>\n<li><em>title char(255)<\/em> &#8211; tytu\u0142<\/li>\n<li><em>page unsigned INT<\/em> &#8211; ewentualne odniesienie do podstrony w serwisie<\/li>\n<\/ul>\n<p>Dlaczego by nie sprz\u0119\u017cy\u0107 tej tabeli z zawarto\u015bci\u0105 stron? To temat, kt\u00f3ry zosta\u0142 ju\u017c <a href=\"http:\/\/www.netcoffee.pl\/pogodzinach\/2005\/10\/30\/predkosc-czy-miejsce\/\">rozklepany<\/a>.<\/p>\n<h3>O czym powinni\u015bmy pami\u0119ta\u0107?<\/h3>\n<p>U\u017cywaj kolejno nast\u0119pujacych po sobie poziom\u00f3w zag\u0142\u0119bienia. Np. je\u015bli nadrz\u0119dnym jest <em>1<\/em>, to nast\u0119pny powinien by\u0107 <em>2<\/em>, a nie wy\u017cszy. Dlaczego? Raz: aby unikn\u0105\u0107 chaosu, dwa: w\u00f3wczas wymaga\u0142oby to u\u017cycia kolejnej p\u0119tli w kodzie (analogicznie jak to ma miejsce w przypadku <em>&lt;\/ul&gt;&lt;\/li&gt;<\/em>).<\/p>\n<h3>Aktualizacja<\/h3>\n<p>Powy\u017cszy kod dzia\u0142a, OK. Ale ucina ostatni element&#8230; Gdyby liczba wpis\u00f3w w tablicy\/tabeli by\u0142a nieparzysta, to wszystko by pi\u0119knie dzia\u0142a\u0142o. Jest parzysta, ups &#8211; uci\u0119\u0142o&#8230; Posiedzia\u0142em przy serniku i herbacie, uda\u0142o mi si\u0119 z tym poradzi\u0107.<\/p>\n<p>Przyda si\u0119 drugi rodzaj p\u0119tli <em>while<\/em>, ten rzadziej wykorzystywany. Ca\u0142a p\u0119tla wygl\u0105da tak (reszta bez zmian):<\/p>\n<p><code lang=\"php\">do{<br \/>\n    list($key,$r) = each($d);<\/p>\n<p>    if($start){<br \/>\n        $buff1 = $r;<br \/>\n        $buff2 = $d[1];next($d);<br \/>\n        $start = false;<br \/>\n    }else{<br \/>\n        $buff1 = $buff2;<br \/>\n        $buff2 = $r;<br \/>\n    }<\/p>\n<p>    if($buff1==null){<br \/>\n        break;<br \/>\n    }<\/p>\n<p>    $curr = $buff1[1];<br \/>\n    $next = $buff2[1];<\/p>\n<p>    echo '<\/p>\n<li>'.$buff1[0];\n<p>    if($next!==null){<br \/>\n        if($next>$curr){<br \/>\n            echo '<\/p>\n<ul>';<br \/>\n        }<\/p>\n<p>        if($next<$curr){\n            for($x=0;$x<($curr-$next);$x++){\n                echo '<\/li>\n<\/ul>\n<p>';<br \/>\n            }<br \/>\n        }<\/p>\n<p>        if($next==$curr){<br \/>\n            echo '<\/li>\n<p>';<br \/>\n        }<\/p>\n<p>    }else{<br \/>\n        echo '<\/li>\n<p>';<br \/>\n    }<\/p>\n<p>}while($buff1!=null);<\/code><\/p>\n<p>EOF;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>aktualizacja W Sieci mo\u017cna natkn\u0105\u0107 si\u0119 na wiele przepis\u00f3w, w jakis spos\u00f3b stworzy\u0107 drzewka zapisywane w DB. Niekt\u00f3re maj\u0105 wi\u0119cej, niekt\u00f3re mniej wad&#8230; Ale poszukiwa\u0142em jakiego\u015b w miar\u0119 uniwersalnego rozwi\u0105zania, kt\u00f3re by najlepiej pasowa\u0142o do moich potrzeb. Za\u0142o\u017cenia Potrzebujemy drzewka kategorii. Ot, takie najprostsze, np: kategoria podkategoria1 podkategoria2 kategoria<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,12],"tags":[30,74,78],"_links":{"self":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/143"}],"collection":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/comments?post=143"}],"version-history":[{"count":0,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/143\/revisions"}],"wp:attachment":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/media?parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/categories?post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/tags?post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}