přechod z Nette 3.2 na 4.0 problém s inicializací dabataze
- iru
- Člen | 113
Pokouším se převést aplikaci z Nette 3.2 do 4.0, ale nemůžu se dostat
přes inicializaci databáze. Hlásí mi to chybu:
Typed property App\Presenters\BasePresenter::$database must not be accessed
before initialization
V BasePresenter to mám takto:
public function __construct(private Nette\Database\Explorer $database, private MenuModel $model)
{
$this->database=$database;
}
Nevím kde hledat problém.
Původně jsem měla BasePresenter takto:
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
public $database;
/**
@var \App\Model\MenuModel @inject*/
public $model;
/** @persistent */
public $locale;
public function __construct(Nette\Database\Context $database)
{
$this->database=$database;
}
Ale v Nette4 mi to hlásilo chybu v načítání modelu, tak jsem to opravila viz výše. Ostatní presentery dědí z tohoto BasePresenteru. Další dotaz je, jak to mám řešit v dalších presenterech, když potřebuji načítat jiný model. Třeba v ClankyPresenter to mám takto:
final class ClankyPresenter extends BasePresenter
{
/** @persistent */
public $url;
public $ImagesDir_2='images/foto';
public function __construct(private MenuModel $model, private GalerieModel $modelGaleries, private GalerieFotoModel $modelFoto )
{
}
Prosím o radu a pokud možno vysvětlení, abych to pochopila také proč. Děkuji moc.
- nightfish
- Člen | 519
@iru
Na první pohled:
- Pokud chceš
$database
z BasePresenteru i v jeho potomcích, udělej$database
alespoň protected. $this->database=$database;
můžeš odstranit, protože používáš constructor property promotion, takže se přiřazení už provedlo automaticky
Dále je pak otázka, ve kterém presenteru ti PHP uvedenou chybu
hlásí – předpokládal bych, že je to v některém z potomků
BasePresenteru , protože v potomkovi máš uvedeno
public $database;
nebo protected $database;
a
nestaráš se o naplnění té property (a zároveň není vidět
$database
z BasePresenteru, protože jsi v bodu 1 použila
viditelnost private
).
Poznámka k předávání závislostí do BasePresenteru: závislosti,
které v BasePresenteru předáváš přes konstruktor, pak musíš vyžadovat
i ve všech poděděných presenterech, takže se často všechny závislosti
BasePresenteru předávají přes atribut #[Inject]
nebo metodou
inject*()
– viz dokumentaci.
Stranou nyní nechávám otázku toho, jak moc preferované je používat
univerzální předky (BasePresenter
,
BaseModel
), protože je to na
delší diskusi.
A ještě nesouvisející podotek ohledně struktury aplikace na závěr: Do
svých presenterů si nikdy nepředávám jako závislost databázové spojení,
protože práce s databází není zodpovědností presenteru. Místo toho mám
databázový přístup obalený do služby, která logiku čtení/zpracování
dat zapouzdřuje a teprve tuto službu používám v presenteru.
Pravděpodobně něco podobného jako tvůj
App\Model\MenuModel
.
- iru
- Člen | 113
nightfish napsal(a):
@iru
Na první pohled:
- Pokud chceš
$database
z BasePresenteru i v jeho potomcích, udělej$database
alespoň protected.$this->database=$database;
můžeš odstranit, protože používáš constructor property promotion, takže se přiřazení už provedlo automatickyDále je pak otázka, ve kterém presenteru ti PHP uvedenou chybu hlásí – předpokládal bych, že je to v některém z potomků BasePresenteru , protože v potomkovi máš uvedeno
public $database;
neboprotected $database;
a nestaráš se o naplnění té property (a zároveň není vidět$database
z BasePresenteru, protože jsi v bodu 1 použila viditelnostprivate
).Poznámka k předávání závislostí do BasePresenteru: závislosti, které v BasePresenteru předáváš přes konstruktor, pak musíš vyžadovat i ve všech poděděných presenterech, takže se často všechny závislosti BasePresenteru předávají přes atribut
#[Inject]
nebo metodouinject*()
– viz dokumentaci. Stranou nyní nechávám otázku toho, jak moc preferované je používat univerzální předky (BasePresenter
,BaseModel
), protože je to na delší diskusi.A ještě nesouvisející podotek ohledně struktury aplikace na závěr: Do svých presenterů si nikdy nepředávám jako závislost databázové spojení, protože práce s databází není zodpovědností presenteru. Místo toho mám databázový přístup obalený do služby, která logiku čtení/zpracování dat zapouzdřuje a teprve tuto službu používám v presenteru. Pravděpodobně něco podobného jako tvůj
App\Model\MenuModel
.
Díky za přehledné vysvětlení. Takže v BasePresenter jsem to udělala takto. Změnila $databaze na protected a zároveň další závislosti předávám pomocí #[Inject]:
#[Inject]
public MenuModel $model;
public function __construct(protected Nette\Database\Explorer $database)
{
}
Tady se jedná o převod starší aplikace, takže toho nechci zase tolik
předělávat, ale pro nový projekt zvážím zapouzdření databázových
věcí přes model.
Ty univerzální předky BasePresenter používám kvůli navigaci a dalším
věcem, které potřebuji ve všech presenterech, přijde mi to praktičtější
mít na jednom místě. Jak to řešit lépe?
- m.brecher
- Generous Backer | 873
@iru
V zásadě je odpověď na Tvoje otázky jednoduchá a vše bylo již řečeno výše, posílám to jenom shrnuté do jednoduchého kopyta, které sám používám a všem ho doporučuji
hierarchie presenterů
Používat BasePresenter? Určitě to má smysl, ale pouze pro věci, které se používají napříč celým webem. BasePresenter by měl být takový poloprázdný, jenom pár věcí co jsou úplně všude. Pokud aplikace má veřejnou část a administraci, tak se vyplatí mít dvě úrovně abstraktních presenterů:
abstract BasePresenter
abstract FrontPresenter
final HomepagePresenter
.......
abstract AdminPresenter
final AdminEntryPresenter
final ArticlePresenetr
.....
Tuto hierarchii lze velmi vhodně doplnit použiutím trait PHP, které se ideálně hodí na vkládání věcí, co jsou v několika presenterech paralelně, ale ne ve všech. Traitami lze šikovně zpřehlednit i jeden jediný příliš rozsáhlý presenter.
předání služeb v presenterech
Předání služeb (závislostí) do presenterů, je v PHP 8.0 a Nette 4.0 velmi jednoduché:
a) abstraktní presentery
use Nette\DI\Attributes\Inject; // toto se nesmí zapomenout!
abstract class BasePresenter extends Presenter
{
#[Inject]
public MenuModel $menuModel; // musí být public, nesmí být readonly !!
// zde NESMÍ být konstruktor !!!
}
b) finální presentery
nová syntaxe >= PHP 8.0:
final class ArticlePresenter extends AdminPresenter
{
// #[Inject] zde nepoužívat
public function __construct(private ArticleModel $articleModel, ....) // používat konstruktor
{}
}
stará syntaxe pro < PHP 8.0, funguje stále, ale používej tu jednodušší novou:
final class ArticlePresenter extends AdminPresenter
{
private ArticleModel $articleModel;
public function __construct(ArticleModel $articleModel, ....) // používat předání přes konstruktor
{
$this->articleModel = $articleModel;
}
}
předání služeb v modelových třídách
V modelových třídách se používá předání konstruktorem – stejné jako u finálních presenterů
registrace služeb
předávané objekty do presenteru/modelu jsou buďto vestavěné služby Nette Frameworku (Explorer, User, …), nebo Tvoje vlastní objekty – ArticleModel apod…, které musíš registrovat jako služby v konfiguraci ***.neon:
a) jednotlivě jako anonymní služby:
services:
- App\Model\ArticleModel
- App\Model\PhotoGalleryModel
......
Lepší způsob je pomocí search registrovat hromadně určitý typ tříd, nemusíš potom ručně dokola editovat konfiguraci:
search:
default:
in: %appDir%
classes:
- *Model
Tento search Ti zaregistruje úplně všechny třídy končící na ***Model jako služby.
konvence názvosloví
pro modelové třídy se často používají názvy jako ArticleFacade, nebo ArticleRepository. Když ale v Nette používám databázovou knihovnu Explorer a presentery jsou uloženy v /Presenters/ArticlePreesenter.php je logické ukládat modelové třídy do /Model/ArticleModel.php – děláš to správně ;).
Aby se Ti mozek zbytečně nezatěžoval, používej:
a) názvy tabulek v databázi, názvy presenterů, modelových tříd a
všech dalších tříd – pouze anglicky
místo ClankyModel/ClankyPresenter – > ArticleModel/ArticlePresenter
mixování českých a anglických slov znepřehledňuje kód
b) pro property stejný název jako je v názvu třídy – kód je pak přehlednější, dle Nette Coding Standard nejprve specifické, pak obecné tj. ArticlePresenter, nikoliv PresenterArticle, nebo ArticleModel nikoliv ModelArticle
final class ArticlePresenter extends AdminPresenter
{
public function __construct(
private ArticleModel $articleModel,
private GalleryModel $galleryModel,
private GelleryPhotoModel $galleryPhotoModel,
)
{}
}
Dalo by se pokračovat, ale nechci toto téma zahlcovat. Dobře navržené postupy a konvence ušetří obrovské množství času a námahy.