Použití Nette\Database pro entity a DAO

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

Napsal jsem si jednoduchou vrstvu fabik/database nad Nette\Database, která přidává podporu pro použití různých tříd pro různé druhy řádků a tabulek.

Takže mám např. třídu Article, která je potomkem ActiveRow a slouží jako přepravka na data a obsahuje logiku pro články, a třídu Articles, která je potomkem Table a definuje logiku pro načítání a ukládání článků.

Nelíbilo se mi ale, že v aplikaci používám řádky (row gateway) jako entity a tabulky (table gateway) jako DAO.

Napadlo mě to vyřešit tak, že si v modelu definuji entity a DAO jako rozhraní (viz ukázka, bod 4.). Samotné řádky a tabulky jsou pak jen implementací těchto rozhraní (viz ukázka, bod 5.), takže se navenek skutečně projevují jako entity a DAO. V aplikaci pak nepracuji přímo s tabulkami a řádky, ale jen s těmito rozhraními.

Výhodou je, že se mohu kdykoli rozhodnout, že vyměním způsob ukládání (např. za webovou službu, za jinou databázi apod.) tím, že použiji jinou implementaci DAO.

Myslíte si, že je takové řešení správné?

Objevil se tady také návrh (issue #805), že by se podobná věc přidala i do frameworku. Já bych byl pro, kdyby bylo v dokumentaci doporučeno vytvořit si rozhraní pro entity a DAO a třídy pro row a table gateway, které by je implementovaly.

Editoval jansfabik (11. 1. 2013 23:15)

enumag
Člen | 2118
+
0
-

Kód je takto čistší, to uznávám, ale je to poměrně dost ukecané… Nevím jestli překousnu psaní 2 rozhraní + 2 tříd kvůli každé entitě. (Také používám NDB a tvou fabik/database jsem se výrazně inspiroval při psaní svého extension.)

EDIT: Když tak nad tím přemýšlím tak oba ty interfacy se dají celkem bez problémů vygenerovat (na anotace IArticle by byla potřeba struktura db).

Editoval enumag (12. 1. 2013 0:27)

jansfabik
Člen | 193
+
0
-

Myslím si, že čas, který věnuješ psaní entity se ti vrátí, když ti pak bude IDE našeptávat properties. ;-)

Určitě by se na to dal napsat nějaký jednoduchý generátor. Zkusím o tom popřemýšlet.

enumag
Člen | 2118
+
0
-

To mi napovídá i když to mám nečistě bez interfaců (Entity extends ActiveRow s anotacemi)… A ještě jedna věc. Když v aplikaci používáš to rozhraní tak ti IDE nenapoví metody jako Users::findOneByEmail nebo Articles::search a ani bys je neměl používat protože to rozhraní tu metodu nemá.

EDIT: Ten generátor bych si představoval jako něco co by se dalo spouštět přes debug bar. Ale jak chceš, klidně ho napíšu. Jen nevim kdy, mám teď jiné priority.

Editoval enumag (12. 1. 2013 12:21)

jansfabik
Člen | 193
+
0
-

To mi napovídá i když to mám nečistě bez interfaců (Entity extends ActiveRow s anotacemi)…

Pak to ale není skoro žádný psaní navíc, jenom ty anotace budeš dávat do jiného souboru.

A ještě jedna věc. Když v aplikaci používáš to rozhraní tak ti IDE nenapoví metody jako Users::findOneByEmail nebo Articles::search a ani bys je neměl používat protože to rozhraní tu metodu nemá.

Asi jsi ty metody IArticleDao::search() a IUserDao::findOneByEmail() přehlédl, protože tam jsou. IDE mi je tedy bude napovídat.

Editoval jansfabik (12. 1. 2013 13:38)

enumag
Člen | 2118
+
0
-

Aha, pardon. Opravdu jsem je přehlédl. :-)

gliny
Člen | 25
+
0
-

Zdar a sílu,
předně díky za výbornou logiku celé téhle nádstavby, chtěl jsem použít entity a tohle mě mile překvapilo.

Co jsem dělal? Postupoval jsem podle návodu na gitu (readme) akorát jsem article a user zaměnil za invoice a item.

Proč tu otravuju? Bo jsem to nerozjel :(
print laděnky
config

Laděnka teda píše že posílám parametr špatného typu, což je ale hodně podivné.
Zkoušel jsem v configu nastavit reflexi tak i tak i bez nastaveni reflexe.

A zadruhé mě udivilo to že mě netbeans nabízí (červeným vykřičníkem) implementovat metody save a search, které mám uvedené v interface, ale v tom interface jsou ještě metody (findAll, find, ......) a ty mi nenutil

use Fabik\Database\ActiveRow,
    Fabik\Database\Table,
    Nette\Database\SqlLiteral;

class Invoices extends Table  implements IInvoiceDao
{
    /*
     * Table name
     */
    protected $name = 'faktury';

    public function save($invoice) {

    }

    public function search($query) {

    }
}

Jestli někdo víte kde je zakopaný pes tak budu vděčen za radu :)

PHP 5.4.4–12
Apache/2.2.22 (Debian)
Nette Framework 2.0.8 (revision b7f6732 released on 2013–01–01)

enumag
Člen | 2118
+
0
-

Je to (zřejmě) psané pro Nette 2.1-dev. U reflexe se měnily argumenty konstruktoru.

gliny
Člen | 25
+
0
-

