Výběr databáze – how&best practice

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
premek_k
Člen | 172
+
0
-

Dobrý den, pánové.
Obracím se na vás s velkou prosbou. Nejsem žádný zkušený programátor, co se PHP týče, dá se říct panic. I přesto jsem na základě materiálů zde na stránkách a i radách ve fóru dokázal napsat vlastní aplikaci. Je pravdou, že mi spousta věcí uniká a spoustě věcí nerozumím, ale vždy jsem se zde dozvěděl, jak vzniklý problém řešit případně i spoustu informací, jak danou věc udělat lépe, správněji.
Poslední, co mi v tuto chvíli stojí v cestě je myslím hojně využívaný model aplikace, kdy si uživatel v rámci svého přihlášení vybere i jednu z připravených databází a nad ní pracuje. Ty databáze jsou strukturou naprosto shodné a liší se pouze vlastními daty – typicky testovací vs. provozní databáze. S tímto si stále nevím rady a studium zdejších materiálů a prolézání fóra nevede k cíli. Návodů a řešení zde několik je, ale ani jeden z nich není plně použitelný – resp. jsem nic, od čeho bych se s mými chabými znalostmi odpíchnul, jsem nenašel.

Představa je taková, že použiji NetteDB a že v config.neon budu mít definici spojení s několika MySql databázemi. Po spuštění aplikace je uživateli zobrazen přihlašovací formulář obsahující mimo ostatního i selektbox s výběrem přednastavených databází (vytažených právě z config.neon). V rámci přihlášení uživatele resp. v prvém kroku tohoto procesu si aplikace přebere a nastaví vybrané databázové spojení a dále již nad touto vybranou databází provede ověření uživatele, jeho přihlášení a další práci. Jelikož jsou všechny databáze strukturou shodné, je jedno, kterou databázi si uživatel vybere. Pokud se chce uživatel přepnout do jiné databáze, nejprve se odhlásí z aplikace a následně v přihlašovacím dialogu vybere jiné připojení.

Přesně toto výše popsané chování potřebuji zaimplementovat do své aplikace tak, abych mohl rozšiřovat počet databází jen jejich založením na db serveru a přidáním connectu do config.neon, tzn. bez dalších zásahů do kódu aplikace.

Bohužel po tom, co jsem se zde z různých článků a příspěvků ve fóru dozvěděl, mám stále silnější pocit, že toto nelze v tomto rozsahu v Nette realizovat. Navíc tím, jak se zrovna tato část v Nette vyvíjela, je zde k nalezení spousta již neplatných či zastaralých informací (např. právě způsob zápisů do config.neon), které mne velmi matou a ani zkoušením pokus/omyl se nemohu se hnout z místa.

Proto vás chci poprosit, zda by mne – a nejen mne, všechny ostatní začátečníky s tímto problémem – mohl někdo z vás zkušenějších nasměrovat či nějak poradit, jak uvedené funkčnosti docílit – případně potvrdit mou domněnku, že toto realizovat nelze. Opravdu bych byl velice vděčný za tuto pomoc.

Přemek

premek_k
Člen | 172
+
0
-

Aby jste si nemysleli, že tady z vás tahám rozumy a píšu něco, na čem nějak zbohatnu „na váš úkor“, tak můžete kouknout na to o co jde na http://www.geame.cz a aplikace pak „běží“ na http://play.geame.cz

petr.pavel
Člen | 535
+
0
-

Doufal jsem, že odpoví někdo, kdo tomu rozumí, protože mě by taky zajímaly nějaké ty best practices.
Tak ti aspoň napíšu, jak to bastlím já. Založené je to na volání $databaze->query("USE $nazevDatabaze");

  • Jeden db uživatel má přístup ke všem databázím.
  • V mém případě se všechny databáze jmenují stejně, až na konec, který znamená rok (sezona_2012, sezona_2013…).
  • V config.neon mám připojení k db definováno bez názvu databáze:
