Database Explorer
Az Explorer intuitív és hatékony módot kínál az adatbázissal való munkára. Automatikusan gondoskodik a táblák közötti kapcsolatokról és a lekérdezések optimalizálásáról, így Ön az alkalmazására koncentrálhat. Azonnal működik beállítás nélkül. Ha teljes kontrollra van szüksége az SQL lekérdezések felett, használhatja az SQL megközelítést.
- Az adatokkal való munka természetes és könnyen érthető.
- Optimalizált SQL lekérdezéseket generál, amelyek csak a szükséges adatokat töltik be.
- Lehetővé teszi a kapcsolódó adatokhoz való könnyű hozzáférést JOIN lekérdezések írása nélkül.
- Azonnal működik bármilyen konfiguráció vagy entitásgenerálás nélkül.
Az Explorerrel a Nette\Database\Explorer objektum
table()
metódusának meghívásával kezdhet (a csatlakozás részleteit a Csatlakozás és konfiguráció fejezetben találja):
A metódus egy Selection objektumot ad
vissza, amely egy SQL lekérdezést képvisel. Erre az objektumra további metódusokat láncolhatunk az eredmények szűrésére
és rendezésére. A lekérdezés csak akkor áll össze és fut le, amikor elkezdjük kérni az adatokat. Például egy
foreach
ciklussal történő bejáráskor. Minden sort egy ActiveRow objektum képvisel:
Az Explorer alapvetően megkönnyíti a táblák közötti kapcsolatokkal való munkát. A következő példa bemutatja, milyen könnyen tudunk adatokat kiírni összekapcsolt táblákból (könyvek és szerzőik). Figyelje meg, hogy nem kell semmilyen JOIN lekérdezést írnunk, a Nette létrehozza őket helyettünk:
A Nette Database Explorer optimalizálja a lekérdezéseket, hogy a lehető leghatékonyabbak legyenek. A fenti példa csak két SELECT lekérdezést hajt végre, függetlenül attól, hogy 10 vagy 10 000 könyvet dolgozunk fel.
Ráadásul az Explorer figyeli, hogy mely oszlopokat használják a kódban, és csak azokat tölti be az adatbázisból, ezzel további teljesítményt takarítva meg. Ez a viselkedés teljesen automatikus és adaptív. Ha később módosítja a kódot, és elkezd további oszlopokat használni, az Explorer automatikusan módosítja a lekérdezéseket. Nem kell semmit beállítania, sem azon gondolkodnia, mely oszlopokra lesz szüksége – bízza ezt a Nette-re.
Szűrés és rendezés
A Selection
osztály metódusokat biztosít az adatok kiválasztásának szűrésére és rendezésére.
where($condition, ...$params) |
WHERE feltételt ad hozzá. Több feltétel AND operátorral van összekötve. |
whereOr(array $conditions) |
OR operátorral összekötött WHERE feltételek csoportját adja hozzá. |
wherePrimary($value) |
WHERE feltételt ad hozzá az elsődleges kulcs alapján. |
order($columns, ...$params) |
Beállítja az ORDER BY rendezést. |
select($columns, ...$params) |
Meghatározza a betöltendő oszlopokat. |
limit($limit, $offset = null) |
Korlátozza a sorok számát (LIMIT) és opcionálisan beállítja az OFFSET-et. |
page($page, $itemsPerPage, &$total = null) |
Beállítja a lapozást. |
group($columns, ...$params) |
Csoportosítja a sorokat (GROUP BY). |
having($condition, ...$params) |
HAVING feltételt ad hozzá a csoportosított sorok szűréséhez. |
A metódusok láncolhatók (ún. fluent interface):
$table->where(...)->order(...)->limit(...)
.
Ezekben a metódusokban speciális jelölést is használhat a kapcsolódó táblákból származó adatokhoz való hozzáféréshez.
Escapelés és azonosítók
A metódusok automatikusan escapelik a paramétereket és idézőjelek közé teszik az azonosítókat (tábla- és oszlopneveket), ezzel megakadályozva az SQL injectiont. A helyes működéshez néhány szabályt be kell tartani:
- A kulcsszavakat, függvényneveket, eljárásneveket stb. nagybetűkkel írja.
- Az oszlop- és táblaneveket kisbetűkkel írja.
- A stringeket mindig paramétereken keresztül adja át.
where(string|array $condition, …$parameters): static
Szűri az eredményeket WHERE feltételekkel. Erőssége az intelligens munka különböző típusú értékekkel és az SQL operátorok automatikus kiválasztása.
Alapvető használat:
A megfelelő operátorok automatikus felismerésének köszönhetően nem kell különböző speciális esetekkel foglalkoznunk. A Nette megoldja őket helyettünk:
A metódus helyesen kezeli a negált feltételeket és az üres tömböket is:
Paraméterként átadhatunk egy másik tábla eredményét is – al-lekérdezés jön létre:
A feltételeket tömbként is átadhatjuk, amelynek elemei AND-del lesznek összekötve:
A tömbben használhatunk kulcs ⇒ érték párokat, és a Nette ismét automatikusan kiválasztja a megfelelő operátorokat:
A tömbben kombinálhatunk SQL kifejezéseket helyettesítő kérdőjelekkel és több paraméterrel. Ez alkalmas komplex feltételekhez pontosan definiált operátorokkal:
A where()
többszöri hívása automatikusan AND-del köti össze a feltételeket.
whereOr(array $parameters): static
Hasonlóan a where()
-hez, feltételeket ad hozzá, de azzal a különbséggel, hogy OR-ral köti
össze őket:
Itt is használhatunk komplexebb kifejezéseket:
wherePrimary(mixed $key): static
Feltételt ad hozzá a tábla elsődleges kulcsához:
Ha a táblának összetett elsődleges kulcsa van (pl. foo_id
, bar_id
), tömbként adjuk át:
order(string $columns, …$parameters): static
Meghatározza a sorok visszaadási sorrendjét. Rendezhetünk egy vagy több oszlop szerint, csökkenő vagy növekvő sorrendben, vagy saját kifejezés szerint:
select(string $columns, …$parameters): static
Meghatározza az adatbázisból visszaadandó oszlopokat. Alapértelmezés szerint a Nette Database Explorer csak azokat az
oszlopokat adja vissza, amelyeket ténylegesen használnak a kódban. A select()
metódust olyan esetekben
használjuk, amikor specifikus kifejezéseket kell visszaadnunk:
Az AS
segítségével definiált aliasok ezután elérhetők az ActiveRow objektum tulajdonságaként:
limit(?int $limit, ?int $offset = null): static
Korlátozza a visszaadott sorok számát (LIMIT), és opcionálisan lehetővé teszi az offset beállítását:
Lapozáshoz célszerűbb a page()
metódust használni.
page(int $page, int $itemsPerPage, &$numOfPages = null): static
Megkönnyíti az eredmények lapozását. Elfogadja az oldal számát (1-től számolva) és az oldalankénti elemek számát. Opcionálisan átadható egy referencia egy változóra, amelybe az oldalak teljes száma kerül mentésre:
group(string $columns, …$parameters): static
Csoportosítja a sorokat a megadott oszlopok szerint (GROUP BY). Általában aggregáló függvényekkel együtt használják:
having(string $having, …$parameters): static
Feltételt állít be a csoportosított sorok szűréséhez (HAVING). Használható a group()
metódussal és
aggregáló függvényekkel együtt:
Adatok olvasása
Az adatok adatbázisból történő olvasásához számos hasznos metódus áll rendelkezésre:
foreach ($table as $key => $row) |
Iterál az összes soron, $key az elsődleges kulcs értéke, $row egy ActiveRow objektum |
$row = $table->get($key) |
Visszaad egy sort az elsődleges kulcs alapján |
$row = $table->fetch() |
Visszaadja az aktuális sort és a mutatót a következőre lépteti |
$array = $table->fetchPairs() |
Asszociatív tömböt hoz létre az eredményekből |
$array = $table->fetchAll() |
Visszaadja az összes sort tömbként |
count($table) |
Visszaadja a sorok számát a Selection objektumban |
Az ActiveRow objektum csak olvasásra szolgál. Ez azt jelenti, hogy nem lehet módosítani a tulajdonságainak értékeit. Ez a korlátozás biztosítja az adatok konzisztenciáját és megakadályozza a váratlan mellékhatásokat. Az adatok az adatbázisból töltődnek be, és bármilyen változtatást explicit módon és ellenőrzötten kell végrehajtani.
foreach
– iteráció az összes soron
A legegyszerűbb módja a lekérdezés végrehajtásának és a sorok megszerzésének a foreach
ciklussal
történő iterálás. Automatikusan elindítja az SQL lekérdezést.
get($key): ?ActiveRow
Végrehajtja az SQL lekérdezést és visszaadja a sort az elsődleges kulcs alapján, vagy null
-t, ha nem
létezik.
fetch(): ?ActiveRow
Visszaadja a sort és a belső mutatót a következőre lépteti. Ha már nincsenek további sorok, null
-t ad
vissza.
fetchPairs(string|int|null $key = null, string|int|null $value = null): array
Visszaadja az eredményeket asszociatív tömbként. Az első argumentum határozza meg annak az oszlopnak a nevét, amely kulcsként lesz használva a tömbben, a második argumentum pedig annak az oszlopnak a nevét, amely értékként lesz használva:
Ha csak az első paramétert adjuk meg, az érték az egész sor lesz, azaz az ActiveRow
objektum:
Duplikált kulcsok esetén az utolsó sor értéke lesz használva. Ha null
-t használunk kulcsként, a tömb
numerikusan lesz indexelve nullától kezdve (ekkor nem történik ütközés):
fetchPairs(Closure $callback): array
Alternatívaként megadhat egy callbacket paraméterként, amely minden sorhoz vagy magát az értéket, vagy egy kulcs-érték párt ad vissza.
fetchAll(): array
Visszaadja az összes sort ActiveRow
objektumok asszociatív tömbjeként, ahol a kulcsok az elsődleges kulcsok
értékei.
count(): int
A count()
metódus paraméter nélkül visszaadja a sorok számát a Selection
objektumban:
Figyelem, a count()
paraméterrel aggregáló COUNT függvényt hajt végre az adatbázisban, lásd alább.
ActiveRow::toArray(): array
Átalakítja az ActiveRow
objektumot asszociatív tömbbé, ahol a kulcsok az oszlopnevek, az értékek pedig a
megfelelő adatok.
Aggregáció
A Selection
osztály metódusokat biztosít az aggregáló függvények (COUNT, SUM, MIN, MAX, AVG stb.) egyszerű
végrehajtásához.
count($expr) |
Megszámolja a sorok számát |
min($expr) |
Visszaadja a minimális értéket egy oszlopban |
max($expr) |
Visszaadja a maximális értéket egy oszlopban |
sum($expr) |
Visszaadja az értékek összegét egy oszlopban |
aggregation($function) |
Lehetővé teszi tetszőleges aggregáló függvény végrehajtását. Pl.
AVG() , GROUP_CONCAT() |
count(string $expr): int
Végrehajt egy SQL lekérdezést a COUNT függvénnyel és visszaadja az eredményt. A metódus arra használatos, hogy megállapítsuk, hány sor felel meg egy bizonyos feltételnek:
Figyelem, a count() paraméter nélkül csak a sorok számát adja vissza a Selection
objektumban.
min(string $expr) és max(string $expr)
A min()
és max()
metódusok visszaadják a minimális és maximális értéket a megadott oszlopban
vagy kifejezésben:
sum(string $expr)
Visszaadja az értékek összegét a megadott oszlopban vagy kifejezésben:
aggregation(string $function, ?string $groupFunction = null)
Lehetővé teszi tetszőleges aggregáló függvény végrehajtását.
Ha olyan eredményeket kell aggregálnunk, amelyek már maguk is valamilyen aggregáló függvényből és csoportosításból
származnak (pl. SUM(érték)
csoportosított sorokon keresztül), második argumentumként megadjuk azt az
aggregáló függvényt, amelyet ezekre a köztes eredményekre kell alkalmazni:
Ebben a példában először kiszámítjuk a termékek teljes árát minden kategóriában
(SUM(price * stock) AS category_total
), és csoportosítjuk az eredményeket a category_id
szerint.
Ezután az aggregation('SUM(category_total)', 'SUM')
segítségével összeadjuk ezeket a category_total
köztes összegeket. A második argumentum 'SUM'
azt mondja, hogy a köztes eredményekre a SUM függvényt kell
alkalmazni.
Beszúrás, Frissítés és Törlés
A Nette Database Explorer leegyszerűsíti az adatok beszúrását, frissítését és törlését. Minden említett metódus
kivételt Nette\Database\DriverException
dob hiba esetén.
Selection::insert(iterable $data)
Új rekordokat szúr be a táblába.
Egy rekord beszúrása:
Az új rekordot asszociatív tömbként vagy iterable objektumként (például az űrlapokban használt ArrayHash) adjuk át, ahol a kulcsok megfelelnek a tábla oszlopneveinek.
Ha a táblának definiált elsődleges kulcsa van, a metódus egy ActiveRow
objektumot ad vissza, amely újra
betöltődik az adatbázisból, hogy figyelembe vegye az adatbázis szintjén végrehajtott esetleges változásokat (triggerek,
oszlopok alapértelmezett értékei, auto-increment oszlopok számításai). Ez biztosítja az adatok konzisztenciáját, és az
objektum mindig az aktuális adatokat tartalmazza az adatbázisból. Ha nincs egyértelmű elsődleges kulcsa, a átadott adatokat
tömb formájában adja vissza.
Több rekord beszúrása egyszerre:
A insert()
metódus lehetővé teszi több rekord beszúrását egyetlen SQL lekérdezéssel. Ebben az esetben a
beszúrt sorok számát adja vissza.
Paraméterként átadható egy Selection
objektum is adatkiválasztással.
Speciális értékek beszúrása:
Értékként átadhatunk fájlokat, DateTime objektumokat vagy SQL literálokat is:
Selection::update(iterable $data): int
Frissíti a tábla sorait a megadott szűrő szerint. Visszaadja a ténylegesen megváltozott sorok számát.
A módosítandó oszlopokat asszociatív tömbként vagy iterable objektumként (például az űrlapokban használt ArrayHash) adjuk át, ahol a kulcsok megfelelnek a tábla oszlopneveinek:
Numerikus értékek módosításához használhatjuk a +=
és -=
operátorokat:
Selection::delete(): int
Törli a tábla sorait a megadott szűrő szerint. Visszaadja a törölt sorok számát.
Az update()
és delete()
hívásakor ne felejtse el a where()
segítségével megadni a módosítandó/törlendő sorokat. Ha nem használja a where()
-t, a művelet az egész
táblán végrehajtódik!
ActiveRow::update(iterable $data): bool
Frissíti az adatokat az ActiveRow
objektum által képviselt adatbázis-sorban. Paraméterként egy iterable-t
fogad el a frissítendő adatokkal (a kulcsok az oszlopnevek). Numerikus értékek módosításához használhatjuk a
+=
és -=
operátorokat:
A frissítés végrehajtása után az ActiveRow
automatikusan újra betöltődik az adatbázisból, hogy
figyelembe vegye az adatbázis szintjén végrehajtott esetleges változásokat (pl. triggerek). A metódus csak akkor ad vissza
true-t, ha tényleges adatváltozás történt.
Ez a metódus csak egyetlen konkrét sort frissít az adatbázisban. Több sor tömeges frissítéséhez használja a Selection::update() metódust.
ActiveRow::delete()
Törli az adatbázisból azt a sort, amelyet az ActiveRow
objektum képvisel.
Ez a metódus csak egyetlen konkrét sort töröl az adatbázisból. Több sor tömeges törléséhez használja a Selection::delete() metódust.
Kapcsolatok a táblák között
Relációs adatbázisokban az adatok több táblára vannak osztva, és idegen kulcsok segítségével kapcsolódnak egymáshoz. A Nette Database Explorer forradalmi módot kínál ezekkel a kapcsolatokkal való munkára – JOIN lekérdezések írása és bármi konfigurálása vagy generálása nélkül.
A kapcsolatokkal való munka illusztrálására egy könyvadatbázis példáját használjuk (megtalálható a GitHubon). Az adatbázisban a következő táblák vannak:
author
– írók és fordítók (oszlopok:id
,name
,web
,born
)book
– könyvek (oszlopok:id
,author_id
,translator_id
,title
,sequel_id
)tag
– címkék (oszlopok:id
,name
)book_tag
– kapcsolótábla a könyvek és címkék között (oszlopok:book_id
,tag_id
)