Díky ti moc, bylo to samozřejmě tím.

Bohužel mám další bug a nebo fičuru?

v DB mam sloupec pojmenovany cisloFaktury (velke F)
a v template kdyz delam foreach a chci si vypsat $invoice->cisloFaktury tak mi to vyhodi

Cannot read an undeclared column "cislo_faktury"

tak nevim jestli je to bug nebo fičura že to převadi nazvy sloupcu, vlastně je blbost tomu říkat názvy sloupců, když by to defakto měly být třídní proměnné.

pokud je teda tohle

@property string $cisloFaktury

nějaký pseudo getter a setter, což jsem pochopil tak, že je a mám to nastaveno v interface.

//edit
Teď jsem si ještě vytvořil geter a setter v interface i třídě, ale když jej chci volat tak si nejsem jistý jak bych ho měl vlastně zavolat v template

$invoice->getCisloFaktury

takhle to asi nebude.
Tohle už je asi trochu OT, ale ta první část dotazu by ještě tak OT být nemusela, každopádně díky za případnou odpověď.

Editoval gliny (16. 3. 2013 11:07)

gliny
Člen | 25
+
0
-

tak jsem na to přišel, fabik implementoval metodu formatColumnName v souboru ActiveRow, což se mi teda zdá na nic, standardně to např nette database nedělá, ne?

Možná to má pro mě skrytý význam, ale aktuálně je mi to na nic, protože když jsem tvořil databázi tak jsem ji netvořil podle konvence db ale oop, a tak jsem nepoužíval _ ale velká písmena, a když mi to teď převede tak jsem samozřejmě v koncích. Takže z toho vyplývá, nedodržováním konvencí jsem si pěkně zase zavařil :D

enumag
Člen | 2118
+
0
-

Význam to má zřejmě ten že v mysql je docela problém mít názvy tabulek v camel-case, proto se používá podtržítková konvence. Aby to bylo jednotné tak se používá často i u sloupců. V PHP ale podtržítkovou konvenci nechceme takže proto ten převod. – Je to jen hypotéza, fabikovi do hlavy nevidim ;-)

EDIT: Ne NDB to standardně opravdu nedělá. Začíná se mi to ale líbit takže to do asi nějak napasuju do svého budoucího ORM.

Editoval enumag (16. 3. 2013 12:14)

gliny
Člen | 25
+
0
-

Díky, takhle jsem si to nakonec myslel.

Teď řeším další blbovinu, mám v databázi položku faktury s cenou s DPH a do entity bych si třeba chtěl vytvořit privatni proměnnou ve ktere by bylo DPH a částka bez DPH.

Takže mam interface pro položku a pak třídu, která to implementuje a myslel jsem že když do téhle třídy dopíšu get a set a privatni proměnne tak se na ně pak z presenteru dostanu, ale očividně asi ne :(

//edit: pomalu na to nakonec přicházím, takže tyhle věci začínají býti irelevantními.

Editoval gliny (16. 3. 2013 15:56)

kubco2
Člen | 9
+
0
-

Prosim Vas, pise mi

Call to undefined method Fabik\Database\ActiveRow::getProfileValues().

Zatial som nepouzival interafce, iba som popisal priamo entity

/**
 * @property int $id
 * @property string $nickOrMail
 * @property string $password
 * @property-read \Model\Entity\UserProfileValue[]|\Traversable $uProfileValues
 */
class User extends ActiveRow {

    /** @return \Model\Entity\UserProfileValue[]|\Traversable */
    public function getProfileValues() {
        return $this->related('uProfileValues', 'user_id');
    }

}
/**
 * @property int $id
 * @property int $parent_id
 * @property User $user
 * @property ValueDescription $valueDescription
 * @property int $position
 * @property string $value
 */
class UserProfileValue extends ActiveRow {

}

a volam

$this->template->userValues = $this->users->findByUsername("test")->getProfileValues();

find by username mi da usera … ale ako keby nebol obaleny tou entitou User, a preto nemal veci navyse, neviete co mi chyba? Postupoval som poda navodu.

edit: tak nejak som myslel, ze pojde tu entitu aj vytvorit prazdnu, ale pise, ze mu chyba nejaky argument…
Myslel som, ze tento doplnok bude odpoved na to, ako budem riesit este prazdne entity.

Doteraz som riesil iba read tak, ze som generoval strom z DB Row[] a v riadkoch som mal uz vsetky stlpce->hodnoty a cudzi kluc k typom ktore potrebujem ku generovaniu formulara, ked vsak chcem vygenerovat novy strom napriklad z predlohy iba identifikatorov, tak by som potreboval tiez vytvorit prazdnu entitu, do ktorej by som vlozil data o typoch, na zaklade nich mohol vygenerovat formularovy strom a po odoslani dosadil zvysok. Lubi sa mi, ze ked vytiahnem Row tak je to objekt, ktorym sa viem pekne pohybovat. Ale mrzi ma, ze je to iba jednosmerna cesta … DB->getRow->aplikacia … ale nie aplikacia->new Row->DB, cize to co spravim pre Rows z DB, aj tak potrebujem pri uplne novom inserte, bez ziadnych Rows riesit nejako inak.
Je teda jedina cesta urobit si entity sam? a tym padom vsetko mapovat alebo prejst rovno na doctrine.
Co poradite?

Editoval kubco2 (24. 3. 2013 20:09)