Koetan nyt tehdä itselleni mahdottomuuksien rajoissa piilevää asiaa PHP-harjoituksieni nimissä: monitasoista oksavalikkoa (vrt. resurssienhallinta, eng. "tree menu"). Olen typistänyt sen tiedot sisältävän MySQL-kannan kolmeen kenttään (otsikot korostuksen vuoksi isolla, todellisuudessa pienellä), alla. Kaikki kentät ovat varchareja.
CID ITEMNAME PARENT 0 Makkarat 1 Bratwurst 0 2 Metwurst 0 3 Eläimet 0 4 Possu 3 5 Hevonen 3 6 Letut 7 Pinaatti 6 8 Normaalit 6
Yksinkertaisuudessaanhan (aika sana) mikäli rivillä ei ole "parent"-kentässä mitään, se on päätason elementti. Jos siinä taas on jotain, se on sen numeroisen elementin alielementti. Oudosti sanottu, mutta ehkä sen älyää. Tuosta pitäisi saada seuraavanlainen valikko aikaiseksi (siis järjestää ne aakkosjärjestykseen samalla - muutenkin rivit voivat olla ihan sekaisin):
Makkarat Bratwurst Eläimet Hevonen Possu Metwurst Letut Normaalit Pinaatti
Tarkoitus olisi myös, että PHP:n avulla valikoita voi supistella ja laajennella miten vain sielu sietää - tosin vain yksi ylemmän tason elementti kerrallaan - valikkoa ei siis voisi avata täyteen loistoonsa kerrallaan (vrt. NetAnttila.comin harmaa sivuvalikko). Olen osannut tehdä vain ensimmäisen alatason toimintaan alla olevalla koodilla. Eikä se edes järjestä niitä aakkosiin.
<?php function getTopParameters($ditch = false){ $path = $_SERVER['REQUEST_URI']; $p = explode("?", $path); if($ditch == true){ $p2 = explode("&openid", $p[1]); return $p2[0]; } else { return $p[1]; } } //Pure Jenkki $phandle = mysql_query("SELECT * FROM tree WHERE parent = ''", $link); $phandle2 = mysql_query("SELECT COUNT(*) FROM tree WHERE parent = ''", $link); //Kiitos, Grez $pinfo2 = (mysql_fetch_assoc($phandle2)); for($i=0;$i<$pinfo2["COUNT(*)"];$i++){ $pinfo = (mysql_fetch_assoc($phandle)); echo "<a href=\"?".getTopParameters(true)."&openid=".$pinfo["cid"]. "\">".$pinfo["itemname"]."</a><br>"; if($_GET["openid"] == $pinfo["cid"]){ $cha = mysql_query("SELECT * FROM tree WHERE parent = '".$pinfo["cid"]."'", $link); $cha2 = mysql_query("SELECT COUNT(*) FROM tree WHERE parent = '".$pinfo["cid"]."'", $link); //Kiitos, Grez $cin2 = (mysql_fetch_assoc($cha2)); for($a=0;$a<$cin2["COUNT(*)"];$a++){ $cin = (mysql_fetch_assoc($cha)); echo " <a href=\"?".getTopParameters(true)."&openid=". $cin["cid"]."\">".$cin["itemname"]."</a><br>"; } } } ?>
Mutta jos mitään niistä avatuista alatasoista klikkaa, koko homma sulkeutuu (koska openid ei ole enää sama). Miten voisi hyppelehtiä teoriassa vaikka kuinka pitkään seuratakseen parent-kenttiä pitääkseen koko polun auki? Toisenlaisia toteutustapoja? Mitään vinkkejä?
Oliskohan tuosta iloa
http://www.sitepoint.com/article/hierarchical-data-database
Erilaisia tree-traversal algoritmeja on vaikka kuinka
Cornix taisi olla joka kyseli irkissä tämän perään tässä taannoin, lopputulos:
http://vesa.piittinen.name/joku.php
Ja koodi:
http://vesa.piittinen.name/joku.php.txt
Tuo taulukon data näyttäisi olevan yks-yhteensopivaa tämän esimerkin kanssa.
Elementit voi järjestää aakkosjärjestykseen tietokantakutsussa ihan yksiulotteisella tasolla.
Sain siitä väännettyä MySQL-version: http://sooda.dy.fi/paste/?0DO
Nyt kun vain saisi ne avattavaksi/suljettavaksi asti, niin kaikki olisi hyvin. Omat taidot ei riitä, varsinkaan kun tässä on niin hirveästi itselleni uusia funktioita (reset, current, next, key... jne) eikä ole omaa koodia...
Voisiko joku auttaa? Kiitollisna.
Olihan ne minullekin uusia käytettäviä tässä yhteydessä, mutta PHP.netistä kyllä saa aika hyvän kuvan siitä mistä on kunkin funktion osalta kyse. Eli tärkeää tässä olisi nyt se, että tosiaan itse katsot vaihe vaiheelta mitä tuo koodi kussakin vaiheessa tekee, selvittää itsellesi miksi se toimii. Kukaan muu kun ei voi tehdä sitä sinulle. Kun ymmärrät miksi se toimii, niin tiedät jo sitten aika pian että miten saat laajennettua sitä.
(Vinkki: saat sulkeutumisen ja avautumisen päätoiminnon toteutettua yhdellä ainokaisella tarkistuksella ja lisäarvolla.)
Monen kirosanan jälkeen ymmärsin suunnilleen, miten koodi toimii. Mutta - usko tai älä - vieläkään ei valjennut, miten tuon avaus/sulkuhomman toteuttaisi...
Estät tulosta_lapset -funktiossa lasten tulostuksen jos kyseisen listaelementin lapset on merkitty suljetuksi. Ja sen saat tietää lisäämällä jonnekin kohtaan tiedon siitä, onko isännöivän elementin lapset suljettu vaiko ei (esim. true/false -arvo).
Ei, ei, nyt ei vieläkään onnaa. Ehkä pitäisi mieluummin omistautua savenvalannan hienoon taiteeseen. Mutta aion kuitenkin saada tämän nyt valmiiksi.
Ajattelin laittaa $navi
in neljänneksi booleanin näyttämään, josko lapset on suljettu vaiko eikö. Kaikki sujui niin kauan kunnes piti keksiä, miten ja missä se määritellään, onko juuri kyseisen isännän lapset suljettuja vaiko eikö ja aagh, menin solmuun.
Älysin, että paras paikka joka rivin true/falsen määrittelemiseen olisi tuo while
, koska siinä joka rivi käydään läpi joka tapauksessa. Paras heittoni:
<?php // .... while( $rivi = current($navi) ) { //loopataan koko $navi, $rivi sisältää nykyisen tietueen sisällön if($rivi[1] == '') { // jos PARENT on tyhjä, eli se on päätason elementti $isannat[] = key($navi); // lisätään se isäntien listaan } else { // jos se onkin lapsi $navi[ $rivi[1] ][3][] = key($navi); //lapsi listataan arrayhyn navitietueen loppuun } if($_GET["openid"] == key($rivi)){ $navi[ $rivi[1] ][4] = false; } next($navi); //seuraavaan valikon kohtaan, loop alusta } // .... ?>
mutta sittenhän siitä hassu tuli. Trial-and-error ei nyt ihan toimi - olen tässä säätämässä vielä joulunakin. Eli en nyt siis millään keksi, miten. Miten? Kiitos kaunis.
Tuossa ei nyt ainakaan missään vaiheessa aseteta niitä true-arvoja sille puulle, jonka pitäis olla avoinna.
Asetan sen trueksi oletuksena arrayn kehittämisessä. Koko koodi: http://sooda.dy.fi/paste/?id=W5n&vrj=php
function tulosta_lapset($id, $lapset) { //lasten kirjoitus - key $navissa, lapsiarray global $navi; //tuodaan valikko mukaan echo '<li><a href="?openid='.$id.'">'.$navi[$id][0].'</a>'; //kirjoitetaan linkki if( count($lapset) && $navi[$id][4] ) { //jos silläkin on lapsia = array on olemassa if($navi[ $lapset[$i] ][4]){ echo '<ul>'; //sisennys for($i = 0, $maxi = count($lapset); $i < $maxi; $i++) { //tulostetaan nekin tulosta_lapset($lapset[$i], $navi[ $lapset[$i] ][3]); //...kutsumalla itseään } //loppuruljanssi alkaa echo '</ul>'; } } echo '</li>'; }
Eli muutettu rivi on
if( count($lapset) && $navi[$id][4] ) {
nyt siis jos arvo on true, niin lapset näkyy, jos false, niin eivät näy.
Kiitos. Vaan nyt meni oudoksi. Ei muuten tapahtunut mitään, mutta kun otin tuon if($navi[ $lapset[$i] ][4]){
sieltä pois, niin rupesi näkymään actionia. Tosin ei sellaista, kuin haluaisin.
Valikossa on yksi ylimääräinen pallura kun kaikki on kiinni, ja ylemmän päätason elementin saa auki alemmasta päätason elementistä (Makkarat-valikko aukeaa koko kokonaisuudessaan, kun painaa Letut-linkkiä).
Sitten muutin koodia niin, että alkujaan joka $navi
n kohdassa lukisi oletuksena false
, ja se muutettaisiin while
'ssä true
ksi jos openid
on sama kuin nykyinen rivi
. Nyt oikea valikko aukeaa, kun klikkaa tiettyä päätason elementtiä. Mutta valikko vieläkin avautuu kokonaisuudessaan (joka alitaso pitäisi voida avata ja sulkea erikseen), päätason elementti sulkeutuu kun jotakin sen lapsista klikkaa, ja siellä kummittelee aina välillä se outo extrapallura.
Tämä on aivan liian vaikeaa... :(
Nyt ryhtiä tuohon ajatusmalliin hyvä mies! Lähtökohta on aina se, että osaat tehdä asian tai ainakin selvittää miten asia tehdään. Jos et tiedä, piirrä vaikka paperille logiikka jonka mukaan asioiden pitäisi toimia. Varmista pariin otteeseen että se tosiaan toimisi kuten on tarkoitus. Sitten ala verrata sitä nykyiseen koodiin.
(Eli joo, en ole nyt sillä tuulella että jaksan tehdä kaiken valmiiksi, ja toisaalta näitä hieman monimutkaisempia asioita on ihan hyvästä opetella ajattelemaan alusta loppuun itse, ei sitä muuten oikeasti opi ohjelmointia. Tässähän ei nyt ole kyse PHP-kielestä oikeastaan ollenkaan, vaan siitä että kykenee ajattelemaan asian mielessään ja muodostamaan sen pohjalta toimivaa koodia.)
Se alkuperäinen skriptini muuten värjäsi avoinna olevaan sivuun asti boldilla. Oletko miettinyt että sen voi adaptoida pienin muutoksin pitämään huolta avaamisista ja sulkeutumisista?
Aihe on jo aika vanha, joten et voi enää vastata siihen.