Nette Database vs dibi

Mezi některými uživateli Nette Frameworku v poslední době vznikají zmatky: myslí si, že knihovny Nette Database a dibi spolu nějak souvisí. Takže si nejprve připomeneme elementární rozdíl mezi těmito dvěmi rozdílnými knihovnami.

dibi

Každý nějak začínal. Ti největší punkeři ještě pamatují mysql_* funkce, které jsou neohrabané a samy o sobě pouze nabízí PHPku přístup k databázi. Cokoliv, co se přibližuje bezpečnosti, si uživatel musí zařídit sám.

Proto vznikla knihovna dibi, která obaluje tyto hloupé funkce do chytré knihovny. Řeší SQL injekci a lahodí oku programátora.

Dibi dosáhla svého vrcholu, a jak sám autor říká, už není nic dalšího, co by měla umět. Je to stabilní konzervativní knihovna, perfektní, pokud znáte alespoň základy SQL a učíte se programovat, nebo vyvíjíte masivní aplikaci. Prostě vám kryje záda.

Dibi je externí knihovna a není součástí Nette.

Nette Database

Nette Database je velice inovativní ale i magická knihovna. Nette Database je z většiny „něco jako dibi“, ale k tomu kombinuje hlavní feature NotORM od Jakuba Vrány a pozměnuje (příliš) magické API, aby se lépe používala.

Tou feature převzatou z NotORM je inteligentní získávání dat z databáze. Snaží se ušetřit nás od psaní opakujících se SQL dotazů a pokládá minimální množství maximálně efektivních SQL dotazů. Může se tvářit příliš magicky, ale to je právě její výhoda.

Nette Database je součástí Nette, ale používat jej nemusíte, pokud vám nevyhovuje. Můžete používat klidně dibi, Doctrine2, nebo jiný nástroj.

Použití dibi

Pokud začínáte nový projekt, je nejlepší zaregistrovat DibiConnection jako službu.

parameters:
    database:
        host: localhost
        username: root
        password: ===
        database: foo
        lazy: true

services:
    connection:
        class: DibiConnection(%database%)

Tohle nám může zjednodušit rozšíření pro dibi, které je součástí dibi (ve verzi 2). Zaregistrovat ho můžeme v app/bootstrap.php, před tím, než vytvoříme DI Container.

$configurator->onCompile[] = function ($configurator, $compiler) {
    $compiler->addExtension('dibi', new DibiNetteExtension);
};

Nyní se nám konfigurace zjednoduší na

dibi:
    host: localhost
    username: root
    password: ===
    database: foo
    lazy: true

a službu budeme odkazovat né @connection, ale @dibi.connection.

V Presenteru

V presenteru k připojení přistoupíme přes DI.

class ArticlePresenter extends Nette\Application\UI\Presenter
{
    /** @var DibiConnection @inject */
    public $db;

    public function actionDefault($id)
    {
        $row = $this->db->query('SELECT * FROM articles WHERE id = %i', $id);
        // ...
    }

    // ...
}

V Modelu

Pokud pracujete s modely (což je velice dobře), tak by váš model mohl vypadat cca takto

namespace MyApp;

class Articles
{
    /** @var \DibiConnection */
    private $db;

    public function __construct(\DibiConnection $connection)
    {
        $this->db = $connection;
    }

    // zde následují vlastní metody na práci s články

    public function find($id)
    {
        // dibi dovoluje i fluent zápis (místo klíčových slov SQLka jsou metody)
        return $this->db->select('*')->from('articles')
            ->where('id = %i', $id)
            ->fetch();
    }

    public function delete($id)
    {
        $this->db->delete("articles")->where("id = %i", $id)->execute();
    }

    // další metody ...
}

Takový model se správně zaregistruje do DI Containeru a nastavíme DI Container, aby mu při vytvoření předal službu connection, kterou jsme si definovali na začátku.

services:
    articles:
        class: MyApp\Articles(@dibi.connection)

Nyní máme model zaregistrovaný v DI Containeru a můžeme ho použít v presenteru stejně, jako jsme použili samotné připojení.

class ArticlePresenter extends Nette\Application\UI\Presenter
{
    /** @var Articles @inject */
    public $articles;

    public function actionDefault($id)
    {
        $row = $this->articles->find($id);
        // ...
    }

    // ...
}

Stará aplikace, se statickými modely

Nový DI Container je stále ještě velice čerstvá záležitost a je možné, že vaše aplikace má modely statické a došli jste do bodu, kdy vám takovéto řešení nestačí. Není nic jednoduššího, než si zaregistrovat dibi tak, aby fungovaly staré statické modely, ale zároveň bylo dibi jako služba. Díky tomu můžete bezbolestně přepisovat statické modely a psát nové modely lépe.

parameters:
    database:
        driver: mysql
        host: localhost
        username: root
        password: ===
        database: foo
        lazy: true

services:
    connection:
        class: DibiConnection
        factory: dibi::connect(%database%)
        run: true

DI Container umožňuje pověřit vytvořením služby i jinou službu, nebo třídu a klidně i statickou. Takže toho využijeme a nadefinujeme si, aby dibi bylo vytvářeno staticky, jeho instance se uložila jako služba a automaticky se spouštěla při startu aplikace.

Tento zápis je identický se zastaralým zápisem z app/boostrap.php

dibi::connect($container->parameters['database']);

Použití Nette Database

Zde nejsem ten nejoprávněnější něco obsáhlého psát, takže jen stručně. Platí v podstatě vše, co bylo napsáno o dibi, jen modely a použití bude malinko jiné, kvůli povaze knihovny.

Ukáži tedy pouze základ.

v Presenteru

Protože Nette Database je součástí Nette Frameworku, ten toho využívá naplno a usnadňuje nám vytváření služby s Nette Database pro připojení. Nastavení konfiguračního souboru si můžete prohlédnout zde.

Použití bude podobné, jako u dibi:

class ArticlePresenter extends Nette\Application\UI\Presenter
{
    /** @var Nette\Database\Context @inject */
    public $db;

    public function actionDefault($id)
    {
        // získáme si selector pro tabulku
        $articles = $this->db->table('articles');
        $row = $articles->get($id);

        // ...
    }

    // ...
}

Jak vidíte, Nette Database nám ze začátku vývoje aplikace může plně suplovat vlastní třídy s modely, dokud nebude potřebné oddělit složitější logiku. Toto prosím neznamená, že modelové třídy nemáte psát. To znamená, že může nějakou dobu trvat, než zjistíte, že je potřebujete.