Přejít k navigační liště

Zdroják » PHP » Testování v PHP: testy integrace s databází

Testování v PHP: testy integrace s databází

Články PHP, Různé

V tomto díle se posuneme v abstrakci o úroveň výše a vyzkoušíme integrační testování, konkrétně testování integrace s databází MySQL za pomoci rozšíření DbUnit frameworku PHPUnit.

Minulým dílem seriálu jsme uzavřeli pomyslnou kapitolu věnovanou unit testování. Dnes se posuneme v abstrakci o úroveň výše a vyzkoušíme integrační testování. Příkladem integračního testování může být např. testování integrace s databází, kterému bude věnován tento díl.

Instalace PHPUnit pomocí Composer

Ještě než se vrhneme na téma tohoto dílu, rád bych doplnil díl věnovaný základům PHPUnit. V době psaní uvedeného dílu ještě nebyla dořešena možnost instalace PHPUnit pomocí nástroje Composer, což je nyní už možné. Instalovat PHPUnit tak můžeme dalšími dvěma způsoby.

Lokálně, jako závislost daného projektu. Stačí jej uvést v souboru composer.json

{
    "require-dev": {
        "phpunit/phpunit": "3.7.*"
    }
}

V tomto případě bude PHPUnit nainstalován do adresáře vendor, je tedy nutné jej také lokálně spouštět:

$ vendor/bin/phpunit

Globálně. Stačí do adresáře, kam se chystáme PHPUnit instalovat umístit soubor composer.json a spustit instalaci.

{
    "name": "phpunit",
    "description": "PHPUnit",
    "require": {
        "phpunit/phpunit": "3.7.*"
    },
    "config": {
        "bin-dir": "/usr/local/bin/"
    }
}

Integrační testování

Dosud jsme všechny naše třídy testovali v naprosté izolaci. Pokud měla třída nějaké závislosti, tak jsme je odstínili pomocí mocků. Tímto postupem můžeme dobře otestovat vlastní funkčnost třídy, ale je-li jednou ze závislostí abstrakce databázové vrstvy, jak otestovat, zda jsou data správně ukládána nebo načítána? Zde přichází na řadu integrační testy. Připravíme si testovací databázi a testovací případy provádíme proti ní.

Integrační testování (testování integrace s databází obzvláště) má celou řadu nevýhod a vždy je třeba si počínat mnohem opatrněji než při unit testování. Jedním z úskalí může být dodržení jednotného fixture (prostředí) pro každý test case. Spouštět test case v prostředí, které bylo modifikováno předchozím testem, je nepřípustné. Je tedy třeba, aby byl vždy dodržen postup:

  1. příprava prostředí (fixture)
  2. spuštění test case
  3. vyhodnocení
  4. úklid prostředí

Pokud budeme uvažovat testy s databází, pak prvním bodem je myšlena obnova databáze do námi definovaného počátečního stavu, nad kterým budeme všechny test case spouštět, a ukazuje hned několik nevýhod integračního testování – jsou pomalé, vyžadují komplexní práva, jsou závislé na prostředí apod.

Zajímavý článek na téma integračního testování před nedávnem napsal Daniel Kolman: Jak na integrační testy s databází. Určitě stojí za přečtení!

Rozšíření DbUnit

Framework PHPUnit pro podporu testování integrace s databází přináší rozšíření jménem DbUnit, a tomu se budeme věnovat v tomto díle. Rozšíření aktuálně podporuje MySQL, PostgreSQL, Oracle a SQLite. Pomocí Zend Framework nebo Doctrine2 je možné pracovat i s IBM DB2 nebo Microsoft SQL Server. Alespoň to tvrdí dokumentace.

Instalace

Pro instalaci rozšíření můžeme volit ze dvou možností:

instalátor PEAR

$ [sudo] pear install phpunit/DbUnit

nástroj Composer

{
    "require-dev": {
        "phpunit/dbunit": ">=1.2"
    }
}

Třetí možností může být samozřejmě naklonování zdrojových kódů z repositáře: https://git­hub.com/sebastianbergmann/dbu­nit. Ať vyberete cokoli, všechny uvedené varianty by měly proběhnout bez problémů.

Začínáme s DbUnit

