Adatbázis-kezelés 2. rész - Bevezetés az Eloquent használatába

Attila | 2022. 03. 03. 17:07 | Olvasási idő: 4 perc

Címkék: #Adatbázis (Database) #Eloquent #Laravel #MySQL #Query Builder

Most már mindenféle adatbázishoz tudunk csatlakozni. A migrációs fájlok segítségével tudunk táblaszerkezeteket létrehozni és módosítani is (SQL DDL). Következhet az adatbázis-kezelés (SQL) manipulációs (DML) és lekérdezési (DQL) része. Elsőként az Eloquent-tel fogunk megismerkedni.
Laravel_Eloquent

Megjegyzés: ha a bevezetőben használt rövidítések nem lennének ismerősek, akkor érdemes újra átfutni az előző blogbejegyzésemet.

A mostani blogbejegyzéshez használt ábra szerencsére nagyon beszédes: ha jobb oldalról haladunk, akkor láthatjuk, hogy használhatunk mindenféle adatbáziskezelő rendszert (mi is alkalmaztunk már a MySQL-en kívül SQLite-ot, MS SQL-t, felhőbeli adatszolgáltatást is). A "DB Connection" rész felelős volt azért, hogy ezekhez a típusú rendszerekhez fel tudjuk építeni az alkalmazásunkból a kapcsolatot. Most pedig rátérünk arra, hogy hogyan lehet majd manipulálni az adattábláinkat (INSERT, UPDATE, DELETE stb.) illetve lekérdezni belőlük (SELECT). Ez a témakör olyan nagy, hogy biztosan több blogbejegyzésen át fogom ismertetni ezt a területet.

A Laravel keretrendszer kétféle lehetőséget nyújt a számunkra:

  1. Eloquent: ez egy ORM (Object–Relational Mapping) eszköz, ami lehetővé teszi, hogy objektumokat használjunk az adatbázis műveletek elvégzésére, a művelet eredményét pedig könnyedén feldolgozzuk a segítéségével. Ezzel tehát egy olyan eszközhöz jutunk, amellyel könnyedén tudunk ún. CRUD (Create-Read-Update-Delete, vagyis a leggyakrabban használt) műveleteket végrehajtani az adatbázisunkban. Érdemes most tehát jól figyelni majd, mert ilyen ORM eszközt nem csak a Laravel keretrendszer nyújt nekünk, hanem minden más fejlett programozási keretrendszer is, csak éppen azok nem Eloquent-nek hívják, hanem például a .NET-ben Entity Framework-nek... de a mögöttes logika ugyanaz ezeknél az ORM eszközöknél, a használatuk célja azonos, adatokhoz szeretnénk vele hozzájutni vagy manipulálni könnyedén.
  2. Query Builder: ez pedig egy olyan eszköz, amelynek a segítségével az SQL utasításokra kísértetiesen hasonlító lekérdezéseket rakhatunk össze, építhetünk fel utasításrészletek egymáshoz fűzésével. Ezzel a témakörrel akkor fogunk foglalkozni, ha az Eloquent-et már kellőképpen megismertük. De ha beszélhetek szimpátiáról, akkor én a gyakorlatban jobban szeretem ezt alkalmazni, ízlések és pofonok... megismerjük mindkettőt és akkor mindenki maga választhatja majd ki, hogy neki mi áll jobban kézre.

Miért is használjuk ezeket? Miért nem írunk simán SQL lekérdezéseket? A válasz többrétű, mivel ezen fenti két eszköz használata olyan előnyöket nyújt a számunkra, amelyeket vétek lenne nem kihasználni, például:

  • helyes és optimalizált SQL lekérdezéseket generálnak a háttérben, kisebb a hibázási lehetőség,
  • a lekérdezések könnyebben frissíthetők, karban tarthatók, újrafelhasználhatók,
  • megvédik a használóját (minket) attól, hogy SQL beszúrásos (injection) támadást tudjanak végrehajtani a kódjaik ellen,
  • segítségével lehetőségünk van arra, amit korábban már csináltunk is: könnyedén leválthatják az alkalmazásunk alatt futó adatbáziskezelő-rendszert, és nem kell emiatt megváltoztatnunk (vagy csak nagyon ritkán) sem az Eloquent, sem a Query Builder-rel összerakott lekérdezéseinket.
  • ... és talán a legfontosabb: a programozók úgy manipulálhatják a kódjukat, mintha objektumokkal dolgoznának. Ezt mindjárt kifejtem és gyakorlatban is megnézzük a működését.

