OSA IV Yhteenveto. Oppitunti. 23. Esimerkki (Osa 1) 24. Esimerkki (Osa 2)

Samankaltaiset tiedostot
OPPITUNTI 24 Esimerkki (Osa 2)

OPPITUNTI 12 MySQL-tietokannan käyttö

Tuotteiden tiedot: Lisää uuden tuotteen tiedot. Muuta tai poista tuotteen tiedot. Selaa kaikkien tuotteiden tietoja.

Harjoituksen aiheena on tietokantapalvelimen asentaminen ja testaaminen. Asennetaan MySQL-tietokanta. Hieman linkkejä:

OPPITUNTI15 Päivämäärien käsittely

LINUX-HARJOITUS, MYSQL

OPPITUNTI 20 Tilan tallentaminen istuntofunktioilla

Insert lauseella on kaksi muotoa: insert into taulu [(sarakenimet)] values (arvot)

Proseduurit, funktiot ja herättimet - esimerkkeinä Oracle, SQL Server, MySQL ja OCELOT. Jouni Huotari S2008

PROSEDUURIT, FUNKTIOT JA HERÄTTIMET - ESIMERKKEINÄ ORACLE, SQL SERVER, MYSQL JA OCELOT JOUNI HUOTARI K2009

Written by Administrator Monday, 05 September :14 - Last Updated Thursday, 23 February :36

OPPITUNTI 19 Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla

ICT1TN004. Lomakkeet. Heikki Hietala

Harjoitustyö: virtuaalikone

Verkkokaupan ohje. Alkutieto. Scanlase verkkokauppa. Sisäänkirjautuminen

OPPITUNTI 11 DBM-funktioiden käyttö

Kirjasto Relaatiotietokannat Kevät Auvinen Annemari Niemi Anu Passoja Jonna Pulli Jari Tersa Tiina

Tiedonhallinnan perusteet. H11 Ovien ja kulun valvontajärjestelmän tietokanta

SQL Buddy JAMK Labranet Wiki

TIETOKANTOJEN PERUSTEET MARKKU SUNI

Sähköpostitilin käyttöönotto

OHJE 1 (14) Peruskoulun ensimmäiselle luokalle ilmoittautuminen Wilmassa

Sonera Viestintäpalvelu VIP VIP Laajennettu raportointi Ohje

SQL-perusteet, SELECT-, INSERT-, CREATE-lauseet

Pika-aloitusopas. Sisältö: Projektin luominen Projektin muokkaaminen ja hallinnointi Projektin/arvioinnin tulosten tarkastelu

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

ProTieto Oy. Verottajan ilmoitus. Käyttöohje alihankkijoille

Sähköpostitilin luonti

OSA III PHP:n käyttö. Oppitunti

Copyright Basware Corporation. All rights reserved. Pikaopas toimittajille Supplier Portal (Toukokuu 2013)

8. Oliot 123. Kuinka luokkia luodaan ja olioita saadaan aikaan. Kuinka luodaan ja käsitellään ominaisuuksia ja metodeja

Tietokannat II -kurssin harjoitustyö

ITKP102 Ohjelmointi 1 (6 op)

1. Valitse käyttäjänimi 2. Kirjoita salasana 3. Anna sähköpostiosoitteesi 4. Keksi wikillesi nimi

SELVITYSRAPORTTI LABRA-VERKON MYSQL:n JA PHP:n KÄYTTÖÖNOTOSTA. Jarkko Kähkönen

TIETOKANNAT: MYSQL & POSTGRESQL Seminaarityö

Open Badge -osaamismerkit

DriveGate -ohjeet. DriveGate-käyttöohjeet: Rekisteröityminen palveluun. Rekisteröitymisohjeet ja rekisteröitymisprosessin kuvaus

Geni - pikaopas. Kuvalliset pikaohjeet, jotta pääset aloittamaan Geni -sukupuusi rakentamisen.

Joomla pikaopas. Yksinkertainen opas, jossa neuvotaan esimerkkisivuston teko Joomla julkaisujärjestelmällä vaihe vaiheelta.

JavaScript alkeet Esimerkkikoodeja moniste 2 ( Metropolia)

Ohjelmoinnin jatkokurssi, kurssikoe

Mainonnanhallinta Käyttöopastus. Tekniikka. Olli Erjanti Mediareaktori

Tiedonhallinnan perusteet. Viikko 1 Jukka Lähetkangas

Sisällys Clerica Web-sovellusten käytön aloittaminen 2

OPAS KULTA2 -JÄRJESTELMÄN KÄYTTÖÖN

Toimittajaportaalin rekisteröityminen Toimittajaportaalin sisäänkirjautuminen Laskun luonti Liitteen lisääminen laskulle Asiakkaiden hallinta Uuden

Mikä on Twitter? Rekisteröityminen

Toimittajaportaalin pikaohje

SMT / SUOMEN MATKATOIMISTO OY Y-TUNNUS / FO-NUMMER / BUSINESS ID

Ohjelmoinnin perusteet Y Python

1. ASIAKKAAN OHJEET Varauksen tekeminen Käyttäjätunnuksen luominen Varauksen peruminen... 4

Informaatiotekniikan kehitysyksikkö

Webinaari -koulutukset

Toimittajaportaalin pikaohje

ITKP102 Ohjelmointi 1 (6 op)

Yliopistohaku.fi -palvelun Oma haku -palvelu

OHJEET WORDPRESS-BLOGIN LUOMISEEN JA TAVALLISIMPIIN BLOGITOIMINTOIHIN

6. Funktiot 85. Kuinka funktioita määritellään ja kutsutaan. Kuinka funktioille viedään arvoja ja niistä palautetaan arvoja

Toteutusdokumentti. Aija. Helsinki Ohjelmistotuotantoprojekti HELSINGIN YLIOPISTO Tietojenkäsittelytieteenlaitos

EU Login. EU Login kirjautuminen. EU Login tilin luominen

Tietokannat II -kurssin harjoitustyö

Ohjelmoinnin perusteet Y Python

SELECT-lauseen perusmuoto

OPPITUNTI 5 Ohjelman kulku

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Järjestelmän syötteet ja tulosteet Kohahdus Helsinki Ohjelmistotuotantoprojekti HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos

OSAAMISENHALLINTA OMAT TIEDOT. Peruskäyttäjän pikaohje

WWW-PALVELUN KÄYTTÖÖNOTTO LOUNEA OY

Käyttöohje Suomen Pankin DCS2-järjestelmään rekisteröityminen

Lomake kannattaa asemoida taulukkoon: table. Silloin selitteet ja kentät saadaan sarakkeisiin. Kenttien ulkoasu voidaan määritellä tyyleillä.

Harjoitus 2 (viikko 45)

Datahub-tukipalvelu ServiceNow. Käyttöohje

11. oppitunti III. Viittaukset. Osa. Mikä on viittaus?

SUOMI LIIKKUU KOULULIIKUNTATAPAHTUMIEN ILMOITTAUTUMISJÄRJESTELMÄ

HÄLYRI-SOVELLUKSEN KÄYTTÖOHJEET

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

JYVÄSKYLÄN SEUDUN. 1. Sisältö * * Tähdellä merkityt kohdat ovat pakollisia. Sivun oikeassa yläkulmasta löytyy Lisää oma tapahtumasi.

Ohjelmoinnin perusteet Y Python

Oma kartta Google Maps -palveluun

Verkkojulkaiseminen Minna Väisänen. HTML5-tehtävä

Tehtävä 1. Tietojen lisääminen, poistaminen, päivittäminen ja tulostaminen

INTINU13A6 Java sovellukset

Drupal-sivuston hallintaopas

Tässä tehtävässä käsittelet metodeja, listoja sekä alkulukuja (englanniksi prime ).

Maventa Connector Käyttöohje

Tapahtumakalenteri & Jäsentietojärjestelmä Ylläpito

TIETOJENKÄSITTELY/PIKATAIVAL2 Tehtävä G

Ohje Emmi-sovellukseen kirjautumista varten

Innocent drinks Cookie Policy

Basware Portal palvelun ohje toimittajille

KESKUSTANUORTEN NETTISIVUT- OHJEITA PIIRIYLLÄPITÄJÄLLE 1. KIRJAUTUMINEN

Ohjelman käyttöön ei sisälly muita kuluja kuin ohjelman lisenssimaksu ja mahdolliset webbipalvelusi käyttömaksut.

Tik Tietojenkäsittelyopin ohjelmatyö Tietotekniikan osasto Teknillinen korkeakoulu KÄYTTÖOHJE. LiKe Liiketoiminnan kehityksen tukiprojekti

