Vytvoření \Nette\Database\Context v předkovi
- bluray
- Člen | 178
Dobrý den,
koukám na tutoriál na devbooku a použil jsem třídu Base:
http://www.itnetwork.cz/…ework-modely
ta je potomkem mého modelu, problém je ale s tím že se mi neinicializuje
proměnná $database.
můj potomek vypadá nějak takto:
class Pokus extends Base{
public function __construct($neco, $neco1){
}
public function ulozDoDb(){
$this->database->query("INSERT INTO...");
}
}
v config.neon mám tohle:
base: App\Model\Base
Díky za radu
- qteck
- Člen | 164
Tak jde spíš o to, že když tam napíšeš novej kontruktor tak ten rodičovskej přepíšeš, takže by si měl volat parent:__construct().
Měl jsem s tím podobné obtíže jako ty a nakonec jsem to vyřešil tak, že jsem se tomu vyhnul a konstruktory v dceřiných (nebo jak se to nazývá) třídách nepíšu.
ale mě moc neber za slovo, začínám.
- Šaman
- Člen | 2666
Jak to, že by nemohl mít jiné parametry? V první řadě si ujasni,
jestli chceš třidu pokus vytvářet ručně operátorem new
(u služeb k tomu většinou není důvod), nebo používat jako službu v DI
kontejneru.
Pokud ručně, tak si ten DbContext injectni do presenteru (nebo kde ji budeš
vytvářet) a každému potomkovi ho předáš.
Pokud jako skužbu, kterou si budeš vytvářet v DI kontejneru, tak
samozřejmě můžeš v potomcích parametry přidávat.
<?php
class Base
{
protected $db;
public function __construct(NS\Context $db)
{
$this->db = $db;
}
}
class FooBar extends Base
{
public function __construct(NS\Context $db, NS\JinaSluzba $jinaSluzba)
{
parent::__construct($db);
…
}
}
?>
Editoval Šaman (1. 6. 2014 19:31)
- bluray
- Člen | 178
No tuhle třídu budu vytvářet pomocí faktory v jiné třídě. ale to
první řešení v presenteru je rozumné, už tomu pomalu začínám rozumět.
Ale moc nerozumím tomu s DI kontejnerem, jak to bude fungovat? můžeš mi to
prosím trochu rozepsat? v potomcích nechci předávat jiné služby, ale
i nějaké proměnné.
Já se omlouvám, možná jsem trochu natvrdlý ale je to pro mě nové a
přijde mi že je to dost komplikovaný. Navíc to člověku dost svazuje ruce
s nějakým objektovým návrhem, s návrhovými vzory, když musím
předávat třídy frameworku…
Nevidím v tom žádnou výhodu.
- Šaman
- Člen | 2666
Jestli ji budeš vytvářet ručně, tak té tovární třídě musíš
injectovat Context a pak už si ho předávat.
Nikdo tě nenutí vytvářet služby v kontejneru, ale ten pomáhá právě
s tím předáváním závislostí.
Samozřejmě, že pokud vytváříš nějaké instance které nejsou služby, tak to neděláš přes kontejner, ale služba bude ta tovární třída.
- Oli
- Člen | 1215
bluray: On ten OOP přístup má tu výhodu, že by to mělo „přiblížit“ programování reálný situaci. Je to jinej pohled na problém. Ty návrhový vzory nemusíš znát. Pravdou ale je, že návrhový vzor je best practice, neco co se pro daný problém ustálilo jako optimální řešení, proto, pokud to myslíš vážně se jim asi stejně nevyhneš. Alespoň nějakých 5 vzorů budeš postupně využívat ;-)
Jednu z největších praktických výhod vidím v oddělení zodpovědností a závislostí. Například, když máš vypsat data uživatele, tak víš, že se o to stará třída UserManager. Ta třída má závislost (kterou zpravidla najdeš v konstruktoru) např na UserRepository, která tahá ty data z databáze. Jsou to oddělené zodpovědnosti a pokud máš takových objektů (jako je User) třeba 50, tak to potom hodně zpřehlední.
Chci pracovat s komentářema pod článkem, otevřu si třídu CommentsManager, která se o to stará. Potřebuju dostat komentáře z databáze: vlezu do CommentsRepository, protože vím, že to je můj „přístupový bod“ do databáze…
Doufám, že nepopisuju kolo, ale podle tvého příspěvku mě přišlo, že právě tohle ti není uplně jasné. Jestli jsem to blbě pochopil, tak se omlouvám za OT :-)
- Džůny
- Člen | 19
Uf, no že to dalo práce, najít téma, ve kterém se řeší můj problém (místo zakládání nového). :) Tušil jsem, že tohle už někdo prostě řešit musel. Takže, jen abych se ujistil, vážně není jiná možnost, než předávat Context i v konstruktorech potomků? Můj problém ve zkratce:
Mám v modelu abstraktní třídu Base, která obsahuje klasické získání závislosti přes konstruktor (DB):
namespace App\Model;
use Nette;
abstract class Base extends Nette\Object
{
/** @var Nette\Database\Context */
public $database;
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
…
A nějaké obecné metody (selectById(), selectAll(), update(), delete() a tak dále). Od této třídy odvozuji další součásti (třídy) patřící do modelu. Co když chci v potomkovi další závislosti? Musím to řešit nějak takhle:
public function __construct(Nette\Database\Context $database, Cities $cities, Nette\Http\Request $httpRequest)
{
parent::__construct($database);
$this->cities = $cities;
$this->httpRequest = $httpRequest;
}
Zkrátka, musím znovu předávat Context. Připadá mi to takové hloupé. Jak už psal někdo nahoře: jedním z důvodů proč nějakou třídu Base mám, bylo právě to, abych nemusel v každé nové třídě modelu znovu do konstruktoru vypisovat Context. Ale co já vím, jak by řekl Homer: Jsem tady první den. :)
A na závěr otázka, proč mi v modelu nefunguje
@inject
? Co jsem kde přehlídnul?
Díky všem za odpovědi a čas.
Edit: A teď mi ještě došlo, že i tak mám smůlu, pokud nechám Base jako abstraktní, protože bez instance se konstruktor volá těžko, žejo. :D
Editoval Džůny (10. 5. 2015 14:52)
- dyamon
- Člen | 11
Polovina tvojí otázky, ale vůbec nesouvisí s nette. Přepisování konstruktoru je věcí objektového návrhu PHP a je to logické. Při vytváření potomka voláš konstruktor potomka, takže pokud chceš předat závislosti i jeho bázové třídě (skrz konstruktor) musí je vyžadovat i jeho vlastní konstruktor.
Co se týká anotace "@inject" tak mám dojem, že je její funkčnost omezená pouze na presentery… rozhodně by se nikde jinde používat neměla.
- David Matějka
- Moderator | 6445
Jak pise @dyamon, jen doplnim, ze to inject muzes zapnout i jinde prepsanim:
services:
- MyModel
na
services:
-
class: MyModel
inject true
#respektive zkracene na
- {class: MyModel, inject: true}
Od 2.3 pak toto muzes zapnout treba pro vsechny potomky Base modelu:
decorator:
App\Model\Base:
inject: true
Ale pouzivani inject properties neni mimo presentery doporucovano
- Šaman
- Člen | 2666
Nedoporučuji zapínat inject, ale můžeš si rovnou do všech potomků
nastavit databázi, třeba pomocí setteru, takže ti konstruktor zůstane
volný. Ukázku máš tady.
P.S. Jen pro pořádek připomínám, že se to provede u všech
potomků, které vytváříš v DI kontejneru (tedy jsou zapsané
v configu).
Editoval Šaman (11. 5. 2015 16:44)
- Šaman
- Člen | 2666
Naopak, jen se tomu často neříká model (tím je celá část aplikace), ale třeba Repository
- Šaman
- Člen | 2666
Nikoliv, neříká. To taky nebylo v zadání a tenhle projekt ani žádné objektové entity nezná. Je to jen ukázka primitivního repozitáře nad NDbT.
Jestli chceš používat ORM, tak tak tam už nějaké base třídy máš, ne? Tam už přece nepředáváš ručně databázi každé entitě, nebo repository/DAO.
- Džůny
- Člen | 19
@dyamon To je mi jasné. Chtěl jsem se dozvědět, jestli není možnost hodit tuhle práci na Nette (DI Container) a nějak to zautomatizovat (jinak než pomocí @inject, když to není doporučováno a defaultně povoleno).
@DavidMatějka, @Šaman díky za informace a nabídnutí možností.
Ještě se teda zeptám, proč @inject není dobré používat mimo presentery? Proč je to u BasePresenter(u) OK a v modelu ne?
@Azathoth Nejsi sám, ono se to tady dost řeší, co jsem tak zběžně koukal. Ale viděl jsem to v několika projektech takhle, hádám, že je to zkrátka nejsnazší a nejjednodušší. Když se nad tím člověk zamyslí (a nad alternativama), tak mi to připadá prašť jak uhoď…
- Šaman
- Člen | 2666
Proč nepoužívat inject mimo presentery?
- krátká verze: protože funguje jen v presenterech (lze obejít, ale v základu tomu tak je)
- a proč je to povolené jen v presenterech je dlouhá verze. (Už je to staré vlákno, ale princip je stejný. Dnes je další důvod ten, že anotace fungují jen na public property, tedy porušují zapouzdření.)