Oproti testům, které jsme pomocí PHPUnit zatím psali, se integrační testy s rozšířením DbUnit liší hned několikrát. Hlavním rozdílem je, že sady testů dědí od abstraktní třídy PHPUnit_Extensions_Database_TestCase, která vyžaduje implementaci dvou abstraktních (šablonových) metod:

  • getConnection() – vracející instanci typu PHPUnit_Extensions_Databa­se_DB_IDatabaseConnection, reprezentující wrapper připojení k databázi.
  • getDataSet() – vracející instanci typu PHPUnit_Extensions_Databa­se_DataSet_IDataSet. Tato metoda vrací tzv. data set, což představuje inicializační sadu dat.

Připojení k databázi

Možná vás to zprvu trochu zmate, ale opravdu, DbUnit požaduje připojení k databázi předat jako wrapper implementující rozhraní PHPUnit_Extensions_Databa­se_DB_IDatabaseConnection. Ale není třeba se děsit, v drtivé většině případů si vystačíme s defaultním wrapperem: PHPUnit_Extensions_Databa­se_DB_DefaultDatabaseConnec­tion, který obaluje připojení k databázi pomocí PDO:

protected function getConnection()
{
    return $this->createDefaultDBConnection(
        new PDO("mysql:host=localhost;dbname=test", "tester", "password"));
}

Data set

Druhou metodou, kterou jsme nuceni implementovat, je getDataSet() vracející inicializační data set. Co si pod tímto představit? Jak už jsme si řekli před chvílí – před každým testem je nutné uvést databázi do nějakého známého, předem definovaného, stavu, který bude pro všechny test case stejný. A přesně k tomu slouží tzv. data sety. Před každým testem je databáze nejprve vyprázdněna a naplněna daty, která můžeme definovat pomocí několika formátů.

Flat XML DataSet

Asi nejpoužívanější ze všech formátů. Jeho struktura je jednoduchá: co element, to záznam v tabulce jejíž název je shodný s názvem elementu. Sloupce tabulky (záznamu) jsou reprezentovány atributy. Největší nevýhodou tohoto formátu je špatná podpora hodnoty NULL. Pokud potřebujeme některým ze sloupců záznamu nastavit hodnotu NULL, pak je lepším řešením např. formát XML DataSet, který hodnotu NULL plně podporuje.

Příklad Flat XML DataSet:
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
    <product id="1" name="Test product 1" price="123" />
    <product id="2" name="Test product 2" price="456" />
    <product id="3" name="Test product 3" price="789" />
</dataset>
Natažení Flat XML DataSet:
protected function getDataSet()
{
    return $this->createFlatXmlDataSet("flatDataSet.xml");
}

XML DataSet

Tzv. plný XML formát. Pomocí něj můžeme definovat zvlášť struktury tabulek a zvlášť jednotlivé záznamy (řádky). Plně podporuje hodnotu NULL, viz. ukázka. Jeho nevýhodou je poměrně velká „ukecanost“ daná formátem XML.

Příklad XML DataSet:
<?xml version="1.0" ?>
<dataset>
    <table name="product">
        <column>id</column>
        <column>name</column>
        <column>price</column>
        <row>
            <value>1</value>
            <value>Test product 1</value>
            <value>123</value>
        </row>
        <row>
            <value>2</value>
            <value>Test product 2</value>
            <null />
        </row>
    </table>
</dataset>
Natažení XML DataSet:
protected function getDataSet()
{
    return $this->createXMLDataSet("xmlDataSet.xml");
}

MySQL XML DataSet

Data set, který je možné vytvořit pomocí backup nástroje mysqldump. Nevýhoda je zřejmá – formát je použitelný pouze pro testování integrace s MySQL.

Použití nástroje mysqldump:
$ mysqldump --xml -t -u [username] --password=[password] [database] > /path/to/file.xml
Příklad XML exportu:
<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <database name="test">
        <table_data name="product">
            <row>
                <field name="id">1</field>
                <field name="name">Test product 1</field>
                <field name="price">123</field>
            </row>
            <row>
                <field name="id">2</field>
                <field name="name">Test product 2</field>
                <field name="price">456</field>
            </row>
        </table_data>
    </database>
</mysqldump>
Natažení MySQL XML DataSet:
protected function getDataSet()
{
    return $this->createMySQLXMLDataSet("/path/to/file.xml");
}

YAML DataSet

Dalším dostupným formátem, který si získává stále větší popularitu, je YAML. Je podobný JSONu, jen ještě o malinko úspornější. Problém s hodnotou NULL nemá, stačí vynechat hodnotu. Pozor, prázdná hodnota neznamená prázdný řetězec!