Vianova Systems Finland Oy:n Novapoint käytön tuki

Tonttihakemuksen tekeminen

Kuva: Ilpo Okkonen

TYÖNTEKIJÄN KÄYTTÖOHJEET - SOKU

Transkriptio:

OSA IV Yhteenveto Oppitunti 23. Esimerkki (Osa 1) 24. Esimerkki (Osa 2)

23. Esimerkki (Osa 1) 395 OPPITUNTI 23 Esimerkki (Osa 1) Jos olet ollut mukana alusta alkaen, sinulla pitäisi nyt olla hyvät perustyökalut PHP-ohjelmien tekemiseen. Tällä ja seuraavalla tunnilla luomme kokonaisen toimivan esimerkin, jossa käytetään useita aiemmissa luvuissa esitettyjä tekniikoita. Tämän tunnin aiheita ovat seuraavat: Kuinka luodaan projektisuunnitelma Kuinka käytetään include()-funktiota funktiokirjastojen ja joustavien navigointialkioiden luomiseen Kuinka ylläpidetään tilaa kyselymerkkijonoilla, tietokannoilla ja istuntofunktioilla Kuinka erotetaan HTML PHP:stä, jotta tekniikasta tietämättömät kehittäjät osaisivat työskennellä dynaamisten ympäristöjen kanssa Kuinka käytetään header()-funktiota johdattamaan käyttäjä toisaalle Kuinka muodostetaan tilien suojaamisen strategia

396 23. Esimerkki (Osa 1) Lyhyesti Olettakaamme, että yhteisön portaali on pyytänyt meitä rakentamaan pienen vuorovaikutteisen tapahtumapäiväkirjan pienille kaupungeille, joita se palvelee. Kerhot ja yhtyeet voisivat rekisteröityä sisälle ja julkistaa tapahtumansa. Käyttäjät voisivat sitten tarkistaa tapahtumatietokannan nähdäkseen, mitä on meneillään. Käyttäjät voisivat kaventaa etsintäänsä kerhotyyppeihin ja erilaisiin tapahtumiin; he voisivat myös valita alueen, jolla tapahtumien tulisi olla. Tällä ja seuraavalla tunnilla työskentelemme tämän sovelluksen alustavalla versiolla. Rakenne Ennen kuin alamme kirjoittaa koodia, meidän tulee päättää tarkasti, kuinka skripti toimii. Kuinka käyttäjä voisi navigoida ympäristössä? Mitä näyttöjä tulisi ottaa mukaan? Projekti jakautuu luonnollisesti kahteen ympäristöön. Ensiksikin on jäsenten alue, jota käytetään kerhoinformaation ylläpitoon ja uusien tapahtumien lisäämiseksi päiväkirjaan. Toiseksi on käyttäjien alue, jolla he selaavat tietokantaa. Kuva 23.1 näyttää sovelluksen rakenteen. KUVA 23.1 Sovelluksen rakenne Ohjelmiston osatekijät (valikot ja tietojoukot) ovat siis: Members (Jäsenet) Menu (Valikko) Home (Koti) Join Login (Liity) Update (Päivitä) Club (Kerho) Review (Katsele) Events (Tapahtumat) Update (Päivitä) Event (Tapahtuma) View (Näytä)

23. Esimerkki (Osa 1) 397 Events (Tapahtumat) View (Näytä) Members Public (Jäsenet) Uudet jäsenet liittyvät kerhoon sivun join.php kautta; sivulla he voivat asettaa nimi- ja salasanatietonsa. Olettaen, että heidän valitsemansa nimi ei ole jo varattu, heidät johdatetaan lomakkeelle updateclub.php, jossa he voivat antaa kerhotietonsa. Kun tämä lomake on täytetty, voivat jäsenet lisätä tapahtumia. Kun kerhot on lisätty, jäsen lähetetään jäsenten valikkosivulle (membersmenu.php), josta löytyvät kaikki jäsenten tarvitsemat toiminnot. Olemassa oleva jäsen voi aloittaa liittymissivulta (login.php). Kun nimi ja salasana on tarkistettu, jäsen voidaan ohjata suoraan sivulle membersmenu.php. Valikkosivulta jäsenet voivat lisätä uusia tapahtumia (updateevent.php) ja katsella listaa asetetuista tapahtumista (reviewevents.php). He voivat myös muuttaa kerhotietojaan milloin tahansa (updateclub.php). Kaikki käyttäjät voivat katsella tapahtumalistaa, joko kuukausitasolla tai vuositasolla tai hakea tapahtumia tyypin tai alueen mukaan; hakeminen tapahtuu sivulta viewevents.php. He voivat myös tutkia sijainnin tai tyypin mukaan generoitua kerholuetteloa; toiminto löytyy sivulta viewclubs.php. Käyttäjät voivat myös klikata kerhoa tai tapahtumaa ja saada tarkempaa tietoa sivulta viewevent.php tai viewclub.php. Tietokannan suunnittelu Luomme nyt tietokannan nimeltä organizer ja laitamme siihen neljä taulua: clubs, events, areas ja types. Meidän on selvästikin tallennettava tietoa kerhoista ja tapahtumista erillisiin tauluihin, koska yhdestä kerhosta on linkkejä useisiin tapahtumiin. Rakennamme erilliset taulukot määrittämään tapahtumatyypit ja alueet, koska silloin meidän on helpompi rakentaa alasvetovalikoita tiedon etsintää ja lisäämistä varten. Luomme taulukot käsin; ensiksi clubs-taulu: create table clubs ( id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY( id ), name VARCHAR(50), type CHAR(3), area CHAR(3), mail VARCHAR( 50 ), description BLOB, login VARCHAR(8), password VARCHAR(8) ); Jäsen lisää tietoa kenttiin name, mail, description, login ja password. Kenttä nimeltä type tallentaa tyypin ja area taas alueen ja noiden kenttien kautta saadaan tästä taulusta yhteys types- ja areas-tauluihin.

398 23. Esimerkki (Osa 1) The events-taulu tallentaa tietoa kerhojen tapahtumista: create table events( id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY( id ), type CHAR(3), area CHAR(3), edate INT, ename VARCHAR(100), evenue VARCHAR(100), eaddress VARCHAR( 255 ), ezip VARCHAR(20), edescription BLOB, eclub INT NOT NULL ); Huomaa, että myös tässä taulussa on type- ja area-kenttä. Kerho saattaa sijaita kaupungin pohjoispuolella, mutta tapahtuma pidetään etelässä. Jokin yhdistys voi pitää koulutustilaisuuden tai poliittisen tilaisuuden. Kenttä nimeltä eclub tallentaa sen kerhon ID-tunnisteen, jolle tapahtuma kuuluu. Voimme käyttää sitä luettelemaan kaikki tapahtumat, jotka kuuluvat tietylle kerholle, tai ottamaan esille kerhotietoa tapahtumalistasta käsin. Alueita ja tyyppejä varten toteutetut taulut, types ja areas, ovat yksinkertaisia: create table areas( id CHAR(3), area VARCHAR( 30 ) ); create table types( id CHAR(3), type VARCHAR( 30 ) ); Jäsen ei voi muuttaa näiden taulukoiden tietoja. Sen sijaan me voimme lisätä tauluihin omaa luokittelutietoamme käyttämällä SQL-kielen INSERT-lauseita. Jäsenelle esitetään nuo arvot vaihtoehtoina: INSERT INTO types ( id, type ) VALUES("COM", "Community"); Taulukot 23.1 ja 23.2 sisältävät lisättävät tiedot.

