SQL pristop
Nette Database ponuja dve poti: lahko pišete SQL poizvedbe sami (SQL pristop) ali pa jih pustite samodejno generirati (glej Explorer). SQL pristop vam daje popoln nadzor nad poizvedbami in hkrati zagotavlja njihovo varno sestavljanje.
Podrobnosti o povezavi in konfiguraciji podatkovne baze najdete v poglavju Povezava in konfiguracija.
Osnovno poizvedovanje
Za poizvedovanje v podatkovni bazi služi metoda query()
. Ta vrne objekt ResultSet, ki predstavlja rezultat poizvedbe.
V primeru napake metoda vrže izjemo. Rezultat poizvedbe lahko prehajamo z zanko
foreach
ali uporabimo katero od pomožnih funkcij.
Za varno vstavljanje vrednosti v SQL poizvedbe uporabljamo parametrizirane poizvedbe. Nette Database jih naredi maksimalno preproste – zadostuje, da za SQL poizvedbo dodamo vejico in vrednost:
Pri več parametrih imate dve možnosti zapisa. Lahko SQL poizvedbo „prepletate“ s parametri:
Ali pa najprej napišete celotno SQL poizvedbo in nato priključite vse parametre:
Zaščita pred SQL injection
Zakaj je pomembno uporabljati parametrizirane poizvedbe? Ker vas ščitijo pred napadom, imenovanim SQL injection, pri katerem bi napadalec lahko podtaknil lastne SQL ukaze in s tem pridobil ali poškodoval podatke v podatkovni bazi.
Nikoli ne vstavljajte spremenljivk neposredno v SQL poizvedbo! Vedno uporabljajte parametrizirane poizvedbe, ki vas ščitijo pred SQL injection.
Seznanite se z možnimi varnostnimi tveganji.
Tehnike poizvedovanja
Pogoji WHERE
Pogoje WHERE lahko zapišete kot asociativno polje, kjer so ključi imena stolpcev in vrednosti podatki za primerjavo. Nette Database samodejno izbere najprimernejši SQL operator glede na tip vrednosti.
V ključu lahko tudi eksplicitno določite operator za primerjavo:
Nette samodejno obravnava posebne primere, kot so null
vrednosti ali polja.
Za negativne pogoje uporabite operator NOT
:
Za združevanje pogojev se uporablja operator AND
. To lahko spremenite z uporabo nadomestnega znaka ?or.
Pravila ORDER BY
Razvrščanje ORDER BY
lahko zapišemo z uporabo polja. V ključih navedemo stolpce, vrednost pa bo boolean, ki
določa, ali razvrščati naraščajoče:
Vstavljanje podatkov (INSERT)
Za vstavljanje zapisov se uporablja SQL ukaz INSERT
.
Metoda getInsertId()
vrne ID zadnje vstavljene vrstice. Pri nekaterih podatkovnih bazah (npr. PostgreSQL) je treba
kot parameter določiti ime sekvence, iz katere naj se ID generira z uporabo
$database->getInsertId($sequenceId)
.
Kot parametre lahko posredujemo tudi Posebne vrednosti kot so datoteke, objekti DateTime ali naštevni tipi.
Vstavljanje več zapisov hkrati:
Večkratni INSERT je veliko hitrejši, ker se izvede ena sama poizvedba podatkovne baze namesto mnogih posameznih.
Varnostno opozorilo: Nikoli ne uporabljajte kot $values
nevalidiranih podatkov. Seznanite se z možnimi tveganji.
Posodabljanje podatkov (UPDATE)
Za posodabljanje zapisov se uporablja SQL ukaz UPDATE
.
Število prizadetih vrstic vrne $result->getRowCount()
.
Za UPDATE lahko uporabimo operatorja +=
in -=
:
Primer vstavljanja ali urejanja zapisa, če že obstaja. Uporabimo tehniko ON DUPLICATE KEY UPDATE
:
Opazite, da Nette Database prepozna, v kakšnem kontekstu SQL ukaza vstavljamo parameter s poljem in glede na to iz njega
sestavi SQL kodo. Tako je iz prvega polja sestavil (id, name, year) VALUES (123, 'Jim', 1978)
, medtem ko je drugega
pretvoril v obliko name = 'Jim', year = 1978
. Podrobneje se temu posvečamo v delu Namigi za sestavljanje SQL.
Brisanje podatkov (DELETE)
Za brisanje zapisov se uporablja SQL ukaz DELETE
. Primer s pridobivanjem števila izbrisanih vrstic:
Namigi za sestavljanje SQL
Namig je poseben nadomestni znak v SQL poizvedbi, ki pove, kako naj se vrednost parametra prepiše v SQL izraz:
Namig | Opis | Samodejno se uporabi |
---|---|---|
?name |
uporabi za vstavljanje imena tabele ali stolpca | – |
?values |
generira (key, ...) VALUES (value, ...) |
INSERT ... ? , REPLACE ... ? |
?set |
generira prirejanje key = value, ... |
SET ? , KEY UPDATE ? |
?and |
združi pogoje v polju z operatorjem AND |
WHERE ? , HAVING ? |
?or |
združi pogoje v polju z operatorjem OR |
– |
?order |
generira klavzulo ORDER BY |
ORDER BY ? , GROUP BY ? |
Za dinamično vstavljanje imen tabel in stolpcev v poizvedbo služi nadomestni znak ?name
. Nette Database poskrbi
za pravilno obdelavo identifikatorjev glede na konvencije dane podatkovne baze (npr. zapiranje v povratne narekovaje
v MySQL).
Opozorilo: simbol ?name
uporabljajte samo za imena tabel in stolpcev iz validiranih vnosov, sicer se
izpostavljate varnostnemu tveganju.
Drugih namigov običajno ni treba navajati, saj Nette pri sestavljanju SQL poizvedbe uporablja pametno samodejno zaznavanje
(glej tretji stolpec tabele). Lahko pa ga uporabite na primer v situaciji, ko želite združiti pogoje z OR
namesto
AND
:
Posebne vrednosti
Poleg običajnih skalarnih tipov (string, int, bool) lahko kot parametre posredujete tudi posebne vrednosti:
- datoteke:
fopen('image.gif', 'r')
vstavi binarno vsebino datoteke - datum in čas: objekti
DateTime
se pretvorijo v format podatkovne baze - naštevni tipi: instance
enum
se pretvorijo v njihovo vrednost - SQL literali: ustvarjeni z
Connection::literal('NOW()')
se vstavijo neposredno v poizvedbo
Pri podatkovnih bazah, ki nimajo nativne podpore za podatkovni tip datetime
(kot SQLite in Oracle), se
DateTime
pretvori v vrednost, določeno v konfiguraciji podatkovne baze z vnosom
formatDateTime
(privzeta vrednost je U
– unix timestamp).
SQL literali
V nekaterih primerih morate kot vrednost navesti neposredno SQL kodo, ki pa se ne sme razumeti kot niz in ubežati. Za to
služijo objekti razreda Nette\Database\SqlLiteral
. Ustvarja jih metoda Connection::literal()
.
Ali alternativno:
SQL literali lahko vsebujejo parametre:
Zaradi česar lahko ustvarjamo zanimive kombinacije:
Pridobivanje podatkov
Bližnjice za SELECT poizvedbe
Za poenostavitev nalaganja podatkov Connection
ponuja več bližnjic, ki kombinirajo klic query()
z naslednjim fetch*()
. Te metode sprejemajo enake parametre kot query()
, torej SQL poizvedbo in
neobvezne parametre. Popoln opis metod fetch*()
najdete spodaj.
fetch($sql, ...$params): ?Row |
Izvede poizvedbo in vrne prvo vrstico kot objekt Row |
fetchAll($sql, ...$params): array |
Izvede poizvedbo in vrne vse vrstice kot polje objektov Row |
fetchPairs($sql, ...$params): array |
Izvede poizvedbo in vrne asociativno polje, kjer prvi stolpec predstavlja ključ in drugi vrednost |
fetchField($sql, ...$params): mixed |
Izvede poizvedbo in vrne vrednost prvega polja iz prve vrstice |
fetchList($sql, ...$params): ?array |
Izvede poizvedbo in vrne prvo vrstico kot indeksirano polje |
Primer:
foreach
– iteracija čez vrstice
Po izvedbi poizvedbe se vrne objekt ResultSet,
ki omogoča prehajanje rezultatov na več načinov. Najlažji način za izvedbo poizvedbe in pridobitev vrstic je iteracija
v zanki foreach
. Ta način je pomnilniško najbolj varčen, saj vrača podatke postopoma in jih ne shranjuje vseh
hkrati v pomnilnik.
ResultSet
je mogoče iterirati samo enkrat. Če potrebujete iterirati večkrat, morate najprej
naložiti podatke v polje, na primer z metodo fetchAll()
.
fetch(): ?Row
Vrne vrstico kot objekt Row
. Če ni več vrstic, vrne null
. Premakne notranji kazalec na naslednjo
vrstico.
fetchAll(): array
Vrne vse preostale vrstice iz ResultSet
kot polje objektov Row
.
fetchPairs(string|int|null $key = null, string|int|null $value = null): array
Vrne rezultate kot asociativno polje. Prvi argument določa ime stolpca, ki se uporabi kot ključ v polju, drugi argument določa ime stolpca, ki se uporabi kot vrednost:
Če navedemo samo prvi parameter, bo vrednost celotna vrstica, torej objekt Row
:
V primeru podvojenih ključev se uporabi vrednost iz zadnje vrstice. Pri uporabi null
kot ključa bo polje
indeksirano numerično od nič (potem do kolizij ne pride):
fetchPairs(Closure $callback): array
Alternativno lahko kot parameter navedete povratni klic (callback), ki bo za vsako vrstico vrnil bodisi samo vrednost ali par ključ-vrednost.
fetchField(): mixed
Vrne vrednost prvega polja iz trenutne vrstice. Če ni več vrstic, vrne null
. Premakne notranji kazalec na
naslednjo vrstico.
fetchList(): ?array
Vrne vrstico kot indeksirano polje. Če ni več vrstic, vrne null
. Premakne notranji kazalec na naslednjo
vrstico.
getRowCount(): ?int
Vrne število prizadetih vrstic zadnje poizvedbe UPDATE
ali DELETE
. Za SELECT
je to
število vrnjenih vrstic, vendar to morda ni znano – v takem primeru metoda vrne null
.
getColumnCount(): ?int
Vrne število stolpcev v ResultSet
.
Informacije o poizvedbah
Za namene razhroščevanja lahko pridobimo informacije o zadnji izvedeni poizvedbi:
Za prikaz rezultata kot HTML tabele lahko uporabimo:
ResultSet ponuja informacije o tipih stolpcev:
Dnevniško beleženje poizvedb
Lahko implementiramo lastno dnevniško beleženje poizvedb. Dogodek onQuery
je polje povratnih klicev (callback),
ki se pokličejo po vsaki izvedeni poizvedbi: