Wiki

Clone wiki

shake/database / Home

Shake \ Databáze & ORM

Shake\Database přináší do Nette aplikací hned několik výhod. Za prvé je to ORM, díky kterému můžete výsledky z databáze rozšířit o další metody. Za druhé pak jednoduchá podpora vnořených transakcí, a to i u databází, které vnořené transakce nepodporují (MySQL).

ORM

Shake automaticky mapuje výsledky z databáze do vašich vlastních tříd, které reprezentují dané entity z databáze, případně kolekce entit. A pokud tyto třídy nemáte napsané? Nic se neděje, pak dostanete místo entit instance ActiveRow, přesně jak jste zvyklí z klasického Nette :-)

  • Entity

    Vezměme si obyčejné získání jednoho záznamu z databáze. Třeba posledního vloženého článku z tabulky articles.

    <?php
    namespace App\Model;
    use Shake\Scaffolding\Repository;
    
    class ArticleRepository extends Repository 
    {
        public function findLast()
        {
            return $this->select()
                ->order('created DESC')
                ->fetch();
        }
    }
    

    Pokud neexistuje žádná entita s názvem ArticleEntity, vrátí tato metoda instanci třídy Nette\Database\Table\ActiveRow.

    Entity jsou v pojetí Shake frameworku třídy, které se vždy nazývají podle názvu tabulky, ze které pochází, plus přívlastek Entity. Tedy např. tabulka article -> entita ArticleEntity, tabulka article_category -> entita ArticleCategoryEntity.
    Entity vždy dědí z Shake\Database\Orm\Entity.

    <?php 
    namespace App\Model;
    use Shake\Database\Orm\Entity;
    
    class ArticleEntity extends Entity
    {
    }
    

    Poté, co napíšeme tuto entitu a uložíme jí třeba do app/model/ArticleEntity.php, začnou všechny dotazy do databáze vracet místo ActiveRow instanci této entity.

    Ta se chová úplně stejně jako ActiveRow. Můžete nad ní volat metody ->related(), získávat data atd. Navíc ji ale můžete rozšířit o vlastní funkce, což je přesně její účel.

    <?php 
    namespace App\Model;
    use Shake\Database\Orm\Entity;
    
    class ArticleEntity extends Entity
    {
        public function getPosts()
        {
            return $this->related('article_post')
                ->order('date DESC');
        }
    }
    

    Entity podporují gettery, stejně jako array-access. K příspěvkům daného článku se tak můžete dostat kterýmkoli z následujících způsobů:

    <?php
    $article = $articleRepository->findLast();
    
    $article->getPosts();
    $article['posts']
    $article->posts;
    

    což se hodí zejména v šablonách

    {foreach $article->posts as $post} 
        ... 
    {/foreach}
    
  • Table

    Table fungují úplně stejně, jako Entity, jen se týkají kolekce záznamů, místo jednoho konkrétního záznamu.

    Při získávání více řádků z databázové tabulky vrací Shake (stejně jako Nette) instanci Nette\Database\Table\Selection. Pokud ale existuje *Table třída, je místo Selection vrácena tato *Table třída.

    <?php 
    namespace App\Model;
    use Shake\Database\Orm\Table;
    
    class ArticleTable extends Table 
    {
    }
    

    Všechny *Table třídy dědí z Shake\Database\Orm\Table a díky tomu mají stejnou funkčnost, jako klasický Selection z Nette. A stejně jako Entity je můžeme o další chování rozšířit.

Vnořené transakce

Vnořené transakce jsou prostě transakce v transakci :-) Klasické MySQL je nepodporuje, Shake\Database ale tento nedostatek obchází, takže je ve výsledku pouźít můžete.

Hlavní transakce je závislá na svých vnořených transakcích. Pokud se jen jedna sub-transakce nepovede, jsou zrušeny i všechny ostatní metody transakce i jejich potomků.

<?php
namespace App\Model;
use Shake\Scaffolding\Repository;

class ArticleRepository extends Repository 
{

    public function doSomethingFunny() 
    {
        try {
            $this->connection->beginTransaction();
            ...
                $this->connection->beginTransaction();
                ...
                $this->connection->commit();

                $this->connection->beginTransaction();
                ...
                $this->connection->commit();
            ...
            $this->connection->commit();

        } catch (Exception $e) {
            $this->connection->rollBack();
        }
    }

}

Updated