Kde pracovat s db model nebo presenter?

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

Zdravím,

Jsem úplný začatečník v Nette, potažmo i OOP, sice vím nějaké základy, ale nějak extra zběhlý v tom nejsem.

Kam by se správně měla umistňovat práce s DB?

  • Do presenteru?
    • Možná mě zmatl Quickstart, ale tam se všude píšou dotazy na DB do presenteru, je to optimální cesta?
  • Do modelu?
    • Podle MVC, co jsem se dočetl tak práce s DB by měla patřit do modelu, ne?

Děkuji za každé nakopnutí správným směrem :)

F.Vesely
Člen | 369
+
+7
-

Do modelu, v Quickstartu je to jen pro zjednoduseni a asi by to tam byt nemelo, pak to lidi mate. :)

vosik199
Člen | 6
+
0
-

Aha, super díky :)

Je tedy správná cesta řešení toto?

app/model/Sluzby.php

namespace App\Model;
use Nette;
class Sluzby extends Nette\Object{

    /** @var Nette\Database\Context */
    private $database;

    public function __construct(Nette\Database\Context $database) {
        $this->database = $database;
    }

    function selectTable() {
        return $this->database->table('sluzby');
    }
    public function getAll() {
        return $this->selectTable()->fetchAll();
    }
}

app/presenters/HomepagePresenter.php

namespace App\Presenters;

use Nette;
use App\Model;

class HomepagePresenter extends BasePresenter {
    /**
     * @inject
     * @var \App\Model\Objednavky
     */
    public $objednavky;

    public function renderDefault() {
        $this->template->objednavky = $this->objednavky->getAll();
    }

}

Díky za odpověď.

CZechBoY
Člen | 3608
+
0
-

Krom toho, že tam máš jinou třídu tak jo.
Lepší je udělat BaseModel, který ti zařídí nějakou základní funkčnost – vyžádání DbContextu přes konstruktor, findAll, getTable, findById, … abys to nemusel psát v každým modelu ručně znova a znova.

chemix
Nette Core | 1310
+
+2
-

@vosik199 Ahoj, smer mas dobry. Psal jsem na toto tema clanek do dokumentace, ale jeste neni final … ocenil bych, kdyby jsis ho precetl a pripadne rekl cemu tam neni rozumet :) https://github.com/…ll/271/files

diky

vosik199
Člen | 6
+
0
-

@CZechBoY jasně já jsem vzal špatnou část kódu :D

@chemix já bych řekl že je lehce srozumitelný, osobně bych uvítal v dokumentaci nějaké rozsáhlejší povídání o modelové vrstvě :)

Takže bych měl udělat něco jako BaseModel, v něm mít vytvořené metody které mám stejné v ostatních modelech, tam potom podědit z BaseModel a upravit si tam metodu např. na výběr správné tabulky?

Díky všem za odpovědi moc si toho vážím.

CZechBoY
Člen | 3608
+
0
-

@vosik199 V dokumentaci k Nette asi není potřeba opisovat věci, který najdeš jinde (jak navrhovat třídy, co je model a k čemu slouží, …).
Já mám třeba v každým konkrétním modelu proměnnou, která udává název tabulky, z který se vytahuje při těch generickým metodách (findAll, findByPrimary, getTable).
Lepší by asi bylo udělat interface na modely s metodou, která by jen vracela název té tabulky.

interface IRepository
{
	/**
	 * @return string
	 */
	function getTableName();
}

class Sluzby extends BaseRpository implements IRepository
{
	/**
	 * @return string
	 * @internal
	 */
	public function getTableName ()
	{
		return 'sluzba';
	}
}

class BaseRepository extends Nette\Object
{
	/**
	 * @var Nette\Database\Context
	 */
	protected $db;

	/**
	 * @param Nette\Database\Context $db
	 */
	public function __construct (Nette\Database\Context $db)
	{
		$this->db = $db;
	}

	/**
	 * @return Nette\Database\Table\Selection
	 */
	protected function getTable ()
	{
		$this->getTable($this->getTableName());
	}
}

edit: přejmenováno na repozitář

Editoval CZechBoY (12. 12. 2015 22:13)

vosik199
Člen | 6
+
0
-

Super díky, moc jste mi pomohli :)

Šaman
Člen | 2665
+
+5
-

Není dobré pojmenovat ty třídy Model. Model je většinou jen jeden a je to celá logika aplikace. Pokud by bylo více modelů, bylo by to třeba model pro sklad, model pro nákupy apod. (Zjednodušeně se dá za model považovat celá logika nad jednou databází, ale kromě databáze i veškeré další rutiny.) A ty by spolu komunikovaly jen pomocí definovanych rozhraní. Navíc model dělá spoustu jiných věcí, než práci s databází.

Tomu, co popisuje @CZechBoY, se většinou říká Repository, nebo DAO. Případně obecně Service.

iNviNho
Člen | 352
+
+2
-

