Generátor testovacích dat v PHP - Návrh, entity a jádro

PHP Knihovny Generátor testovacích dat v PHP - Návrh, entity a jádro American English version English version

Vítám vás v tutoriálu o tvorbě generátoru náhodných dat do databáze. Tento doplněk, chcete-li script, nám vygeneruje různé (náhodné) kombinace dat a ty odešle do databáze. Tutoriál počítá s tím, že rozumíte databázovému ovladači PDO a OOP. Představme si, že děláme aplikaci, která pracuje s databází – častý model. Aby aplikace fungovala, potřebujete ji otestovat, ale kde data sehnat? Představme si, že tvoříme tabulku uživatelů, potřebujeme na nějakých uživatelích aplikaci otestovat a k tomuto účelu se bude hodit náš script, který databázovou tabulku (uživatelé) naplní náhodnými daty a to zcela automaticky.

Pro lepší představu si ukažme obrázek z posledního dílu, na kterém jsou vidět řádky v tabulce uživatelů. Tyto řádky sama vygenerovala aplikace podle jejího nastavení (na obrázku jich je jen několik, doopravdy jich je v databázi 500).

Vygenerovaní uživatelé

K vygenerování testovacích uživatelů stačí napsat tento kód:

$generator->Generate("uzivatele", 500, array(
        "id" => "Id",
        "jmeno" => "Jmeno",
        "prijmeni" => "Prijmeni",
        "email" => "Email:jmeno,prijmeni",
        "telefon" => "Telefon",
        "datum_narozeni" => "Datum:1970-01-01,2013-01-01",
        "expirace_platebni_karty" => "Datum:2015-01-01, 2030-01-01"));

Návrh

Aby mělo vše větší smysl, budeme skript tvořit objektově. Před tvorbou budeme přemýšlet nad návrhem. Náš script bude velmi robustní a bude velmi dobře rozšířitelný. Možná jste si zatím pod pojmy robustní a dobře rozšířitelný nic nepředstavili. Tak se zase podívejme na databázovou tabulku. Co budeme generovat? Jména, příjmení, data, čísla, emailové adresy,... A aby to bylo ještě zábavnější, budeme umět data i propojovat s dalšími tabulkami. Teď již víme, co znamená to robustní, s tím mírně souvisí i snadno rozšířitelný. Proč by si každý programátor, který script použije, nemohl dopsat své vlastní datové entity? Co když náš script nebude umět generovat PSČ a on ho zrovna bude potřebovat?

V článku budu používat 2 pojmy.

  1. Entita - jednotka, která nám bude generovat hodnoty (např. jméno, příjmení, tel. číslo,...)
  2. Jádro - jádro našeho scriptu, které jednak bude pracovat se samotnou databází, všemi tabulkami a samozřejmě s entitami.

Entita

Entita musí vždy předat nějaké informace jádru. Možná si řeknete co víc než náhodnou hodnotu budeme potřebovat? Ale uvědomte si, že Id je entita a když v databázové tabulce používáte primární klíče, tak víte, že při vkládání do databáze se tyto klíče neuvádí. Stejně tak proč by naše jádro mělo složitě zjišťovat, jaký SQL typ má použít, když mu to entity mohou snadno říct.

Pro Entitu si napíšeme rozhraní IRandomizableItem. Rozhraní bude mít dvě bezparametrické metody: - RandomValue() - Vrací náhodnou hodnotu

  • ColumnSqlType() - Vrací string s SQL typem (např. "varchar(55)").

Tímto entita však nebude končit.

Vezměme si, že i propojení sloupců bude vlastně entita (ostatně proč ne, také pracuje se sloupci a hodnotami), proto všem Entitám předáme v metodě Load několik parametrů, se kterými by mohli pracovat. Tím prvním bude rozpracovaná položka, která se odešle do databáze. Druhý parametr budou parametry, které budou předány při definici datového typu (k tomu se dostaneme později). Třetí parametr bude pole všech tabulek a jejich hodnot a čtvrtý, poslední, parametr bude definice sloupců všech tabulek.

Každá Entita musí mít metodu Load, ale kvůli tomu, že ne každá entita potřebuje všechny parametry volání, metodu Load nemůžeme zařadit do rozhraní. Nakonec bude mít každá entita veřejné atributy $insertToDefinition a $insertToTable. $insertToDefinition říká, jestli se sloupec vkládá do definice tabulky při vytváření a $insertToTable zas zda se má hodnota vkládat do tabulky při vkládání dat. Kvůli těmto atributům parametry předáváme v Load() a ne v konstruktoru. Rozhraní bude vypadat následovně:

