Tallinna Ülikool, Informaatika Instituut
Veebirakenduste loomine
PHP ja MySQLi abil
Jaagup Kippar
Tallinn, 2009
Sisukord
Sissejuhatus 4
PHP tutvustus 5
Päris algus 5
Ülesandeid 5
Muutuja, valik ja kordus 6
Ülesandeid 7
PHP HTMLi sees 7
Sisestusega veebileht 8
Ülesandeid 11
Lehe koostamine alamosadest 11
p2is.php 12
menyy.php 12
kujundus.css 13
jalus.php 13
Sisuleht 13
Ülesandeid 15
Andmefailid eraldi kataloogis 15
liinivaataja.php 16
p2is.php 16
menyy.php 17
Ülesandeid 18
Andmebaas veebilehestiku juures 18
Ühe andmetabeliga seotud veebilehestik 19
Ülesandeid 22
Andmetabeli sisu kuvamine PHP abil. 22
Ülesandeid 24
Teadete valik 24
Ülesandeid 27
Andmete lisamine ja kustutamine 28
Ülesandeid 32
Andmete muutmine veebilehel 32
Ülesandeid 37
Graafiline tekstiredaktor 37
Ülesandeid 42
Veebilehestiku lõikude hoidmine andmebaasis 42
Funktsioonid eraldi failis 42
Ülesandeid 45
Andmed mitmes tabelis 45
Kaubad ja kaubagrupid 46
Ülesandeid 53
Sortimine 53
Ülesandeid 57
Otsimine 57
Ülesandeid 60
Haldamine 60
Ülesandeid 63
Andmete muutmine 63
Ülesandeid 69
Funktsioonid klassis 70
Ülesandeid 73
Smarty lehemallid 73
Ülesandeid 77
Sessioonimuutujaga meldimine 77
Ülesandeid 83
Mitu mitmele seos ehk kolm seotud tabelit 83
Kasutajate haldus 85
Ülesandeid 100
Andmetabel päringus mitme koopiana 101
Ülesandeid 103
Agregaatfunktsioonid, grupeerimine 104
Ülesandeid 105
Failide üleslaadimine 105
Ülesandeid 109
Sissejuhatus
Veeb tänapäevasel kujul asus jõudsalt levima 1990ndate aastate keskel. Suurelt jaolt kasutati seda küll staatiliste tekstide ja piltide mugavaks välja näitamiseks, kuid juba algusest peale olid kasutusel juures lisad, mis võimaldasid lehe sisu vastavalt kasutajale või tema tegevusele eraldi näidata. Levinumate näitena selle kohta kehtiva kuupäeva näitamine või otsing soovitud andmete alusel. Sellisel juhul ei piisa paljast valmistehtud lehe kopeerimisest kasutajale, vaid tuleb lehe sisu tekitada või ühendada mõne programmi võimaluste abil.
Vahendeid selleks on aegade jooksul olnud mitmesuguseid. Siiani kasutusel on võimalus käivitada suvalises meeldivas keeles koostatud programm veebiserveris ning lasta sel suhelda brauserist tulnud päringuga vastava liidese (CGI – Common Gateway Interface) abil. Keskkonnamuutujate kaudu saadakse kätte edastatavad andmed (näiteks otsisõna) ning programmi trükitav väljund suunatakse brauseri poole teele – tehniliselt nõnda lihtne see ongi. Iga programmeerija võis endiselt kirjutada omale sobivas keeles ning kõik toimis.
Veebisisendi ja -väljundiga programmidel on aga mõned eripärad. Olenevalt rakendusest, kuid küllalt sageli tuleb väljastada suures koguses muutumatut HTML-teksti ning sinna vahele vaid üksikud kohad, mis programmiga muuta vaja. Teiseks probleemiks veebirakenduste juures on, et kunagi ei saa usaldada sisendit kasutajalt – üle veebi võib tulla ligi suvaline häkker ning otsisõna asemele panna teele näiteks mõne videofilmi sisu binaarkujul. Sekelduste vältimiseks on seetõttu kasulik lisada sisendile piiranguid ja kontrolle.
Veebi leviku laienedes lisandus ka raamistikke, keeli ja keeletäiendusi, mille abil peaks veebiprogramme olema mugavam kokku panna kui ”tavaliste” programmeerimiskeelte abil. Suure veebileviku osaliseks sai keel nimega PERL, millel võrrelduna näiteks tol ajal muidu väga levinud C-ga olid tunduvalt mugavamad ja mitmekülgsemad vahendid tekstidega ümber käimiseks. Tuntumad eraldi veebi jaoks loodud vahendid ehk ASP (nüüdseks põhjalikult muudetuna ASP.NET), Zope, Java Servletid ja JSPd. Lihtsamate ja ka keskmiselt keerukate veebilehestike juures sai valitsevaks keeleks PHP.
PHP tutvustus
Algselt ühe koolipoisi katsetustest levima hakanud kodulehe koostamise abivahend (Personal Home Page) sai oma lihtsuse ja vabade kasutusõiguste tõttu üllatavalt populaarseks. Eks lihtsusega käivad koos ka mõned ohud, mida on uuemates versioonides püütud lappida. Kuid võlu, et kõik kohe arusaadav ja kasutatav on, paneb paljudki programmeerijad heal meelel PHPst alustama.
Päris algus
PHP-leht on tavaline teksti (või siis üldjuhul HTML-leht), kus sobivate märkide vahel saab programmikoodi käima lükata – nõnda nagu enamiku muudegi veebiprogrammeerimissüsteemide puhul. Lihtsaim demo näeb välja järgmine:
PHP-võimelise veebiserveri kaudu välja kuvatavasse kataloogi tuleb paigutada järgneva sisuga fail.
= 3+2 ?>
Laiendiks php – näiteks nimega algus.php
Kui nüüd leht veebilehitsejas avada, võiks seal ilutseda üks ilus suur number 5. Kolm ja kaks liideti kokku.
Väike seletus ka juurde: = näitab, et nüüd järgneb avaldis, mille väärtus enne kasutajale saatmist kokku arvutatakse. Avaldise lõppu tähistab ?>. Ning vahepealne 3+2 lihtsalt arvutatigi kokku ja trükiti välja. Kui lehel alguses või lõpus oleks veel muudki teksti, trükitaks ka see välja.
Ülesandeid
* Hangi või tee selgeks enesele võimalus PHP-võimelises veebiserveris veebilehtede loomiseks.
* Koosta tervitav leht ja vaata seda veebiserveri kaudu
* Muuda lehe sisu ning uuendusnupu vajutuse järel veendu muutuse kajastumises ka veebilehitsejas.
* Käivita konspektis olnud näide kahe arvu liitmise kohta.
* Muuda arve ja tehet, kontrolli tulemusi.
Muutuja, valik ja kordus
Järgnevalt juba veidi pikem koodilõik. Kui ei piirduta vaid ühe arvu või lause väljastusega, siis tuleb ploki alguseks märkida = asemel -ga tähistatud ploki lõpuni rahulikult programmikoodi kirjutada -.see pannakse käima ning tulemus saadetakse veebilehistsejasse.
Programmeerimiskeeltes on levinud võimalus andmeid muutuja ehk märksõna alla meelde jätta. PHPs algavad muutujate nimed dollarimärgiga. See võimaldab neid hiljem vabamalt teksti sisse panna. Lõik
$eesnimi="Juku";
echo "Tere, $eesnimi!";
trükib aimatavalt välja ”Tere, Juku”.
Valiku jaoks on käsklus if. Tingimus pannakse ümarsulgude sisse. Kui tingimus vastab tõele (praegusel juhul vanus on väiksem kui seitse), siis täidetakse järgnevate looksulgude vahele paigutatud plokk.
$vanus=5;
if($vanus<7){
echo "Oled noor!";
}
Korduse ehk silmuse ehk tsükli puhul võidakse looksulgude vahele kirjutatud toimetus ette võtta korduvalt. Järgnev näide teatab viis korda Kuku!
for($i=0; $i<5; $i++){
echo "Kuku!";
}
Seletus ka juurde. Muutuja $i (PHPs algavad kõik muutujad dollarimärgiga) aitab meeles pidada, mitmenda korra juures ollakse. Käsu for ümarsulgude sees on kolm semikoolonitega eraldatud tsooni. Esimeses neist on algväärtustus, täidetakse üks kord, kohe for-ini jõudmise juures. Tüüpiliselt antakse siin loendurile algväärtus, praegusel juhul i-le 0.
Keskmises tsoonis kontrollitakse tingimuse kaudu, et kas on põhjust for-ile järgnevate looksulgude vahel olevaid käske täitma hakata. Kui tingimus on tõene siis jah, muidu mitte. Võib ka juhtuda, et tingimus on juba esimesel korral väär - sellisel juhul ei täideta tsükli keha ühtegi korda.
Viimasesse ehk kolmandasse tsooni paigutatakse tsüklis edasiliikumise tegevus(ed) - siin juhul suurendatakse $i väärtust, et järgmisel korral oleks juba suurem arv loenduriga võrrelda. Lugemisega alustatakse tüüpiliselt nullist - siis hiljem massivide puhul kergem toimetada.
Koodilõigu loodud väljund järgmine:
Ülesandeid
* Käivita nähtud näide, muuda andmeid, jälgi tulemusi
* Lisa tervitatavate inimeste eesnimesid, igaüks oma muutujas
* Lisa tingimus üle saja-aastaste jaoks teatega "oled väga vana".
* Lisa for-tsükli sisse iga Kuku järjekorranumber.
PHP HTMLi sees
Nagu näha, võib PHP ka lihtsat teksti väljastada. Kui aga tahta kokku panna viisaka kujundusega veebilehte kus ka kasutaja midagi sisestada saab, siis tuleb HTMLi reeglitega arvestama hakata. Viisakasti tasub , tema sees peamiste suurte plokkidena
ja . Esimesse neist tulevad enamikus metaandmed ehk siis andmed dokumendi kohta, sisse nähtav tekst ise.
Lihtsaim asjalik lõik PHPd HTML koodi sees võiks välja näha ehk järgmine:
Nagu aimata võite, võib selle tulemusena näha veebiserveri kella aega veebilehel kliendi masinas. Tekst "Kell on " tuleb välja jutumärkide vahelt. Järgnev punkt on operaator tekstide liitmiseks (sidurdamiseks). Ning käsklus date võimaldab kuupäeva ja kellaaja väljastada ettemääratud kujul. Siin näites kasutatud H tähendab tunde 24 tunni süsteemis, i minuteid (sest m ehk month on kuude jaoks) ning s sekundeid. Koolonid trükitakse nende vahele niisama välja. Leht tervikuna siis:
PHP katsetused
PHP katsetused
Sisestusega veebileht
Lehele sisestuselementide lisamiseks on sinna kõigepealt vaja panna kujunduse mõttes nähtamatu plokk nimega form. Parameeter action näitab, millisele lehele saadetakse elementidesse kirjutatud andmed. Praeguses näites on fail ise nimega "teine.php" ning action saadab andmed ka faili "teine.php" ehk siis failile iseenesele. Kui andmed kirjutatakse väljadesse ja vajutatakse submit-nuppu, siis pannakse nad praegusel juhul kaasa avatava faili aadressiribale -
Järgmisel avamisel võib näha aadressiribal failinime näiteks kujul
teine.php?eesnimi=Juku&vanus=7
Järgmine samm on vormi kaudu aadressiribale jõudnud andmed kätte saada ning nendega midagi ette võtta. Andmete püüdmiseks sobib massiiv nimega $_REQUEST (teadjamatele: sinna jõuavad kokku andmed massiividest $_GET ja $_POST). Sisestatud tegelase lihtsaks tervitamiseks sobib rida
echo "Tere, $_REQUEST[eesnimi]";
Selle juures on aga probleemiks, et tervitada püütakse ka juhul, kui nime andmeid tegelikult ei ole. Sel juhul ilmuks ekraanile paljas tere koos komaga.
Kui me pole kindlad, kas andmed saabuvad või mitte, siis on viisakas nende olemasolu enne kasutamist kontrollida. Kusjuures on kaks täiesti erinevat andmete puudumise juhtu. Ühel puhul lihtsalt leht avati niisama, sisestades aadress aadressiribale. Teine puudumise puhk on juhul, kui lehele küll sisenetakse vormi kaudu andmeid sisestades ja submit-nupule vajutades, kuid tekstivälja väärtus jäeti tühjaks. Aadressiriba tekib siis kujul näiteks
teine.php?eesnimi=&vanus=7
Elemendi olemasolu kontrollib PHPs funktsioon nimega isSet, väljastades tõeväärtuse true/false. Näiteks
if(isSet($_REQUEST["eesnimi"])){...}
Sisestatud teksti olemasoluks on aga hea moodus kindlaks teha selle teksti pikkus käsuga strlen. Kui tekst juhtub tühi olema, siis lihtsalt on selle pikkus 0. Nõnda saabki kokku koodilõigu, mis esimesel avamisel ei ütle midagi. Nimelahtri tühjaksjätmisel aga teatab nime puudumisest.
if(isSet($_REQUEST["eesnimi"])){
if(strlen($_REQUEST["eesnimi"])>0){
echo "Tere, $_REQUEST[eesnimi]!";
} else {
echo "Nimi kirjutamata!";
}
}
Eks arvude puhul saab olemasolu samamoodi kontrollida. Esialgu on ka vanus arvuti jaoks lihtsalt tekst ehk sümbolid. Seepärast ka sisestuslahter sai kirja
Vanus:
Kui tahta kontrollida, et sinna lahtrisse saaks kirjutada vaid numbreid, siis tuleks selleks eraldi Javaskripti lõik kirjutada või mõne raamistiku abil sisse panna.
Kui serveris aga tahta saabunud arvuga viisakalt ümber käima hakata, siis on viisakas saabunud tekst mälus arvulisele kujule ümber muundada. Täisarvude puhul aitab seda teha funktsioon nimega intval. Edasi võib veebist tulnud arvuga käituda juba sarnaselt nagu iga muu arvuga. Siin siis tervitame nime sisestanut nõnda palju kordi, palju tal aastaid on.
if(strlen($_REQUEST["vanus"])>0){
$v=intval($_REQUEST["vanus"]);
for($i=0; $i<$v; $i++){
echo "Õnne! ";
}
}
Veebisisestuse üle ei saa aga kunagi kindel olla: kui juhtub, et mõni katsetaja kirjutab oma vanuseks miljoni, siis see koodijupp tervitab teda rõõmsalt miljon korda, kulutades vastavalt serveri aega. Selline tegevus pole küll serveri andmetele ohtlik, kuid kui häkkerid tahaksid hakata masinat kõvasti koormama, siis paarikümnest kohast pidevalt end miljoni kaupa tervitada laskmine on hea moodus serveri kiusamiseks. Kõike selliseid võimalusi ei jõua ega saagi kinni panna. Aga mõnigikord peab mõtlema, et kas, mida ja kui palju on põhjust turvata, ehk siis kiusajatele takistusi teha.
Nime ja vanuse järgi tervitav ja õnnitlev leht siis järgmine.
PHP katsetused
PHP katsetused
0){
echo "Tere, $_REQUEST[eesnimi]!";
} else {
echo "Nimi kirjutamata!";
}
}
echo "Kell on: ".date("H:i:s");
?>
0){
$v=intval($_REQUEST["vanus"]);
for($i=0; $i<$v; $i++){
echo "Õnne! ";
}
}
?>
Kõigepealt avades teatatakse vaid kellaaeg ja antakse ette sisestuskastid.
Sinna kannatab andmed sisse kirjutada.
Edasi jõuavad need andmed aadressiriba kaudu faili juurde kuhu form-i action suunab. Ning selle lehe ülesandeks on juba saabunud andmetele vastav sisu kuvada.
Ülesandeid
* Lase kasutajal sisestada kaks arvu. Programm väljastab nende korrutise
* Trüki kasutaja määratud ridade ja veergude arvuga korrutustabel
Lehe koostamine alamosadest
Aastate eest oli tähtsaks näitajaks serveri või teenusepakkuja juures, et kas too toetab SSI-d ehk Server Side Include't. Nüüd saab sama tulemuse PHP käskudega mugavasti kätte.
Paljude sarnase kujundusega lehtede juures on paratamatult hulk korduvat koodi - soovitakse samasse kohta paigutada logo, menüü ja ehk muudki. Üheks võimaluseks on algul blanketileht teha, seal tähtsamad kujunduseasjad ära määrata ning seejärel blanketist sobiv hulk koopiaid teha. Kui lehestik nõnda ühekordselt kokku pannakse, siis selline lähenemine täiesti sobib. Samuti aitavad mõned keskkonnad (nt. FrontPage) selliseid sarnaseid lehti genereerida.
Kui aga tehakse lehti lihtsa HTML-redaktoriga, samuti kui tahetakse lehe koodi ise kontrollida ning sinna hiljem programmilõikegi sisse panna, sellisel juhul on sisseloetavad alamosad igati head abilised.
Näitena koostame bussiliinide andmeid avaldava lehestiku, kus muutuvad ainult liini andmed. Lehe päis, jalus ja menüü jäävad ikka samadesse kohtadesse. Alamosad loetakse sisse järgneval joonisel näha olevatest failidest:
Päiseosa saab failist p2is.php. Sinna tuleb kogu HTMLi algusots: DOCTYPE, head ja title ning lehe keha algus.
p2is.php
Bussiliinid
Samuti loetakse sinna juurde sisse menüü failist menyy.php.
menyy.php
Menüü on hea eraldi kohas hoida. Siis on sinna mugav lisada uusi ning eemaldada vanu lehekülgi. Samuti võib hiljem vajadusel paigutada menüükihi lehe koodis mõnda muusse, hetkel sobivamasse kohta.
Ka kujundus on paigutatud eraldi faili. Ehkki siinse sisselugemise teel saaks kujunduseosa suhteliselt mugavalt panna ka päisefaili, on vähegi pikema kujunduslõigu puhul see mõttekas panna omaette faili. Päisefaili kaudu lugedes arvatakse see kujunduslõik iga kord eraldi faili osaks olema ning sikutatakse kogupikkuses serverist kohale. Kui aga viidatakse eraldi css-failile, siis piisab selle ühe korra kohale tõmbamisest, ülejäänud osa ajast saab puhvris olevaid andmeid kasutada.
Kujundusefailis määratakse ära lehel asuvate plokkide asukohad. Käsklus float paneb kihi lehe peal "ujuma". Kuna nii menüükihil kui ka sisukihil on omadus float:left, siis nad ujuvad üksteisel sabas. Menüü on nõnda lai, nagu teksti laius teda lükkab, sisukihil on praegu määratud laiuseks 70% akna suurusest, et ta kasvaks ja kahaneks koos aknaga. Lõputeate kihi clear:left ütleb, et tuleb taas uuelt realt oma paiknemist alustada, nii jõuab ta ilusti eelmiste elementide alla.
kujundus.css
body{
background-color: #ffeb90;
}
#menyy{
float: left;
padding-right: 30px;
}
#sisu{
width: 70%;
float: left;
}
#loputeade{
clear: left;
}
Jalus praeguse seisuga lihtne fail, kus lõputeade ning HTML-lehe ots.
jalus.php
Lehe koostas Jaagup
Sisuleht
Kui abitükid olemas, siis saab asuda sisulehti koostama. Nemad loevad enesele require-käsuga sisse ette päise ja taha jaluse. Päis haarab enese külge veel kaasa menüü. Sisule ka omaette väike kiht paigutamiseks ümber ning võibki rahumeeli vajalikud andmed sinna sisse kirjutada.
726 HAAPSALU-TAEBLA-PALIVERE-RISTI-LAITSE-TALLINN
12:00 HAAPSALU
12:05 UUEMÕISA (LÄÄNEMAA)
12:10 RANNAKÜLA
12:15 TAEBLA
12:17 PRIGULDI
12:19 VÕNTKÜLA
12:23 PALIVERE EIK
12:25 PALIVERE
12:30 JAAKNA
12:35 RISTI (LÄÄNEMAA)
12:38 REHEMÄE
12:45 ELLAMAA
12:50 TURBA (HARJUMAA)
12:57 NISSI TEE
13:05 LAITSE TEE
13:06 RUILA TEE
13:12 HARUTEE
13:25 VANA-PÄÄSKÜLA
13:45 TALLINN
Kaks kõrvutist lehte erinevad vaid sisu poolest - siin siis teise bussiliini ajad ja peatused. Päis ning jalus võetakse külge samasugustena. Käsklus require erineb mõnel pool levinumast include-st selle poolest, et require annab otsitava faili puudumisel veateate, include jätab selle lihtsalt näitamata. Tegemise juures vigade vältimiseks on esimene variant kindlam.
Liin 727, HAAPSALU-TAEBLA-PALIVERE-RISTI-TALLINN
13:00 HAAPSALU
13:05 UUEMÕISA (LÄÄNEMAA)
13:10 RANNAKÜLA
13:15 TAEBLA
13:17 PRIGULDI
13:19 VÕNTKÜLA
13:23 PALIVERE EIK
13:25 PALIVERE
13:30 JAAKNA
13:35 RISTI (LÄÄNEMAA)
13:38 REHEMÄE
13:45 ELLAMAA
13:50 TURBA (HARJUMAA)
13:57 NISSI TEE
14:12 HARUTEE
14:25 VANA-PÄÄSKÜLA
14:45 TALLINN
Veebilehitsejas tuleb avada sisuleht. Viimane haarab omale ette ja taha vajalikud tükid külge ning võibki rahumeeli sõiduplaani vaadata.
Ülesandeid
* Tee näited läbi
* Lisa kolmanda bussiliini andmed
* Koosta sarnasel moel väike lehestik suvise matka korralduse tarbeks: osalejad, varustus, marsruut.
Andmefailid eraldi kataloogis
Kuni kümnekonna harva muudetava faili puhul on eelmine lähenemine mugav küllalt. Andmete lisamist ja eemaldamist saab aga ka mugavamaks teha. Järgnevas näites korraldati nõnda, et liini andmete näitamiseks piisab vaid sobiva nimega faili paigutamisest selle jaoks ette nähtud kataloogi. Ülejäänuga saab PHP-leht juba ise hakkama.
Lehe sisu näitamiseks tuli juurde fail liinivaataja.php. Temale antakse aadressiriba kaudu ette näidatava liini number. Liinivaataja haarab ette päise, taha jaluse ning liiniandmete kataloogist keskele sisse etteantud numbriga algava tekstifaili sisu. Eeldatakse, et failinimed on kujul 726.txt, 727.txt jne. Samuti eeldatakse, et faili nimi enne laiendit on number - sissemurdmise vastaseks kaitseks töödeldakse saabunud parameetrit $_REQUEST["liininr"] käsuga intval, mis muudab iga talle antud teksti arvuks. Arvud jäävad ikka pärast .txt-ga liitmist samasuguseks tekstiks. Kui aga parameetriks antakse midagi muud, siis annab intval sellele tulemuseks 0. Nõnda pole karta, et keegi võiks failinime sisse paigutada kataloogide vahel liikumise või muid andmete õngitsemiseks tarvilikke käske.
Käsklus file_get_contents tagastab parameetriga ette antud faili sisu. @-märk käsu eel annab teada, et tekkivaid veateateid ei kuvataks ekraanile - need jällegi asjad, mis kipuvad häkkeritele masina ülesehituse kohta teavet andma ning sellest tasub turvalisuse huvides hoiduda.
liinivaataja.php
Päis endiselt samasugune
p2is.php
Bussiliinid
Menüü skanneerib läbi kõik liiniandmete kaustas leiduvad failinimed. PHP 5st alates kättesaadav funktsioon scandir tagastab etteantud kataloogis leiduvatest failinimedest koosneva massiivi. foreach-tsükliga võetakse sealsed nimed ükshaaval ette, käsklus explode tükeldab failinime punkti koha pealt massiiviks nii, et ühe punkti korral tekib kaheelemendiline massiiv. Kohal 0 on nimi ise ning kohal 1 on laiend. Kontrollitakse üle, et kui faililaiend ikka on .txt, siis lisatakse menüüloetellu vastava faili nimi - pannes see ka ühtlasi viitega kaasa saadetavaks parameetriks.
menyy.php
Nüüd piisab liiniandmete faili juures vaid andmetest endist, päist ja muid käsklusi pole lisada enam vaja, need tulevad liinivaataja.php juurest.
liiniandmed/726.txt
12:00 HAAPSALU
12:05 UUEMÕISA (LÄÄNEMAA)
12:10 RANNAKÜLA
12:15 TAEBLA
12:17 PRIGULDI
12:19 VÕNTKÜLA
12:23 PALIVERE EIK
12:25 PALIVERE
12:30 JAAKNA
12:35 RISTI (LÄÄNEMAA)
12:38 REHEMÄE
12:45 ELLAMAA
12:50 TURBA (HARJUMAA)
12:57 NISSI TEE
13:05 LAITSE TEE
13:06 RUILA TEE
13:12 HARUTEE
13:25 VANA-PÄÄSKÜLA
13:45 TALLINN
Ja jällegi võib ilusti omale sobivad sõiduajad valida :-)
Ülesandeid
* Tee näited läbi
* Koosta sarnaselt laulusõnade lehestik: iga laul omaette lehel. Lihtsamal juhul on laulul pealkirjaks number. Keerukamal juhul on failinimi vabam, aga tasub kontrollida, et erisümboleid sisse ei jääks.
* Koosta eelmise näite abil pildigalerii. Kataloogis olevaid pilte saab veebi kaudu vaadata.
* Jaga galerii alateemadeks, ehk siis alamkataloogideks. Koosta navigeerimisvahendid lehtede vahel liikumiseks.
Andmebaas veebilehestiku juures
Nagu eelnenud näidetest näha, pole veebi koostamisel andmebaas sugugi hädavajalik lisandus. Küll aga võimaldab veebilehe andmebaasitabeliga ühendamine mitmete toimetustega mugavamal ja/või ohutumalt hakkama saada. Hulk võimalusi, mis muidu tuleks ise kodeerida, on juba andmebaasiprogrammidesse sisse ehitatud. Tüüpilised kohad, kus andmebaaside eelised välja tulevad on järgmised:
* samas veebilehestikus kasutatavad mitmesuguse struktuuriga seotud andmed
* otsing parameetrite järgi
* andmete muutmine veebi kaudu - eriti mitme administraatori korral
Eelnevalt nähtud suhteliselt staatilised lehed seega, kus kasutatakse lihtsalt ühiseid päiseid ning sarnase struktuuriga pikematekstilisi andmeid - selliste lehtede puhul pole andmebaas sugugi hädavajalik.
Kui aga soovida veebi kaudu muudetavat/täiendatavat lehestikku teha, siis on andmebaasi abi kindlasti omal kohal.
Enne näite juurde minemist märgime siia edaspidi kasutatavad mõisted.
* Andmebaasiprogramm, andmeohjur - masinasse installeeritud programm või draiver andmebaasidega ümber käimiseks. Nt. MySQL, Oracle, PostgreSQL, Access.
* Andmebaas - andmeohjuri poolt hallatav tabelite komplekt. Nt. konkreetse veebipoe jaoks tarvilikud andmetabelid. Ühes masinas võib olla (enamasti ongi) mitu andmebaasi. Igas andmebaasis saab olla palju andmetabeleid. Ühe andmebaasi piires on eri andmetabelitest andmeid mugavam/kiirem siduda kui eri andmebaaside vahel.
* Andmetabel - ridade ja veergudega andmete hoidmise koht. Igal veerul ehk tulbal on nimi ja tüüp - nt. tekst, kuupäev, täisarv, reaalarv.
* SQL - tõenäoliselt levinuim keel andmebaasile käskude andmiseks. Selle abil saavad andmebaasiga suhelda nii inimesed kui andmebaasivälised rakendusprogrammid (nt. PHP). Inimeste jaoks tehakse vahel ka graafilisi haldusvahendeid, kuid teised programmid suhtlevad üldjuhul andmebaasiga SQLi kaudu.
Siinses õppematerjalis kasutame andmebaasina MySQLi. Ta sobib PHPga hästi kokku, kuna on suunatud enamvähem sarnasele sihtgrupile: lihtsamate veebirakenduste lihtne loomine tasuta tarkvara abil. Eestis vähemasti 2009nda aastani tõenäoliselt levinuim moodus veebirakenduste loomiseks. (Üli)suurtes süsteemides nagu nt. pangalehed jääb vahenditesse sisseehitatud kontrolle ja stuktueeritust väheks. Aga enamike veebirakenduste nagu foorumid, uudistelehed, registreerumisvormid jaoks on võimalusi piisavalt. Arendamine ja ülespanek nõuab riistvaraliselt vähe ressursse, lehtede ülespanekuks kasutatavaid teenusepakkujaid (nt zone.ee, elkdata.ee) on piisavalt. Täiesti korraliku PHP ja MySQLi veebimajutuse leiab 2009nda aasta seisuga alates 100st kroonist kuus.
Ühe andmetabeliga seotud veebilehestik
MySQLis ja ka teistes relatsioonilistes ehk tabelipõhistes andmebaasides hoitakse ja väljastatakse pea kõiki andmeid tabelitena. Ning viimastel aastakümnetel üle ¾ andmebaasidest ongi relatsioonilised.
Andmebaasis toimetamiseks peab kõigepealt sellesse sisenema. Oma arvutisse nt. XAMPP abil veebiloomiskomplekti installides on MySQL kohe kättesaadav. Avalikumas serveris tuleb sisenemiseks enne administraatorilt kasutajakonto paluda. Ning mõnes teenusepakkuja keskkonnas saab ligi vaid graafilise kasutajaliidese (nt. PHP MyAdmin) kaudu.
Käsurealt sisenemine näeb välja ligukaudu järgmine. Võti -ujaagup näitab, et kasutajaks (user) on jaagup. Järgmine jaagup tähistab andmebaasi nime. Ning -p teatab, et parool küsitakse eraldi nõnda, et selle tähti ekraanile ei kuvata. Kui kõik õnneks läheb, siis ilmub lõpuks ette käsuviip mysql >
jaagup@tigu:~$ mysql -ujaagup jaagup -p
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 47574
Server version: 5.0.51a-24+lenny2-log (Debian)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql>
Tabeli loomiseks käsklus CREATE TABLE. Käsu nimele järgneb tabeli nimi (praegusel juhul "lehed"). Ning siis sulgudes komadega eraldatuna tulpade nimed ning nende parameetrid. Iga tabeli esimeseks tulbaks on üldjuhul id - identifikaator, mille abil hiljem ridu eristada ja neile viidata. Parameetrid võivad lihtsamate rakenduste puhul enamasti samaks jääda. Selgitused:
INT - täisarv
NOT NULL - väärtus ei tohi puududa
AUTO_INCREMENT - server arvutab lisamisel ise juurde sobiva seni veel kasutamata väärtuse
PRIMARY KEY - selle tulba väärtust kasutatakse edaspidi tabeli vastavale reale viitamisel (näiteks muutmise või kustutamise juures).
Tulp pealkiri siin näites tüübiga VARCHAR(50) ehk siis tekst pikkusega kuni 50 tähte. Sisu tüübiks TEXT, mis tähendab, et pikkust ei piirata.
Kokku siis lause järgmine, mis tasub valmis kirjutada ning MySQLi käsuviibale kopeerida:
CREATE TABLE lehed(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
pealkiri VARCHAR(50),
sisu TEXT
);
Kui vastuseks tuli Query OK, siis järelikult ettevõtmine õnnestus. Muidu tuleb veateateid uurida ja uuesti proovida.
Andmete lisamiseks on loodud käsklus INSERT INTO. Järgneb tabeli nimi, siis sulgudes tulpade nimed kuhu lisatavad andmed tulevad. Edasi sõna VALUES ning sulgude sisse komadega eraldatult igale tulbale vastav väärtus. Tekstilised andmed paigutatakse ülakomade vahele.
INSERT INTO lehed (pealkiri, sisu) VALUES ('Ilmateade', 'Kuiv ilm');
Tahtes rohkem andmeid lisada, tuleb INSERT lauset lihtsalt mitu korda käivitada, igal korral eraldi andmed sisse pannes.
mysql> INSERT INTO lehed (pealkiri, sisu) VALUES ('Korvpall', 'Treening reedel kell 18');
Query OK, 1 row affected (0.00 sec)
Kui mõned sees, siis on hea vaadata ja kontrollida, et mis sinna täpsemalt sai. Andmete küsimiseks on SQLis loodud käsklus SELECT. Tärn tähendab, et kuvatakse kõikide olemasolevate tulpade andmed. Sõnale FROM järgneb tabeli nimi ning käsu lõppu käivitamiseks semikoolon. Lehtede tabeli sisu tuleb siis välja järgnevalt.
mysql> SELECT * FROM lehed;
+----+-----------+-------------------------+
| id | pealkiri | sisu |
+----+-----------+-------------------------+
| 1 | Ilmateade | Kuiv ilm |
| 2 | Korvpall | Treening reedel kell 18 |
+----+-----------+-------------------------+
2 rows in set (0.00 sec)
Nagu näha, id-väärtused on automaatselt ise pandud, kuna vastaval tulbal on juures omadus AUTO_INCREMENT.
Tahtes andmeid veel juurde panna, tuleb taas käivitada INSERT-lause sobivate andmetega. Teksti sisestamisele vastab kõige korrasoleku puhul MySQL taas "Query OK", lisades sinna vahel ka mõjutatud ridade arvu ja kulunud aja - tähtis pigem suuremate andmestike korral.
mysql> INSERT INTO lehed (pealkiri, sisu) VALUES ('Matemaatika', 'Homme tunnikontroll');
Query OK, 1 row affected (0.00 sec)
SQLi selecti abil saab andmeid kergesti sobivas järjekorras ja kujul välja küsida. Kui lause lõppu lisatakse ORDER BY koos vastava tulba nimega, siis tulevad andmed välja selle tulba järgi tähestiku järjekorda panduna (kui vastav tulp oli tekstitulp).
mysql> SELECT * FROM lehed ORDER BY sisu;
+----+-------------+-------------------------+
| id | pealkiri | sisu |
+----+-------------+-------------------------+
| 3 | Matemaatika | Homme tunnikontroll |
| 1 | Ilmateade | Kuiv ilm |
| 2 | Korvpall | Treening reedel kell 18 |
+----+-------------+-------------------------+
3 rows in set (0.00 sec)
Saab küsida ka ainult ühe rea väärtusi:
mysql> SELECT pealkiri, sisu FROM lehed WHERE id=3;
+-------------+---------------------+
| pealkiri | sisu |
+-------------+---------------------+
| Matemaatika | Homme tunnikontroll |
+-------------+---------------------+
Kui leitakse, et rida pole enam vajalik, siis selle kustutamiseks sobib käsklus DELETE, kus soovitavalt id järgi määratakse ära, milline rida kustutada.
mysql> DELETE FROM lehed WHERE id=3;
Query OK, 1 row affected (0.00 sec)
Uue SELECT-päringuga saab kontrollida, mis siis sinna tegelikult alles jäi. Kui nüüd juhtutaks INSERT-lausega taas andmeid lisama, siis sellele reale enam id väärtust 3 välja ei antaks - välistamaks näiteks olukorda, kus vanale teatele pandud kommentaarid satuksid uue külge. Primaarvõtmetulba id väärtuseks tuleks uue rea lisamisel vähemasti 4.
mysql> SELECT * FROM lehed;
+----+-----------+-------------------------+
| id | pealkiri | sisu |
+----+-----------+-------------------------+
| 1 | Ilmateade | Kuiv ilm |
| 2 | Korvpall | Treening reedel kell 18 |
+----+-----------+-------------------------+
MySQList saab välja käsuga quit
Ülesandeid
* Tee näited läbi
* Välju MySQList, sisene uuesti.
* Lisa teadete tabelisse veel mõned read.
* Väljasta teated sorteerituna pealkirja järgi
* Loo tabel "veised" tulpadega id, nimi, mass ja vanus
* Lisa mõned andmed
* Väljasta andmed sordituna vanuse järgi
* Väljasta ühe veise andmed id järgi.
* Kustuta see veis.
* Väljasta veised, kelle mass on 200-500kg (kahe tingimuse vahel AND).
Andmetabeli sisu kuvamine PHP abil.
MySQLi ja PHP ühendamiseks on aegade jooksul kasutatud mitmesuguseid vahendeid. Levinuimad on tõenäoliselt käsud mysql_connect, mysql_select_db ja mysql_query, millede abil on ka suurem osa kasutatavaid rakendusi kirjutatud. Selle komplekti puuduseks on aga sisendandmete suhteliselt vaba sattumine SQLi lausesse, mis teeb rakenduse kergemini haavatavaks. Aastaid on abiks kasutusel olnud eraldi loodud teek nimega PEAR (http://pear.php.net/). PHP 5. versiooniga aga tuli kaasa andmebaasipäringute poolest sarnaseid võimalusi pakkuv pakett MySQL Improved, mille abil ka siinse õppematerjali näited tehakse.
Järgnev PHP-leht kuvab andmetabelis olnud teated ekraanile. MySQL Improved Extensioni (http://ee.php.net/mysqli) võimaluste kasutamiseks tuleb kõigepealt vastav objekt luua.
$yhendus=new mysqli("localhost", "jaagup", "xxxxxx", "jaagup");
Edasi tehakse valmis käsklus.
$kask=$yhendus->prepare("SELECT id, pealkiri, sisu FROM lehed");
Siis määratakse, millistesse muutujatesse satuvad tulpadest tulevad andmed ning käivitatakse käsklus andmebaasiserveris. Muutujatesse pannakse andmed samas järjekorras, kui need tulevad välja SELECT-lausest.
$kask->bind_result($id, $pealkiri, $sisu);
$kask->execute();
Kui andmed käes, siis võib neid ühe rea kaupa ette võtma hakata. Iga fetch() täidab bind_result-käsklusega määratud muutujad uue rea andmetega. Tsükkel while jätkab senikaua kuni päringu vastuseks veel ridu on, ehk kuni $kask->fetch tagastab tõese väärtuse kursori edasiliikumise õnnestumise kohta.
Veebilehel näitamiseks pannakse pealkirjale ja sisule ümber käsklus htmlspecialchars - see asendab HTML-koodis tekstiosas lubamatud sümbolid (nt. < ja > ) vastavate asenduskombinatsioonidega ( < ja > nende märkide puhul).
while($kask->fetch()){
echo "
".htmlspecialchars($pealkiri)."
";
echo "
".htmlspecialchars($sisu)."
";
}
Lõpuks on viisakas ühendus kinni panna. Ilma vastava käsuta küll ühendus suletakse ka mingi aja pärast, kuid jääb siiski mõneks ajaks rippuma ning tiheda kasutusega serveris võib tekkida olukord, kus vabade andmebaasiühenduste limiit saab otsa ja mõnda aega pole võimalik baasipäringuid kasutada. Kui aga ühendus selgesõnaliselt kinni panna, siis on vastav kanal vaba ning selle võib avada uue päringu tarbeks.
$yhendus->close();
Tabeli andmeid veebilehel näitav kood tervikuna:
prepare("SELECT id, pealkiri, sisu FROM lehed");
$kask->bind_result($id, $pealkiri, $sisu);
$kask->execute();
?>
Teated lehel
Teadete loetelu
fetch()){
echo "
".htmlspecialchars($pealkiri)."
";
echo "
".htmlspecialchars($sisu)."
";
}
?>
close();
?>
Käivitamise tulemusena valmis HTML:
Teated lehel
Teadete loetelu
Ilmateade
Kuiv ilm
Korvpall
Treening reedel kell 18
Ning selle põhjal järgmise väljanägemisega veebileht:
Ülesandeid
* Hangi omale võimalus MySQL-andmebaasile käsklusi saata - olgu siis PHPMyAdmini, käsurea või mõne muu koha kaudu.
* Koosta tabel koerte andmetega: id automaatselt suurenev primaarivõti, koeranimi kuni 30 sümboli pikkune tekst (VARCHAR) ning koera sünniaasta (INT). Lisa mõned andmed.
* Küsi tabelist koerad kord nime, kord sünniaastate järgi järjestatuna välja-
* Näita vaid koeri, kelle sünniaasta on suurem kui 2000.
* Koosta veebileht, kus on näha kõikide andmetabelis olevate koerte nimed ja sünniaastad.
* Kujunda leht kasutades võimalusel koerte pilte.
Teadete valik
Lühema sisuga teadete puhul sobib kõigi andmete korraga välja näitamine täiesti- vajalikud andmed on hea ülevaatlikult leida. Kui aga teated palju või nad pikemad, siis kipub kõige korraga nägemine tülikas olema. Esmaseks abiks sobib, kui algul paistavad välja vaid pealkirjad ning hiljem nendele vajutades saab ühe teate kaupa vaadata teate sisu teksti.
Selleks tuleb lehele andmete vaatamiseks luua kaks suhteliselt eraldi osa: menüü teadete loetelu jaoks ning keskel olev sisuosa ühe valitud teate näitamiseks.
Menüü kood on suhteliselt sarnane eelmise näite teadete loetelu omale. Lihtsalt loetelus näidatakse vaid teate pealkirja, sisu jäetakse näitamata. Pealkirjad muudetakse viideteks siiasamale failile nimega teadetevalik.php. Aadressiribale pannakse küsimärgi järel kaasa lähemalt vaadata soovitava teate id. Selle järgi on võimalik hiljem teate andmed küsida ning neid kasutada.
Teine plokk on id järgi konkreetsete andmete küsimine baasist ning näitamine veebilehele. See võetakse ette vaid juhul, kui aadressiribale on lisatud parameeter nimega "id". SELECT-lausele tuleb selle väärtus bind_param-käsu kaudu sisse ajada. Jutumärkide vahel olev "i" näitab, et parameetrina etteantavad id-d loetakse integeri ehk täisarvuna. Muude sisendite puhul jõuab päringulausesse selle koha peale lihtsalt arv 0.
Andmete kätte saamiseks määratakse bind_result-käskluse juures muutujate nimed, kuhu iga fetch-käsuga andmed sisestatakse. Endise while($kask->fetch) asemel on siin if($kask->fetch), sest mitut kirjet sama id põhjal nagunii küsida ei saa. Kui aga ühtegi ei anta, siis tõenäoliselt on keegi aadressiribale kirjutanud katsetamise huvides olematu id-numbri ning talle on viisakas teatada, et ta andmed on vigased.
prepare("SELECT id, pealkiri, sisu FROM lehed
WHERE id=?");
//Kysim2rgi asemele pannakse aadressiribalt tulnud id,
//eeldatakse, et ta on tyybist integer (i). (double - d, string - s)
$kask->bind_param("i", $_REQUEST["id"]);
$kask->bind_result($id, $pealkiri, $sisu);
$kask->execute();
if($kask->fetch()){
echo "
".htmlspecialchars($pealkiri)."
";
echo htmlspecialchars($sisu);
} else {
echo "Vigased andmed.";
}
} else {
echo "Tere tulemast avalehele! Vali menüüst sobiv teema.";
}
?>
Kui id-väärtust aadressiribal pole üldse märgitud, siis tõenäoliselt on tegemist lehe esmaavamisega, kus pole veel teate id-d määratud. Viisakuse poolest pannakse avatud ühendus lehe lõpul ka kinni. Kujundust enam eraldi failidesse paigutada pole mõtet, sest kogu andmete näitamise töö teebki ära üks ja seesama fail.
Teated lehel
prepare("SELECT id, pealkiri, sisu FROM lehed
WHERE id=?");
//Kysim2rgi asemele pannakse aadressiribalt tulnud id,
//eeldatakse, et ta on tyybist integer (i). (double - d, string - s)
$kask->bind_param("i", $_REQUEST["id"]);
$kask->bind_result($id, $pealkiri, $sisu);
$kask->execute();
if($kask->fetch()){
echo "
close();
?>
Avalehel näidatav teatepealkirjade loetelu
Valitud teate andmete kuvamine. Aadressiribal on näha valitud teate id.
Ülesandeid
* Koosta andmetabel koerte andmete hoidmiseks
* Loetelus on näha tabelisse sisestatud koerte nimed
* Koera nimele vajutamisel näidatakse muid andmeid selle koera kohta.
Andmete lisamine ja kustutamine
Baasist tulevaid andmeid on hea ja ilus vaadata, ent kuidagi peavad need andmed baasi saama. Kui andmestik kuigivõrd ei muutu, siis võib ju ka andmebaasi enese haldusliidese abil nad sinna kirja panna. Kui aga tahta, et tavakasutaja saaks mugavasti sättida, mis tal lehe peal kirjas, siis tema hea meelega PHP MyAdmini või otse SQL käsuviiba taha ei lähe. Lihtne arusaadav veebileht andmete sisestamiseks ja muutmiseks sobib palju paremini.
Andmete lisamiseks tuleb kõigepealt toiminguga algust teha: kasutajale antakse pealkirjade menüü lõpus võimalus valida toiming "Lisa ...". Tehniliselt suunatakse kasutaja samale veebilehele, aga andes lehele kaasa parameetri lisamine=jah.
Lisa ...
Selle peale kuvatakse lehel välja sisestusvorm. Vormi kohustuslik parameeter on action, mis näitab lehe aadressi, kuhu andmed jõuavad. Kui kogu haldus toimub sama lehe juures, siis on mugavaks mooduseks kirjutada action'iks $_SERVER["PHP_SELF"] - siis pannakse sinna viide lehele enesele. Ning oma juurde saab ligi ka juhul, kui lehe nime vahetatakse. Hilisema toimingu eristamise huvides lisan varjatud väljana (hidden) ka teate uusleht=jah.
if(isSet($_REQUEST["lisamine"])){
?>
prepare("INSERT INTO lehed (pealkiri, sisu) VALUES (?, ?)");
$kask->bind_param("ss", $_REQUEST["pealkiri"], $_REQUEST["sisu"]);
$kask->execute();
header("Location: $_SERVER[PHP_SELF]");
$yhendus->close();
exit();
}
Andmete kustutamise juures tuleb kõigepealt kasutajale anda võimalus teate kustutamiseks. Sobib see näiteks teate vaatamise lehele, kus vastava teate andmed juba nagunii olemas on. Sealt loon viite samale lehele, andes aadressireal küsimärgi järele parameetri nimega "kustutusid", mille väärtuseks saab vastava teate id-number.
if($kask->fetch()){
echo "
".htmlspecialchars($pealkiri)."
";
echo htmlspecialchars($sisu);
echo " kustuta";
} else {
echo "Vigased andmed.";
}
Lehe uuel avamisel saab kontrollida, kas esineb parameeter kustutusid. Kui mitte, siis pole vaja kustutamisega tegelda. Kui aga leidub, siis tuleb sobiv DELETE-lause käivitada. Primaarvõtmeks olev id-number siin täisarvuna, sellest ka "i" bind_param käskluse esimese parameetrina.
Kustutuse juures praegusel juhul korduva lehe avamise kontrolli ei ole, sest korduval sama id-ga teate kustutamisel ei juhtu midagi - vähemasti senikaua, kui pole eelnevat kontrolli tehtud, kas kustutatav asi üldse olemas on. Kui aga eeldan heauskset kasutajat, kes aadressireale käske ei kirjuta, vaid viisakasti viidete järgi rakendusega suhtleb, siis tal üksi oma lehestikku administreerides ei tohiks sellist üllatust juhtuda.
if(isSet($_REQUEST["kustutusid"])){
$kask=$yhendus->prepare("DELETE FROM lehed WHERE id=?");
$kask->bind_param("i", $_REQUEST["kustutusid"]);
$kask->execute();
}
Edasi juba lisamis- ja kustutusvõimelise lehe kood tervikuna.
prepare("INSERT INTO lehed (pealkiri, sisu) VALUES (?, ?)");
$kask->bind_param("ss", $_REQUEST["pealkiri"], $_REQUEST["sisu"]);
$kask->execute();
header("Location: $_SERVER[PHP_SELF]");
$yhendus->close();
exit();
}
if(isSet($_REQUEST["kustutusid"])){
$kask=$yhendus->prepare("DELETE FROM lehed WHERE id=?");
$kask->bind_param("i", $_REQUEST["kustutusid"]);
$kask->execute();
}
?>
Teated lehel
prepare("SELECT id, pealkiri, sisu FROM lehed
WHERE id=?");
$kask->bind_param("i", $_REQUEST["id"]);
$kask->bind_result($id, $pealkiri, $sisu);
$kask->execute();
if($kask->fetch()){
echo "
close();
?>
Andmete lisamine veebilehel:
Lisatud teate andmete vaatamine:
Ülesandeid
* Koosta/otsi andmetabelipõhine veebileht koerte andmete vaatamiseks
* Võimalda koeri lisada
* Võimalda koeri kustutada
Andmete muutmine veebilehel
Lihtsaim andmete muutmine sageli koosnebki kustutamisest ja uuest lisamisest. Nii on mõnigi kord kergem teha, kui viisakamat muutmisvahendit luua. Pikkade ja keerukate andmete puhul pole aga kasutajad kuigivõrd rahul, kui väikese trükivea või täienduse pärast peaks terve rea jagu andmeid uuesti sisse panema. Samuti on kustutamiseta muutmine tähtis oludes, kus andmeid hakatakse juba omavahel siduma. Kui näiteks koeral on kindel omanik, ehk viide inimeste andmetabeli reale. Siis selle rea juures nt. telefoninumbri muutmisel jääb koera juurde ikka sama omanik. Kui aga telefoninumbri muutmiseks inimese rida ära kustutatakse ja uuesti luuakse, siis on juba keerukam hoolitseda, et koera juures andmebaasis kindel sama omanik oleks.
Järgneva näite pealt näebki, kuidas PHP abil andmeid muuta nõnda, et ei peaks kõike maha kustutama ja uuesti sisestama, vaid saab paranduse paigutada vaid sobivasse kohta.
Kõigepealt lisame kustutamise viite kõrvale viite ka muutmise kohta. Anname lehe uuel avamisel kaasa muudetava teate id, samuti parameetri "muutmine" väärtusega "jah".
echo " kustuta ";
echo "muuda";
Nende põhjal saab siis järgmisel avamisel kontrollida, kas on seatud parameeter nimega "muutmine".
if(isSet($_REQUEST["muutmine"])){
Kui jah, siis luuakse vorm ja pannakse sinna kaasa muutmisid - mille järgi siis muudatuste salvestamise juures teab, milllise teate juures muutus tuleb kirja panna.
Väljadele antakse sisse olemasolevad väärtused, et neid saaks siis soovi järgi täiendada/parandada.
Muutmisvormi loov koodilõik siit tervikuna silma ette. Tekstiväljale ja tekstialale saab vana sisu suhteliselt lihtsalt sisse panna. Rippenüüde puhul tuleb näiteks sobiv rida kavalasti ette kerida - aga sellest juba edaspidi. Ning eks peab jääma ka lõik tavalise teate näitamiseks - ilma muutmata. Ning viisakuse poolest ka kontroll selle kohta, et kui soovitakse olematut teadet - olgu siis aadressirea muutmise tõttu või selle poolest, et keegi vahepeal serverist teate ära kustutas - siis antakse ka sõnadega teada, et nende andmete põhjal teadet kätte ei saa.
if($kask->fetch()){
if(isSet($_REQUEST["muutmine"])){
echo "
";
} else {
echo "
".htmlspecialchars($pealkiri)."
";
echo htmlspecialchars($sisu);
echo " kustuta ";
echo "muuda";
}
} else {
echo "Vigased andmed.";
}
Kui uued andmed sees, vajutatakse submit-nuppu ning andmed jõuavad taas lehele. Parameetri "muutmisid" olemasolu järgi teab, et nüüd tasub olemasoleva teate andmeid muutma hakata. Eks jällegi tuleb kokku panna SQL-lause. Kõik muudetavad väärtused saavad SET-osas omale veebilehelt tulnud andmete põhjal uue sisu ning lõpus WHERE osas määratakse, millise teate kohta see muutus käib. Siit kusjuures oht: kui kogemata unustatakse WHERE osa märkimata, siis kirjutatakse nende andmetega üle kõik tabelis leiduvad read ilma midagi eelnevalt küsimata ega kontrollimata.
Parameetrite tüüpideks siin "ssi", sest pealkiri ja sisu on stringid, tabelirida määrav id aga integer. Käsk taas käima ning andmed muudetud. Siingi ei ole lehe ümbersuunamist tehtud, sest kui ka juhtutaks lehe värskendusnupule vajutama, siis kirjutataks uued andmed lihtsalt veel korra tabelisse ning midagi muud sellega ei kaasne.
if(isSet($_REQUEST["muutmisid"])){
$kask=$yhendus->prepare("UPDATE lehed SET pealkiri=?, sisu=? WHERE id=?");
$kask->bind_param("ssi", $_REQUEST["pealkiri"], $_REQUEST["sisu"], $_REQUEST["muutmisid"]);
$kask->execute();
}
Edasi muutmisvõimelise lehe lähtekood tervikuna.
prepare("INSERT INTO lehed (pealkiri, sisu) VALUES (?, ?)");
$kask->bind_param("ss", $_REQUEST["pealkiri"], $_REQUEST["sisu"]);
$kask->execute();
header("Location: $_SERVER[PHP_SELF]");
$yhendus->close();
exit();
}
if(isSet($_REQUEST["kustutusid"])){
$kask=$yhendus->prepare("DELETE FROM lehed WHERE id=?");
$kask->bind_param("i", $_REQUEST["kustutusid"]);
$kask->execute();
}
if(isSet($_REQUEST["muutmisid"])){
$kask=$yhendus->prepare("UPDATE lehed SET pealkiri=?, sisu=? WHERE id=?");
$kask->bind_param("ssi", $_REQUEST["pealkiri"], $_REQUEST["sisu"], $_REQUEST["muutmisid"]);
$kask->execute();
}
?>
Teated lehel
close();
?>
Piltidelt on näha, kuidas kõigepealt avatakse id järgi sobiv teade.
Edasi muutmisviitele vajutades avatakse leht koos vormiga andmete muutmiseks:
Siis saab parandused sisse viia
Ning lõpuks tasub muudetud lehte imetleda.
Ülesandeid
* Tee näide läbi.
* Anna muutmisel hoiatus juhul, kui sisestatud pealkirja pikkus ületab 50 sümbolit
* Loo/otsi koerte andmeid näitav veebirakendus
* Lisa rakendusele andmete muutmise võimalus.
Graafiline tekstiredaktor
Aastaid oli veebikoostajate märgatavaks osaks tööst lehekülgede kaupa HTMLi kirjutada ja hoolitseda, et selle abil kirja pandud tekst rahvale viisakalt loetav oleks. Eks sellist kujundamist ole praegugi - kui tahtmist oma leht pikslipealt paika joonistada. Aga veebipõhised redaktorid aitavad küllaltki mugavalt tavakasutajatel omaloodud sisu kujundada ilma, et arvutiabilist pidevalt kõrval oleks.
Graafilisi ehk WYSIWYG (What You See Is What You Get) tekstiredaktoreid veebilehel on tekkinud hulgem, tuleb vaid omale sobiv leida. Väiksematel piisab mõnekilobaidisest Javaskripti failist, keerukamatel võib allalaetav skript koos abipiltidega mitmesaja kilobaidini ulatuda. Samuti suudab mõni hakkama saada vaid üksikute brauserite ja versioonidega, teisel on paindlikkust rohkem.
Kirjutise autor on juhtunud kasutama Javaskriptipõhiseid redaktoreid TinyMCE ja Kupu. Ning subjektiivselt parima mulje on jätnud CKEditor (endine FCKEditor). Seda kasutame ka siinses näites.
Redaktor tuleb kõigepealt alla laadida ja lahti pakkida. Tasub vaadata, et kus kaustas asub fail ckeditor.js - see tuleb varsti meie lehe külge haakida.
Siin puhul sattusid redaktori failid eraldi alamkausta ckeditor - seega laeme sisse faili aadressilt ckeditor/ckeditor.js.
Uus versioon on nõnda targaks tehtud, et teksti ala graafiliseks redaktoriks muutmiseks on tarvis vaid talle külge panna atribuut class='ckeditor'
Ning kui kõik andmed ilusti kätte saadakse, siis võibki rahulikult menüüst kujunduskäske valida ja teksti suurust muuta.
Sama asi ka lisamise juures: tuleb lihtsalt vaadata, kus asub tekstiala, talle külge panna klass ckeditor ning jällegi on elu tunduvalt värvilisem. Kui millegipärast ei ole, siis tasub kõigepealt uurida, et kas viidatud kohast ikkagi javaskripti fail kergesti kätte saadakse. Selle aadressi võib kasvõi brauserireale sisse lüüa ja kontrollida, et mis sealt välja antakse. Samuti tasub kontrollida, et ega veebilehitsejal pole Javaskript kinni keeratud - turvapõhjustel seda mõnikord tehakse.
if(isSet($_REQUEST["lisamine"])){
?>
prepare("UPDATE lehed SET pealkiri=?, sisu=? WHERE id=?");
$kask->bind_param("ssi", $_REQUEST["pealkiri"], stripslashes($_REQUEST["sisu"]), $_REQUEST["muutmisid"]);
$kask->execute();
}
Redaktorit kasutava lehe kood tervikuna.
prepare("INSERT INTO lehed (pealkiri, sisu) VALUES (?, ?)");
$kask->bind_param("ss", $_REQUEST["pealkiri"], stripslashes($_REQUEST["sisu"]));
$kask->execute();
header("Location: $_SERVER[PHP_SELF]");
$yhendus->close();
exit();
}
if(isSet($_REQUEST["kustutusid"])){
$kask=$yhendus->prepare("DELETE FROM lehed WHERE id=?");
$kask->bind_param("i", $_REQUEST["kustutusid"]);
$kask->execute();
}
if(isSet($_REQUEST["muutmisid"])){
$kask=$yhendus->prepare("UPDATE lehed SET pealkiri=?, sisu=? WHERE id=?");
$kask->bind_param("ssi", $_REQUEST["pealkiri"], stripslashes($_REQUEST["sisu"]), $_REQUEST["muutmisid"]);
$kask->execute();
}
?>
Teated lehel
close();
?>
Ning näide CKEditori abil kujundatud teatest.
Ülesandeid
* Tutvu lehel http://ckeditor.com/ redaktori demoga
* Otsi võimalusel ka teisi Javaskripti-põhiseid redaktoreid ning katseta neid
* Loo või otsi rakendus koerte andmete sisestamiseks ja vaatamiseks
* Võimalda koera kirjeldus kujundada graafilise redaktori abil.
Veebilehestiku lõikude hoidmine andmebaasis
Siiani tehtud näidetes on muudetav sisu olnud lehestiku struktuuri mõttes samas kohas - teateid on kord rohkem kord vähem, kuid nad saab ikka ühest loetelust kätte.
Kui kellegi tarbeks veebilehestikku teha, siis on küllalt tavaline, et sisu soovitakse muuta mitmesugustes kohtades - täiesti erinevatel lehtedel ning ka lehe paigutuse suhtes mitmesugustes kohtades. Üheks võimaluseks sellist lehestikku veebi kaudu hallatavaks teha on hoida kõik muudetavad kohad teadetena andmebaasis ning lehe näitamisel siis vastavad sisud sealt välja võtta. Nõnda on lehe haldus suhteliselt sarnane siinsele teadete haldusele. Lehte sisu poolest näidatakse just sellisena nagu ta tehtud on ning tekstide muutmise võimalus ei sea kuigivõrd piiranguid lehestiku ülesehitusele. Lihtsalt muudetavaid kohti peab olema piiratud hulk- et nende hilisem otsimine teadete menüüst ei osutuks üle jõu käivaks. Viisaka plokkide nimetamise abil saab ka sellest murest üle.
Funktsioonid eraldi failis
Väiksema lehestiku puhul kannatab enamiku vajaminevast ühte faili kokku kirjutada. Mõnda aega on niimoodi hea ülevaatlik. Ning uue rakenduse loomiseks piisab lihtsalt vastava lehe kopeerimisest. Suurema rakenduse puhul aga kipuks nõnda ühes failis olevate koodiridade hulk keerukalt suureks minema. Ligikaudu tuhatkond rida on suurus, mida kannatab hea süsteemi korral veel ühes failis hallata. Üle selle kipub igatmoodi kirjuks minema. Kui aga funktsioonid teemade kaupa eraldi failidesse - põhjalikuma struktueerituse puhul ka klassidesse - paigutada, siis on lootust veel tükk aega rahus elada enne, kui kood päriselt üle pea kasvama kipub.
Samuti on funktsioonide eraldamise eeliseks, et siis on kujundus võimalikult ühes failis ja programmeerimise pool teises. Nii ei teki liialt palju nii kujundajate kui ka arendajate poolt kirutud "spageti-koodi". Erinevalt mõnest muust keelest (ASP.NET, Zope lehemallid) ei ole PHPl kindlat ja üheselt tunnustatud koodi ja kujunduse eraldamise tava välja kujunenud, kuid kodeerimispraktikaid ja mallisüsteeme on loodud päris mitmesuguseid.
Siin näites loodi eraldi fail "sisufunktsioonid.php", kuhu esimeses järjekorras pannakse funktsioon teate sisu küsimiseks vastavalt ploki id-numbrile. Lehtede tabelist küsitakse otsitud teate sisu ning tagastatakse funktsioonist.
sisufunktsioonid.php
prepare("SELECT sisu FROM lehed WHERE id=?");
$kask->bind_param("i", $id);
$kask->bind_result($sisu);
$kask->execute();
if($kask->fetch()){
return $sisu;
}
return "Andmed kadunud";
}
?>
Avalehel - või ka mujal - teadete näitamiseks tuleb hoolitseda, et sisufunktsioonide fail oleks sisse loetud. Edasi saab teate andmed näidata andes funktsioonile ette vastava teatekoha identifikaatori. Kui kord teated valmis teha, siis edaspidi võib neile juba julgesti id järgi viidata. Kuni teadet vaid muudetakse, aga ei kustutata, senikauaks jääb id püsima. Nõnda siis piisabki hallatava sisuploki nägemiseks vaid kahest reast
require_once("sisufunktsioonid.php");
echo kysiSisu(16);
avaleht.php