[PHP] Gnezdenje
8 naročnikov
8 naročnikov
Z Recursive functions sem uredil tako, da ima lahko kategorija "neomejeno" podkategorij in vsaka podkategorija še dodatne podkategorije itd. Zdaj me pa zanima ali obstaja kakšen boljši način takšnega gnezdenja kot da uporabim recursive function? Težave je, ker je na strežniku omejitev za gnezdenje, določen je "Maximum function nesting level of '100'" in ko je ta limit dosežen, se skripta ustavi. A dam limit na, ne vem, 10.000 in pozabim na vse skupaj ali obstaja kakšen boljši način?
20 odgovorov
ah... ja če povečaš xdebug.maxnestinglevel bi ti pomagalo, vendar ti priporočam da spremeniš logiko da ne bo potrebne toliko rekurzije, saj ne vidim razloga zakaj bi moral imeti vso strukturo na voljo. Strukturo zlagaj postopoma, da ne presežeš limita oz. še bolje naloži samo vidne kategorije, vse ostale pa naložiš šele ko uporabnik klikne na njih.
Obremenjuješ se nepotrebnimi stvarmi.
V kolikor te skrbi preveč poizvedb na bazo, dodaj strukturo v cache. Iz baze jo pa beri samo če se kaj spremeni.
Tako boš imel podatke v memoriju, branje in rekurzija bo hitra in malo potratna.
V kolikor ti je to še vedno potratno lahko keširaš tudi html izpis menija za naprimer prvi in drugi nivo.
Obstaja še mnogo načinov za optimizacijo, ampak načelo mora biti, da se optimizira takrat ko je to potrebno.
poglej si še nested tree set (https://en.wikipedia.org/wiki/Nestedsetmodel), zna olajšat select querye
- na faksu te naučijo, da se vsaka rekurzija da predelat v iteracijo (celo na izpitu imaš to :) )
Preberi vse zapise in poženi skozi zanko sortiranje v array, je tudi kar nekaj primerov po netu. Vse skupaj lažje za keširat in v bistvu imaš samo en klic na bazo :D
bostjan:
poglej si še nested tree set (https://en.wikipedia.org/wiki/Nestedsetmodel), zna olajšat select querye
- na faksu te naučijo, da se vsaka rekurzija da predelat v iteracijo (celo na izpitu imaš to :) )
Zanimivo.
carli: saj točno tako imam sedaj
carli:
Kako? Ne rabiš delat rekurzivnih klicev, za to da sortiraš!
Ne vem, če se midva razumeva. Lahko potem pokažeš, kako bi rešil spodnji primer brez uporabe recursive?
V bazi ima vsaka kategorija id ter parent_id, gnezdi se lahko, jasno, neomejeno globoko.
Išči iterating over trees ali pa RecursiveIteratorIterator ... za display, ko imaš že enkrat sortiran array iz baze, pa seveda potem skeširaj output, ker zna biti počasnejši pri večjih arrayih.
Večkrat je zadeva uporabna pri prikazu folderjev, itd...
En način je, da celoten meni sestaviš že na bazi. To bi bilo v primeru, da bi imel v tabeli en kup zapisov, aktualni bi pa bili samo nekateri:
http://guilhembichot.blogspot.com/2013/11/with-recursive-and-mysql.html
V MS-SQL se za te namene uporablja rekurzivni klic s pomočjo WITH ukaza, pri MySQL je pa potrebno malo več telovadbe.
Drugi način, ki je verjetno bolj primeren zate, je pa da uporabiš kakšen bolj ali manj učinkovit (= enostavnejši) algoritem za sestavljanje drevesa - kot so ti že predlagali.
Čisto enostavni način, ki je hkrati zelo neučinkovit, ko imaš veliko podatkov, je da uporabiš dve FOR zanki. Z vsako od njih se sprehodiš skozi svoj seznam in na PARENT_ID dodajaš ID zapise.
// Primer, ko imaš polje $kateg, kjer imaš pod vsakim zapisom spet polje vrednosti (lahko pa to predelaš z uporabo FOREACH)
// Tole pišem na pamet ni pretestirano
// Dele, ki se začnejo s TODO je potrebno še doprogramirati
$result = [];
// Spodnje zanke ponavljaj toliko časa, dokler ne bo nič več za dodati
$repeat = true;
while ($repeat) {
$repeat = false;
for ($a = 0, $ac = count($kateg); $a < $ac; $a++) {
$a_id = $kateg[$a]['id'];
$a_pid = $kateg[$a]['parent_id'];
// TODO: Dodaj preverjanje, če je element že v seznamu $result[], casting
if ($a_pid === null) $result[$a_id]['id_path'] = $a_id; // Sestavljaj pot povezanih ID-jev
for ($b = 0, $bc = count($kateg); $b < $bc; $b++) {
$b_id = $kateg[$b]['id'];
$b_pid = $kateg[$b]['parent_id'];
// TODO: Ni optimalno, ker se mora parent že nahajati v končnem seznamu
if ($a_id === $b_pid && in_array($a_id, $result)) {
// Dodaj v končni seznam
// TODO: Dodaj preverjanje, če je element že v seznamu $result[], casting
$result[$b_id]['id_path'] = $result[$a_id]['id_path'] . '#' . $b_id; // Sestavljaj pot povezanih ID-jev
$repeat = true;
}
}
}
}
Na koncu še posortiraš polje po 'id_path' ter urediš zamike.
Optimizacije so možne: npr. beležiš zapise, ki so že bili dodani in se po njih ne sprehajaš več v naslednjih korakih, idr.