interface IRandomizableItem {
        function RandomValue();
        function ColumnSqlType();
}

Nyní si pojďme implementovat první entitu. Začněme třeba jménem. Vytvořme si třídu Jmeno, která implementuje rozhraní IRandomizableItem.

Poznámka: Doporučuji používat autoloader.

Třídě nastavíte veřejné atributy $insertToDefinition a $insertToTable na true, jméno se bude vkládat všude. Naše třída bude mít pole jmen a z toho vybere náhodnou položku. Přidejme si statické pole $dataJmena s nějakými hodnotami:

static $dataJmena = array("Michal", "David", "Ondřej", "Matěj", "Ludík", "Josef", "Adam", "Tomáš");

Metoda RandomValue() tedy vybere jednu náhodnou hodnotu a metoda ColumnSqlType() vrátí tento string:

varchar(64) COLLATE utf8_czech_ci

Nakonec nezapomeňte implementovat metodu Load(), ta v případě jména nebude brát žádný parametr a nebude nic dělat. Celá třída by mohla vypadat následovně:

class Jmeno implements IRandomizableItem {
        static $dataJmena = array("Michal", "David", "Ondřej", "Matěj", "Ludík", "Josef", "Adam", "Tomáš");
        public $insertToDefinition = true;
        public $insertToTable = true;

        function load() {}

        function RandomValue() {
                return self::$dataJmena[rand(0,count(self::$dataJmena) - 1)];
        }
        function ColumnSqlType() {
                return "varchar(64) COLLATE utf8_czech_ci";
        }
}

Podobné budou následující entity, od těch se ale teď vzdálíme k jádru.

Jádro

Přidejme si do projektu soubor SampleDataGene­rator.php a přidejme si do něj stejnojmennou třídu. Do souboru si nakonec přiřaďte i třídu DatabaseManager, což bude pro nás takový na míru přizpůsobený databázový wrapper a třídu DataRow, což je jakási jednotka pro vytváření řádek, ostatně to bude právě objekt, který bude entitám předán v prvním parametru.

SampleDataGenerator

Náš SampleDataGenerator bude mít dvě veřejná pole - $Tables a $TableColumns. $Tables bude asociativní pole, které bude mapovat název tabulky na pole DataRow. $TableColumns bude také slovníkové pole v podobném formátu, které bude mapovat název tabulky na asociativní pole [sloupec] => datový typ sloupce. Čili TableColumns bude mít ještě vnořená slovníková pole.

SampleDataGenerator bude mít metody Generate() a Save(). Save() nepřijímá žádný parametr a pouze odesílá na databázi dotazy. Generate() přijímá tři parametry: název tabulky, počet řádků, sloupce. Název tabulky a počet řádku je myslím docela jasný, parametr sloupce bude opět slovníkové pole ve formátu [název sloupce] => (string) jeho typ s parametry. Zde si ještě všimněte typu s parametry, ten se bude zapisovat ve formátu typ:parametry. Parametry budou vždy ve stringu a nebude pro ně platit žádný separátor. Každá entita si však může "nadefinovat" a implementovat vlastní. No, pokud jste se v této snůšce odborných pojmů trochu ztratili, nevadí, vlastně jsme si řekli, jak se script bude používat. Ukážeme si ještě kód:

$generator = new SampleDataGenerator();
$generator->Generate("uzivatele", 500, array(
        "id" => "Id",
        "jmeno" => "Jmeno",
        "prijmeni" => "Prijmeni",
        "email" => "Email:jmeno,prijmeni",
        "telefon" => "Telefon",
        "datum_narozeni" => "Datum:1970-01-01,2013-01-01",
        "expirace_platebni_karty" => "Datum:2015-01-01, 2030-01-01"));

$generator->Save();

Na prvním řádku si vytvoříme generátor, snadné. Poté mu řekneme, ať vytvoří tabulku uzivatele a tu naplní 500 lidmi, přičemž má naplnit sloupce id, jmeno, prijmeni, email, telefon a data v minulosti a budoucnosti. Všimněte si emailu, tam v parametrech předávám názvy sloupců, ze kterých poté bude email čerpat. Dále jsou zajímavá data, těm specifikujeme rozsah od kdy do kdy. Výsledné náhodné datum bude v tomto rozmezí. Nakonec to pošleme do databáze. Nejdříve ale vytvoříme třídu reprezentující řádek - DataRow.

