Ajax ja WWW-grafiikka - Demo 9
Kertaillaan ensin hieman JavaScriptin tapahtumakäsittelyä. Hyödynnetään XMLHttpRequestia lomakkeen täytön apuna. Tehdään muutamia kaaviota tietokantatietojen perusteella. Lopuksi tehdään kuvagalleria, jossa hyödynnetään Ajaxia, tietokantoja ja GD-grafiikkakirjastoa.
Rekisteröinti - Ajax
Tehdään osoitelomake, joka hakee automaattisesti postitoimipaikan tietokannasta.
Tapahtumankäsittelyä
- Tee peopleen hakemisto demo9:iä varten. Luo uusi XHTML-dokumentti ja lomake, jossa on
kysytään nimeä, lähiosoitetta ja postinumeroa.
- index.html:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi"> <head> <title>Syötä osoitetiedot</title> <link href="rek.css" rel="StyleSheet" type="text/css" /> </head> <body> <form action="lisaa.php" method="post"> <fieldset> <legend>Tilaajan tiedot</legend> <div> <label for="etunimi">Etunimi</label> <input type="text" id="etunimi" name="etunimi" size="40" maxlength="100"/> <span id="etunimi_req">(*)</span> </div> <div> <label for="sukunimi">Sukunimi</label> <input type="text" id="sukunimi" name="sukunimi" size="40" maxlength="100"/> <span id="sukunimi_req">(*)</span> </div> <div> <label for="lahiosoite">Lähiosoite</label> <input type="text" id="lahiosoite" name="lahiosoite" size="40" maxlength="100"/> <span id="lahiosoite_req">(*)</span> </div> <div> <label for="postinro">Postinumero</label> <input type="text" id="postinro" name="postinro" size="5" maxlength="5"/> <span id="postinro_req">(*)</span> </div> <div> <label for="postipaikka">Postitoimipaikka</label> <input type="text" id="postitoimipaikka" name="postitoimipaikka" size="40" disabled="disabled"/> <span id="postitoimipaikka_req">(*)</span> </div> <div> <label for="oppilaitosryhma">Oppilaitosryhmä</label> <select id="oppilaitosryhma" name="oppilaitosryhma"></select> </div> <div> <label for="oppilaitos">Oppilaitos</label> <select id="oppilaitos" name="oppilaitos"></select> </div> </fieldset> <p><input type="submit" name="laheta" id="laheta" value="Lähetä!" /></p> </form> </body> </html>
- rek.css:
label { width: 20%; float: left; display: block; } .hidden { display: none; }
- index.html:
- Lataa samaan hakemistoon MochiKit.js ja tee uusi skriptitiedosto osoite.js. Lisää skripti XHTML:n head-osaan script-elementillä.
- Lisää kaikille lomake-elementeille tarkistus, että niissä on jotain tekstiä.
- Käytä tapahtumankäsittelijän lisäämiseen MochiKitin connect-metodia.
- Jos kyseisessä elementissä on jotain, niin piilota sitä vastaava (*)-merkki (
class-ominaisuuteen luokka hidden). Elementin id:n saa tapahtumankäsittelijässäthis.getAttribute("id")-metodilla. - Jos jossain kentässä on vikaa, niin poista lähetä-nappi käytöstä (disabled-attribuutti). Helpointa tässä tapauksessa on
tutkia kaikkien span-elementtien class-attribuutteja (
getElementByTagName). Tarkistuksesta kannattaa tehdä erillinen funktio, jota kutsutaan tapahtumankäsittelijästä.
XMLHttpRequest ja responseText
Toteutaan postitoimipaikan valinta postinumeron perusteella.
- Lisää postinumeron onchange-tapahtumaan vielä toinen tapahtumankäsittelijä. Tämän funktion tarkoitus on tarkistaa onko postinumeroa tietokannassa ja lisää vastaavan postitoimipaikan tekstikenttään.
- Luo demo9-hakemistoon SQLitellä uusi tietokanta, jossa on yksi taulu:
CREATE TABLE Postiosoite ( postinro CHAR(5) PRIMARY KEY NOT NULL, postitoimipaikka VARCHAR(100) NOT NULL );
- Lisää muutama testinumero:
INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('40740', 'Jyväskylä'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('40100', 'Jyväskylä'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('31600', 'Jokioinen'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('00100', 'Helsinki'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('33101', 'Tampere'); - Tee samaan hakemistoon PHP-tiedosto haetoimipaikka.php.
- Avaa PDO:lla tietokantayhteys luomaasi tietokantaan.
- Preparoi kysely
SELECT postitoimipaikka FROM Postiosoite WHERE postinro = :postinro
- Tee muuttuja $postinro ja aseta sen arvoksi
$_GET['postinro']. Kytke $postinro preparoituun kyselyyn. - Hae tuloksista ensimmäinen rivi (pitäisi tulla vain 0-1 riviä)
- Palautus tehdään pelkkänä tekstinä, joten aseta sivun MIME-tyypiksi text/plain ja merkistöksi ISO-8859-1:
header("Content-type: text/plain; charset=ISO-8559-1"); - Printtaa tämän jälkeen hakutuloksista postitoimipaikka mikäli sellainen saatiin kyselystä. Tulosta muuten tyhjä merkkijono.
- Lisää seuraava Javascript-ohjelmaasi:
var baseurl = "http://people.cc.jyu.fi/~tunnus/sovellukset/demo9/haetoimipaikka.php"; // hakasulkeet tarvitaan, koska queryString vaatii parametreiksi Array-muuttujia var query = queryString(["postinro"], [document.getElementById("postinro").value]); var url = baseurl + "?" + query;Muodosta baseurl-muuttujaan edellä tekemäsi PHP-ohjelman osoite. - Lisää MochiKitin avulla
doSimpleXMLHttpRequest-kutsu ja sille muodostettu osoite.
Tee funktiot lisaaPaikka(response) ja lisaaPaikkaVirhe. Lisää nämä kutsun palauttaman olion callback-funktioiksi (
addCallbacks). - Aseta lisaaPaikka-funktiossa postitoimipaikka-kenttään (
value) saatu teksti (response.responseText).- Jos vaste on jotain muuta kuin tyhjä merkkijono (ts. löytyi jokin postitoimipaikka), niin sijoita se postitoimipaikka-kenttään ja piilota postitoimipaikka_req -id:llä merkitty span-elementti.
- Jos vaste on tyhjä merkkijono, niin sijoitetaan se postitoimipaikkakenttään, mutta ei poisteta postitoimipaikan (*)-merkintää.
- Hyväksy myös submit-nappulan käyttö, jos kaikki (*) on piilossa.
- lisaaPaikkaVirhe-funktio suoritetaan jos palvelimelta ei saada HTTP-vastetta tai vasteena tuli jokin virheilmoitus. Näytä silloin postitoimipaikka_req -id:llä merkitty span-elementti ja estä submit-nappulan käyttö.
XMLHttpRequest ja responseXML
Toteutetaan vielä oppilaitoksen valinta.
Oppilaitoksien lisääminen tietokantaan
Lisää luomaasi tietokantaan vielä taulut oppilaitosryhmistä ja oppilaitoksista seuraavien SQL-lauseiden avulla:
- Hae oppilaitos.sql demo-hakemistoon:
wget http://appro.mit.jyu.fi/sovellukset/demot/demo9/oppilaitos.sql
- Avaa sql-komentotulkki:
sqlite3 omatietokantatiedosto
- Suorita tiedostossa olevat sql-lauseet
.read oppilaitos.sql
- Poistu komentotulkista
.quit
- Lisää luku- ja kirjoitusoikeudet tietokantatiedostoon ja hakemistoon kaikille käyttäjille.
Ryhmien hakeminen tietokannasta ja lisääminen valintalistaan
- Toteuta haeryhmat.php-sivu, joka hakee oppilaitosryhmät tietokannasta ja tulostaa ne select-option-listana.
- Avaa yhteys PDO:lla
- Preparoi SQL-kysely
SELECT RyhmaID, RyhmaNimi FROM Oppilaitosryhma
- Lisää otsikkotietoihin MIME-tyypin määritys text/xml (header).
- Tulosta xml-deklaraatio ja select-elementti.
Lisää select-elementtiin attribuutit
- xmlns="http://www.w3.org/1999/xhtml" Nimiavaruusmääritys tarvitaan, että elementti voidaan siirtää XHTML-dokumenttiin.
- id="oppilaitosryhma" Elementin valintaa varten.
- name="oppilaitosryhma" Lomakkeenkäsittelijää varten.
- Tulosta select-elementin loppu. XML-palasen voisi luoda print-lauseiden sijaan myös PHP:n DOM-rajapinnan avulla (kts. luento 18)
- Lisää pääsivun
window.onload-funktioon doSimpleXMLHttpRequest, joka pyytää haeryhmat.php-sivua. - Lisää callback-funktiot
lisaaRyhmat(resp)jalisaaRyhmatVirheonnistuneelle ja epäonnistuneelle HTTP-pyynnölle. Epäonnistuneessa tapauksessa voi toistaiseksi ilmoittaa virheestä esim.alert-funktiolla. - Toteutetaan
lisaaRyhmat-funktioonselect-elementin vaihto. Hae saadusta XML-vasteesta juurielementti ja sen lapsielementit (resp.responseXML.documentElement) importNode-funktiolla johonkin muuttujaan. Saatu select-elementti voidaan vaihtaa toiseen swapDOM-funktiolla. - IE:n puutteellinen DOM-tuki ja erilliset XML- ja HTML-komponentit aiheuttavat kuitenkin vielä ongelmia. Tehdään vielä vaihtoehtoinen viritys IE:lle. Tutki ensin
onko käytössä
document.importNode-funktiota. Jos ei ole, niin suorita seuraava koodi:var temp = createDOM("div"); temp.innerHTML = toHTML(resp.responseXML.documentElement); swapDOM("oppilaitosryhma",temp.firstChild);Tämä muuntaa saadun sisällön HTML-tekstiksi ja lisää sen itse luodun väliaikaisen DOM-elementin sisälle. Nyt elementin sisältö voidaan siirtää sivun dokumenttipuuhun.
Oppilaitoksen vaihtaminen valinnan perusteella
- Toteuta haeoppilaitokset.php-sivu, jonka ottaa GET-parametriksi ryhmaid:n ja palauttaa sen perusteella ryhmään kuuluvat oppilaitokset.
Pohjana voit käyttää haeryhmat.php:ta eli tee siitä kopio.
- Avaa yhteys PDO:lla tietokantaasi.
- Valmistele SQL-kysely
SELECT OppilaitosID, Nimi FROM Oppilaitos WHERE Oppilaitosryhma_RyhmaID = :ryhmaid
- Luo muuttuja $ryhmaid, johon asetetat
$_GET-taulukosta saadun ryhmätiedon. - Kytke :ryhmaid ja $ryhmaid
bindParam-funktiolla. - Lisää otsikkotietoihin MIME-tyypin määritys text/xml (header).
- Tulosta xml-deklaraatio ja select-elementti. Lisää select-elementtiin attribuutit xmlns="http://www.w3.org/1999/xhtml" id="oppilaitos" name="oppilaitos".
- Lisää
window.onload-funktioon MochiKitinconnect-funktiollaonchange-tapahtumankäsittely oppilaitosryhmat-valintalistalle. - Kutsu tämän tapahtumankäsittelijässä
doSimpleXMLHttpRequest-funktiollahaeoppilaitos.php-sivua query-muuttujan parametreilla. query-muuttujaan tulee ryhmän id, joka saadaan valintalistan arvosta:var baseurl = "http://people.cc.jyu.fi/~tunnus/sovellukset/demo9/haeoppilaitos.php"; var query = queryString(["ryhmaid"], [getElement("oppilaitosryhma").value]); var url = baseurl + "?" + query; - Lisää callback-funktiot tälle XMLHttpRequest-kutsulle. Virhetapaus voi olla vaikka sama kuin edellä. Onnistuneessa
tapauksessa täytä oppilaitoslista saadulla XML-vasteella kuten edellä.
- Itseasiassa on mahdollista käyttää samaa funktiota,
jos
ensin selvität saadusta vasteesta kumpi select-elementti pitää vaihtaa
(
resp.responseXML.documentElement.getAttribute("id")), mutta voit tehdä myös erilliset funktiot.
- Itseasiassa on mahdollista käyttää samaa funktiota,
jos
ensin selvität saadusta vasteesta kumpi select-elementti pitää vaihtaa
(
- Lisää vielä
window.onload-funktioon vielä kutsusignal("oppilaitosryhma","onchange"), että listaan tulee ensimmäiset vaihtoehdot.
WWW-grafiikka
Diagrammit Libchart-kirjastolla
Tehdään muutama diagrammi tietokantaan tallennettujen tietojen perusteella.

- Luo uusi SQLite-tietokanta, joka pitää kirjaa pienen kuvitteellisen sijoittajaporukan osakesalkuista
- Lataa luontiin tarvittavat sql-lauseet
wget http://appro.mit.jyu.fi/sovellukset/demot/demo9/osake.sql
- Avaa sql-komentotulkki:
sqlite3 osake
- Suorita tiedostossa olevat sql-lauseet
.read osake.sql
- Poistu komentotulkista
.quit
- Lisää luku- ja kirjoitusoikeudet tietokantatiedostoon ja hakemistoon kaikille käyttäjille.
- Lataa luontiin tarvittavat sql-lauseet
- Hae Libchart-kirjasto
wget http://naku.dohcrew.com/libchart/files/libchart-1.1.tar.gz
- Pura paketti
tar -xvzf libchart-1.1.tar.gz
- Luo uusi PHP-sivu, joka palauttaa henkilöiden osakemäärät pylväsdiagrammina osakkeen koodin perusteella
- Liitä sivulle libchart-kirjaston luokat:
require_once ("libchart-1.1/libchart/libchart.php"); - Luo PDO:lla tietokantayhteys osake-kantaan
- Valmistele kysely, joka hakee henkilöt, määrät ja osakkeet:
SELECT OsakeNimi, Maara, HenkiloNimi FROM Osake, Omistaa, Henkilo WHERE Omistaa.Osake_OsakeKoodi = Osake.OsakeKoodi AND Omistaa.Henkilo_HenkiloID = Henkilo.HenkiloID AND Osake.OsakeKoodi = :koodi
- Aseta muuttujaan $koodi GET-metodista saatu tieto. Kytke $koodi-muuttujaan kyselyn :koodi
- Luo uusi VerticalChart (kts. esimerkkikoodi).
- Suorita SQL ja käy läpi saadut tiedot rivi kerrallaan.
- Luo uusi piste, jonka labelina on henkilön nimi ja arvopisteenä saatu määrä. Sijoita osakkeen nimi johonkin muuttujaan.
- Tulosta MIME-tyypiksi image/png header-funktiolla.
- Lisää palkille otsikoksi osakkeen nimi.
- Piirrä kaavio render-funktiolla. Älä anna tiedostoparametria, jolloin kuvadata tulee suoraan HTTP-vasteeseen.
- Liitä sivulle libchart-kirjaston luokat:
- Kokeile kaaviosivun toimintaa esimerkiksi osoitteilla:
http://people.cc.jyu.fi/~tunnus/sovellukset/demo9/kaavio.php?koodi=NOK1V http://people.cc.jyu.fi/~tunnus/sovellukset/demo9/kaavio.php?koodi=OUT1V
- Tehdään vielä piirakkadiagrammi, joka kertoo koko porukan osakkeiden jakautumisen eri toimialoittain
- Kopioi edellinen PHP-sivu toiselle nimelle
- Vaihda kyselyksi seuraava:
SELECT TyyppiNimi, SUM(Maara) as Osakemaara FROM Osake, Tyyppi, Omistaa WHERE Omistaa.Osake_OsakeKoodi = Osake.OsakeKoodi AND Osake.Tyyppi_TyyppiID = Tyyppi.TyyppiID GROUP BY TyyppiNimi
- Poista
bindParam, koska nyt toimitaan pelkästään tietokannasta saatujen tietojen perusteella. - Vaihda
VerticalChart-olion tilallePieChart-olion luonti. Luonti toimii vastaavasti. - Vaihda uuden pisteen luontiin labeliksi riviltä saatu
TyyppiNimija arvoksiOsakemaara - Kaavion otsikoksi voi laittaa esim.
Osakkeiden jakautuminen aloittain.
- Kokeile kuvaa www-sivulta, esim.
http://people.cc.jyu.fi/~tunnus/sovellukset/demo9/piirakka.php
- Luo uusi XHTML-sivu, jossa on useampi edellä tehty kaavio. Käytä img-elementtiä ja laita src-attribuuttiin sopiva PHP-tiedosto sopivilla parametreilla. Tietokannassa on seuraavia osakekoodeja: NOK1V, TIE1V, ELQAV, UPM1V, OUT1V, RTRKS ja ORNBS.
Kuvien siirto palvelimelle ja muokkaaminen GD-kirjastolla
Toteutetaan pieni sovellus, jonka avulla voidaan siirtää kuvia palvelimelle ja näyttää ne pienennettynä nimen kera.
- Luo uusi tietokanta ja sinne seuraava taulu:
CREATE TABLE Kuva ( KuvaID INTEGER PRIMARY KEY AUTOINCREMENT, KuvaNimi VARCHAR(100) NOT NULL, TiedostoNimi VARCHAR(100) NOT NULL ) ;
Anna tietokantatiedostoon kirjoitusoikeus. - Tehdään uusi PHP-sivu, jolla voidaan siirtää kuvia palvelimelle. Lisää lomake, jossa kysytään kuvan nimeä
ja tiedostoa. Tiedostovalinnan saa tehtyä
<input type="file" name="tiedosto" />-elementillä. Lisäksiform-elementin attribuuttiin on lisättäväenctype="multipart/form-data". - Tee demo-hakemistoon uusi hakemisto kuvat. Anna tähän hakemistoon kaikille kirjoitusoikeus (mikä ei yleensäottaen ole turvallista..).
-
Tee jollekin sivulle lomakkeelle käsittelijä. Tiedostoa pystyy nyt käsittelemään $_FILES-taulukon avulla.
Tutkittava väliaikaistiedosto löytyy taulukosta indekseillä
$_FILES['tiedosto']['tmp_name']. - Tutki onko lähetetty tiedosto tullut perille is_uploaded_file-funktiolla.
-
Lisäksi olisi syytä tarkistaa, että kuvan pääte on
jpgtaijpeg(funktioilla substr ja strrpos) ja että kuva on oikeasti jpeg-tyyppinen (testaamalla onnistuuko kuvan lataaminen GD-kirjastolla) ja että tiedoston koko on järkevä ($_FILES['tiedosto']['size']ja PHP:nMAX_FILE_SIZE-asetus). Näitä ei nyt tarvitse kuitenkaan toteuttaa.
-
Lisäksi olisi syytä tarkistaa, että kuvan pääte on
- Jos siirto on onnistunut hyvin, niin siirrä tiedosto kuvat-kansioon move_uploaded_file-funktiolla.
Pelkän tiedoston nimen saa
$_FILES['tiedosto']['name']-muuttujasta. Lisää tietokantaan käyttäjä antama kuvan nimi ja tiedoston nimi. Tarvittava SQL:INSERT INTO Kuva (KuvaNimi, TiedostoNimi) VALUES (:kuvanimi, :tiedostonimi)
:kuvanimi ja :tiedostonimi voidaan kytkeäbindParam-metodilla$_POST-muuttujasta saatuihin tietoihin. Sijoita tiedot ensin sopiviin väliaikaismuuttujiin. - Tee uusi PHP-tiedosto, joka antaa kuvan pienennettynä ja lisää yläkulmaan kuvan nimen
- Avaa tietokantayhteys ja hae kuvan nimi ja tiedostonimi kuvaid:n perusteella. Tarvittava SQL:
SELECT KuvaNimi, TiedostoNimi FROM Kuva WHERE KuvaID = :kuvaid
- Kytke GET-parametrista saatu kuvaid SQL-kyselyn
:kuvaid:hen. Suorita SQL ja hae ensimmäinen (ja ainoa) rivi $row-muuttujaan. - Lataa kuva
$kuva = imagecreatefromjpeg("./kuvat/".$row['TiedostoNimi']); - Luo uusi kuva, jonka koko on 100x100:
$smallkuva = imagecreatetruecolor(100, 100);
- Haetaan alkuperäisen tiedoston koko ja muutetaan se pienemmäksi
list($leveys, $korkeus) = getimagesize("./kuvat/".$row['TiedostoNimi']); imagecopyresized($smallkuva, $kuva, 0, 0, 0, 0, 100, 100, $leveys, $korkeus) - Lisää teksti yläkulmaan seuraavasti
// asetetaan väriarvot $white = imagecolorallocate($smallkuva, 255, 255, 255); $black = imagecolorallocate($smallkuva, 0, 0, 0); // tehdään pohjalle valkoinen laatikko tekstin pituuden mukaan imagefilledrectangle($smallkuva, 0, 0, 7.5*strlen($row['KuvaNimi']), 15, $white); // lisätään teksti kuvaan imagestring($smallkuva, 3, 0, 0, $row['KuvaNimi'], $black);
- Tulosta MIME-tyypiksi image/jpeg header-funktiolla.
- Tulosta kuvadata kutsumalla
imagejpeg($smallkuva).
- Avaa tietokantayhteys ja hae kuvan nimi ja tiedostonimi kuvaid:n perusteella. Tarvittava SQL:
- Tee vielä PHP-tiedosto, joka hakee kaikki KuvaID:t tietokannasta
SELECT KuvaID FROM Kuva;
- Käy läpi palautusta riveittäin ja tulosta joka kierroksella:
print '<img src="kuva.php?kuvaid="'.$row['KuvaID'].'" alt="" />';

