Public připojení k databázi pro všechny presentery

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

Zdravím, všechny moje presentery potřebují připojení k databázi. V BasePresenteru jsem teda napsal

namespace App\Presenters;

abstract class BasePresenter extends Nette\Application\UI\Presenter {

    /** @var Nette\Database\Context Pristup k databazi*/
    protected $repository; // nebo: public $repository;

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

v podřízeném presenteru můžu kdekoliv zavolat $this->repository->table('tabulka'):

namespace App\Presenters;
use Nette;

class HomepagePresenter extends BasePresenter {

public function renderDetail($id) {
            $product = $this->repository->table('products')->get($id);
            $this->template->p = $product;
}

} // class

A pokud nepotřebuju nic složitějšího, nepotřebuju modelovou třídu ani nic zapisovat do configu, navíc mi to dává jakýsi přehled o tom co přesně se děje, protože se musí uvádět table().

Je v pořádku, že připojení k databázi je v PUBLIC (nebo protected) proměnné? Čtením návodů v dokumentaci a na fóru jsem nějak pochopil, že se tak porušuje pravidlo zapouzdření a že teoreticky nevím kde se $this->repository vzalo. Je to ale zcela funkční a pohodlné – prostě mám databázi kdekoliv, kde dědím od BasePresenter. Mohlo by to představovat nějaký skutečný problém?

EDIT: public proměnná změněna na protected.

Editoval ludek (2. 9. 2016 8:32)

Oli
Člen | 1215
+
+1
-

Mít to v public proměnné není téměř nikdy úplně správně. To je důvod, proč používám kdyby/autowired.

Každopádně větší problém vidím v tom, že by jsi předával repository přímo do presenteru. Jde o to, že to potom bude velice neznovupoužitelný. Mnohem lepší je, když si uděláš třídu například ProductsRepository a v ní budeš pracovat s tabulkou products. Jde o to, že potom, když budeš dělat další projekt, tak můžeš mít v obou projektech třeba články. Tak jen vezmeš už existující třídu ArticlesRepository a máš napsaný propojení s databází i s vyzkoušenýma/otestovanýma dotazama.

Další krok potom je, dávat i logiku mimo presentery, do komponent. Kdy máš presenter, kterej si jako jedinou závislost vytáhne ArticleComponent. ArticleComponent si vytáhne ArticlesRepository, vytahá z databáze co potřebuje a zobrazí to. Potom můžeš celej balík s modelem, komponentou + další související věci vzít a použít dohromady v jiným projektu jen tak, že si v presenteru zavoláš protected function createComponentArticle(){...} a upravíš šablonu aby zapadala do designu.

jiri.pudil
Nette Blogger | 1032
+
+2
-

Jelikož používáš inject metodu, ne @inject anotaci, můžeš tu property mít klidně protected, a kdybys to chtěl mít echt zapouzdřené, tak private a v potomcích k ní přistupovat přes getter.

Jan Suchánek
Člen | 404
+
0
-

@jiri.pudil: mě se líbí jak je tyhle vlastnosti a metody pěkně schovat do trait viz. Nebo je to špatně?

jiri.pudil
Nette Blogger | 1032
+
+1
-

@jenicek já sice všechno s výjimkou továren na komponenty předávám v konstruktoru, takže traitou bych si práci spíš přidělal, ale na injecty mi to přijde ok.

Jan Suchánek
Člen | 404
+
0
-

@jiri.pudil: jj je pak líp vidět závislost.

ludek
Člen | 83
+
0
-

Díky za odpovědi. Prozatím jsem nastavil na protected a ještě to uvážím.
Znovupoužitelné to právě je. Zvlášť pro jednoduché dotazování, kde prostě vím, že v `$this->repository" je přístup k databázi. Naopak se mi zdá velmi složité psát modelovou třídu pro každou tabulku, když stejně všechny dělají v podstatě totéž.

Šaman
Člen | 2666
+
+2
-

Když všechny dělají vpodstatě totéž, tak nemusíš nic psát, jen podědit. Ale ve chvíli, kdy při práci s články chceš všechny, které mají nadprůměrné hodnocení a při práci s uživateli chceš často jen aktivované, tak to jsou přesně metody, které patří do jednotlivých repozitářů.
Úplně nejjednodušší abstraktní repozitář pro mé potřeby je tady, v BookRepozitory je i jedna specifická metoda. Samozřejmě to lze hodně vylepšit, tohle je minimalistický příklad bez magie a anotací pro školní potřeby.

Jinak traity už nepoužívám, v abstraktních presenterech injectuji nad public property, v konkrétních chci používat konstruktor (ale taky používám anotaci, protože jsem líný). Proti kdyby/autowired mám osobní námitku, že je to magie a zbytečná závislost navíc.

Dnes, s decoratorem bych za akademicky nejčistější považoval úplné vypnutí podpory inject metod a anotace a používat všude konstruktor, jen u abstraktních tříd setter injection kterou bych explicitně nastavil v configu. Ale je to hodně psaní navíc. A úplně čisté to taky není, protože se i povinné závislosti budou předávat pomocí setteru, tedy nám nic nezaručí, že se opravdu předají. Stále zůstává jediné čisté řešení předávání všech povinných závislostí konstruktorem, ale odměnou takovému puntičkáři budiž constructor hell :)

Takže jestli anotaci, inject metody, kdyby, nebo decorator, to závisí jen na osobních preferencích, jaký poměr pracnost/čistota chcete mít. Všechno je to kompromis.