23. Esimerkki (Osa 1) 399 TAULUKKO 23.1 Tieto, joka lisätään types-tauluun ID MUS FAM SOC COM Types Music Family Social Community TAULUKKO 23.2 Tieto, joka lisätään areas-tauluun ID NOR SOU EAS WES Areas North South East West Suunnitteluvaihtoehdot Olet jo nähnyt vaihtoehdot ja valinnat, joita olemme tehneet rakennetta pohtiessamme. Aiomme kehittää erillisen sivun jokaista näyttöä kohti sen sijaan, että yksittäinen sovellus palvelisi useita eri sivuja aina olosuhteiden mukaan. Valitsemallamme lähestymistavalla on hyvät ja huonot puolensa. Dynaamisen ympäristön muodostaminen, joka käyttää useita sivuja, voi johtaa toistuviin koodirakenteisiin ja tehdä projektista vaikeammin ylläpidettävän, varsinkin, jos projekti laajenee. Toisaalta voimme antaa täydellisen prototyypin suunnittelijoille ja sivujen kehittäjille ja he voivat kehittää sivut melkein kuin ne olisivat standardeja HTML-sivuja. Jäsenten ympäristö Nyt on aika aloittaa koodaaminen! Tämän luvun loppuosassa kehitetään jäsenille tarkoitettu sovelluksen osio. On hyvä aloittaa tämä jakso laittamalla tauluihin ensin testitietoa. Muttaa ennen kuin voimme tehdä niin, meidän tulee kuitenkin luoda tili itsellemme. join.php ja dblib.inc Tiedosto join.php tallentaa lomakkeen, jonka kautta uusi jäsen voi lähettää käyttäjänimensä ja salasanansa. Tarkistaaksemme, ovatko tunnisteet jo käytössä, ja lisätäksemme sitten uutta tietoa meidän tulee ensin avata tietokanta. Tätä toimintoa tarvitsemme usein, joten lienee parasta tehdä toimintoa varten funktio ja tallentaa se erilliseen asiakirjaan. Kyseinen tiedosto voidaan sitten sisällyttää kaikille sivuille include()- lauseella. Itse asiassa käytämme tuota asiakirjaa, jolle annamme nimen dblib.inc, kaikkien tietokantaa käsittelevien funktioiden majapaikkana. Näin PHP-sivumme pysyvät puhtaina SQL-lauseista. Tietokantafunktioiden erottaminen pääkoodista helpottaa myös skriptien muuttamista sellaisiksi, että ne toimivat erilaisten tietokantasovellusten kanssa. Joskus funktioita saatetaan joutua kirjoittamaan uusiksi, mutta kutsuvan koodin tulisi toimia odotetusti sellaisenaan. Luokaamme nyt funktio, joka kytkeytyy tietokantaan.

400 23. Esimerkki (Osa 1) LISTAUS 23.1 Ote tiedostosta dblib.inc 1: $link; 2: connecttodb(); 3: function connecttodb() 4: { 5: global $link; 6: $link = mysql_connect( "localhost", "harry", "elbomonkey" ); 7: if (! $link ) 8: die( "Couldn't connect to MySQL" ); 9: mysql_select_db( "organizer", $link ) 10: or die ( "Couldn't open organizer: ".mysql_error() ); 11: } Edellä oleva connecttodb()-funktio esittelee globaalin muuttujan $link, joka tallentaa tietokantatunnisteen, jonka mysql_connect() palauttaa. Olemme määritelleet $link-muuttujan globaaliksi, jotta toiset tietokantakyselyitä tekevät funktiot voisivat käyttää sitä. Emme pelkästään kytkeydy mysql-demoniin connecttodb()-funktiolla; yritämme myös valita organizer-tietokannan. Koska näiden operaatioiden menestyminen on olennaista koko ympäristön toiminnalle, lopetamme suorituksen, jos joko mysql_connect() tai mysql_select_db() epäonnistuu. Luomme edelleen kirjaston, jonka kaikki sivut jakavat. Sen nimi on nyt clublib.inc ja se sisältää funktioita, jotka auttavat istunnon hallinnassa ja todentamistoiminnoissa. Käytämme istuntofunktioita tallentamaan assosiatiivisen taulukon nimeltä $session. Käynnistämme sitten clublib.inc-tiedostossa funktion session_start() aloittamaan tai eväämään istunnon ja funktion session_register() yhdistämään muuttujan siihen: Listaus 23.2 Ote tiedostosta clublib.inc 1: session_start(); 2: session_register( "session" ); Muista, että sisällytettävissä tiedostoissa oleva PHP-koodi tulee laittaa tagien (<?php) ja (?>) sisälle. Saattaa näyttää melkoisen hämäävältä tallentaa toiminnallisuutta erillisiin tiedostoihin, mutta se vähentää meiltä runsaasti toistuvia koodiosia. Olemme nyt valmiita luomaan sivun join.php. Koodi on listauksessa 23.3.