DataRow

DataRow bude potřebovat sloupce tabulky, kam patří a všechny tabulky. Tyto hodnoty mu předáme v konstruktoru. Nakonec budeme potřebovat atribut $values což bude slovníkové pole ve formátu [název sloupce] => [hodnota sloupce pro položku].

public $tables;
public $columns;
public $values = array();

function __construct($columns, $tables) {
        $this->columns = $columns;
        $this->tables = $tables;
}

DataRow bude mít také metodu Generate(), která nebude přijímat žádný parametr a vygeneruje hodnoty řádku. Metoda projde cyklem foreach sloupce ($this->columns), z nich v tomto cyklu dostane jméno a jeho typ.

foreach ($this->columns as $columnName => $columnType)

Typ sloupce, jak již jsme si řekli, obsahuje název a parametry. Název je název naší entity a parametry jsou volitelné parametry. Tyto dvě části jsou oddělené dvojtečkou. Hodnoty dosadíme do proměnných $generatorName a $paramsForGene­rator, přičemž $paramsForGenerator bude mít výchozí hodnotu prázdný string.

$generatorParts = explode(":", $columnType);
$generatorName = $generatorParts[0];

$paramsForGenerator = "";
if (isset($generatorParts[1])) {
        $paramsForGenerator = $generatorParts[1];
}

Asi vás napadne, jak ale vytvořit instanci entity ze stringu? PHP má na toto krásnou syntaxi, jednoduše mu řekneme new $nazevPromenne(pa­rametry) a on vytvoří novou instanci třídy, jejíž název obsahuje proměnná $nazevPromenne, přičemž můžeme samozřejmě doplnit parametry. Jak již víme, parametry se entitám nepředávají v konstruktoru ale v metodě load().

$generator = new $generatorName();
$generator->load($this, $columnName, $paramsForGenerator,
        $this->tables, $this->columns);

Měli bychom ověřit, jestli uživatel neposílá scriptu injekci a nechce vytvořit instanci něčeho, co není entita. Ověříme to jednoduše, entity musí implementovat rozhraní IRandomizableItem, to ověříme operátorem instanceof, která vrátí pravdivostní hodnotu, zdali levý operand implementuje pravý operand. Pokud tato podmínka platit nebude (instance není entita) ukončíme práci skriptu a vypíšeme chybovou hlášku, k tomu se dobře hodí funkce die().

if(!$generator instanceof IRandomizableItem) {
        die("Chyba: Pokus o PHP injekci");
}

Nakonec, pokud sloupec chce, aby byla jeho hodnota vkládaná do databáze (třeba entita Id to nechce) tak ji tam vložíme.

if ($generator->insertToTable) {
        $this->values[$columnName] = $generator->Randomvalue();
}

Nyní máme řádek kompletní. Vraťme se k samotnému generátoru. Implementujeme vytváření řádků kódu. V SampleDataGene­ratoru vytvořte metodu Generate() s parametry table, count, columns. V prvé řadě si uložíme sloupce generované tabulky.

$this->tableColumns[$table] = $columns;

Nyní cyklem začneme vytvářet jednotlivé řádky. V každé iteraci vytvoříme nový řádek, předáme mu potřebné parametry a zavoláme na něj Generate(). Nakonec řádek přidáme do tabulky.

for ($i = 0; $i < $count; $i++) {
        $random = new DataRow($columns, $this->tables);
        $random->Generate();
        $this->tables[$table][] = $random;
}

Tak už umíme i vygenerovat data. To je pro tento díl vše, ještě je před námi kus práce. Příště se podíváme na specializovaný databázový wrapper a poprvé si vygenerujeme tabulku plnou jmen. :)


 

  Aktivity (3)

Článek pro vás napsal Michal Žůrek (misaz)
Avatar
Autor se věnuje tvorbě aplikací pro počítače, mobilní telefony, mikroprocesory a tvorbě webových stránek a webových aplikací. Nejraději programuje ve Visual Basicu a TypeScript. Ovládá HTML, CSS, JavaScript, TypeScript, C# a Visual Basic.

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 


Miniatura
Předchozí článek
Dokončení knihovny Image v PHP
Miniatura
Všechny články v sekci
Knihovny pro PHP

 

 

Komentáře

Avatar
Jan Vargovský
Redaktor
Avatar
Jan Vargovský:

Celkem mi bije do ta čechoangličtina.

 
Odpovědět  +2 17.5.2014 16:45
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 1 zpráv z 1.