Asi nechápu flow, basePresenter vs. other Presenters
- MW
- Člen | 626
Zdravím a prosím o pomoc.
Presenter pro načítání dat „nastavení“ kde data v pohodě načtu (SettingsModel $settingsModel):
Final class SettingsPresenter extends BasePresenter {
/** @persistent */
public $backlink = '';
private Forms\SettingsFormFactory **$settingsFactory**;
private $settingsModel;
public function __construct(Forms\SettingsFormFactory $settingsFormFactory, SettingsModel $settingsModel) {
$this->settingsFactory = $settingsFormFactory;
$this->settingsModel = $settingsModel;
}
public function renderDefault(): void {
$this->template->title = "Nastavení";
}
/**
* Settings form factory.
*/
public function createComponentSettingsForm(): Form {
$form = $this->settingsFactory->create();
$form->setDefaults($this->settingsModel->getSettingsTable()->fetch());
$form->onSuccess[] = function ($form) {
$this->settingsModel->update($form->values);
$this->flashMessage("Nastavení uloženo.");
$this->redirect('Settings:');
};
return $form;
}
}php
rád bych toto delegoval do BasePresenteru, kde jsem se o to pokusil:
/**
* Base presenter for all application presenters.
*/
abstract class BasePresenter extends Nette\Application\UI\Presenter {
private $settingsModel;
private $settings;
public function __construct(SettingsModel $settingsModel) {
$this->settingsModel = $settingsModel;
$this->settings = $settingsModel->getSettingsTable()->fetch();
}
protected function startup() {
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->flashMessage('Přihlašte se prosím');
$this->redirect('Sign:in');
}
If (($this->getUser()->isLoggedIn()) AND (!$this->getUser()->isInRole('admin') AND !$this->getUser()->isInRole('user') AND !$this->getUser()->isInRole('person'))) {
$this->flashMessage("Váš účet ještě nebyl aktivován", 'danger');
$this->redirect('Sign:in');
}
}
}
Problém je, že v Base presenteru mám pořád NULL.. a to už od „settingsModel“ a to nechápu proč..
Netuší prosím někdo, co bych mohl dělat blbě?
Editoval MW (2. 7. 23:24)
- Marek Bartoš
- Nette Blogger | 1260
Přetěžuješ konstruktor, ale konstruktor rodiče v tom přetíženém nevoláš.
Editoval Marek Bartoš (2. 7. 23:34)
- m.brecher
- Generous Backer | 863
@MW
abstract class BasePresenter extends Nette\Application\UI\Presenter {
protected $settingsModel;
protected $settings;
public function injectSettingsModel(SettingsModel $settingsModel)
{
$this->settingsModel = $settingsModel;
}
protected function startup(): void
{
parent::startup();
$this->settings = $this->settingsModel->getSettingsTable()->fetch();
// .......
}
}
- v abstraktních presenterech ze kterých dědí finální presentery nikdy nepoužívej konstruktor, místo něj předávej služby metodou inject<*>()
- naopak ve finálních presenterech nepoužívej inject<*>(), ale konstruktor, tím předejdeš problémům jak píše @MarekBartoš
- v metodě injectSettingsModel() bych pouze předal službu a dotaz do databáze bych dal raději do metody startup() – je to přehlednější (ale ne nutné)
- property $settings i $settingsModel se budou používat ve finálních presenterech a musí být tedy protected
- Marek Bartoš
- Nette Blogger | 1260
Jak nepomohlo? Pokaždé když přetížíš metodu, tak musíš zavolat implementaci z rodiče, jinak se nevykonaná. Volání parent::__construct() v SettingsPresenter zavolá konstruktor z BasePresenter a v BasePresenter volání parent::__construct() zavolá konstruktor z Nette\Application\UI\Presenter
- Šaman
- Člen | 2658
Musel bys to volat takhle. Potomek zavolá metodu předka se všemi parametry, které předek potřebuje. A to je přesně důvod, proč to dělat nechceš (googli constructor hell).
abstract class BasePresenter extends Nette\Application\UI\Presenter {
private $settingsModel;
private $settings;
public function __construct(SettingsModel $settingsModel) {
$this->settingsModel = $settingsModel;
$this->settings = $settingsModel->getSettingsTable()->fetch();
}
}
final class SettingsPresenter extends BasePresenter {
public function __construct(SettingsModel $settingsModel, Forms\SettingsFormFactory $settingsFormFactory) {
parent::__construct($settingsModel);
$this->settingsFactory = $settingsFormFactory;
}
}
Editoval Šaman (3. 7. 19:40)
- m.brecher
- Generous Backer | 863
@MW
Chápu tedy, že inject*<> zajistí předání všem potomkům. Je to tak?
Metodu inject<*>() spouští Nette Framework na objektech, které jsou „služby“ a to takto:
- v presenterech automaticky
- v jiných třídách automaticky ne a je potřeba režim inject zapnout v konfiguraci
- metoda inject<*>() musí být public
Nette Framework předá do parametrů uvedených v metodě inject<*>() služby podle typehintu, nic víc nedělá – tzv. injektování.
Pokud chceš mít předanou službu k dispozici v jiných metodách presenteru, musíš sám ručně injektovanou službu uložit do property. Potom je k dispozici ve všech potomcích abstraktního presenteru, včetně abstraktního presenteru.
- Marek Bartoš
- Nette Blogger | 1260
Mimo presentery není dobré inject zapínat. Jen tím pak skrýváš, že má třída hodně závislostí a dělá toho moc
- m.brecher
- Generous Backer | 863
@MW
konfigurace služeb DI containeru v souboru services.neon:
decorator:
MyForms\FormControl:
inject: true
Používám pro všechny formuláře abstraktního předka FormControl, ze kterého dědí finální formuláře třeba UserForm. Formuláře mám udělané jako potomek Form z nette zabalený do potomka FormControl, který dědí z Nette UI\Control.
Finální formulář konstruktorem přebírá modelovou třídu – stejně jako u presenterů. Do předka FormControl ale potřebuji předat službu FormFactory, která dodá každé instanci finálního formuláře unikátní instanci Form. Na to používám metodu InjectFormFactory() (vyhnu se constructor hellu) a protože FormControl není presenter, musím si režim inject ručně zapnout.
FormControl ale nedodává do finálního formuláře holý Form, nýbrž ho určitým způsobem upraví. Nejedná se tedy o zamlžování závislostí třídy o níž hovoří @MarekBartoš, ale použití inject je v tomto případě OK.
V příkladu konfigurace je použit decorator, který provede operace na všech potomcích uvedené třídy/interface.
Používej inject jenom v presenterech a mimo presentery předávej závislosti konstruktorem – to je ideální pravidlo pro začátek.