Címkék: #Adatbázis (Database) #Adatbetöltő (Seeder) #Adatgyár (Factory) #Eloquent #Laravel #Laravel 9 #Query Builder #Tesztelés (Testing)
Tesztadatokra nagyon sokszor szükségünk van, már csak azért is, hogy a webalkalmazásunk funkcionalitásait is tudjuk vizsgálni működés szempontjából. Sikeresen tudom-e lekérni a légitársaságokhoz tartozó repülőjáratokat, vagy a járatok utasait meg tudom-e jeleníteni az oldalon... ezekhez mindig tesztadatokra van szükségünk. Én készítettem újra tesztadatokat az alkalmazásomhoz, de most megnézzük közösen, összefoglalóan, hogy hogyan is működnek a Seeder osztályok, amelyekkel pont ezt tudjuk elérni nagyon egyszerűen. De még mielőtt ezt megtennénk, előtte nézzük meg, hogy milyen lehetőségünk volt eddig tesztadatok létrehozására.
Adatbázis lekérdezés - INSERT
Elég fapados módszer volt, de működött az, ha létrehoztunk egy új útvonalat a web.php -ban, a tartalmát pedig feltöltöttük egy olyan Query Builder-rel készült lekérdezéssel, ami a háttérben gyakorlatilag egy beszúrást hajtott végre.
Route::get('dbseeder', function () {
DB::table('flights')->insert([
'name' => Str::random(10),
'number' => Str::random(10),
'price' => rand(100,500),
'captain_id' => 1
]);
});
(A szükséges importálásokat persze hajtsuk végre: DB, Str) Ekkor a dbseeder útvonal meglátogatásával már be is szúródott egy sor a flights adattáblánkba.
Ha véletlenszerű (random) adatokkal történt meg a feltöltés, akkor gyakorlatilag egy ciklusba is betehettük volna ezt a lekérdezést, és ezekután a ciklus lefutásának száma határozta meg, hogy mennyi adatsor töltődik be az adattáblába.
Ellenőrizhetjük, hogy beszúródott-e a sor és szerencsére azt látjuk, hogy a flights táblánkba bekerült az új elem, viszont a created_at és az updated_at mezők nem kaptak értéket, pedig az jó lett volna. Ez abból adódik, hogy nincs nekik beállítva alapértelmezett érték, amit akár módosíthatnánk is egy új migrációs fájlban, de talán jobb megoldás lesz az, ha az adatbázis gyárat alkalmazzuk a tesztadatokhoz, lásd a következő alfejezetet.
Megjegyzés: nem akarok előre rohanni, de ugyanezt a lekérdezést viszont betehetjük az újonnan létrejövő Seeder osztályunk run() metódusába és akkor a php artisan db:seed parancs lefutásával ez a lekérdezés is végrehajtódna.
Adatgyárak - Factory
Adatgyárat már használtunk, méghozzá úgy, hogy a Tinker-ben hajtottuk végre őket. Tudjuk, hogy működnek, hiszen leteszteltük őket. Miért ne tehetnénk meg, hogy az új útvonalunkban a már jól működő gyárak lefutásait meghívnánk? Ezt megtehetjük, nézzük, hogy hogyan.
Route::get('dbseeder', function () {
Airline::factory()->count(10)->create();
City::factory()->count(5)->create();
Flight::factory()->count(20)->create();
Passenger::factory()->count(100)->create();
});
(A szükséges importálásokat persze hajtsuk végre: Airline, City, Flight, Passenger) A dbseeder útvonal egyszeri meglátogatásával a böngészőben most már létrejön 10 helyett 10+20=30 légitársaság (mivel a repülőjáratokhoz mindig új légitársaság jön létre), 5 város, 20 repülőjárat, 100 utas. Az adatbázis Seeder osztályok logikája nagyon hasonló, csak ott más esemény váltja ki ezeknek a gyáraknak a beindulását és működését: itt ugye egy útvonal meglátogatása volt az esemény, ott pedig majd egy parancs kiadása lesz az esemény, ami beindítja a tesztadatok gyártását.
Automatikus adatbetöltő - Seeder
Ezek a Seeder osztályok csak egyetlen egy metódust tartalmaznak, ami a run(). Ez a metódus kerül meghívásra, amikor a
php artisan db:seed
parancsot futtatjuk, alapértelmezetten a database/seeders/DatabaseSeeder osztály run metódusa fog végrehajtódni. Tudjuk így futtatni az összes Seeder osztály mindegyik run() metódusát (mindjárt meg is nézzük, hogyan... de előbb:) vagy pedig egyesével is ki tudjuk jelölni, hogy melyik adatbetöltő hajtódjon végre, ha a --class kapcsolót használjuk és egyelővé tesszük a Seeder osztály nevével (pl.: FlightSeeder).
Töröljük (vagy kommenteljük ki) a web.php -ból a dbseeder útvonalunkat, mivel inkább Seeder osztályt használjunk a nagyobb mennyiségű tesztadatok létrehozásához.
php artisan make:seeder FlightSeeder
A névkonvcencióra itt is érdemes figyelni, hiszen a Laravel ezután már tudni is fogja, hogy mi a Flight Model osztályhoz szeretnénk adatbetöltőt készíteni. Be is kerül a parancs kiadásának hatására az új osztályunk a database/seeders mappába. (Megjegyzés: a Laravel segít amúgy, mutat példát, hiszen ebben a mappában már ott van egy DatabaseSeeder.php fájl, aminek a run() metódusában ott van kikommentelve egy adatgyáras sor, szóval mi is ezt fogjuk csinálni.)
Adjuk hozzá a run() metódus magjához azokat az adatgyáras parancsokat, amelyek korábban a dbseeder útvonalnál szerepeltek a web.php-ban és természetesen adjuk hozzá a fájlhoz a szükséges importálásokat is. Majd futtassuk a következő parancsot:
php artisan db:seed --class=FlightSeeder
Említettem, hogy több Seeder osztály run metódusát is tudjuk futtatni egyidejűleg. Ehhez hajtsuk végre a következő pontokat:
public function run()
{
$this->call([
FlightSeeder::class,
PassengerSeeder::class
]);
}
Végül adjuk ki a php artisan db:seed parancsot az indításhoz. Így mindkét Seeder osztály run() metódusa meghívásra kerül, mi pedig sok-sok tesztadattal gazdagodtunk az adatbázisunkban.
A blogbejegyzéshez tartozó változások elérhetők ebben a Github commit-ben.
Tipp: ha egy Model-hez a Laravel-ben szeretnénk egyből factory és seeder osztályt is létrehozni, akkor a következő parancsot alkalmazhatjuk:
php artisan make:model ModelName -fs