IPRO - PC Magazine Romania, Martie 2003
SOLUŢII PENTRU PROGRAMATORII ŞI DESIGNERII WEB Totul
despre situri dinamice (III)
Konstantin Klyagin
Continuăm seria de articole despre crearea siturilor dinamice. În acest
număr, veţi găsi informaţii despre operaţiile asupra bazei de date despre
animale dispărute, utilă binecunoscutului erou Ace Ventura, explicaţii legate
codul PHP-urilor de pe CD, descrierea metodei de lucru cu fişiere de tip
imagine, trimiterea fişierelor prin formularul online, identificarea caracteristicilor
pozelor, redimensionarea şi includerea lor în baza de date.
Conectarea la baza de date
Nu prea are sens să explic fiecare linie din scripturile exemplului nostru,
de aceea vă voi arăta cele mai interesante şi utile locuri din cod.
Începem cu metoda de accesare a bazei de date, care, după cum vedem din fişierul
connect.php, se face cu ajutorul a două funcţii:
mysql_connect("localhost", "ace", "ace2") or die("Could not connect");
mysql_select_db("acedb") or die("Could not select database");
Prima linie din codul de sus face conectarea cu server-ul de baze de date,
iar a doua accesează baza de date. Apropo, dacă avem mai multe conexiuni
cu baze de date diferite, putem folosi o variabilă, pe care o returnează
prima funcţie. Dar în proiectul nostru avem o singură bază de date, deci
şi o singură conexiune. Sintaxa cu "or" vine din Perl şi permite definirea
unei acţiuni speciale, în caz că funcţia care a fost chemată întoarce o eroare.
Respectiv, funcţia die() opreşte executarea scriptului, afişând mesajul dorit.
Operaţii
asupra bazei de date
Să vedem cum se adaugă şi se şterg informaţii din baza de date. În fişierul
pets.php selectăm toate înregistrările din tabela pets:
select id, name, lostdate, reward from pets
Iar pentru a o trimite la serverul de baze de date pentru a fi executată,
folosim funcţia mysql_query():
$r = mysql_query("select id, name, lostdate, reward from pets");
Rezultatul execuţiei îl găsim într-o variabilă de tip resursă "$r".
while ($row = mysql_fetch_assoc($r))
Sunt o sumedenie de funcţii de genul fetch la partea de suport pentru MySQL
în PHP. Una dintre ele este mysql_fetch_assoc(), care de fapt este foarte
comodă pentru a accesa datele selectate. Rezultatul este un masiv asociativ
ale cărui elemente se referă după nume. Numele câmpului este numele elementului
respectiv din masiv. În exemplul nostru, vom avea următoarele variabile:
$row["id"], $row["name"], $row["lostdate"] şi $row["reward"]. mysql_fetch_assoc()
întoarce o valoare pozitivă până când se termină înregistrările, de aceea
am folosit intrucţiunea repetitivă "while".
Ca parametri ai instrucţiunii "select", putem avea atât câmpuri din baza
de date, cât şi funcţii. Iată, spre exemplu, primul "select" din petdetails.php:
$r = mysql_query("select p.name as pname, p.marks,
date_format(p.lostdate, '%d.%m.%Y') as lostdate, p.reward,
not isnull(p.picture) and p.picture != '' as haspic,
o.name as oname, p.owner_id
from pets p, owners o where p.id = $id and o.id = p.owner_id");
Da, aici avem un "select" adevărat: sunt prezente pseudonime pentru tabele
şi parametri, un apel de funcţie, precum şi o expresie logică al cărei rezultat
îl primim la ieşire. Pseudonimele se folosesc atunci când într-o solicitare
sunt expresii logice, apeluri la funcţii, sau numele câmpurilor sunt identice
în mai multe tabele şi nu este clar pe care vrem să îl selectăm. Expresia
logică cu câmpul "picture" din tabela pets determină dacă înregistrarea include
o imagine, iar rezultatul se regăseşte sub numele "haspic" şi va fi 0 sau
1.
Uneori prin cod vedem şi apeluri de genul:
header("Location: pets.php");
Ca la orice protocol de comunicare, HTTP are atât părţi de conţinut (date)
cât şi cele de "header", care de obicei spun ce urmează, lungimea şi opţiunile
necesare pentru recepţie şi procesare. Există cazuri în care nu există conţinut
ci doar o redirectare către o altă pagină. De exemplu, în owneradd.php, după
ce s-au verificat toate datele trimise de către utilizator, a fost adăugată
o nouă înregistrare în tabela "owners". În continuare, browserul este redirectat
către pagina ce afişează lista. Aşa funcţionează redirecţionarea, cu o singură
limitare: înaintea sa nu poate fi nici un alt "output", altfel PHP ne dă
următoarea înştiinţare:
Warning: Cannot add header information - headers already sent by
(output started at <unde: fişierul şi linia>)
in <unde s-a produs eroarea>
Formulare online
Dintr-o pagină web, informaţiile pot fi trimise către baza de date printr-un
formular online. Definirea unui formular începe cu tag-ul <FORM>. Dacă
numele scriptului care prelucrează formularul diferă de cel care a generat
pagina, se adaugă parametrul "action". Metoda de transfer a datelor se defineşte
prin parametrul METHOD. Astfel, avem:
<form action="petadd.php" method="get">
<input type="text" name="name" value="Tomcat">
...
</form>
O metodă pentru păstrarea parametrilor de la o prelucrare la alta, este
includerea în formular a unor câmpuri ascunse ("hidden"), care chiar dacă
nu sunt vizibile, se trimit o dată cu datele din formular. Găsim un exemplu
în petadd.php.
<input type="hidden" name="mode" value="<?= $mode ?>">
<input type="hidden" name="id" value="<?= $id ?>">
Sintaxa "<?= $variabilă ?>" este prescurtarea pentru "<?php echo($variabila);
?>" care afişează valoarea variabilei respective.
În instrucţiunea de mai jos, am apelat funcţia htmlspecialchars() care transformă
anumite caractere în coduri speciale HTML. De exemplu " (ghilimelele duble)
devin '"'.
<input type="text" name="name" value="<?= htmlspecialchars($name)
?>">
Adăugarea înregistrărilor în petadd.php şi owneradd.php se face după o schemă
destul de răspândită: formularul trimite datele către acelaşi script care
a generat iniţial pagina. Pentru a face diferenţa între operaţiile de adăugare
şi modificare a informaţiilor în baza de date, folosim un "if" simplu:
if(!empty($name)) {
După trimiterea formularului, în script e suficient să verificăm doar pentru
unul dintre parametri dacă a fost transmis, deoarece dacă un parametru a
ajuns la script, atunci ajung şi ceilalţi.
Scriptul care primeşte datele trimise prin formularul online, face atât adăugarea,
cât şi modificarea înregistrărilor.
Ştergerea înregistrărilor se face din aceleaşi script-uri care afişează listele
de animale sau de proprietari. Aici, ca şi în petadd.php şi owneradd.php,
regimul de folosire depinde de parametrul "mode". Dacă este "remove", se
afişează câte un "checkbox" lângă fiecare înregistrare, precum şi două
butoane noi în josul paginii. Acestea sunt "Remove checked" şi "Back". Fiecare
"checkbox"
are un identificator "id" din baza de date ca nume. Scriptul care prelucrează
formularul este remove.php, care primeşte lista de id-uri pentru checkbox-urile
selectate. Acum să vedem ce face programul respectiv. Mai întâi, se verifică
dacă a fost apăsat butonul "Back" care anulează operaţiunea:
if(empty($back)) {
Dacă nu, se plimbă prin parametri construind condiţia pentru comanda "delete":
foreach($_GET as $id => $val) {
if($id != "src") {
if($del != "") $del .= " or ";
if($upd != "") $udp .= " or ";
$del .= "id = $id";
$upd .= "owner_id = $id";
}
}
" .=" reprezintă operaţia de concatenare a două şiruri.
Ştim că în afară de "src", care reprezintă numele tabelei de modificat, toţi
parametrii sunt id-uri, şi nu toate, ci doar id-urile checkbox-urilor care
au fost marcate. Astfel, dacă 1, 3 şi 4 au fost selectate, condiţia va
fi, "id = 1 or id = 3 or id = 4". Restul comenzii nu se schimbă:
mysql_query("delete from $src where $del");
Dacă se şterge un proprietar, ne asigurăm că toate animalele sale se marchează
ca "fără proprietar" (câmpul "owner_id" la ele devenind zero):
if($src = = "owners")
mysql_query("update pets set owner_id = 0 where $upd");
Trimiterea fiŞierelor
prin formulare online
O metodă interesantă se foloseşte şi la transferul fişierelor prin formulare
online. Putem transfera o poză şi imediat ea va apare în sit. Dacă ne uităm
la script-ul de adăugare a unui animal, vedem o deosebire în definirea formularului:
<form enctype="multipart/form-data" method="post">
După cum ştim, metoda POST permite trimiterea unor şiruri mai mari de informaţii,
iar parametrul "enctype" este un element obligatoriu pentru încărcarea fişierelor.
Altfel nu va merge. Apoi punem un câmp de tip "file" în formular:
<input type="file" size=50 name="photo">
Pentru fiecare tip de parametru, avem diverse variabile. Pentru un parametru
cu tipul "file", avem variabilele:
$photo
Numele local temporar al fişierului încărcat.
$photo_name
Numele original al fişierului de la client (browser).
$photo_type
Tipul MIME al fişierului. Acest tip conţine un text scurt care îi spune
programului "browser" cum să trateze datele care urmează: dacă ceea ce vine
este o imagine sau un document PDF. Imaginile de obicei au image/jpeg, image/gif,
ş.a.m.d.
$photo_size
Mărimea fişierului în octeţi.
Iată cum folosim noi aceste variabile:
if(!empty($photo)) {
$orig = @imagecreatefromstring(fread(fopen($photo, "r"),
filesize($photo)));
if(empty($orig)) {
$msg = "The picture format is unknown or unsupported.";
}
}
După trimiterea unei poze, conţinutul fişierului este prelucrat de către
funcţia imagecreatefromstring() care crează în memorie o variabilă de tip
resursă. Acestă funcţie are o sintaxă interesantă: prefixul "@" elimină mesajele
de eroare pe care le poate produce funcţia apelată. Aici poate apărea o eroare
spunând că ceea ce a primit funcţia n-a fost o imagine, însă noi vrem să
evităm producerea erorilor interne în sit. De aceea, punem "@". Dacă imagecreatefromstring()
nu reuşeşte să citească imaginea, vom avea variabila $orig goală şi în acest
caz va apărea un mesaj.
Acesta n-a fost însă cel mai interesant lucru care se poate face cu imaginile,
pentru că urmează schimbarea dimensiunilor pozei. Într-adevăr nu avem nevoie
de nişte panouri de reclamă atunci când ne este lene să micşorăm imaginea
într-un editor grafic. Mai bine să lasăm scripturile de pe sit să facă
tot ce e necesar, mai ales că PHP-ul ne permite să facem diverse prelucrări
asupra
fişierelor grafice. Iată funcţia scalepicture() din connect.php:
function scalepicture($orig) {
$needx = $needy = 250;
$rx = imagesx($orig);
$ry = imagesy($orig);
if($rx > $needx) {
$ry *= $needx/$rx;
$rx = $needx;
}
if($ry > $needy) {
$rx *= $needy/$ry;
$ry = $needy;
}
$res = imagecreate($rx, $ry);
imagecopyresized($res, $orig, 0, 0, 0, 0, $rx, $ry,
imagesx($orig), imagesy($orig));
$tn = tempnam("img", "/tmp");
imagejpeg($res, $tn);
$content = addslashes(fread(fopen($tn, "r"),
filesize($tn)));
unlink($tn);
return $content;
}
După cum observaţi din definiţie, ea are un parametru reprezentând imaginea
citită şi procesată cu fread() şi imagecreatefromstring(). La început, limita
o avem în $needx şi $needy. Dacă nu ne convin dimensiunile originale - sunt
mai mari decât cele maxime posibile, adică 250x250, calculăm noile dimensiuni
păstrând proporţiile. Este clar ce facem în cele două if-uri. Sintaxa "*="
vine din C, şi înseamnă că rezultatul mai întâi se multiplică în partea dreaptă
a expresiei: "$ry *= $needx/$rx" este egal cu "$ry = $ry*$needx/$rx".
După ce am calculat noile dimensiuni, trecem direct la treabă. Creăm imaginea
goală în $res cu dimensiunile dorite, şi apoi o copiem pe cea originală
schimbându-i dimensiunea. După ce această operaţiune este gata, obţinem în
$tn un nume
unic pentru un fişier temporar unde scriem rezultatul în format JPEG. Numele
fişierului rezultat va fi pus într-un şir de caractere, se adaugă şi slash-urile
ca să fie gata pentru a-l include într-o comandă "insert". Ştim bine, că
în cazul în care valoarea unui câmp conţine ghilimele neecranate cu semnul
"slash - /", întreaga comandă va fi considerată incorectă.
Rezultatul returnat de scaleimage() este deja foarte uşor de folosit:
if($id = mysql_insert_id()) {
if(!empty($orig)) {
$content = scalepicture($orig);
mysql_query("update pets set picture = '$content'
where id = $id");
}
}
El se include imediat în comanda de "update". Apropo, să vedem cum putem
accesa ultima înregistrare inclusă în baza de date, în cazul în care tabela
respectivă are un câmp "auto_increment". Funcţia mysql_insert_id() ne dă
valoarea acestuia ca apoi să putem adăuga sau schimba ceva ştiind exact cu
ce înregistrare lucrăm. În exemplu, mai întâi se pun informaţiile generale
şi apoi, dacă este prezentă, se adaugă şi imaginea trimisă de către utilizator.
După ce imaginile se întroduc în baza de date, folosim script-ul fetchimage.php
pentru a le vizualiza. După cum ştim din documentaţia de HTML, imaginea statică
se afişează cu ajutorul tag-ului <IMG SRC="locaţia imaginii">. Diferenţa
între afişarea imaginilor dinamice şi a celor statice constă în faptul că
la cele dinamice, în loc de calea şi numele imaginii trebuie dată locaţia
programului CGI care ne returnează imaginea. Cum în cazul nostru aceasta
este fetchimage.php, includem atât numele cât şi parametrii în tag:
<img src="fetchimage.php?src=owners&id=1">
După fiecare modificare a datelor, se execută comanda "commit", pentru a
salva schimbările:
mysql_query("commit");
În acest loc facem "commit" şi noi. Deoarece există atât de multe tehnologii
fascinante, software minunat, inclusiv cel free şi Open Source, ne întrebăm
de ce să nu avem mai multe situri interesante şi utile. Pentru început, cunoştinţele
din acest articol ar trebui să fie suficiente, iar mai multe detalii puteţi
găsi foarte uşor în siturile programelor utilizate în exemplu (PHP - www.php.net şi MySQL - www.mysql.com). Vă urez să aveţi multe idei de realizat şi inspiraţie,
care este foarte importantă pentru a face un produs de calitate.
Situl demo se află online la:
http://konst.org.ua/ace
|