Az Eloquent használatához szükség van arra, hogy az eddig "érintetlen" MVC elemhez nyúljunk, ez pedig nem más, mint a Model. Aki jól figyelt a blogbejegyzéseimre (főleg erre), akkor az már tudhatja, hogy a Model-ek két főbb dologért felelnek:

  1. Ők biztosítják az üzleti logikát, tehát olyan adattagokat és metódusokat, amelyek az alkalmazásunk funkcióinak működését teszik lehetővé.
  2. Ők azok, akik az Eloquent ORM segítségével elérik a hozzájuk tartozó (!) adatbázistáblát.

Az első pontra ismét felhívnám a figyelmet, mert nagyon sokan összekeverik az "üzleti logika megvalósítása" szempontjából a Model-t a Controller-rel. Fontos, hogy megjegyezzük, hogy a Model biztosítja az üzleti logikát.

De térjünk rá most a második pontra, hiszen az Eloquent szempontjából vizsgáljuk majd a Model-eket. Hozzunk is létre egy Model-t a projektünkbe:

php artisan make:model Post

Ennek hatására létrejött egy PHP fájl, a helye pedig, ha a Laravel 5.7-es verziójával dolgozunk, akkor az app mappába került be, ha pedig magasabb verziószámú a Laravel alkalmazásunk, akkor az app / Models mappába került be a Post.php fájl (a Laravel keretrendszerünk verzióját így tudjuk lekérdezni: php artisan --version). Igazából a mostani gyakorlatunk szerint nincs jelentősége annak, hogy melyik verziójú keretrendszerrel dolgozunk.

Ami viszont fontos: figyeljünk a névkonvenciókra! A Model neve nagy kezdőbetűvel indul és angolul egyes számban van, viszont alapértelmezetten ez a posts nevű (kis kezdőbetűs, bár az SQL erre nem érzékeny, de fontosabb, hogy angolul többes számban van) táblához fogja biztosítani a hozzáférést.

A létrejött Post Model-ünket megnézhetjük, azt láthatjuk, hogy ez pusztán egy egyszerű osztály, egy class, ami kiterjeszti a Model ősosztályt. Egyelőre ne is írjunk még bele semmi, pusztán használjuk!

Ha megvan még a PostsController-ünk (figyelem, névkonvenció itt is, angolul a Controller nevében többesszámban használjuk a Post-ot), akkor a show() metódusára tekintsünk. Bár akkor még nem tudtuk, amikor beleírtuk, de igazából egy Query Builder-t raktunk össze akkor: \DB::table('posts')->where('slug', $slug)->first(). Ezt fogjuk most lecserélni, úgyhogy kikommentezhetjük azt a sort és helyette írjuk be ezt:

$post = Post::where('slug', $slug)->first();

És a dd() -s sor elől vegyük ki a kommentezést, hogy kapásból megjelenjenek itt a $post objektum értékei majd.

A VSCode egyből alá is fogja húzni a Post szöveget, ez azért van, mert importálnunk kellene az előbb létrehozott Post osztályunkat. Szerencsés szituációban vagyunk, ha használunk Laravel-specifikus kiterjesztéseket a VSCode-ban, mert ekkor a Post szövegre kattintva jobb egérgombbal előjön a helyi menü, benne pedig az "Import class" menüpont (itt javasoltam a kiterjesztés csomagot). Ahogy korábban említettem, a Laravel keretrendszerünk verziószámától függően fogja az iménti Import-álás hatására vagy az App\Post vagy az App\Models\Post osztályt importálni a fájl tetejére. Ennek hatására megszűnik a Post felirat pirossal történő aláhúzása, most már hibamentes a kódunk újra.

(Remélhetőleg) elég sokat tesztelgettük már a rendszerünket, az adatbázisunkat, úgyhogy a további helyes működés érdekében szúrjunk be egy sort a posts nevű adattáblánkba:

INSERT INTO `posts` (`slug`, `title`, `body`) VALUES ('my-first-original-post', 'My first original post', 'This is my first original post with a title.');