Příklad YAML DataSet:
product:
  -
    id: 1
    name: "Hello buddy!"
    price: 124
  -
    id: 2
    name: "I like it!"
    price: 546
Natažení YAML DataSet:
protected function getDataSet()
{
    return new PHPUnit_Extensions_Database_DataSet_YamlDataSet(
        "yamlDataSet.yml"
    );
}

CSV DataSet

Posledním ze statických formátů data setů, které momentálně PHPUnit nabízí, je notoricky známý formát: CSV. Bohužel i tento formát trpí problémy s hodnotou NULL. Pokud vynecháte hodnotu sloupce, pak bude vložena defaultní hodnota podle definice, nikoli NULL.

Příklad CSV DataSet:
id,name,price
1,"Test product 1",123
2,"Test product 2",456
Natažení CSV DataSet:
protected function getDataSet()
{
    $dataSet = new PHPUnit_Extensions_Database_DataSet_CsvDataSet();
    $dataSet->addTable("product", "csvDataSeed.csv");
    return $dataSet;
}

Kromě statických data setů existují i dynamické, ty si ale ukážeme později, prozatím vyloženě nesouvisí s tématem. Co si ale ještě ukážeme, jsou tři dekorátory: Replacement, Composite a Filter. Umožní nám další možnosti práce s popsanými formáty.

Replacement DataSet

Pomocí Replacement DataSet můžeme definovat sadu pravidel pro úpravu data setů před jejich natažením. Např. je takto možné řešit problém s hodnotou NULL:

<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
    <product id="1" name="Test product 1" price="123" />
    <product id="2" name="Test product 2" price="###NULL###" />
</dataset>

Hodnotu NULL v data setu definujeme pomocí makra ###NULL### a před natažením všechny makra nahradíme:

public function getDataSet()
{
    $dataSet = $this->createFlatXmlDataSet("flatDataSet.xml");
    $replacement = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($dataSet);
    $replacement->addFullReplacement("###NULL###", null);
    return $replacement;
}

Composite DataSet

Těm z vás, kteří znají návrhový vzor Composite, bude účel tohoto dekorátoru jasný – slučování několika data setů do jednoho. Pozor na omezení – není možné slučovat dva data sety obsahující data jedné tabulky! V dokumentaci je uveden příklad, který nefunguje!

První dataset:

<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
    <product id="1" name="Test product 1" price="123" />
</dataset>

Druhý dataset:

category:
  -
    id: 2
    name: "Mobile phones"

Sloučení obou data setů. Záměrně použijeme dva různé formáty.

public function getDataSet()
{
    $ds1 = $this->createFlatXmlDataSet("flatXmlDataSet.xml");
    $ds2 = new PHPUnit_Extensions_Database_DataSet_YamlDataSet(
        "yamlDataSet.yml");

    $compositeDs = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet(array());
    $compositeDs->addDataSet($ds1);
    $compositeDs->addDataSet($ds2);

    return $compositeDs;
}

Filter DataSet

Poslední z dekorátorů, jak jeho název napovídá, používá se k filtrování data setů. K čemu to může být dobré? Např. pokud máte velký data set a pro účely daného testu jej nepotřebujete importovat vždy celý. Víte, že test se bude týkat pouze tabulky product, tak proč importovat i tabulky category, user, session a další, které vám jen prodlouží set-up. Ukažme si na příkladu vzorového data setu:

<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
    <product id="1" name="Test product 1" price="123" bar="1" />
    <product id="2" name="Test product 2" price="456" bar="2" />

    <foo id="4" bar="val" baz="valval" />
    <foo id="5" bar="val" baz="valval" />
</dataset>

Řekněme, že pro účely testu chceme importovat pouze data tabulky product, ale ne úplně vše – chceme navíc ignorovat sloupec bar.

protected function getDataSet()
{
    $ds = $this->createFlatXmlDataSet("flatDataSet.xml");

    $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($ds);
    $filterDataSet->addIncludeTables(array('product'));
    $filterDataSet->setExcludeColumnsForTable('product', array('bar'));

    return $filterDataSet;
}

Příprava fixture

Tím máme víceméně pokryté možnosti, které nám PHPUnit nabízí, co se týče přípravy dat pro fixture. Umíme vyrobit připojení k testovací databázi a importovat testovací data z nejrůznějších formátů. Než vše spojíme do nějakého ukázkového příkladu, pojďme se ještě podívat na to, jak řídit přípravu fixture.