nette:
  database:
    dsn: 'mysql:host=localhost'
  • V configu mám taky, jaká je aktuální sezóna – ta se zvolí jako výchozí.
  • Všechny objekty, které pracují s db dědí base, která dostává můj objekt MenicDatabaze, kterému předávám šablonu pro název db („sezona_“) a aktuální sezónu („2013“).

    Takhle to mám proto, abych parametry v configu musel psát jen jednou a ostatní objekty pak dostanou MenicDatabaze přes autowiring __construct(NConnection $databaze, MenicDatabaze $menicDatabaze)

services:
  menic: MenicDatabaze(%sablonaDb%, %aktualniSezona%)
  hriste: HristeModel	# ano, v modelu přistupuji k databázi, nekamenujte mě
  • MenicDatabaze dostává v konstruktoru i NConnection $databaze, které mu předává autowiring.

    V metodě zvolSezonu($rok) pak zkombinuje $this->sablonaDb a $rok a pomocí

    $this->databaze->query("USE $nazevDatabaze"); zvolí aktuální databázi.

Cítím, že to není tak úplně to pravé, ale nic lepšího mě nenapadlo.

castamir
Člen | 629
+
0
-

Já bych to dělal malinko jinak. Následující text bude spíš nástin, než funkční prototyp.

parameters:
  database:
    default:
      dns: ...
      user: ...
    default > 2012
      dbname: ...
    default > 2013
      dbname: ...

services:
  db: SpravceDb(%database%)
  nejakaDbSluzba: NazevSluzby (@SpravceDb)
class SpravceDb {
    private $connections = array();
    function __construct(array $connection_strings) {
        foreach ($connection_strings as $cs) {
            $dns = $cs["driver"] . ":" . "host=" . $cs["host"] . ";dbname=" . $cs["dbname"];
            // pripadne by mel jit i tento zkraceny zapis
            $dns = "{$cs['driver']}:host={$cs['host']};dbname={$cs['dbname']}";

            $this->connections[$cs["dbname"]] = new \Nette\Database\Connection($dns, $cs["user"], $cs["password"])
        }
    }

    public function &__get($dbname) {
        return $this->connections[$dbname];
    }
}

class NazevSluzby {
    private $spravceDb;
    private $table = "tabulka"; // toto jsem jeste pridal

    function __construct(SpravceDb $spravceDb ) {
        $this->spravceDb = $spravceDb;
    }

    public function find($dbname, $key) {
        return $this->spravceDb->{$dbname}->table($this->$table)->find($key);
    }
}

Kód je zjednodušený a nejspíš tam budou i nějaké chyby, ale princip snad chápete

Edit: pridan atribut $table a upravena metoda na ziskani udaju z parametricky volene databaze.

Edit2: $this->db ⇒ $this->spravceDb

Editoval castamir (1. 3. 2013 14:51)

petr.pavel
Člen | 535
+
0
-

@castamir: Buď jsem to nepochopil nebo máš chybu v

public function find($dbname, $key) {
    return $this->db->{$dbname}->find($key);
}

Jestli používáš Nette Database nebo NotORM, tak {$dbname} v tomhle kontextu není název databáze ale tabulky. Takže vlastně databázi nikde nevolíš.

castamir
Člen | 629
+
0
-

Tak dobře, doplním tam trošku víc upřesňující kód. Databázi volím v SpravceDb metodou __get, nicméně ano, chybí mi tam před find ještě table.

premek_k
Člen | 172
+
0
-

Dobrý den,
děkuji za odpovědi. Výběr databáze pomocí Sql příkazu „USE“ se nabízel jako nejjednodušší řešení a aplikaci jsem již dříve upravil tímto způsobem. Bohužel mi čáru přes rozpočet udělal hosting, kdy až při pokusu to tam dostat jsem s hrůzou zjistil, že nepovolují stejného usera k více databázím (moc nechápu proč) a každá databáze musí mít vlastního jednoznačného uživatele. „Grant“ taky nepomohl, mají ho zakázaný.