Ja len pripomeniem, že by každá trieda (ak je to možné) mala robiť ibá tú vec, ktorú vo svojom názve, alebo vo svojej podstate predstavuje.

  • Takže ak si nazvem trieda ArticleRepository tak táto trieda ťahá/ukladá/vkladá/maže iba dáta ohľadom article z DB a nemá žiadnu logiku.
  • Ak mám triedu ArticleService tak táto trieda dokáže vyťiahnúť dáta ohľadom article z ArticleRepository a spraví nád nou nejakú logiku.
  • Ak mám ArticlePresenter tak v nom napríklad zobrazujem Article, alebo teda Articles … čiže požiadam ArticleService o nejakú „službu“ alebo nech mi dodá to čo potrebujem
  • Ak používam nejaké ORM ako doctrine 2/yetorm, tak by mi ešte pripudlo ArticleEntity, ktorá reprezentuje práve jednu entitu z Article tabuľky (entita=riadok).

Bol som na jednom pive, ale aspoň trocha prispejem do debaty :)

Editoval iNviNho (13. 12. 2015 1:44)

suwer
Člen | 33
+
0
-

Casto vidim, ze se Repository pouzivaji pouze pro jednu konkretni db tabulku. Jenze i v pripade nejake tabulky article urcite budu mit dalsi tabulky s autory, tagy apod. A casto se stane, ze potrebuju vytahnout vetsinu souvisejicich informaci.

  • Nacist si nejprve pres ArticleRepository data clanku a pak pres AuthorRepository a TagRepository postupne dotahovat zbytek mi prijde jako spousta zbytecne rezie navic. Nemluve o vykonu. Mit tisice/miliony zaznamu, tak to sakra pocitim.
  • Mnohem logictejsi a vyrazne rychlejsi je poslat do db kompletni query, nechat ji zpracovat a zpatky dostat kompletni data. No a porad mi dava smysl, ze prave tohle resi ArticleRepository.
  • Horsi je, ze nekdy potrebuju vytahnout nazev clanku, perex a tagy. Jindy potrebuju jeste obsah a autora. To uz se mi ArticleRepository zacina rozrustat o X vyberovych metod, nebo tam musi byt nejaky konfigurovatelny datasource, ktery pouzije Service/Facade nad Repository.

Mam to v hlave poskladane spravne, nebo opravdu pouzivate jeden Repository prave pro jednu tabulku a nejakou rezii nad tim?

Editoval suwer (2. 1. 2016 15:14)

Šaman
Člen | 2665
+
+1
-

Repository by měl být pro jednu entitu. Ta může být rozlezlá po více tabulkách. (Nebo dokonce více entit v jedné tabulce a pak bude několi repozitářů pracovat nad stejnou tabulkou).

V ORMech a Nette\Database\Table se práce s vazbami té entity řeší na úrovni traverzování mezi entitami, což extrémně zjednoduší ten repozitář (vytáhnu si autora a pak už si jeho vazby tahám přes něho, nikoliv přes repozitář – třeba $author->books).

Ale obecně máš pravdu. Ovšem narazíš při tom na spoustu problémů, které řeší ORMy. Třeba to, že tvůj article by měl obsahovat property tags, což je kolekce entit, které ale každá zase obsahuje property articles. Kruhová závislost. A další věc je, že načítání tagů řeší TagRepository a ten ArticleRepository, když potřebuje tagy pro zkompletování article, by si měl říct tomu TagRepository a nenačítat je svépomocí.

Dodatek: A co se týče toho načítání jen části informací – v akademicky čistém OOP bys to neměl nikdy dělat. Entita má být konzistentní a ne že jednou bude obsahovat perex a podruhé tam bude NULL. Pokud tohle budeš opravdu potřebovat kvůli optimalizaci, tak musíš obětovat kus čistoty na oltář výkonu. Ale taky musíš předem vědět, že to opravdu chceš a že se to vyplatí. Druhá možnost u extrémně velkých a málokdy používaných properties (třeba životopis u autora) je vytvořit vazbu 1:1 a v případě potřeby si ho donačíst (třeba NDbT to dělá lazy, takže pokud si vytáhneš spoustu autorů jen třeba kvůli abecednímu výpisu, tak se ti všechny ty životopisy nenačtou a nepřenáší.)

Editoval Šaman (2. 1. 2016 15:46)

suwer
Člen | 33
+
0
-

Šaman napsal(a):

Repository by měl být pro jednu entitu. Ta může být rozlezlá po více tabulkách. (Nebo dokonce více entit v jedné tabulce a pak bude několi repozitářů pracovat nad stejnou tabulkou).

To je asi to hlavni. Uvedomit si, ze entita nerovna se jeden radek v jedne tabulce, ale odrazi realitu. Tedy objekt, ktery opravdu potrebuju k praci. Takhle mam pokrytou vetsinu pripadu. Zbytek uz jsou velmi, velmi specificke problemy.