Tämä koodivinkki sisältää vaatimattomasti sanottuna kaiken, mitä tarvitsee tietää, jos haluaa luoda yksinkertaisen MySQL-tietokantaa käyttävän nettisovelluksen PHP:llä. Koodivinkin aihe on tuoterekisteri, jossa on näkyvissä varastossa tms. olevia tuotteita hintoineen ja määrineen. Tuotteita pystyy myös lisäämään, muokkaamaan ja poistamaan, joten SQL-lauseiden koko kirjo tulee esille.
Tuotteet sisältävä SQL-taulu luodaan seuraavasti:
CREATE TABLE tuotteet ( id INT AUTO_INCREMENT, nimi VARCHAR (30), vari INT, hinta INT, maara INT, PRIMARY KEY(id))
Kenttien nimistä on tuskin vaikeaa arvata niiden merkitystä. Kenttä id on taulun avain varustettuna automaattisella kasvatuksella (AUTO_INCREMENT), mikä tarkoittaa, että jokainen uusi tuote saa tietokantaan lisättäessä automaattisesti seuraavan järjestyksessä olevan vapaan id:n.
Tauluun voi lisätä testiaineistoa esim. seuraavilla komennoilla:
INSERT INTO tuotteet(nimi, vari, hinta, maara) VALUES ('peruna', 4, 1, 100) INSERT INTO tuotteet(nimi, vari, hinta, maara) VALUES ('porkkana', 3, 2, 75) INSERT INTO tuotteet(nimi, vari, hinta, maara) VALUES ('tomaatti', 3, 1, 100) INSERT INTO tuotteet(nimi, vari, hinta, maara) VALUES ('nauris', 4, 3, 50) INSERT INTO tuotteet(nimi, vari, hinta, maara) VALUES ('herne', 5, 1, 90) INSERT INTO tuotteet(nimi, vari, hinta, maara) VALUES ('salaatti', 5, 3, 65)
Tiedosto lista.php näyttää kaikki tietokannassa olevat tuotteet. Taulukon voi järjestää halutun kentän mukaan ylimmän rivin linkeistä. Jokaisen tuotteen kohdalla listassa on linkki, jota painamalla tuotteen tietoja pystyy muuttamaan. Tämä tapahtuu sivulla muokkaus.php, joka ohjaa edelleen sivulla tallennus.php, kun käyttäjä on tehnyt haluamansa muutokset. Lisäksi tuotteita voi poistaa merkitsemällä listassa poistettavat tuotteet ja painamalla "Poista merkityt" -nappia. Tämän napin vieressä on "Uusi tuote" -nappi, joka ohjaa aiemmin mainitulle sivulle muokkaus.php. Nyt kuitenkin id on 0, mikä tarkoittaa uutta tuotetta.
Tuoterekisterin PHP-koodi on tarkoituksella melko suoraviivaista, ja liikoja hienouksia on vältetty. Kuitenkin yksi erityishuomion ansaitseva yksityiskohta mahtuu mukaan: implode-funktion käyttö listan rivien luonnissa ja valitut tuotteet poistavan SQL-komennon muodostuksessa. Kannattaa tarkoin tutkia näitä kohtia koodissa, jos implode-funktio on jäänyt vieraaksi. Rivien tiedot olisi toki voinut kirjoittaa myös suoraan merkkijonoon ja poistettavat tuotteet olisi voinut käydä läpi for-silmukassa, mutta implode-funktio on näitä perinteisiä keinoja tyylikkäämpi vaihtoehto.
Tuoterekisteriä voi kokeilla toiminnassa seuraavassa osoitteessa:
https://www.ohjelmointiputka.net/tiedostot/tuoterekisteri/lista.php
<?php // lista.php // yhteys tietokantaan mysql_connect("localhost", "", ""); mysql_select_db("kanta"); // värien luettelo $varit = array("musta", "valkoinen", "sininen", "punainen", "keltainen", "vihreä", "harmaa", "ruskea"); echo "<h1>Tuoterekisteri</h1>"; echo "<form action=\"toiminta.php\" method=\"post\">"; // taulukon aloitus echo "<table border>"; $rivi = array(" ", "<a href=\"lista.php?jarj=nimi\">nimi</a>", "<a href=\"lista.php?jarj=vari\">väri</a>", "<a href=\"lista.php?jarj=hinta\">hinta</a>", "<a href=\"lista.php?jarj=maara\">määrä</a>", " "); echo "<tr><td><b>" . implode("</b></td><td><b>", $rivi) . "</b></td></tr>"; // näiden kenttien mukaan saa järjestää $jkentat = array("nimi", "vari", "hinta", "maara"); // järjestyksen määritys if (isset($_GET["jarj"]) && in_array($_GET["jarj"], $jkentat)) { $jarj = $_GET["jarj"]; } else { $jarj = "nimi"; } // taulukon tiedot tietokannasta $sql = "select id, nimi, vari, hinta, maara from tuotteet order by $jarj"; $tulos = mysql_query($sql); // rivien tulostus silmukassa for ($i = 0; $i < mysql_num_rows($tulos); $i++) { // rivin tiedot tietokannasta $id = mysql_result($tulos, $i, 0); $nimi = mysql_result($tulos, $i, 1); $vari = mysql_result($tulos, $i, 2); $hinta = mysql_result($tulos, $i, 3); $maara = mysql_result($tulos, $i, 4); // rivin tiedot muotoiltuna $rivi = array("<input type=\"checkbox\" name=\"valinta[]\" value=\"$id\">", $nimi, $varit[$vari], $hinta . " e", $maara . " kpl", "<a href=\"muokkaus.php?id=$id\">muokkaus</a>"); // rivin tulostus echo "<tr><td>" . implode("</td><td>", $rivi) . "</td></tr>"; } echo "</table>"; echo "<br>"; echo "<input type=\"submit\" name=\"uusi\" value=\"Uusi tuote\"> "; echo "<input type=\"submit\" name=\"poisto\" value=\"Poista merkityt\">"; echo "</form>"; ?>
<?php // toiminta.php // yhteys tietokantaan mysql_connect("localhost", "", ""); mysql_select_db("kanta"); // uusi sivu if (!empty($_POST["uusi"])) { header("Location: muokkaus.php?id=0"); } // tuotteiden poisto if (!empty($_POST["poisto"])) { // onko tuotteita valittu? if (!empty($_POST["valinta"])) { // tuotteiden poisto yhdellä kyselyllä $lista = implode(", ", array_map("intval", $_POST["valinta"])); $sql = "delete from tuotteet where id in ($lista)"; mysql_query($sql); } header("Location: lista.php"); } ?>
<?php // muokkaus.php // yhteys tietokantaan mysql_connect("localhost", "", ""); mysql_select_db("kanta"); // värien luettelo $varit = array("musta", "valkoinen", "sininen", "punainen", "keltainen", "vihreä", "harmaa", "ruskea"); $id = intval($_GET["id"]); // uusi tuote if ($id == 0) { echo "<h1>Uusi tuote</h1>"; // oletusarvot $nimi = ""; $vari = 0; $hinta = 0; $maara = 0; // tuotteen muokkaus } else { echo "<h1>Tuotteen muokkaus</h1>"; $sql = "select nimi, vari, hinta, maara from tuotteet where id = $id"; $tulos = mysql_query($sql); // vanhat arvot tietokannasta $nimi = mysql_result($tulos, 0, 0); $vari = mysql_result($tulos, 0, 1); $hinta = mysql_result($tulos, 0, 2); $maara = mysql_result($tulos, 0, 3); } echo "<form action=\"tallennus.php\" method=\"post\">"; echo "<input type=\"hidden\" name=\"id\" value=\"$id\">"; echo "<table>"; echo "<tr><td>Nimi:</td><td><input type=\"text\" name=\"nimi\" value=\"$nimi\">"; echo "<tr><td>Väri:</td><td><select name=\"vari\">"; // lista, jossa näkyy vanha valinta for ($i = 0; $i < count($varit); $i++) { if ($vari == $i) { echo "<option value=\"$i\" selected>{$varit[$i]}"; } else { echo "<option value=\"$i\">{$varit[$i]}"; } } echo "</select></td></tr>"; echo "<tr><td>Hinta:</td><td><input type=\"text\" name=\"hinta\" value=\"$hinta\">"; echo "<tr><td>Määrä:</td><td><input type=\"text\" name=\"maara\" value=\"$maara\">"; echo "</table>"; echo "<br>"; echo "<input type=\"submit\" name=\"tallennus\" value=\"Tallenna\"> "; echo "<input type=\"submit\" name=\"peruutus\" value=\"Peruuta\"> "; echo "</form>"; ?>
<?php // tallennus.php // yhteys tietokantaan mysql_connect("localhost", "", ""); mysql_select_db("kanta"); // tallennus if (!empty($_POST["tallennus"])) { $id = intval($_POST["id"]); // vain pienaakkoset kelpaavat nimessä, ja pituutta rajoitetaan $nimi = @strval(@$_POST["nimi"]); $nimi = preg_replace("/[^a-zåäö]/su", "", $nimi); $nimi = preg_replace("/(.{61})....*/su", "$1...", $nimi); if (!strlen($nimi)) { die("Nimi ei kelpaa!"); } // värit ovat 0–7 $vari = max(0, min(7, intval(@$_POST["vari"]))); $hinta = intval($_POST["hinta"]); $maara = intval($_POST["maara"]); // kysely sen mukaan, onko uusi vai vanha tuote if ($id == 0) { $sql = "insert into tuotteet(nimi, vari, hinta, maara) values ('$nimi', $vari, $hinta, $maara)"; } else { $sql = "update tuotteet set nimi = '$nimi', vari = $vari, hinta = $hinta, maara = $maara where id = $id"; } mysql_query($sql); header("Location: lista.php"); } // peruutus if (!empty($_POST["peruutus"])) { header("Location: lista.php"); } ?>
eli ensimmäinen on lista.php eikä index.php ?
Nyt kun PHP5 on jo se virallinen versio, ja moni meistä mielellään pääsisi eroon vanhoillisuuksista ja samalla ikävästä lelukielen leimasta, niin jos sitä vaikka luettelisi parannusehdotuksia noihin "PHP4-aikakauden erheisiin":
-Oliopohjaisuus, jolla saisi aikaa selkeyttä ja dynaamisuutta (kunhan ei vedä enterprisey-överiksi).
-Ei HöTöMöLöä turhaan PHP-tulkin läpi.
-SQL varatut sanat isolla.
-Tulostaessa heitto- ja lainausmerkkien vuorottelu, eskapetus on rumaa.
-Taulukon indekseissä käytetään yleisemmin heittomerkkiä.
-Filter syötteiden filtteröintiin.
-mysql_fetch_array
mysql_resultin
sijaan.
Tuo lista.php jättää ensimmäisen kirjaimen ainakin minun lisäyksestäni pois...
Edit:
Mjaa olisi pitänyt huomata, että se ei tykkää isoista kirjaimista.
Kaikilla palvelimilla/selaimilla mikä vaikuttaakaan ei toimi tuo
header("Location: tiedosto.php");
vaan sen pitää olla
header("Location: ./tiedosto.php");
Ei, vaan
<?php $filepath = 'lista.php'; header("Location: http://{$_SERVER['SERVER_NAME']}/{$filepath}); ?>
Location-header vaatii speksin mukaan absoluuttisen URIn.
Oikeassa sovelluksessa kannattaa sitten käyttäjän syötteen validoinnin tai eskapoinnin kanssa olla myös vähän tarkempi, etteivät pahat pojat liitä SQL-kyselyihin mitään sopimatonta.
Kiitos kommenteista! Tässä vastauksia...
tsuriga kirjoitti:
Oliopohjaisuus, jolla saisi aikaa selkeyttä ja dynaamisuutta
Miten toteuttaisit vastaavan järjestelmän oliopohjaisesti?
tsuriga kirjoitti:
Ei HöTöMöLöä turhaan PHP-tulkin läpi.
Eikö kaikki sivun koodi mene joka tapauksessa PHP:n läpi?
tsuriga kirjoitti:
SQL varatut sanat isolla.
Miksi ne pitäisi kirjoittaa huutaen?
tsuriga kirjoitti:
Tulostaessa heitto- ja lainausmerkkien vuorottelu, eskapetus on rumaa.
tsuriga kirjoitti:
Taulukon indekseissä käytetään yleisemmin heittomerkkiä.
Minusta heitto- ja lainausmerkkien vuorottelu on rumaa.
tsuriga kirjoitti:
Filter syötteiden filtteröintiin.
Moinen ei toimi PHP4:llä, jota käyttää vielä moni.
tsuriga kirjoitti:
mysql_fetch_array mysql_resultin sijaan.
Tähänkin olisi mukava saada perustelu (muu kuin mikrosekunnin nopeusero).
punppis kirjoitti:
Kaikilla palvelimilla/selaimilla mikä vaikuttaakaan ei toimi tuo - -
Voitko mainita yhden palvelimen tai selaimen, jolla se ei toimi?
thefox kirjoitti:
Oikeassa sovelluksessa kannattaa sitten käyttäjän syötteen validoinnin tai eskapoinnin kanssa olla myös vähän tarkempi - -
Missä kohtaa koodissa on mahdollisuus lisätä SQL-kyselyyn ikävyyksiä?
-Toteuttaisin luultavasti jollakin valmiilla frameworkilla, paha sanoa kun en ole itse sen tarkemmin perehtynyt erityisesti web-koodaukseen (aika menee konsoliohjelmien ja teoriapuolen parissa).
-Suosittelisin HTML-sorsan eristämistä PHP-koodista kokonaiskuvan selkeyttämiseksi. Pyritään erittelemään sivun rakenne ja toiminnallisuus. Sinänsä hassu, että PHP:n perustulkki näyttäisi olevan oftentimes nopeampi tulostamaan HTML:n kuin hyppäämään sen yli (mistä en nyt kyllä osaakaan sanoa, että hyppääkö se ollenkaan).
-Varattujen SQL sanojen kirjoittaminen isolla on hyväksi havaittu ja yleisesti käytössä oleva käytäntö, joka edistää tietokantahakujen luettavuutta.
-mysql_fetch_array on ensinnäkin loogisempi tällaisissa tilanteissa, kun kannasta haetaan useita arvoja. Toisekseen sulla on yhdellä fetch_array
-komennolla data valmiina N:n result
-kutsun sijaan (tässä esim. 4 eräässä kohdassa). Lisäksi voit samalla vaihtaa forin foreachiin, joka on taas se verbaalisesti loogisempi vaihtoehto tilanteeseen, jossa tahdotaan kahlata arvolistan läpi. Forille sitten omat käyttötarkoituksensa, kun tarvitaan juoksevia numeroita iteraattorin sijaan. Tai sitten todella nohevat, galaktiset enterprise sankarit vääntävät toki oman Iterator rajapinnan toteuttavan MySQLIterator-luokan.. :). Kolmanneksi, PHP:n manuaali itsekin ehdottaa tätä mikäli haetaan useita soluja, siellä käytetty ilmaisu "MUCH quicker" tuskin tarkoittaa mikrosekuntia.
-PHP4 tuki loppui jo vuoden vaihteessa, aika päivittää parempaan. srsly.
Toisin sanoen, lähes kaikki parannusehdotukseni liittyvät hyvien ja modernien käytäntöjen edistämiseen ja semanttisuuteen.
EDIT 28.01.2008: Lisäsin kolmannen huomion mysql_fetchin paremmuudesta.
Ilmeisesti suhtaudumme eri tavalla PHP-ohjelmointiin. Ne asiat, jotka mielestäsi tekevät PHP:stä lelukielen, ovat minusta sen suurin voimavara. Olio-ohjelmointi ja sen tuoma monimutkaisuus eivät minusta sovi PHP:hen. Lisäksi PHP:n käteviä ominaisuuksia tuomitaan aivan liian ankarasti ("älä käytä sitä, käytä tuota"). On virhe yrittää tehdä PHP:stä salonkikelpoista, kun siitä ei sellaiseksi ole. Kieltä "parannettaessa" uhattuna ovat PHP:n merkittävät vahvuudet: helppous, selkeys ja joustavuus. Murheellisin mielin olen seurannut PHP:n kehitystä ja havainnut, että kieli on etääntymässä niistä arvoista, jotka viehättivät minua vuosia sitten, kun tutustuin ensi kertaa PHP:hen.
Kyllähän PHP on edelleen helppo kieli aloittelijalle lähteä liikkeelle, mutta uudet olio-ominaisuudet tekevät ohjelmoinnista paljon helpompaa, tyylikkäämpää, modulaarisempaa ja tehokkaampaa (lisää randomeita ylistyssanoja tähän), eikä oo:ta tietenkään tarvitse käyttää jos sitä ei hallitse. Hyvänä esimerkkinä eroista vaikkapa seuraavat kaksi vieraskirjaviritystä:
https://www.ohjelmointiputka.net/koodivinkit/
http://mureakuha.com/keskustelut/2?15393#a128885
Kaikki kunnia ja kiitos tsurigalle ahkerasta oo:n tuputuksesta myös tänne putkaan. PHP:n uudet ominaisuudet (iteraattorit, filterit jne.) olisivat varmaan muuten jääneet huomaamatta :)
Mutta kukin vääntäkööt koodia tyylillään. Jos oma tapa on omasta mielestä se paras ja kätevin, se sallittakoot. Kannattaa kuitenkin silloin tällöin katsella aidan toiselle puolelle, josko se ruoho sittenkin olisi siellä vihreämpää...
... nimittäin se on! :)
Allekirjoittanut on tekemässä Zend Framework pohjalle mallia jossa on siis mukana PHP5:n olio-ohjelmointi kaikessa "loistokkuudessaan".
Alkuosa oppaasta valmistunee helmikuun alkupuolella joten sitä odotellessa. Voisin ottaa tuon tuoterekisterin malliksi asiasta ja voidaan tutkia miten homma toimii MVC mallilla.
-W-
Antti Laaksonen kirjoitti:
thefox kirjoitti:
Oikeassa sovelluksessa kannattaa sitten käyttäjän syötteen validoinnin tai eskapoinnin kanssa olla myös vähän tarkempi - -
Missä kohtaa koodissa on mahdollisuus lisätä SQL-kyselyyn ikävyyksiä?
Nyt lähemmin koodia tarkasteltuani selvisikin, että onhan ne syötteet siivottu, joten aiemman kommenttini voi jättää omaan arvoonsa. :)
Minulla on aina ollut sellainen ongelma mysqlin kanssa, että en tiedä mitä tuohon "localhostiin" pitää laittaa. Jos joku vain viitteis auttaa apinaa :)
Wizard kirjoitti:
Allekirjoittanut on tekemässä Zend Framework pohjalle mallia jossa on siis mukana PHP5:n olio-ohjelmointi kaikessa "loistokkuudessaan".
Alkuosa oppaasta valmistunee helmikuun alkupuolella joten sitä odotellessa. Voisin ottaa tuon tuoterekisterin malliksi asiasta ja voidaan tutkia miten homma toimii MVC mallilla.
-W-
Todella upeeta, aivan mahtavaa!! Toivottavasti opas valmistuu lähiaikoina!
Haluaisin tehdä oman pienen tuoterekisterin mutta en koodata kaikkea alusta ja saada luotettavaa jälkeä aikaiseksi. Olen juuri pohdiskelemassa olisiko Zend framework tarpeisiini sopiva, mutta sivujen esimerkit vaikuttavat niin jykeviltä..
Antti Laaksonen kirjoitti:
Ilmeisesti suhtaudumme eri tavalla PHP-ohjelmointiin. Ne asiat, jotka mielestäsi tekevät PHP:stä lelukielen, ovat minusta sen suurin voimavara. Olio-ohjelmointi ja sen tuoma monimutkaisuus eivät minusta sovi PHP:hen. Lisäksi PHP:n käteviä ominaisuuksia tuomitaan aivan liian ankarasti ("älä käytä sitä, käytä tuota"). On virhe yrittää tehdä PHP:stä salonkikelpoista, kun siitä ei sellaiseksi ole. Kieltä "parannettaessa" uhattuna ovat PHP:n merkittävät vahvuudet: helppous, selkeys ja joustavuus. Murheellisin mielin olen seurannut PHP:n kehitystä ja havainnut, että kieli on etääntymässä niistä arvoista, jotka viehättivät minua vuosia sitten, kun tutustuin ensi kertaa PHP:hen.
Itse vältän olioiden käyttöä viimeiseen asti, enkä hirveesti pidä siitä kehityksestä, joka ajaa PHP:tä kyseiseen suuntaan. Kuten tästä varmaan päättelitte, niin en käytä olioita. Se ei silti tee minusta sen "huonompaa" koodaria, koska on aina olemassa toinenkin tapa tehdä samat asiat, mitä oliot ajaa. (Syynä ei ole taitojen puute, en vain ole omaksunut niiden käyttöä.)
Olioiden käyttö on ehkä vain yliarvostettua. Tosi asiassa kolmasosapuoli (asiakas) ei tiedä, onko jokin sovellus toteutettu olioilla vai ei. Kukin siis koodatkoon tavalla, jonka hallitsee parhaiten.
Tämä on just sitä mitä oon hakenut omaan käpistykseeni, mutta nyt en vaan saa toimimaan tuota:
header("Location: lista.php");
Tällainen herja tulee kun painaa muokkauslomakkeen jälkeen tallenna:
Warning: Cannot modify header information - headers already sent by (output started at U:\xampp\htdocs\xampp\uusi\tallennus.php:6) in U:\xampp\htdocs\xampp\uusi\tallennus.php on line 28
Tuo Punppiksen laittama korjaus ei auta ja Tsurigan laittamasta koodin pätkästä en tajua, mihin väliin se pitäis laittaa..?
makas kirjoitti:
Antti Laaksonen kirjoitti:
Ilmeisesti suhtaudumme eri tavalla PHP-ohjelmointiin. Ne asiat, jotka mielestäsi tekevät PHP:stä lelukielen, ovat minusta sen suurin voimavara. Olio-ohjelmointi ja sen tuoma monimutkaisuus eivät minusta sovi PHP:hen. Lisäksi PHP:n käteviä ominaisuuksia tuomitaan aivan liian ankarasti ("älä käytä sitä, käytä tuota"). On virhe yrittää tehdä PHP:stä salonkikelpoista, kun siitä ei sellaiseksi ole. Kieltä "parannettaessa" uhattuna ovat PHP:n merkittävät vahvuudet: helppous, selkeys ja joustavuus. Murheellisin mielin olen seurannut PHP:n kehitystä ja havainnut, että kieli on etääntymässä niistä arvoista, jotka viehättivät minua vuosia sitten, kun tutustuin ensi kertaa PHP:hen.
Itse vältän olioiden käyttöä viimeiseen asti, enkä hirveesti pidä siitä kehityksestä, joka ajaa PHP:tä kyseiseen suuntaan. Kuten tästä varmaan päättelitte, niin en käytä olioita. Se ei silti tee minusta sen "huonompaa" koodaria, koska on aina olemassa toinenkin tapa tehdä samat asiat, mitä oliot ajaa. (Syynä ei ole taitojen puute, en vain ole omaksunut niiden käyttöä.)
Olioiden käyttö on ehkä vain yliarvostettua. Tosi asiassa kolmasosapuoli (asiakas) ei tiedä, onko jokin sovellus toteutettu olioilla vai ei. Kukin siis koodatkoon tavalla, jonka hallitsee parhaiten.
pakko viel kommentoida :) olio ohjelmoinnissa poittina onkin lähinnä peritymisen ansiosta tuleva helppo miten sen nyt sanois... uudelleen käytettävyys
Miten tässä saisi skandit ja välilyönnit toimimaan? Vika on siis tuossa preg_replace komennossa, lista kyllä näyttää hienosti suoraan tietokantaan PhpMyAdminilla lisätyt taajuudet.
Aliisa kirjoitti:
Warning: Cannot modify header information - headers already sent by (output started at U:\xampp\htdocs\xampp\uusi\tallennus.php:6) in U:\xampp\htdocs\xampp\uusi\tallennus.php on line 28
Tämä viittaa siihen että sivulla on jo sisältöä (echo). Tällöin et voi uudelleenohjata. Ainakin notepad++:ssa kannattaa muuntaa merkistö "... (ei BOM)", jos muu ei auta.
radioaktiivinen kirjoitti:
Miten tässä saisi skandit ja välilyönnit toimimaan? Vika on siis tuossa preg_replace komennossa, lista kyllä näyttää hienosti suoraan tietokantaan PhpMyAdminilla lisätyt taajuudet.
Mikä on tiedoston merkistö? entä html:n charset?
PHP 7.0.0 ei enää sisällä näitä vanhoja MySQL-funktioita (mysql_*), eli koodivinkkiä tulisi korjata merkittävästi.
Aihe on jo aika vanha, joten et voi enää vastata siihen.