23. Esimerkki (Osa 1) 401 Listaus 23.3 join.php 1: <?php 2: include("dblib.inc"); 3: include("clublib.inc"); 4: $message=""; 5: if ( isset( $actionflag ) && $actionflag=="join") 6: { 7: if ( empty( $form[login] ) 8: empty( $form[password] ) 9: empty( $form[password] ) ) 10: $message.= "you must fill in all fields<br>\n"; 11: if ( $form[password]!= $form[password2] ) 12: $message.= "Your passwords did not match<br>\n"; 13: if ( strlen( $form[password] ) > 8 ) 14: $message.= "Your password must be less than 15: 8 characters<br>\n"; 16: if ( strlen( $form[login] ) > 8 ) 17: $message.= "Your login must be less than 18: 8 characters<br>\n"; 19: if ( getrow( "clubs", "login", $form[login] ) ) 20: $message.= "Login \"$form[login]\" already exists. 21: Try another<br>\n"; 22: if ( $message == "" ) // löysimme virheitä 23: { 24: $id = newuser( $form[login], $form[password] ); 25: cleanmembersession( $id, $form[login], $form[password] ); 26: header( "Location: updateclub.php?".sid ); 27: exit; 28: } 29: } 30:?> 31: <html> 32: <head>

402 23. Esimerkki (Osa 1) 33: <title>join!</title> 34: </head> 35: 36: <body> 37: <?php 38: include("publicnav.inc"); 39:?> 40: <p> 41: <h1>join</h1> 42: <?php 43: if ( $message!= "" ) 44: { 45: print "<b>$message</b><p>"; 46: } 47:?> 48: <p> 49: <form action="<?php print $PHP_SELF;?>"> 50: <input type="hidden" name="actionflag" value="join"> 51: <input type="hidden" name="<?php print session_name()?>" 52: value="<?php print session_id()?>"> 53: Login: <br> 54: <input type="text" name="form[login]" 55: value="<?php print $form[login]?>" maxlength=8> 56: </p> 57: <p> 58: Password: <br> 59: <input type="password" name="form[password]" value="" maxlength=8> 60: </p> 61: <p> 62: Confirm password: <br> 63: <input type="password" name="form[password2]" value="" maxlength=8> 64: </p> 65: <p>

23. Esimerkki (Osa 1) 403 66: <input type="submit" value="update"> 67: </p> 68: </form> 69: 70: </body> 71: </html> Ensiksi laitamme sivulle kirjastotiedostomme lauseella include(), joten sivulta saadaan heti tietokantayhteys ja aktiivinen istunto. Alustamme muuttujan nimeltä $message. Muuttuja löytyy useilta projektimme sivuilta. Sillä on tuplamerkitys. Täytämme sen virhetiedolla, kun testaamme lähetettyä tietoa. Virhetieto voidaan sitten tulostaa selaimelle. Voimme käyttää muuttujaa myös lippuna, kertomaan meille, onko ongelmia syntynyt. Jos muuttujassa on edelleen tyhjä merkkijono, voimme olettaa, että kaikki suorittamamme testit ovat antaneet hyväksyttävän tuloksen. Testaamme sitten muuttujan $actionflag esiintymistä ja sisältöä. Se on toinen toistuva kuvio. Asetamme piilokentän nimeltä actionflag jokaiselle lomakkeelle, jonka luomme, ja annamme sille sopivan arvon. Jos vastaava muuttuja $actionflag on olemassa ja täytetty odotetulla arvolla, voimme olla varmoja siitä, että lomake on lähetetty ja voimme jatkaa syötön tarkistamista. Jos muuttujaa ei ole olemassa, tiedämme, että jäsen on tullut linkin tai kirjanmerkin kautta eikä ole lähettänyt vielä tietoa. Ohittakaamme nyt HTML-jakso. Sivun BODY-elementissä laitetaan sivulle vielä toinen tiedosto. Siinä on globaalia navigointitietoa. On hyvä laittaa navigaatiotieto jo varhaisessa vaiheessa. Se helpottaa ympäristön testaamista, kun menemme eteenpäin. Lisäämme navigaatioelementtejä sisällytettävään tiedostoon nimeltä publicnav.inc. Toistaiseksi siinä on navigointitietoa vain julkisesti käytettäville sivuille. Listaus 23.4 Ote tiedostosta publicnav.inc 1: <a href="viewclubs.php?<?php print SID?>">Browse clubs</a> 2: <a href="viewevents.php?<?php print SID?>">Browse events</a> 3: <a href="join.php?<?php print SID?>">Join</a> 4: <a href="login.php?<?php print SID?>">Login</a> 5: <a href="index.php?<?php print SID?>">Home</a> Laittamalla navigointialkiot erilliseen tiedostoon voimme kerralla päivittää koko sivuston navigointiin liittyvän ulkoasun. Kun sivun otsikko on kirjoitettu, koodi testaa $message-muuttujan. Jos siinä ei ole tyhjää merkkijonoa, kirjoitamme sen sisällön selaimelle. Kyseessä on mekanismi, jolla voimme lähettää palautetietoa jäsenelle, jos hänen syöttämiään tietoja ei voida hyväksyä. HTML-lomake määrittelee kolme näkyvää kenttää: form[login], form[password] ja form[password2]. Käytämme tällaista outoa nimeämistapaa, koska PHP muuntaa nämä nimet ja niiden vastaavat arvot yksittäiseen assosiatiiviseen taulukkoon nimeltä $form. Tämä auttaa meitä suojautumaan nimiavaruuden hajanaisuudelta. Pidämme kaikki istuntomuuttujat taulukossa nimeltä $session ja kaikki lomakemuuttujat taulukossa nimeltä $form. Näin voimme suojautua virheitä vastaan. On hyvä pitää globaalien muuttujien määrä mahdollisimman vähäisenä, jotta vältyttäisiin sekaannuksilta; tämä projekti on jo niin laaja, että globaaleja muuttujia kannattaa seurata. Käytämme $form-taulukkoa jokaisessa skriptissä, joka sisältää lomakkeen. Luomme myös yhden piilokentän nimeltä actionflag ja toisen, joka tallentaa istunnon nimen ja istuntomuuttujan. Kaikkialla tässä projektissa viemme aina istuntoa koskevan ID:n sivulta toiselle, joten emme kadota asiakkaita, jotka eivät halua tai voi käyttää evästeitä.

404 23. Esimerkki (Osa 1) Jos testaat istuntoja ylläpitäviä skriptejä, on hyvä laittaa evästeet pois päältä ensin. Siten tiedät, oletko kyennyt viemään istunnon ID:n sivulta toiselle. Nyt kun olemme katselleet HTML-lomaketta, voimme tarkistaa koodin, jota käytämme syöttötiedon testaamiseen. Tarkistamme, että jäsen on täyttänyt kaikki kentät ja että mikään niistä ei sisällä enempää kuin kahdeksan merkkiä. Sitten kutsumme uutta funktiota nimeltä getrow(); se sisältyy tiedostoon dblib.inc. Funktio ottaa argumenteikseen taulukon nimen, kentän nimen ja kentän arvon. Sitten se käyttää niitä koettaessaan löytää vastaavan rivin tietokannasta ja palauttaa rivin taulukossa. Listaus 23.5 Ote tiedostosta dblib.inc 1: function getrow( $table, $fnm, $fval ) 2: { 3: global $link; 4: $result = mysql_query( "SELECT * FROM $table WHERE $fnm='$fval'", $link ); 5: if (! $result ) 6: die ( "getrow fatal error: ".mysql_error() ); 7: return mysql_fetch_array( $result ); 8: } Viemme funktiolle taulukon nimen, clubs, kentän nimen, login, ja arvon, jonka jäsen on antanut kohtaan $form[login]. Jos mysql_fetch_array() palauttaa täytetyn taulukon, tiedämme, että tuollaista tunnusta käyttävä jäsen on jo olemassa, joten annamme virheilmoituksen. Jos muuttuja $message sisältää edelleenkin tyhjän merkkijonon, voimme mennä eteenpäin ja luoda uuden jäsenen. Se tehdään kahdessa vaiheessa. Ensiksikin meidän tulee päivittää tietokanta. Luomme uuden dblib.inc-funktion nimeltä newuser() käsittelemään tätä: LISTAUS 23.6 Ote tiedostosta dblib.inc 1: function newuser( $login, $pass ) 2: { 3: global $link; 4: $result = mysql_query( "INSERT INTO clubs (login, password) 5: VALUES('$login', '$pass')", $link); 6: return mysql_insert_id( $link ); 7: }

23. Esimerkki (Osa 1) 405 Tämä funktio ottaa arvoikseen käyttäjätunnuksen ja salasanan. Se käyttää niitä uuden rivin lisäämiseksi clubs-taulukkoon. Se käyttää mysql_insert_id()-funktiota palauttamaan automaattisesti kasvatetun idkentän. Nyt kun meillä on arvo jäsenen ID:lle, voimme kutsua toista kirjastofunktiota; cleanmembersession() on tiedostossa clublib.inc. Se ottaa argumenteikseen ID:n, käyttäjätunnuksen ja salasanan ja tallentaa ne $session-taulukkoon. Nämä arvot ovat nyt jokaisen istunnossa käytettävän sivun saatavilla. Voimme käyttää niitä todentamaan jäsenen jokaisella näytöllä. LISTAUS 23.7 Ote tiedostosta clublib.inc 1: function cleanmembersession( $id, $login, $pass ) 2: { 3: global $session; 4: $session[id] = $id; 5: $session[login] = $login; 6: $session[password] = $pass; 7: $session[logged_in] = true; 8: } Edellä mainittujen id-, login- ja salasana-arvojen lisäksi asetamme myös lippukomponentin nimeltä logged_in. Kun olemme lopuksi päivittäneet istunnon ja tietokannan, voimme lähettää jäsenen eteenpäin join.php:ssa. Kutsumme header()-funktiota, joka johtaa selaimen sivulle updateclub.php, jossa hän antaa lisätietoa rekisteröimästään kerhosta. Tulostus on esitetty kuvassa 23.2. KUVA 23.2 Tulostus tiedostosta join.php. Tällä näytöllä on kaksi eri tarkoitusta: siinä sallitaan uuden jäsenen lisätä kerhotietoa ja lisäksi muuttaa omia tietojaan. Listaus 23.8 esittää tiedoston updateclub.php sisällön.

406 23. Esimerkki (Osa 1) Listaus 23.8 updateclub.php 1: <?php 2: include("dblib.inc"); 3: include("clublib.inc"); 4: $club_row = checkuser(); 5: $message = ""; 6: if ( isset( $actionflag ) && $actionflag=="update" ) 7: { 8: if ( empty( $form[name] ) ) 9: $message.="your club must have a name<br>\n"; 10: if (! getrow( "areas", "id", $form[area] ) ) 11: $message.= "PANIC: That area code can't be found<br>"; 12: if (! getrow( "types", "id", $form[type] ) ) 13: $message.= "PANIC: That type code can't be found<br>"; 14: if ( $message == "" ) 15: { 16: updateorg( $session[id], $form[name], $form[area], 17: $form[type], $form[mail], $form[description] ); 18: header("location: membersmenu.php?".sid); 19: exit; 20: } 21: } 22: else 23: { 24: $form = $club_row; 25: } 26:?> 27: <html> 28: <head> 29: <title>update your club listing</title> 30: </head> 31: 32: <body>

23. Esimerkki (Osa 1) 407 33: <?php 34: include("publicnav.inc"); 35:?> 36: <h1>amend club information</h1> 37: <?php 38: if ( $message!= "" ) 39: { 40: print "<b>$message</b><p>"; 41: } 42:?> 43: <form action="<?php print $PHP_SELF;?>"> 44: <input type="hidden" name="actionflag" value="update"> 45: <input type="hidden" name="<?php print session_name()?>" 46: value="<?php print session_id()?>"> 47: <p> 48: Club name: <br> 49: <input type="text" name="form[name]" 50: value="<?php print stripslashes($form[name])?>"> 51: </p> 52: <p> 53: Club area: <br> 54: <select name="form[area]"> 55: <?php writeoptionlist( "areas", $form[area] )?> 56: </select> 57: </p> 58: <p> 59: Club type: <br> 60: <select name="form[type]"> 61: <?php writeoptionlist( "types", $form[type] )?> 62: </select> 63: </p> 64: <p> 65: Contact e-mail: <br>

408 23. Esimerkki (Osa 1) 66: <input type="text" name="form[mail]" 67: value="<?php print stripslashes($form[mail])?>"> 68: </p> 69: <p> 70: Club description: <br> 71: <textarea name="form[description]" rows=5 cols=30 wrap="virtual"> 72: <?php print stripslashes($form[description])?> 73: </textarea> 74: </p> 75: <p> 76: <input type="submit" value="update"> 77: </p> 78: </form> 79: </body> 80: </html> Jälleen kerran lisäämme tiedostot dblib.inc ja clublib.inc ja säästymme näin kirjoittamasta koodia, jolla avataan tietokanta ja palautetaan istunto. Kutsumme uutta funktiota nimeltä checkuser(), joka on kirjastossa clublib.inc. Funktio tarkistaa jäsenen istuntotiedot suhteessa tietokantaan. LISTAUS 23.9 Ote tiedostosta clublib.inc 1: function checkuser( ) 2: { 3: global $session, $logged_in; 4: $session[logged_in] = false; 5: $club_row = getrow( "clubs", "id", $session[id] ); 6: if (! $club_row 7: $club_row[login]!= $session[login] 8: $club_row[password]!= $session[password] ) 9: { 10: header( "Location: login.php" ); 11: exit; 12: } 13: $session[logged_in] = true;

23. Esimerkki (Osa 1) 409 14: return $club_row; 15: } Funktio checkuser() on suhteellisen tarkka. Se käyttää $session[id]-alkiota yhdessä getrow()-funktion kanssa vastaavan rivin erottamiseen tietokannasta. Se tallentaa rivin assosiatiiviseen taulukkoon, joka on muuttujassa $club_row ja testaa sen käyttäjätunnus- ja salasanaelementit suhteessa niihin, jotka on tallennettu $session-muuttujaan. Jos ne eivät vastaa toisiaan, se lähettää käyttäjän takaisin kirjautumissivulle login.php. Miksi käytämme tietokantaa todentamiseen? Eikö riittäisi, että tarkistamme $session-muuttujan logged_inalkion? Siinä olisi vaaroja, koska epärehellinen käyttäjä voisi yksinkertaisesti lisätä jotain sellaista kuin session%5blogged_in%5d=1&session%5bid%5d=1 kyselymerkkijonoon. PHP muuntaisi tämän $sessiontaulukkoon, joka koostuu GET-parametreistä, jolloin testi menettäisi merkityksensä. Käyttäjä voisi tehdä samalla lailla myös checkuser()-funktion testissä. Koska testaamme kuitenkin käyttäjätunnuksen ja salasanan tietokannan tietojen suhteen, muunnetunkin $session-muuttujan tulisi sisältää oikeaa tietoa, jotta sen arvot hyväksyttäisiin. Lisätuotteena funktion checkuser() testissä on se, että funktio palauttaa kaiken tiedon, joka on yhdistetty kysymyksessä olevaan kerhoon. Sivut, joiden tulee todentaa käyttäjä, voivat käsitellä tätä tietoa. Kun kutsumme checkuser()-funktiota (sijaitsee tiedostossa updateclub.php), palautusarvo tallennetaan muuttujaan $club_row. Kun ohitamme jälleen kerran HTML-rungon, alamme taas kerran sisällyttää navigointitiedostoa publicnav.inc. Olemme edistyneet kuitenkin hieman ja voimme laajentaa tätä asiakirjaa sisällyttämään myös jäsenten navigointialkiot: Listaus 23.10 Ote tiedostosta publicnav.inc 1: <p> 2: <a href="viewclubs.php?<?php print SID?>">Browse clubs</a> 3: <a href="viewevents.php?<?php print SID?>">Browse events</a> 4: <a href="join.php?<?php print SID?>">Join</a> 5: <a href="login.php?<?php print SID?>">Login</a> 6: <a href="index.php?<?php print SID?>">Home</a> 7: </p> 8: <?php 9: if ( $session[logged_in] ) 10: { 11:?> 12: <p> 13: <A HREF="updateclub.php?<?php print SID?>">Your details</a> 14: <A HREF="reviewevents.php?<?php print SID?>">Your events</a> 15: <A HREF="updateevent.php?<?php print SID?>">New event</a> 16: <A HREF="membersmenu.php?<?php print SID?>">Members home</a> 17: </p> 18: <?

410 23. Esimerkki (Osa 1) 19: } 20:?> 21: <hr> Jos $session[logged_in]-lippu on asetettu arvoon tosi, niin myös jäsenten käytettävissä olevat sivut tulostetaan. Huomaa, että laitoimme SID-vakion kaikkiin linkkeihimme. Se varmistaa, että istunnon ID viedään sivulta sivulle, vaikka evästetoiminto olisi poissa päältä. Tiedostossa updateclub.php oleva lomake on huomaamaton. Lisäämme sekä toimintolipun että istunnon ID:n piilotettuihin alkioihin. Varustamme tekstikentät kerhon nimellä, kuvauksella ja email-osoitteella. Saadaksemme aikaan alasvetovalikot kerhon alueelle ja tyypille kutsumme kuitenkin uutta funktiota nimeltä writeoptionlist(). Funktio tallennetaan tietokantakirjastoomme dblib.inc. Se ottaa vastaan taulukon nimen (joko alueina tai tyyppeinä) ja merkkijonon. Listaus 23.11 Ote tiedostosta dblib.inc 1: function writeoptionlist( $table, $id ) 2: { 3: global $link; 4: $result = mysql_query( "SELECT * FROM $table", $link ); 5: if (! $result ) 6: { 7: print "failed to open $table<p>"; 8: return false; 9: } 10: while ( $a_row = mysql_fetch_row( $result ) ){ 11: print "<option value=\"$a_row[0]\""; 12: if ( $id == $a_row[0] ) 13: print "SELECTED"; 14: print ">$a_row[1]\n"; 15: } 16: } Funktio käyttää $table-parametrimuuttujaa valitsemaan jokaisen rivin joko alue- tai tyyppitaulukosta. Sitten se käy silmukassa läpi jokaisen tulosjoukon rivin ja kirjoittaa OPTION-alkion selaimelle. Jos mysql_fetch_row()-funktion palauttaman taulukon toinen alkio vastaa toisena parametrina annettua $idarvoa, lisätään merkkijono "SELECTED" alkioon. Tämä menettely varmistaa, että alasvetovalikon oikea alkio on valittuna, kun lomake esitetään. PHP-koodi tarkistaa, että form[name]-kenttä on täytetty; koodi tarkistaa myös, että form[area]- ja form[type]- kentissä on hyväksyttävät arvot. Jos $message-muuttuja sisältää tyhjän merkkijonon, tiedämme, että kriteerimme, jotka koskevat käyttäjän lähettämää tietoa, on täytetty. Viemme käyttäjän lähettämät tiedot

23. Esimerkki (Osa 1) 411 funktiolle nimeltä updateorg(), joka täyttää vastaavan rivin kerhotaulusta. Funktio ottaa arvoikseen kerhon ID:n sekä nimen, alueen, sähköpostiosoitteen ja kuvaustiedot. Listaus 23.12 Ote tiedostosta dblib.inc 1: function updateorg( $id, $name, $area, $type, $mail, $description ) 2: { 3: global $link; 4: $query = "UPDATE clubs set name='$name', area='$area', 5: type='$type', mail='$mail', description='$description' 6: WHERE id='$id'"; 7: $result = mysql_query( $query, $link ); 8: if (! $result ) 9: die ( "updateorg update error: ".mysql_error() ); 10: } Kun rivi on päivitetty, jäsen voidaan lähettää jäsenen valikkosivulle, membersmenu.php. Jos jäsen pääsi updateclub.php-sivulle linkin tai kirjanmerkin kautta, ei $actionflag-lippua aseteta ja testaus- ja päivityskoodi ohitetaan. Silti on vielä tehtävää. Assosiatiivinen $club_row-taulukko täytettiin, kun funktiota checkuser() kutsuttiin. Taulukko sisältää kenttien tietokannan nykyisen kerhon rivin nimet ja arvot. Sijoitamme tuon taulukon $form-muuttujaan, jolloin voimme varmistaa, että päivityslomake täytetään kerhon nykyisillä tiedoilla. Kuva 23.3 esittää listauksen 23.8 tulostuksen. KUVA 23.3 Listauksen 23.8 tulostus.

412 23. Esimerkki (Osa 1) membersmenu.php Tiedosto membersmenu.php on jäsenten alueen ydin. Siinä on pääosin linkkiluettelo, johon voidaan lisätä uutisia ja tarjouksia, jotka voivat kiinnostaa jäseniä. Tämän sivun koodi on listauksessa 23.13. Listaus 23.13 membersmenu.php 1: <?php 2: include("dblib.inc"); 3: include("clublib.inc"); 4: 5: $club_row = checkuser(); 6: checkclubdata( $club_row ); 7:?> 8: 9: <html> 10: <head> 11: <title>welcome</title> 12: </head> 13: 14: <body> 15: <?php 16: include("publicnav.inc"); 17:?>>Members menu</h1> 18: 19: <a href="updateclub.php?<?php print SID?>">Review your club details</a><br> 20: <a href="reviewevents.php?<?php print SID?>">Review your events</a><br> 21: <a href="updateevent.php?<?php print SID?>">New event</a><br> 22: 23: </body> 24: </html> Tällä sivulla on yksi uusi piirre. Kun olemme kutsuneet funktiota checkuser() varmistaaksemme, että käyttäjä on jäsen, viemme funktion palauttaman taulukon toiselle funktiolla, jota pidämme tiedostossa clublib.php. Funktio checkclubdata() varmistaa, että jäsen on jo luonut kerhoprofiilin. Me emme halua luoda tapahtumia, elleivät ne ole täysin määrittäneet kerhoaan. Minimitietona on kerhon nimi.

23. Esimerkki (Osa 1) 413 Listaus 23.14 Ote tiedostosta clublib.inc 1: function checkclubdata( $clubarray ) 2: { 3: if (! isset( $clubarray[name] ) ) 4: { 5: header( "Location: updateclub.php?".sid ); 6: exit; 7: } 8: } login.php Ennen kuin siirrymme sivuille, joiden avulla käyttäjät ylläpitävät tapahtumiaan, voimme katsella tiedostoa login.php. Tämä skriptin avulla rekisteröity jäsen saa esille ympäristönsä. Koodi on esitetty listauksessa 23.15. Listaus 23.15 login.php 1: <?php 2: include("dblib.inc"); 3: include("clublib.inc"); 4: $message=""; 5: if ( isset( $actionflag ) && $actionflag == "login" ) 6: { 7: if ( empty( $form[login] ) empty( $form[password] ) ) 8: $message.= "you must fill in all fields<br>\n"; 9: if (! ( $row_array = 10: checkpass( $form[login], $form[password] ) ) ) 11: $message.= "Incorrect password try again<br>\n"; 12: if ( $message == "" ) // löysimme virheitä 13: { 14: cleanmembersession( $row_array[id], $row_array[login], 15: $row_array[password] );

414 23. Esimerkki (Osa 1) 16: header( "Location: membersmenu.php?".sid ); 17: } 18: } 19:?> 20: <html> 21: <head> 22: <title>login</title> 23: </head> 24: <body> 25: <?php 26: include("publicnav.inc"); 27:?> 28: <h1>login</h1> 29: <?php 30: if ( $message!= "" ) 31: { 32: print "<p><b>$message</b></p>"; 33: } 34:?> 35: <p> 36: <form action="<?php print $PHP_SELF;?>"> 37: <input type="hidden" name="actionflag" value="login"> 38: <input type="hidden" name="<?php print session_name()?>" 39: value="<?php print session_id()?>"> 40: </p><p> 41: Login: <br> 42: <input type="text" name="form[login]" 43: value="<?php print $form[login]?>"> 44: </p><p> 45: Password: <br> 46: <input type="password" name="form[password]" value=""> 47: </p><p> 48: <input type="submit" value="update">