Když se podíváme, co DbUnit provádí v metodě setUp, tak narazíme na volání metody getSetUpOperation(). Defaultně tato metoda vrací sadu operací (opět viz. návrhový vzor Composite) pojmenovanou CLEAN_INSERT, která se skládá z operace TRUNCATE a INSERT. Před každým test case tedy budou všechny tabulky uvedené v data setu promazané pomocí SQL příkazu TRUNCATE a poté naplněny pomocí INSERT.

Ve většině případů nám toto bude naprosto vyhovovat. Jak ale postupovat ve chvíli, kdy místo TRUNCATE potřebujeme např. prostý DELETE? Řešení je jednoduché – metodu getSetUpOperation() v požadované sadě testů překryjeme:

protected function getSetUpOperation()
{
    return new PHPUnit_Extensions_Database_Operation_Composite(array(
        PHPUnit_Extensions_Database_Operation_Factory::DELETE_ALL(),
        PHPUnit_Extensions_Database_Operation_Factory::INSERT()
    ));
}

Nyní bude příprava fixture probíhat: DELETE všech tabulek uvedených v data setu, potom INSERT dat. Pojďme tedy konečně vše spojit do nějakého příkladu.

Příklad

Budeme testovat jednoduchý repozitář ProductRepository, který pracuje s tabulkou product v MySQL a poskytuje dvě metody:

  • getById($id): pro načtení produktu (instance třídy Product) podle ID
  • save(Product $product): pro uložení produktu do databáze. Metoda interně řeší, zda bude vložen produkt nový nebo aktualizován stávající podle existence jeho ID.

Připomínám, že uvedený příklad slouží především k ilustraci probíraného téma, jeho cílem není 100% code coverage ani 100% use-case coverage ;-)

class Product
{
    private $id = null;

    public $name;
    public $price;

    public function __construct($id = null)
    {
        $this->id = $id;
    }

    public function getId()
    {
        return $this->id;
    }
}

class ProductRepository
{
    /**
     * @var PDO
     */
    protected $db;

    public function __construct(PDO $db)
    {
        $this->db = $db;
        $this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    }