(Ugye az oszlopok nevének sorrendje mindegy, a lényeg, hogy a VALUES után is ugyanabban a sorrendben következzenek az értékek.)

Teszteléshez hozzuk be utána a következő URL-t a böngészőnkben: http://127.0.0.1:8000/posts/my-first-original-post

Eredményül ezt kapjuk, ha az attributes melletti kis háromszögre is rákattintunk:

Látható, hogy egy elég komplex objektumot kaptunk vissza, ami önmagában a $post volt a kódunkban. Ennek az attributes mezőjébe került bele a tömb, ami tartalmazza az adatbázisból érkező egyetlen sor mezőinek értékeit, ahogy az az ábrán látható is.

Az Eloquent ORM (és a Query Builder-es) kapcsolatok nélküli lekérdezéshez kapcsolódó működési elv tehát a következőként fogható fel (példával is illusztrálva):

  • Az imént láthattuk, hogy ami a programkódunkban 1 objektum volt, az az adatbázistáblában 1 sornak felelt meg, vagyis gyakorlatilag egy SELECT lekérdezést csináltunk, ha adatbázisos "szemüveggel" nézzük;
  • Ha több sort kértünk volna le, mondjuk az elmúlt 1 hónap blogbejegyzéseit, akkor programozói szempontból egy gyűjteményt (például egy tömböt) kaphatunk vissza az adattáblából, ami ott több sort jelképezne;
  • Ilyen terminológia szerint tehát a Post osztály jelképezi a posts táblát, egyetlen osztálypéldány (vagyis objektum) jelképez egyetlen adatsort a táblában, több objektum pedig egy gyűjteményt képez, amik az adattáblában több sort jelképeznek.

De mi a helyzet a manipulációs műveletekkel (létrehozás vagyis beszúrás, frissítés, törlés)? Ha így próbáljuk értelmezni az Eloquent ORM működését, akkor ...

  • Például egy Post osztály szerinti egy új objektum létrehozása (példányosítás), adattagjaink értékadása, majd mentése egy új sor beszúrásának felel meg az adattáblába;
  • Egy meglévő (lekért) objektum/adatsor adattagjainak lekérése, majd módosítása és mentése megfelel egy sor frissítésének az adatbázisban;
  • Ha pedig egy adatsort lekérünk egy objektumba, majd töröljük azt és "mentjük", akkor az adatbázisban egy sor törlését hajtottuk végre;
    • Természetesen a frissítésre és a törlésre vonatkozó megfogalmazás igaz akkor is, ha nem csak egy objektumot/sort kérünk le, hanem többet (kvázi egy gyűjteményt), akkor ezeket is tudjuk együttesen frissíteni és törölni is.

Visszatérve a példakódunkra, hogy egy picit szebb eredményt kapjunk, ismét kommentezzük ki a dd()-s sort és adjuk át a $post objektumot a post nézetnek. A nézetben korábban a post body adattagját használtuk fel, most egy kicsit még finomítsuk a post.blade.php kódját: a post title mezőjét írjuk ki egy h1-es fejlécbe a "My blog" helyett.

Ha azt szeretnénk, hogy egy kis hibakezelés is legyen a működésben, akkor a PostsController show metódusában a first() metódust cseréljük le erre: firstOrFail(), ami eléggé beszédes, hogy ha a posts adattáblában nem létező "slug"-ot próbálunk lekérni a böngésző URL-jében, akkor valamilyen barátságosabb hibát kapunk, mint ha simán a first() metódus esetében tennénk ugyanezt.

Megjegyzés: a Laravel 5.7-ben volt ilyen szép 404-es hibakódos nézet. Illetve a fordítás azért létezik, mert korábban már elkészítettük hozzá ennek a bejegyzésnek a végén.

A következő bejegyzésekben mélyítjük az Eloquent-es tudásunkat, használni fogunk egy Tinker nevű alkalmazást a Terminal-ban, aminek a segítségével valós időben fogjuk tudni tesztelni az alkalmazásunk Model-jeit, így az adatbázis menedzselését (beszúrás - INSERT, frissítés - UPDATE, törlés - DELETE), valamint a lekérdezését (SELECT). Utána pedig következhet majd az adattábla kapcsolatok kezelése, de nem rohanok még ennyire előre...

A legutóbbi és ezen blogbejegyzésnek a kódjai elérhetők ennél a git commit-nél.