23. Esimerkki (Osa 1) 415 49: </form> 50: </body> 51: </html> Tämän sivun rakenteen tulisi olla sinulle tuttu. Käytämme tiedostoja dblib.inc ja clublib.inc alustamaan tietokantayhteyden ja palauttamaan istunnon. Jos $actionflag-muuttuja on asetettu, testaamme lomakkeen kentät. Käytämme uutta dblib.inc-funktiota tarkistamaan alkiot $form[login] ja $form[password]. Listaus 23.16 Ote tiedostosta dblib.inc 1: function checkpass( $login, $password ) 2: { 3: global $link; 4: $result = mysql_query( "SELECT id, login, password FROM clubs 5: WHERE login='$login' and password='$password'", 6: $link ); 7: if (! $result ) 8: die ( "checkpass fatal error: ".mysql_error() ); 9: if ( mysql_num_rows( $result ) ) 10: return mysql_fetch_array( $result ); 11: return false; 12: } Funktio checkpass() ottaa argumenteikseen käyttäjätunnuksen ja salasanan ja lähettää yksinkertaisen SELECT-kyselyn kerhotauluihin. Jos mitään virheitä ei löydy, kutsumme cleanmembersession()-funktiota, joka alustaa $session-muuttujan käyttäjätunnuksen, salasanan ja id:n. Sitten ohjaamme jäsenen sivulle membersmenu.php. updateevent.php Kun jäsenet voivat nyt liittyä tai kirjautua palveluumme ja muuttaa kerhotietojaan, meidän tulee antaa heidän luoda ja muokata tapahtumia. Koko updateevent.php-tiedosto on listauksessa 23.17. Listaus 23.17 updateevent.php 1: <?php 2: include("dblib.inc"); 3: include("clublib.inc");