No nicméně – jelikož jsem se obával, že na tuto mou prosbu nebude nikdo reagovat – přece jen to není minutová záležitost a osobně si myslím (dle již stávajících témat s touto problematikou), že je zde i obava o „poplivání“ případně zveřejněného kódu, jal jsem se to zkusit řešit úplně od píky a tak, jak na to v tuto chvíli stačí mé znalosti. Výsledkem je následující kód:

Databáze

CREATE DATABASE `prvni`;
CREATE DATABASE `druha`;
CREATE DATABASE `treti`;
USE `prvni`;
CREATE TABLE `pokus` (
  `PokusId` int(11) NOT NULL AUTO_INCREMENT,
  `PokusText` varchar(50) COLLATE utf8_czech_ci NOT NULL,
  PRIMARY KEY (`PokusId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;

INSERT INTO `pokus` (`PokusId`, `PokusText`) VALUES
(1,	'Databaze Prvni'),
(2,	'Databaze Prvni podruhe');

USE `druha`;
CREATE TABLE `pokus` (
  `PokusId` int(11) NOT NULL AUTO_INCREMENT,
  `PokusText` varchar(50) COLLATE utf8_czech_ci NOT NULL,
  PRIMARY KEY (`PokusId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;

INSERT INTO `pokus` (`PokusId`, `PokusText`) VALUES
(1,	'Databaze Druha'),
(2,	'Databaze Druha podruhe');

USE `treti`;
CREATE TABLE `pokus` (
  `PokusId` int(11) NOT NULL AUTO_INCREMENT,
  `PokusText` varchar(50) COLLATE utf8_czech_ci NOT NULL,
  PRIMARY KEY (`PokusId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;

INSERT INTO `pokus` (`PokusId`, `PokusText`) VALUES
(1,	'Databaze Treti'),
(2,	'Databaze Treti podruhe');

CONFIG.NEON

#
common:
    parameters:
        dbprofiles:
            0:
                name: 'prvni'
                dsn: 'mysql:host=localhost;dbname=prvni'
                user: root
                password: root

            1:
                name: 'duha'
                dsn: 'mysql:host=localhost;dbname=druha'
                user: root
                password: root

            2:
                name: 'treti'
                dsn: 'mysql:host=localhost;dbname=treti'
                user: root
                password: root

    php:
        date.timezone: Europe/Prague
        # zlib.output_compression: yes

    nette:
        application:
            errorPresenter: Error

        database:
            dsn: 'mysql:host=localhost;dbname=prvni'
            user: root
            password: root

        session:
            expiration: 14 days

    services:
        authenticator: Authenticator
        routerFactory: RouterFactory
        router: @routerFactory::createRouter

        pokusRepository: Sandbox\PokusRepository

    factories:

production < common:

development < common:

PokusRepository

<?php
namespace Sandbox;
use Nette;
// ------------------------------------------------------------------
/**
 * Tabulka Pokus
 */
class PokusRepository extends Repository
{
    // ------------------------------------------------------------------
    /**
    * Vrat data z tabulky Pokus
    */
    public function getPokus() {
        return $this->findAll();
    }// end function
}//end class
?>

BasePresenter (Jen z důvodu vyzkoušení, zda s tím není problém)

<?php
/**
 * Base presenter for all application presenters.
 */
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    // ------------------------------------------------------------------
    /**
    * pristup k modelu
    */
    public $pokusRepository;

    // ------------------------------------------------------------------
    /**
    * Startup
    */
    protected function startup()
    {
      parent::startup();
    }//end function

    // ------------------------------------------------------------------
    /**
    * Inject model
    */
    public function injectPokusRepository(Sandbox\PokusRepository $PokusRepository)
    {
        $this->pokusRepository = $PokusRepository;
    }//end function

}//end class
?>

No a to hlavní – HomePagePresenter

<?php
use Nette\Application\UI;
use Nette\Application\UI\Form;

// ------------------------------------------------------------------
/**
 * Homepage presenter.
 */
class HomepagePresenter extends BasePresenter
{
    /** @var \Nette\Database\Connection */
    private $database;

    // ------------------------------------------------------------------
    /**
    * konstruktor
    */
      public function __construct(Nette\Database\Connection $database)
    {
        $this->database = $database;
    }//end method

    // ------------------------------------------------------------------
    /**
    * Startup
    */
    protected function startup()
    {
      //Nastaveni databaze
      $this->setDatabase();
      parent::startup();
    }//end function

    // ------------------------------------------------------------------
    /**
    * Vytvoreni formulare
    */
    protected function createComponentDbSelectForm()
    {
        $form = new UI\Form;
        $form->addSelect('Databaze', 'Databaze:')
             ->setItems($this->getDatabases());
        $form->addSubmit('send', 'Potvrdit');
        $form->onSuccess[] = $this->dbSelectFormSucceeded;
        return $form;
    }//end function

    // ------------------------------------------------------------------
    /**
    * Uspech
    */
    private function dbSelectFormSucceeded($form) {
        $values = $form->getValues();
        //Ulozeni indexu vybrane DB do session
        $session = $this->getSession("SelectedDb");
        $session->setExpiration(0);
        $session->dbIndex = $values->Databaze;
        //redirect
        $this->redirect("HomePage:Default");
    }// end function

    // ------------------------------------------------------------------
    /**
    * Seznam databazi
    */
    private function getDatabases() {
        $res = array();
        $profiles = $this->context->parameters['dbprofiles'];
        foreach($profiles as $profile){
            array_push($res,$profile["name"]);
        }//end foreach
        return $res;
    }// end function


    // ------------------------------------------------------------------
    /**
    * Nastaveni databaze ze session
    */
    private function setDatabase() {
        //Vytahnu si ze session index vybrane databaze
        $session = $this->getSession("SelectedDb");
        $dbIndex = $session->dbIndex;
        //Pokud nebylo jeste nastaveno, bude pouzita vychozi databaze
        if($dbIndex != null){
            //vytahnu info o pripojeni z config.neon
            $profile =  $this->context->parameters['dbprofiles'][$dbIndex];
            $this->database->__construct($profile["dsn"], $profile["user"], $profile["password"]);
        }//end if
    }// end function

    // ------------------------------------------------------------------
    /**
    * Render stranky
    */
	public function renderDefault()
	{
		$this->template->pokus = $this->pokusRepository->getPokus();
	}//end function
}//end class
?>

+ default.latte

{block content}
<h1>Vyber databázi:</h1>

{form dbSelectForm}
    {input Databaze}
    {input send}
{/form}

<h2>Selekt z tabulky patřičné databáze:</h2>
{foreach $pokus as $p}
    <p>{$p->PokusText}</p>
{/foreach}
{/block}

Tímto způsobem to vypadá funkčně, spíš je otázka, jakých nepřístojností jsem se dopustil vůči Nette etice…

Editoval premek_k (1. 3. 2013 13:09)

castamir
Člen | 629
+
0
-

Zkusim ti sem sepsat nedostatky:

  • duplicitni definice pripojeni k databazi (v parameters a pak nette.database)
  • PokusRepository vytvaris jako sluzbu s pripojenim k databazi „prvni“, takze bez ohledu na to, o co se snazis v setDatabase, porad budes pristupovat k databazi „prvni“. A co vic, pokud bys nedejboze zmenil tu databazi z presenteru v PokusRepository, nesmis zapomenou na to, ze se jedna o sluzbu, takze se zmeny promitnou vsude, kde to budes dal pouzivat
  • v HomepagePresenteru injektujes databazi, ale pak znasilnujes jeji konstruktor v setDatabase (rozumnej, volas jiz podruhe stejny konstruktor, ktery jiz byl volan, jen s jinymi parametry)
  • pouzivas context, ktery je prinejmensim nedoporucovany
  • mas vrstvu pro praci s databazi (jednim jeho prvkem je PokusRepository), ale presto si hrajes s databazi jako takovou v presenteru – chyba navrhu

Edit: Takova uzitecna rada pro prehlednejsi a cistejsi kod: Pro metody plati pravidlo „One shot, one kill“, tedy metoda dela jen jedinou vec. Pro objekt plati v podstate to same – mel by se zabyvat v podstate jedinou oblasti, coz v pripade presenteru neni sprava pripojeni k db. Kod, ktery se ridi takovou pouckou se mnohem snad testuje.

Editoval castamir (1. 3. 2013 13:55)

petr.pavel
Člen | 535
+
0
-

@castamir: Souhlas. Ohledně tvého předchozího příspěvku: hlavně ti tam chybí, jak bys v tom __get() měnil tu databázi :-) Jestli přes $db->query("USE $nazevDatabaze"); nebo jinak.

@premek_k: Tvoje setDatabase() je vlastně továrna na NConnection (já používám verzi bez prefixů). Mimochodem, nevolal bych __construct() ale new NConnection(), ale čti dál.
Přijde mi víc Nette definovat to v configu:

services:
	database:
		class: Nette\Database\Connection
		create: MojeDbTovarna::vytvorPripojeni
		arguments: [%dbprofiles%]

vytvorPripojeni pak bude vlastně tvoje setDatabase(). Session tam dostaneš přes auto-wiring (doufám) a definované databáze přes parametry (psáno z hlavy):

class MojeDbTovarna {
  public function vytvorPripojeni($dbprofiles, NSession $session) {
    $mojeSession = $session->getSection("SelectedDb");
    // jestli nemáš důvod používat zvláštní namespace, tak nemusíš
    $dbIndex = $mojeSession->dbIndex;
    //Pokud nebylo jeste nastaveno, bude pouzita vychozi databaze
    if ($dbIndex === null) {
      return null;
    }

    //vytahnu info o pripojeni z config.neon
    $profile =  $dbprofiles[$dbIndex];

    // tohle jsem vykradl z mého zkompilovaného configu z `createServiceNette__database__default()`
    $service = new NConnection($profile["dsn"], $profile["user"],$profile["password"], NULL);
    $service->setCacheStorage($this->getService('cacheStorage'));
    NDebugger::$blueScreen->addPanel('NDatabasePanel::renderException');
    $service->setDatabaseReflection(new NDiscoveredReflection($this->getService('cacheStorage')));
    $service->onQuery[] = array(
      $this->getService('nette.database.defaultConnectionPanel'),
      'logQuery'
    );
    return $service;
  }
}

Editoval petr.pavel (1. 3. 2013 14:40)

castamir
Člen | 629
+
0
-

@petr.pavel tak snad poslední úprava mého prvního příspěvku. Dodefinoval jsem to pole $this->connections ve SpravceDb

premek_k
Člen | 172
+
0
-

Díky ti za připomínky @castamir, chvíli mi bude trvat, než to pochopím, nicméně narychlo:

  • duplicitni definice pripojeni k databazi (v parameters a pak nette.database)

S tímto bych se i smířil. v nette.database je nastavena výchozí databáze, samotný výčet pak v parameters. To že je ta výchozí uvedena v obou sekcích by se dalo ještě ošetřit, ale nepříjde mi to jako zásadní problém.

  • PokusRepository vytvaris jako sluzbu s pripojenim k databazi „prvni“, takze bez ohledu na to, o co se snazis v setDatabase, porad budes pristupovat k databazi „prvni“. A co vic, pokud bys nedejboze zmenil tu databazi z presenteru v PokusRepository, nesmis zapomenou na to, ze se jedna o sluzbu, takze se zmeny promitnou vsude, kde to budes dal pouzivat

No nevím. na uvedeném kódu je to funkční – tzn. po výběru databáze se udělá selekt do té správné a vrátí to správná data. Je mi jasné, že to není košér, ale minimálně to funguje.

  • v HomepagePresenteru injektujes databazi, ale pak znasilnujes jeji konstruktor v setDatabase (rozumnej, volas jiz podruhe stejny konstruktor, ktery jiz byl volan, jen s jinymi parametry)

Njn, to je proto, že se (krom dalších věcí) neumím vyrovnat s problémem, kdy ve __construct metodě se ještě nedostanu k session a tudíž nemám jak získat index vybrané db (což jsem původně zamýšlel takto udělat). K session se dostanu až ve startup, proto takhle „přes hlavu“.

  • pouzivas context, ktery je prinejmensim nedoporucovany

Ano, o tom vím, ale opět – v tuto chvíli se mu neumím ve všech případech vyhnout ($this->context->parameters[‚dbprofiles‘];)

  • mas vrstvu pro praci s databazi (jednim jeho prvkem je PokusRepository), ale presto si hrajes s databazi jako takovou v presenteru – chyba navrhu

Moc nerozumím této výtce – o několik řádků výš jsi napsal „…nedejbože změnil tu databázi v PokusRepository…“ a teď zas že bych měl. (???)

Edit: Takova uzitecna rada pro prehlednejsi a cistejsi kod: Pro metody plati pravidlo „One shot, one kill“, tedy metoda dela jen jedinou vec. Pro objekt plati v podstate to same – mel by se zabyvat v podstate jedinou oblasti, coz v pripade presenteru neni sprava pripojeni k db. Kod, ktery se ridi takovou pouckou se mnohem snad testuje.

Jasně, s tím souhlasím a věřím, že mnou zveřejněné řešení není správné v mnoha ohledech. Ale jsem v situaci, kdy nevím jak na to, správné řešení (které bych pochopil) jsem nikde nenašel, moc tomu vlastně ani nerozumím a tak zkouším, jak z toho ven. Království za kus funkčního, správně napsaného a jednoduchého kódu…

Editoval premek_k (1. 3. 2013 15:06)

castamir
Člen | 629
+
0
-

@premek_k

Moc nerozumím této výtce – o několik řádků výš jsi napsal „…nedejbože změnil tu databázi v PokusRepository…“ a teď zas že bych měl. (???)

Myslel jsem to tak, ze bys to skarede znovuzavolani konstruktoru pouzil i pro PokusRepository a delegoval to primo do ni. Pokud bys pak nekde volal napriklad nejakou komponentu, jez take pouziva PokusRepository, mohlo by to zpusobit neocekavane chovani. Jak bys pak zjistil, kterou databazi pouzivas v te komponente skrz PokusRepository?

Ano, o tom vím, ale opět – v tuto chvíli se mu neumím ve všech případech vyhnout ($this->context->parameters[‚dbprofiles‘];)

Zastavam nazor, ze bys k parameters nemel pristupovat primo z presenteru, ale v konfigu predhodit konkretni prvek parameters nejake tve tride ci metode.

premek_k
Člen | 172
+
0
-

Díky moc, @petr.pavel.
Myšlence rozumím, resp. nevím jen, zda a kde je potřeba vytvorPripojeni volat, či se již o to nemusím starat a vždy budu připojen ke správné databázi (ve vlastní režii jen nastavuji dbIndex v session)???
Facku už jsem si dal…

Editoval premek_k (1. 3. 2013 18:34)

premek_k
Člen | 172
+
0
-

Takže aktuální řešení k případnému připomínkování
Děkuji mnohokrát účastníkům této diskuze za to, že mě nakopli správným směrem. Výsledné řešení je jakýmsi průnikem…

CONFIG.NEON (Sekce „nette:database:“ zcela ostraněna)

common:
    parameters:
        dbprofiles:
            0:
                dbname: 'prvni'
                dns: 'mysql:host=127.0.0.1;dbname=prvni'
                user: root
                password: root
            1:
                dbname: 'druha'
                dns: 'mysql:host=127.0.0.1;dbname=druha'
                user: root
                password: root
            2:
                dbname: 'treti'
                dns: 'mysql:host=127.0.0.1;dbname=treti'
                user: root
                password: root

    php:
        date.timezone: Europe/Prague
        # zlib.output_compression: yes

    nette:
        application:
            errorPresenter: Error

        session:
            autoStart: smart
            expiration: +30 days

        mailer:
            smtp: false

    services:
        routerFactory: RouterFactory
        router: @routerFactory::createRouter

        cFactory: Geame\ConnectionFactory(%dbprofiles%)
        pokusRepository: Geame\PokusRepository(@cFactory)
    factories:
    :
    :

ConnectionFactory

<?php
namespace Geame;
use Nette;
// ------------------------------------------------------------------
/**
* Pripojeni k databazi
*/
class ConnectionFactory {
    private $connection;
    private $dbList = array();

    function __construct(array $connections, Nette\Http\Session $session, Nette\DI\Container $container) {
        // Ze session si vytahnu index databazoveho konektu
        $section = $session->getSection("SelectedDb");
        $dbIndex = $section->dbIndex;
        // Jestlize neni dosud nastaven, nastavuji hlavni databazi (index 0)
        if($dbIndex === null){
            $dbIndex = 0; //Hlavni databaze
        }//end if

        // parametry pripojeni spravne databaze
        $cs = $connections[$dbIndex];
        // vytvoreni a nastaveni konektu
        $connection = new \Nette\Database\Connection($cs["dns"], $cs["user"], $cs["password"]);
        $connection->setCacheStorage($container->getService('cacheStorage'));
        $connection->setDatabaseReflection(new Nette\Database\Reflection\DiscoveredReflection($container->getService('cacheStorage')));

        // nastaveni pro blueScreen a DebugBar
        Nette\Diagnostics\Debugger::$blueScreen->addPanel('\Nette\Database\Diagnostics\ConnectionPanel::renderException');
        $panel = new Nette\Database\Diagnostics\ConnectionPanel;
        $panel->explain = TRUE;
        $panel->name = 'dbConnectionPanel';
        $connection->onQuery[] = array($panel, 'logQuery');
        Nette\Diagnostics\Debugger::$bar->addPanel($panel);

        // uchovani vytvoreneho konektu
        $this->connection = $connection;

        //uchovani seznamu dostupnych databazi
        foreach($connections as $c){
            array_push($this->dbList,$c["dbname"]);
        }//end foreach
    }//end function

    public function &__get($nil) {
        return $this->connection;
    }//end function

    public function getDbList() {
        return $this->dbList;
    }// end function
}//end class
?>

Repository

<?php
namespace Geame;
use Nette;

abstract class Repository extends Nette\Object
{
    private $database;

    function __construct(ConnectionFactory $cFactory ) {
        $this->database = $cFactory->connection;
    }//end function

    protected function getTable()
    {
        // název tabulky odvodíme z názvu třídy
        preg_match('#(\w+)Repository$#', get_class($this), $m);
        return $this->database->table(strtolower($m[1]));
    }//end function

    public function findAll()
    {
        return $this->getTable();
    }//end function
}//end class

class PokusRepository extends Repository
{
    public function getPokus() {
        return $this->findAll();
    }// end function
}//end class
?>

HomePagePresenter

<?php
use Nette\Application\UI\Form;

class HomepagePresenter extends BasePresenter
{
    private $database;
    private $dbList = array();
    private $pokusRepository;

    function __construct(Geame\ConnectionFactory $cFactory ) {
        $this->database = $cFactory->connection;
        $this->dbList = $cFactory->getDbList();
    }//end function

    public function injectPokusRepository(Geame\PokusRepository $PokusRepository)
    {
        $this->pokusRepository = $PokusRepository;
    }//end function

    protected function createComponentDbSelectForm()
    {
        $form = new Form();
        $form->addSelect('Databaze', 'Databaze:')
             ->setItems($this->dbList);
        $form->addSubmit('send', 'Potvrdit');
        $form->onSuccess[] = $this->dbSelectFormSucceeded;
        return $form;
    }//end function

    public function dbSelectFormSucceeded(Form $form) {
        $values = $form->getValues();
        //Ulozeni indexu vybrane DB do session
        $session = $this->getSession("SelectedDb");
        $session->setExpiration(0);
        $session->dbIndex = $values->Databaze;
        $this->redirect('this');
    }// end function

    public function renderDefault(){
	$this->template->pokus = $this->pokusRepository->getPokus();
    }//end function
}//end class
?>

Editoval premek_k (2. 3. 2013 0:42)