A példákban használt adatbázis struktúra ***
A könyvadatbázis példánkban több típusú kapcsolatot találunk (bár a modell egyszerűsített a valósághoz képest):
- Egy-a-többhöz 1:N – minden könyvnek egy szerzője van, egy szerző több könyvet írhat
- Nulla-a-többhöz 0:N – egy könyvnek lehet fordítója, egy fordító több könyvet fordíthat
- Nulla-az-egyhez 0:1 – egy könyvnek lehet folytatása
- Több-a-többhöz M:N – egy könyvnek több címkéje lehet, és egy címke több könyvhöz rendelhető
Ezekben a kapcsolatokban mindig van egy szülő és egy gyermek tábla. Például a szerző és a könyv közötti kapcsolatban
az author
tábla a szülő, a book
pedig a gyermek – elképzelhetjük úgy, hogy a könyv mindig
„tartozik“ valamilyen szerzőhöz. Ez megmutatkozik az adatbázis struktúrájában is: a gyermek book
tábla
tartalmaz egy author_id
idegen kulcsot, amely a szülő author
táblára hivatkozik.
Ha ki kell listáznunk a könyveket a szerzőik nevével együtt, két lehetőségünk van. Vagy egyetlen SQL lekérdezéssel szerezzük meg az adatokat JOIN segítségével:
Vagy két lépésben töltjük be az adatokat – először a könyveket, majd a szerzőiket – és utána PHP-ban összerakjuk őket:
A második megközelítés valójában hatékonyabb, bár ez meglepő lehet. Az adatok csak egyszer töltődnek be, és jobban felhasználhatók a cache-ben. Pontosan így működik a Nette Database Explorer – mindent a felszín alatt old meg, és elegáns API-t kínál Önnek:
Hozzáférés a szülő táblához
A szülő táblához való hozzáférés egyszerű. Olyan kapcsolatokról van szó, mint a könyvnek van szerzője
vagy a könyvnek lehet fordítója. A kapcsolódó rekordot az ActiveRow objektum property-jén keresztül érjük
el – a neve megegyezik az idegen kulcsot tartalmazó oszlop nevével id
nélkül:
Amikor hozzáférünk a $book->author
property-hez, az Explorer a book
táblában keres egy
oszlopot, amelynek neve tartalmazza az author
stringet (tehát author_id
). Az ebben az oszlopban lévő
érték alapján betölti a megfelelő rekordot az author
táblából, és ActiveRow
-ként adja vissza.
Hasonlóan működik a $book->translator
is, amely a translator_id
oszlopot használja. Mivel a
translator_id
oszlop tartalmazhat null
-t, a kódban a ?->
operátort használjuk.
Alternatív utat kínál a ref()
metódus, amely két argumentumot fogad el, a cél tábla nevét és a kapcsoló
oszlop nevét, és egy ActiveRow
példányt vagy null
-t ad vissza:
A ref()
metódus akkor hasznos, ha nem lehet a property-n keresztüli hozzáférést használni, mert a tábla
tartalmaz egy azonos nevű oszlopot (azaz author
). Más esetekben a property-n keresztüli hozzáférés használata
javasolt, amely olvashatóbb.
Az Explorer automatikusan optimalizálja az adatbázis-lekérdezéseket. Amikor ciklusban járjuk be a könyveket, és hozzáférünk a kapcsolódó rekordjaikhoz (szerzők, fordítók), az Explorer nem generál lekérdezést minden egyes könyvhöz külön. Ehelyett csak egy SELECT-et hajt végre minden kapcsolattípushoz, ezzel jelentősen csökkentve az adatbázis terhelését. Például:
Ez a kód csak ezt a három villámgyors lekérdezést hívja meg az adatbázisba:
A kapcsoló oszlop megtalálásának logikáját a Conventions implementációja határozza meg. Javasoljuk a DiscoveredConventions használatát, amely elemzi az idegen kulcsokat, és lehetővé teszi a táblák közötti meglévő kapcsolatokkal való egyszerű munkát.
Hozzáférés a gyermek táblához
A gyermek táblához való hozzáférés fordított irányban működik. Most azt kérdezzük, milyen könyveket írt ez a
szerző vagy fordított ez a fordító. Ehhez a lekérdezéstípushoz a related()
metódust
használjuk, amely egy Selection
-t ad vissza a kapcsolódó rekordokkal. Nézzünk egy példát:
A related()
metódus a kapcsolat leírását egyetlen argumentumként pont-jelöléssel vagy két különálló
argumentumként fogadja el:
Az Explorer képes automatikusan felismerni a helyes kapcsoló oszlopot a szülő tábla neve alapján. Ebben az esetben a
book.author_id
oszlopon keresztül kapcsolódik, mivel a forrástábla neve author
:
Ha több lehetséges kapcsolat létezne, az Explorer AmbiguousReferenceKeyException kivételt dob.
A related()
metódust természetesen használhatjuk több rekord ciklusban történő bejárásakor is, és az
Explorer ebben az esetben is automatikusan optimalizálja a lekérdezéseket:
Ez a kód csak két villámgyors SQL lekérdezést generál:
Több-a-többhöz kapcsolat
A több-a-többhöz (M:N) kapcsolathoz szükség van egy kapcsolótábla létezésére (esetünkben book_tag
),
amely két idegen kulcsot tartalmazó oszlopot (book_id
, tag_id
) tartalmaz. Ezen oszlopok mindegyike az
összekapcsolt táblák egyikének elsődleges kulcsára hivatkozik. A kapcsolódó adatok megszerzéséhez először a
kapcsolótábla rekordjait szerezzük meg a related('book_tag')
segítségével, majd tovább haladunk a
céladatokhoz:
Az Explorer ismét optimalizálja az SQL lekérdezéseket hatékony formába:
Lekérdezés kapcsolódó táblákon keresztül
A where()
, select()
, order()
és group()
metódusokban speciális
jelöléseket használhatunk más táblák oszlopaihoz való hozzáféréshez. Az Explorer automatikusan létrehozza a szükséges
JOIN-okat.
Pont-jelölés (szülő_tábla.oszlop
) a gyermek tábla szemszögéből nézett 1:N kapcsolathoz
használatos:
Kettőspont-jelölés (:gyermek_tábla.oszlop
) a szülő tábla szemszögéből nézett 1:N kapcsolathoz
használatos:
A fenti példában a kettőspont-jelöléssel (:book.title
) nincs megadva az idegen kulcs oszlopa. Az Explorer
automatikusan felismeri a helyes oszlopot a szülő tábla neve alapján. Ebben az esetben a book.author_id
oszlopon
keresztül kapcsolódik, mivel a forrástábla neve author
. Ha több lehetséges kapcsolat létezne, az Explorer AmbiguousReferenceKeyException
kivételt dob.
A kapcsoló oszlopot explicit módon meg lehet adni zárójelben:
A jelölések láncolhatók több táblán keresztüli hozzáféréshez:
JOIN feltételek bővítése
A joinWhere()
metódus kibővíti azokat a feltételeket, amelyeket a táblák összekapcsolásakor az SQL-ben az
ON
kulcsszó után adunk meg.
Tegyük fel, hogy egy adott fordító által fordított könyveket szeretnénk megtalálni:
A joinWhere()
feltételben ugyanazokat a konstrukciókat használhatjuk, mint a where()
metódusban – operátorokat, helyettesítő kérdőjeleket, értékek tömbjét vagy SQL kifejezéseket.
Összetettebb lekérdezésekhez több JOIN-nal definiálhatunk tábla aliasokat:
Figyelje meg, hogy míg a where()
metódus feltételeket ad hozzá a WHERE
záradékhoz, a
joinWhere()
metódus kibővíti a feltételeket az ON
záradékban a táblák összekapcsolásakor.