416 23. Esimerkki (Osa 1) 4: include("date.inc"); 5: $club_row = checkuser(); 6: checkclubdata( $club_row ); 7: $date = time(); 8: $message = ""; 9: if (! empty( $event_id ) ) 10: $event_row = getrow( "events", "id", $event_id ); 11: else 12: $event_id = false; 13: if ( isset( $actionflag ) && $actionflag=="update_event" ) 14: { 15: if ( empty( $form[ename] ) ) 16: $message.="the venue must have a name<br>\n"; 17: if (! getrow( "areas", "id", $form[area] ) ) 18: $message.= "PANIC: That area code can't be found<br>"; 19: if (! getrow( "types", "id", $form[type] ) ) 20: $message.= "PANIC: That type code can't be found<br>"; 21: foreach ( array( "months", "years", "days", "minutes" ) 22: as $date_unit ) 23: { 24: if (! isset( $form[$date_unit] ) ) 25: { 26: $message.= "PANIC: Can't make sense of that date"; 27: break; 28: } 29: } 30: $date = mktime( $form[hours], $form[minutes], 0, $form[months], 31: $form[days], $form[years] ); 32: if ( $date < time() ) 33: $message.= "You've chosen a date in the past!"; 34: if ( $message == "" ) 35: { 36: insertevent( $form[ename], $form[evenue], $form[area],

23. Esimerkki (Osa 1) 417 37: $form[type], $form[eaddress], $form[ezip], 38: $form[edescription], $session[id], $date, 39: $event_id ); 40: header( "Location: reviewevents.php?".sid ); 41: } 42: } 43: elseif ( $event_id ) 44: { 45: //foreach( $event_row as $key=>$value ) 46: // $form[$key] = $value; 47: $form = $event_row; 48: $date = $event_row[edate]; 49: } 50: else 51: { 52: $form[area] = $club_row[area]; 53: $form[type] = $club_row[type]; 54: } 55:?> 56: <html> 57: <head> 58: <title>add/amend event</title> 59: </head> 60: <body> 61: <?php 62: include("publicnav.inc"); 63:?> 64: <h1>amend event</h1> 65: <?php 66: if ( $message!= "" ) 67: { 68: print "<b>$message</b>"; 69: }

418 23. Esimerkki (Osa 1) 70:?> 71: <p> 72: <form action="<?php print $PHP_SELF;?>"> 73: <input type="hidden" name="actionflag" value="update_event"> 74: <input type="hidden" name="<?php print session_name()?>" 75: value="<?php print session_id()?>"> 76: <input type="hidden" name="event_id" 77: value="<?php print $event_id?>"> 78: Event Name: <br> 79: <input type="text" name="form[ename]" 80: value="<?php print stripslashes($form[ename])?>"> 81: </p> 82: <p> 83: Date and time: <br> 84: <select name="form[months]"> 85: <?php writemonthoptions( $date )?> 86: </select> 87: <select name="form[days]"> 88: <?php writedayoptions( $date )?> 89: </select> 90: <select name="form[years]"> 91: <?php writeyearoptions( $date )?> 92: </select> 93: <SELECT NAME="form[hours]"> 94: <? writehouroptions( $date )?> 95: </SELECT> 96: <SELECT NAME="form[minutes]"> 97: <? writeminuteoptions( $date )?> 98: </SELECT> 99: </p> 100: <p> 101: Event area: <br> 102: <select name="form[area]">

23. Esimerkki (Osa 1) 419 103: <?php writeoptionlist( "areas", $form[area] )?> 104: </select> 105: </p> 106: <p> 107: Event type: <br> 108: <select name="form[type]"> 109: <?php writeoptionlist( "types", $form[type] )?> 110: </select> 111: </p> 112: <p> 113: Describe the event: <br> 114: <textarea name="form[edescription]" wrap="virtual" rows=5 cols=30> 115: <?php print stripslashes($form[edescription])?> 116: </textarea> 117: </p> 118: <p> 119: Venue name: <br> 120: <input type="text" name="form[evenue]" 121: value="<?php print stripslashes($form[evenue])?>"> 122: </p> 123: <p> 124: Venue address: <br> 125: <textarea name="form[eaddress]" wrap="virtual" rows=5 cols=30> 126: <?php print stripslashes($form[eaddress])?> 127: </textarea> 128: </p> 129: <p> 130: Venue zip code: <br> 131: <input type="text" name="form[ezip]" 132: value="<?php print stripslashes($form[ezip])?>"> 133: </p> 134: <p> 135: <input type="submit" value="update">

420 23. Esimerkki (Osa 1) 136: </p> 137: </form> 138: </body> 139: </html> Tämä sivu luo lomakkeen, joka vastaa events-taulukon kenttien nimiä. Kuten tavallista, meidän tulee testata nuo arvot ja päivittää tietokanta. Meidän tulee kuitenkin myös palauttaa listaus, jos lomakkeen kautta muutetaan tapahtumaa uuden lisäämisen sijaan. Muuttuja $event_id viedään tälle sivulle kyselymerkkijonossa, jos aiomme työskennellä olemassa olevan listan kanssa. Jos $event_id-muuttuja ei ole tyhjä, käytämme getrow()-funktiota täyttämään muuttuja nimeltä $event_row tapahtuman tiedoilla. Jos $event_id-muuttujaa ei ole olemassa tai se on tyhjä, voimme alustaa sen arvolla epätosi. Jos lomake on lähetetty, testaamme, että kaikki olennainen tieto on annettu. Voimme myös testata, että valittuja päivämäärä- ja aikatietoja ei ole vielä viety. Toteutamme testauksen asettamalla globaaliin muuttujaan nimeltä $date aikaleiman, joka perustuu edelliseen syöttöön. Jos lähetetyt tiedot ovat oikein, voimme kutsua dblib.inc-tiedoston funktiota nimeltä insertevent(). Kuten voit nähdä, se ottaa argumenteikseen kaikki tapahtumataulukon kentät. Viimeinen vaadittava kenttä on tapahtuman ID. Sitä käytetään insertevent()- funktion toimesta määrittämään, tuleeko sen päivittää vai lisätä tietoa. Huomaa, että kerhon ID tallennetaan $session[id]-muuttujaan. Se sijoitetaan tapahtumataulukon eclub-kenttään. Käytämme aikaleimaa päivämäärätiedon tallentamiseksi tietokantaan. Listaus 23.18 Ote tiedostosta dblib.inc 1: function insertevent( $name, $venue, $area, $type, $address, 2: $zip, $desc, $club_id, $timestamp, $event_id ) 3: { 4: global $link; 5: if (! $event_id ) 6: { 7: $query = "INSERT INTO events (ename, evenue, area, type, 8: eaddress, ezip, edescription, eclub, edate ) 9: VALUES( '$name', '$venue', '$area', '$type', '$address', 10: '$zip', '$desc', '$club_id', '$timestamp')"; 11: } 12: else 13: { 14: $query = "UPDATE events SET ename='$name', evenue='$venue', 15: area='$area', type='$type', eaddress='$address', 16: ezip='$zip', edescription='$desc', eclub='$club_id', 17: edate='$timestamp' WHERE id='$event_id'";

23. Esimerkki (Osa 1) 421 18: } 19: $result = mysql_query( $query, $link ); 20: if (! $result ) 21: die ( "insertevent error: ".mysql_error() ); 22: } Kuten näet, insertevent()-funktio testaa $event_id-argumentin. Jos testin tulos on epätosi, muodostetaan INSERT-lause. Muutoin muodostetaan UPDATE-lause. Kun tietokanta on päivitetty, johdatamme jäsenen reviewevents.php-sivulle, jossa hän voi katsella koko aikataulua. Jos jäsen ei ole vielä lähettänyt tietoa, olemme ohittaneet tiedon tarkistuksen ja tietokannan muuttamiskoodin. Jos meillä kuitenkin on $event_id-muuttuja, olemme laittaneet tapahtumatiedot tietokannasta välimuistiin. Teemme sen sijoittamalla $event_row-arvon muuttujaan $form. Asetamme myös globaalin muuttujan $date sieppaamaan $event_row-muuttujan edate-alkion. Jos lomaketta ei ole lähetetty eikä $event_id-muuttujaa ole olemassa, asetamme alkioihin $form[area] ja $form[type] $club_row-muuttujassa olevat kerhotietoja vastaavat arvot. Muista, että $club_row sisältää kerhotaulun rivin. Näin vastaavat alasvetovalikot vastaavat oletuksena jäsenen asettamia kerhon alue- ja tyyppiarvoja. HTML-lomakkeen sisällä kannattaa huomio kiinnittää koodiin, joka kirjoittaa alasvetovalikot tapahtuman päivämäärälle ja ajalle. Nämä valikot olisivat tarpeeksi helppoja koodata suoraan, mutta me tarvitsemme koodeja valitsemaan automaattisesti joko nykyinen aika, viimeksi valittu aika tai tapahtumataulukkoon tallennettu aika. Olemme jo tallentaneet sen $date-muuttujaan. Se alustetaan nykyisellä aikaleimalla skriptin alussa. Jos käyttäjän syöttötieto havaitaan, muodostamme uuden aikaleiman, joka perustuu näihin valintoihin, ja sijoitamme sen muuttujaan $date. Muutoin, jos muokkaamme olemassaolevaa tapahtumaa, täytämme $date-muuttujan events-taulukon edate-kentän arvolla. Jokainen alasvetovalikko vie $datemuuttujassa olevan aikaleiman vastaavalle funktiolle, joka on vielä toisessa kirjastotiedostossa nimeltä date.inc. Koodit on esitetty listauksessa 23.19. Listaus 23.19 date.inc 1: <?php 2: function writemonthoptions( $d ) 3: { 4: $d_array = getdate( $d ); 5: $months = array( "Jan","Feb","Mar","Apr","May","Jun", 6: "Jul","Aug","Sep","Oct","Nov","Dec" ); 7: foreach ( $months as $key=>$value ) 8: { 9: print "<OPTION VALUE=\"".($key+1)."\""; 10: print ( ( $d_array[mon] == ($key+1) )?"SELECTED":"" ); 11: print ">$value\n";

422 23. Esimerkki (Osa 1) 12: } 13: } 14: function writedayoptions( $d ) 15: { 16: $d_array = getdate( $d ); 17: for ( $x = 1; $x<=31; $x++ ) 18: { 19: print "<OPTION VALUE=\"$x\""; 20: print ( ( $d_array[mday] == $x )?"SELECTED":"" ); 21: print ">$x\n"; 22: } 23: } 24: function writeyearoptions( $d ) 25: { 26: $d_array = getdate( $d ); 27: $now_array = getdate(time()); 28: for ( $x = $now_array[year]; $x <= ($now_array[year]+5); $x++ ) 29: { 30: print "<OPTION VALUE=\"$x\""; 31: print ( ( $d_array[year] == $x )?"SELECTED":"" ); 32: print ">$x\n"; 33: } 34: } 35: function writehouroptions( $d ) 36: { 37: $d_array = getdate( $d ); 38: for ( $x = 0; $x< 24; $x++ ) 39: { 40: print "<OPTION VALUE=\"$x\""; 41: print ( ( $d_array[hours] == $x )?"SELECTED":"" ); 42: print ">".sprintf("%'02d",$x)."\n"; 43: } 44: }

23. Esimerkki (Osa 1) 423 45: function writeminuteoptions( $d ) 46: { 47: $d_array = getdate( $d ); 48: for ( $x = 0; $x<= 59; $x++ ) 49: { 50: print "<OPTION VALUE=\"$x\""; 51: print ( ( $d_array[minutes] == $x )?"SELECTED":"" ); 52: print ">".sprintf("%'02d",$x)."\n"; 53: } 54: } 55:?> Kaikki nämä funktiot hyväksyvät aikaleiman ja kirjoittavat joukon OPTION-tageja. Ne käyttävät getdate()- funktiota indeksin sieppaamiseksi aikaleiman tiettyyn osaan (vuosi, kuukausi, kuukaudenpäivä, tunti, minuutti). Ne voivat tarkistaa lukuarvon suhteessa sopivaan arvoalueeseen (esimerkiksi kuukauden päivän tulee olla väliltä 1-31). Jos luku on sopiva, lisätään merkkijono SELECTED selaimelle kirjoitettavaan OPTION-elementtiin. Kuva 23.4 esittää listauksen 23.17 tulostuksen. KUVA 23.4 Listauksen 23.17 tulostus. reviewevents.php Lopuksi tarjoamme käyttäjille keinon ottaa esille kaikki asettamansa tapahtumat. Heidän tulisi nähdä luettelo kaikista tapahtumista ja kyetä muokkaamaan mitä tahansa niistä tai tuhoamaan niitä. Listauksen reviewevents.php koodi on suhteellisen helppoa. Koodi on listauksessa 23.20.

424 23. Esimerkki (Osa 1) Listaus 23.20 reviewevents.php 1: <?php 2: include("dblib.inc"); 3: include("clublib.inc"); 4: $club_row = checkuser(); 5: checkclubdata( $club_row ); 6: function writeevents() 7: { 8: global $club_row; 9: $events = getevents( $club_row[id] ); 10: if (! $events ) 11: { 12: print "You have no events in your schedule<p>"; 13: return; 14: } 15: print "<table border=1>\n"; 16: print "<td><b>date</b></td>\n<td><b>name</b></td>\n 17: <td><b> </b></td>\n"; 18: foreach ( $events as $row ) 19: { 20: print "<tr>\n"; 21: print "<td>".date("j M Y H.i", $row[edate])."</td>\n"; 22: print "<td><a href=\"updateevent.php?event_id=$row[id]&".sid."\">". 23: html($row[ename])."</a></td>\n"; 24: print "<td><a href=\"$globals[php_self]?event_id=$row[id]"; 25: print "&actionflag=deleteevent&".sid."\""; 26: print "onclick=\"return window.confirm('are you sure you 27: want to delete this item')\">"; 28: print "delete</a><br></td>\n"; 29: print "</tr>\n"; 30: } 31: print "</table>\n"; 32: }

23. Esimerkki (Osa 1) 425 33: $message=""; 34: if ( isset( $actionflag ) && 35: $actionflag == "deleteevent" && isset( $event_id ) ) 36: { 37: deleteevent( $event_id ); 38: $message.= "That event is now history!<br>"; 39: } 40:?> 41: <html> 42: <head> 43: <title>review events</title> 44: </head> 45: <body> 46: <?php 47: include("publicnav.inc"); 48:?> 49: <h1>review event schedule</h1> 50: <?php 51: if ( $message!= "" ) 52: { 53: print "<b>$message</b>"; 54: } 55:?> 56: 57: <?php 58: writeevents(); 59:?> 60: </body> 61: </html> Olemme käyttäneet tiedostoa dblib.inc tietokantayhteyden käynnistämiseksi ja tiedostoa clublib.inc aloittamaan istunnon ja estämään pääsy rekisteröimättömiltä käyttäjiltä. Sen jälkeen luomme funktion nimeltä writeevents(). Sitä voidaan kutsua kaikkialta HTML-rungosta ja se tulostaa tapahtumatietoa suoraan selaimelle. Olemme jo tallentaneet kerhotiedot $club_row-muuttujaan, joka sisältää funktion checkuser() palauttamaa tietoa. Voimme käyttää $club_row[id]:ssä olevaa kerhon ID:tä avaimena avaamaan kaikki

426 23. Esimerkki (Osa 1) jäsenen kerhoon liittyvät tapahtumat. Toteutamme sen kutsumalla dblib.inc-tiedoston funktiota getevents(). Käsittelemme tätä funktiota tarkemmin seuraavalla tunnilla. Se hyväksyy kuitenkin kerhon ID:n ja palauttaa moniulotteisen taulukon, joka sisältää kerhoon liittyvät rivit. Tallennamme getevents()-funktion palauttaman arvon muuttujaan nimeltä $events. Saamme itse asiassa enemmän tietoa kuin tarvitsemme, mutta getevents()-funktio on joustava, joten meidän tulee käyttää sitä mikäli skriptiin ei liity suorituskykyongelmia. Jos $events-muuttujan arvo on epätosi, tulostamme viestin selaimelle ja päätämme funktion. Muutoin muodostamme HTML-taulukon. Käymme silmukassa läpi $events-taulukon ja tulostamme joitakin alkioita sen alitaulukoista. Tulostamme muotoillun päivämäärän viemällä jokaisen alitaulukon edate-alkion date()-funktiolle. Luomme myös HTML-linkin käyttämällä jokaista havaittua tapahtuman id-ja ename-kenttää. Tuota id-kenttää käytetään SID-vakion kanssa luomaan kyselymerkkijono, joka voidaan viedä updateevent.php-tiedostolle ja jonka avulla voidaan sitten palauttaa tapahtuma muokkaamista varten.lopuksi luomme silmukassa linkin jokaiselle alkiolle, joka osoittaa takaisin nykyiselle sivulle. Sitä varten rakennamme kyselymerkkijonon, joka yhdistää tapahtuman ID:n ja tapahtumalipun deleteevent-arvoon. Sitä käytetään annetun tapahtuman tuhoamiseen, joten muodostamme JavaScript-tapahtumakäsittelijän, joka estää linkkiä aktivoitumasta, jos jäsen muuttaa mieltään. Kun olemme luoneet writeevents()-funktion, meidän tulee käsitellä sekin mahdollisuus, että jäsen on valinnut tapahtuman tuhoamistoiminnon. Voimme tehdä sen testaamalla muuttujat $actionflag ja $event_id. Jos tilanne vaatii, kutsumme sitten dblib.inc-tiedoston funktiota nimeltä deleteevent(), jolle viemme $event_id-muuttujan. Listaus 23.21 Ote tiedostosta dblib.inc 1: function deleteevent( $id ) 2: { 3: global $link; 4: $query = "DELETE FROM events WHERE id='$id'"; 5: $result = mysql_query( $query, $link ); 6: if (! $result ) 7: die ( "deleteevent fatal error: ".mysql_error() ); 8: return ( mysql_affected_rows($link) ); 9: } Voit nähdä näytteen tiedoston reviewevents.php tulostuksesta kuvassa 23.5. KUVA 23.5 Tiedoston reviewevents.php tulostus.

23. Esimerkki (Osa 1) 427 Yhteenveto Olemme nyt työstäneet kokonaisen jäsenten ympäristön tapahtumaskripteillemme. Laittamalla mahdollisimman paljon tietoa kirjastoihin olemme mahdollistaneet sen, että Web-kehittäjät voivat työstää koodia tehokkaasti. Olemme käyttäneet header()-funktiota käyttäjän siirtämiseksi uusille sivuille, kun syöttö on hyväksyttävää. Olemme käyttäneet istuntofunktioita tallentamaan kirjautumistieto ja tietokantakyselyt, joilla todennetaan jäsen jokaisen pyynnön yhteydessä. Seuraavassa luvussa käytämme samanlaisia tekniikoita muodostaaksemme julkisen näkymän ympäristöstämme. K&V K Olen huomannut, että usealle sivulle sijoitettavia projekteja on vaikea visualisoida ja hallita. Mitä hyviä puolia tällaisissa projekteissa sitten on? V Olemme käyttäneet useimmissa tämän kirjan esimerkeissämme mallia, joka perustuu yksittäiseen skriptiin. Kun projekti laajenee, on ajateltava koko ympäristöä yksittäisenä sovelluksena. Yksittäiset sivut ovat vain vuorovaikutuksen osatoimintoja. Sijoita useamman kuin yhden sivun käyttämä koodi kirjastotiedoston funktioon. Jos mieluummin luot keskitetympiä skriptejä, voisit luoda yksittäisen sivun, joka sisältää vain HTMLperuskoodin mallit. Kaikki muut elementit voidaan sitten luoda dynaamisesti skriptisi kautta. Sinun on vietävä toimintolippu skriptille jokaisen pyynnön yhteydessä, jotta oikea tulostus generoitaisiin. Koodisi voisi näyttää siistimmältä eikä tarvittaisi muuta kuin otsikko-osan ratkaisut. Toisaalta sinun tulee tällöin sisällyttää paljon enemmän HTML-koodia PHP-koodiisi. Työpaja Työpaja tarjoaa joukon kertauskysymyksiä, joiden avulla voit tarkistaa, oletko ymmärtänyt materiaalin sisältöä. Yritä ymmärtää vastaukset ennen kuin jatkat seuraaviin lukuihin. Vastaukset ovat liitteessä A. Kysymyksiä 1. Millä PHP-funktiolla voit kytkeytyä MySQL-tietokantapalvelimelle? 2. Millä PHP-funktiolla voit alustaa tai lopettaa istunnon? 3. Millä funktiolla sisällytetään kirjastotiedostoja projektin sivuille? 4. Millä PHP-funktiolla lähetämme SQL-kyselyjä MySQL-tietokannalle? 5. Milla vakiolla lisäämme istunnon ID :nhtml-linkkiin? 6. Kuinka siirrämme käyttäjän uudelle sivulle? 7. Mitä funktiota käytämme päivämäärätiedon muotoilemiseen?

428 23. Esimerkki (Osa 1) Toiminta 1. Tutki tämän luvun koodia. Sopisivatko jotkin ratkaisut omiin projekteihisi?