    public function getById($id)
    {
        $stm = $this->db->prepare("
            SELECT * FROM product WHERE id = :id");

        if ($stm->execute(array(":id" => $id))) {
            $data = $stm->fetch();
            $product = new Product($data['id']);
            $product->name = $data['name'];
            $product->price = $data['price'];

            return $product;
        }

        return false;
    }

    public function save(Product $product)
    {
        $data = array(":name" => $product->name, ":price" => $product->price);

        if ($product->getId() === null) {

            $stm = $this->db->prepare("
                INSERT INTO product(name, price) VALUES (:name, :price)");

            if ($stm->execute($data)) {
                return $this->db->lastInsertId();
            }

        } else {
            $stm = $this->db->prepare("
                UPDATE product SET name = :name, price = :price
                WHERE id = :id");

            $data[':id'] = $product->getId();
            if ($stm->execute($data)) {
                return $product->getId();
            }
        }

        return false;
    }
}

Struktura tabulky product:

CREATE TABLE IF NOT EXISTS `product` (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(250) NOT NULL,
  price int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
)

Pojďme se vrhnout na test. Nejprve připravíme kostru, tzn. připojení k databázi a inicializační data set. Předpokládejme instanci MySQL, běžící na localhostu, obsahující databázi jménem „test“ a uživatele „test“ s heslem „test“.

class ProductRepositoryTest extends PHPUnit_Extensions_Database_TestCase
{
    protected function getPdo()
    {
        return new PDO("mysql:host=localhost;dbname=test", "test", "test");
    }

    protected function getConnection()
    {
        return $this->createDefaultDBConnection($this->getPdo());
    }

    protected function getDataSet()
    {
        return $this->createFlatXMLDataSet("productsDataSet.xml");
    }
}

Fixture nastavíme pomocí Flat XML data setu, bude obsahovat dva produkty.

<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
    <product id="1" name="Test product 1" price="123" />
    <product id="2" name="Test product 2" price="456" />
</dataset>

Jako první zkusíme otestovat, zda databáze skutečně obsahuje inicializační data.

public function testInitialData()
{
    $repository = new ProductRepository($this->getPdo());

    $this->assertEquals("Test product 1", $repository->getById(1)->name);
    $this->assertEquals(456, $repository->getById(2)->price);
}

Pokud máme správně nastaveno připojení k databázi, tak by nám test měl ohlásit korektní výskedek – jeden test se dvěma asserty. Zkusme nyní test uložení nového produktu.

public function testInsert()
{
    $product = new Product();
    $product->name = "Brand new product";
    $product->price = 111;

    $repository = new ProductRepository($this->getPdo());
    $id = $repository->save($product);

    $this->assertEquals(3, $id);
    $this->assertEquals("Brand new product", $repository->getById(3)->name);
    $this->assertEquals(111, $repository->getById(3)->price);
}

Nakonec ještě test aktualizace existujícího produktu, opět to bude jednoduché. Nejprve ověříme data před úpravou, pak uložíme upravená data a otestujeme změnu.

public function testUpdate()
{
    $repository = new ProductRepository($this->getPdo());
    $product = $repository->getById(1);

    $this->assertEquals("Test product 1", $product->name);
    $this->assertEquals(123, $product->price);

    $product->name = "Updated name";
    $product->price = 0;

    $repository->save($product);

    $this->assertEquals("Updated name", $repository->getById(1)->name);
    $this->assertEquals(0, $repository->getById(1)->price);
}

Nic složitého, že? Zkuste si cvičně implementovat i další metody repozitáře: delete pro odstranění produktu a getAll pro načtení celé kolekce produktů. Jako vždy, zdrojové kódy (nejen) z tohoto dílu jsou dostupné na Githubu:

    https://github.com/josefzamrzla/serial-testovani-v-php

Příště

Příště si ukážeme pár tipů a triků, jak si testování integrace s databází trochu ulehčit.

Komentáře

Subscribe
Upozornit na
guest
7 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
lordiceman

ak je spravne navrhnuta databaza a je vybrany spravny db software, ziadne testovanie db by nemalo byt potrebne, konzistencia databazy by mala byt zarucena samotnymi databazovymi pravidlami

nechcem tym nijak znevazovat tento clanok ani pouzitie dbUnit, chcem tym len povedat, ze keby sa tolko neznasilnovali databazy na to na co nie su urcene, nebol by problem

j

Vetsina databazi je navrzena spatne (dokonce by se dalo rict ze vsechny … ;D). Maly priklad – prakticky kazda aplikace si resi prihlasovani uzivatelu … do nejaky svoji tabulky a na zaklade toho si pak resi i nejakej „svuj“ system prav … kterej v 99,9999% pripadu jednoduse nefunguje. Kdyby misto toho nechal tvurce probublat login az do databaze, tak ma k dispozici otestovany a velmi mocny system prav (minimalne na urovni dat jako takovych).

Jinak testovat na nejakym predem nadefinovanym pocatecnim stavu je podle me naopak spatne – respektive testovat pouze na nem. V takovym pripade mam vetsinou nadefinovanej nejakej stav kterej „Ja“ predpokladam a splnuje to, co ocekavam ze by tam byt melo, nikoli to, co tam byt pripadne muze.

Nadto by se aplikace mela nejak netrivialne (= nezhrouti se) vyporadat i s nekonzistentni databazi.

jos

ad přihlašování – a co v případě že máš stovky tisíc (a víc) uživatelů? nijak to nezpochybňuju, jen se ptam

Hane

Vlož sem prosím tě odkazy na aplikace, kde jsi systém takto vybudoval. Budu rád, když už se nebudu muset prokousávat těma formulářema a celým webem, ale budu se moct připojit do db přes konzoli/nějakého managera.

Vidím v tom řešení hned několik problémů
– počet uživatelů byl zmíněn
– co když se autentizace řeší přes jiný systém (facebook, ldap apod.)
– stačí jeden uživatel, který prolomí db a může přidat oprávnění úplně všem a je to v …
– jak ošetřit, když je nějaká „sdílená“ tabulka a každý uživatel v ní má své řádky (nepočítám Oracle, který má možnost podmínek do where podle uživatelů)
– který uživatel bude připojen do DB, když se zaregistruje nový uživatel? ten musí mít určitě nějaká oprávnění, která bych nikdy veřejně nepoužíval
– bezpečností riziko nehrozí jen ze samotné aplikace, ale z všech míst, odkud se lze připojit do db

Databáze má sice mocný systém práv, ale pro aplikace je většině případů nedostačující, protože různí uživatelů mohou mít různá oprávnění nad různými řádky v tabulce a ty ostatní řádky třeba ani nemají právo vidět.
A že 99.9% procent to řeší špatně je taky zajímavý názor. Nevím kolik z těch tvých procent jsou weby s databází na webhostingu, kde si můžou nechat zdát o procedurách, občas i triggerech a views. Myslím, že budou mít oprávnění vytvářet uživatele v databázi? Tenhle systém přihlašování bych možná tak použil v aplikacích, kde má každý uživatel svoje tabulky a existují systémové tabulky pro systém mimo uživatele. Ale takových určitě není 99.9% na světě.

Že je problém, kam umístit určitou funkcionalitu, zda přímo do DB anebo to nechat v aplikační vrstvě, je známá věc. Vyplatí se přitom používat zdravý rozum než dodržovat nějaké poučky. Ale bezhlavému umisťování loginů přes DB bych se ve většině případů osobně raději vyhnul.

j

Pocet uzivatelu prece nehraje roli, v databazich je to taky nejaka tabulka ktera muze mit klidne miliony zaznamu.

Autentizace pres ldap … by taky nemela byt zasadni problem, vetsina databazi prece umi autentizovat uzivatele pres LDAP – napr u zakaznika pouzivam M$ a mam tam pridany domenovy acc a k nemu pridelena prava.

Pokud ziska uzivatel pristup k DB na urovni admina, tak je uplne uprdele jestli pouzivam nejaky svoje prava nebo prava na urovni DB, ne? Naopak, pokud pouziju klasiku – aplikace pouziva jeden svuj acc, tak staci ziskat ten, coz je o dost jednodussi (dost casto se pouziva minimalne uhodnutelny login).

Dost databazi umi prava i na urovni zaznamu => v principu lze nastavit prava ke kazdy bunce extra.


Pro priklad, adminuju jeden erp, kterej pouzival login do DB, coz melo vyhody v tom, ze pokud jsem chtel nekomu znemoznit videt nejaka data, tak sem to udelal v DB a at zkousel co zkousel, k tem datum se nedostal.

Navic nekteri uzivatele pouzivali moznost pripojit si primo data do excelu, kde opet mohli to, co jim DB prava umozila (samo RO).

Nove verze bohuzel presly prave na system jednoho usera => pripojeni do excelu padlo zcela (respektive musi kvuli tomu mit dalsi extra acc), a vzhledem k neschopnosti dodavatele nejsem schopen osetrit prava – jednoduse to ze nekde zakazu neco, neznamena, ze to nebude videt kdyz se vleze nekam jinam

Priklad – nacionale zamestnancu – sice zakazu uzivatelum pristup do seznamu zanestnancu, ale protoze je na kazdym dokladu „pachatel“ a toho kazdy videt potrebuje a zaroven si to muze rokliknout na detail … kdybych mel moznost nastavit ta prava v DB, tak krom par vyvolenych jednoduse dovolim zobrazovat pouze pole jmeno + prijmeni…. samo, nejak sem to osefoval (odmazal policka z vizualu), ale to neznamena, ze si je user pri trose snahy nemuze zobrazit (system napr umoznuje si udelat uzivatelsky formular, kde si muze vizuelne sestavit i vlastni SQL query …)

BTW: Tenhle uzasnej system mimo jiny generuje logy rychlejs, nez je schopen je mazat …


Jinak samo neberu jen prava – videl sem uz par (stovek) ruznych databazi, a prakticky v kazdy byly zasadni navrhovy chyby. Kuprikaldu takova zhuverilost jako duplikovani poli (trebas vic poli ICO). Mno a to sebou samo nese to, ze ta data nejsou konzistentni.

to_je_jedno

helios orange? (jestli tohle zmenili tak to je teda humáč, naposled jsem to videl kdyz vysla cerstve UTF-8 verze)

Ono je rozdil mezi ERP kde to lze resit 1 firma = 1 DB a users v DB serveru protoze ten server ma admin VZDY pod kontrolou a neco jinyho je to u webu ktery maji byt schopny bezet na relativne kazdem webhostingu…

povinná přezdívka

a co když aplikace nekomunikuje jen s jednou databází ale s více, třeba i na jiných strojích?

co když aplikace volá cizí webservice a přístup k nim chce omezit jen na některé uživatele?

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.