Rozdělení částí prseneteru do více souborů

Michwuanquana
Člen | 22
+
0
-

Zdravím,

chtěl bych rozdělit např. SignPresenter do více souborů. Zkusil jsem k tomu použít trait, následovně to pak vypadá takto:

SignPresenter.php

<?php declare(strict_types = 1);

namespace App\Modules\Front\Sign;

use App\Modules\Front\BaseFrontPresenter;
use Nette\Application\Attributes\Persistent;

final class SignPresenter extends BaseFrontPresenter
{
    #[Persistent]
	public string $backlink;

	use Pages\Register;
	use Pages\Login;
	use Pages\Verify;
}

Pages/Register.php

<?php declare(strict_types = 1);

namespace App\Modules\Front\Sign\Pages;

use App\Model\App;
use App\Model\Account\Exceptions\RegistererException;
use App\UI\Form\BaseForm;
use App\Modules\Front\Sign\Services;

trait Register
{
	use Services\FormFactory;
	use Services\UserManager;
	use Services\Mailer;

	protected function createComponentRegisterForm(): BaseForm
	{
		$form = $this->formFactory->forBackend();
		$form->addEmail('email')
			->setRequired(true);
		$form->addPassword('password')
			->setRequired(true);
		$form->addPassword('password_verify')
			->setRequired(true);
		$form->addSubmit('submit');
		$form->onSuccess[] = [$this, 'processRegisterForm'];

		return $form;
	}

	public function processRegisterForm(BaseForm $form): void
	{
		try {
			if($form->values->password != $form->values->password_verify)
				throw new RegistererException ("Passwords are not same.");

			$user = $this->userManagerService->register($form->values->email, $form->values->password);

			$this->mailer
					->create('verification', $this->lang)
					->to($user->getEmail())
					->body($user->getVerificationToken())
					->send();

			$this->flashMessage("Registration was successful, please check email for verification.");
			$this->redirect(App::DESTINATION_AFTER_REGISTER);
		} catch (RegistererException $e) {
			$form->addError($this->translator->translate($e->getMessage()));
			return;
		}
	}
}

a třeba Services\FormFactory.php

<?php declare(strict_types = 1);

namespace App\Modules\Front\Sign\Services;

use Nette\DI\Attributes\Inject;
use App\UI\Form\FormFactory as ServiceFormFactory;

trait FormFactory {
	#[Inject]
	public ServiceFormFactory $formFactory;
}

Otázka zní, zda-li by nebylo vhodnější to řešit jinak, popř. jak např. v Pages/Register.php zajistit, aby mi IDE našeptávalo $this->user apod.?

Editoval Michwuanquana (7. 5. 2022 11:33)

Pepino
Člen | 249
+
0
-

K čemu to je dobré? Než do traitů to rozděl do více presenterů.

Marek Bartoš
Nette Blogger | 1171
+
0
-

Chytré IDE (phpstorm) umí v traitách napovídat podle toho, v jakých třídách je použité.

Používat traity je ale hrozně špatný nápad. Na pozadí nedělají víceméně nic jiného, než že zkopírují kód a pastnou ho do třídy kde se traita používá. Výsledkem je obří, pomalá třída s desítkami a více metodami.
Stanov si pravidlo jeden presenter = jedna akce.
Presenter se stará jen o získání parametrů z requestu a o jejich předání do komponenty.
Komponenta definuje a zpracovává formulář.
Pokud má formulář po odeslání přesměrovat, tak si předej z presenteru skrze konstruktor callback, ve kterém presenter přesměruje a komponenta jen zavolá callback, nesahá na nadřazený presenter.

Je to víc psaní, ale nezamotáš se v tom. Třída neroste do obludných rozměrů a je snadné ji upravit.

Polki
Člen | 553
+
0
-

Marek Bartoš napsal(a):

K tomuhle mám Marku 2 dotazy:

  1. Jednoakční presentery se řinou ze všech stran a mám ohledně toho otázku, jaký máte obecně systém na nazývání takovýchto jednoakčních presenterů, aby jste se v tom nezamotali? V našich aplikacích je obecně kolem 50 presenterů a každý má ± 4 akce. Když to rozdělíme do jednoakčních presenterů, tak nám sice odpadnou starosti s checkováním práv pro jednotlivé akce (které se však přesunou na úroveň konkrétních presenterů, takže žádná změna) a stane se presenter třídou, která opravdu ctí zásady SingleResponsibility, ale zase zhruba 4× vzroste počet souborů, které budou ve složce presenters a stejně tak počet souborů ve složce templates (složky s šablonami pro jednotlivé presentery se promění na konkrétní šablony pro daný presenter s akcí default.), takže místo 50 presenterů a 50 složek v templates najednou budu mít celkem 400 souborů pod sebou. Už tak se nám zdá o čertech když musíme rolovat v ide v seznamu souborů jako diví a snažíme se to nějak logicky dělit aspoň do modulů, natož tak se v tom vyznat po rozdělení na jednoakční presentery. Jak toto řešíte vy?
  2. Stejně jako s tím, že má být na každou akci jeden presenter se už dlouho řine to, že by Presentery měly mít jen 1 úroveň a tedy by neměl existovat nějaký BasePresenter. Jako důvody se uvádějí špatné použití dědičnosti, konstruktor hell a podobné srandy. Pokud ale udělám jednoakční presentery, které nebudou mít tedy předka, jelikož kód by se opakoval v různých presenterech a to tak, že by se duplicity kódu křížily, že by nestačilo dědění z 1 třídy, tak nastává obří duplicita kódu. Například v aplikaci chceš na všech stránkách zobrazovat komponentu s menu. Tím se ti v rámci všech presenterů pomocí CTRL+C a CTRL+V zduplikuje injectnutí componentFactory a metoda createComponent… No a pokud jsem se to správně naučil od kolegů, co tvoří Symfony, tak přesně pro tyto účely byla traita zavedena. V příkladu od kolegy v dotazu, kdyby chtěl použít ve více presenterech přihlašovací formulář, tak by prostě jen tu danou traitu hodil do dalšího presenteru. Ty ale píšeš ‚Používat traity je ale hrozně špatný nápad.‘ Takže je lepší mít tu duplicitu kódu, nebo kde je pravda?

Editoval Polki (7. 5. 2022 16:54)

Marek Bartoš
Nette Blogger | 1171
+
+2
-

@Polki

1.Já mám appku strukturovanou podle features a povedlo se mi z Nette vykopat mapování, abych se mohl odkazovat rovnou na třídu, takže je to třeba App\GuardedObject\Admin\Create\CreateGuardedObjectPresenter. Též presentery mají své šablony u sebe a nesou stejný název jako třída (CreateGuardedObjectPresenter.php, CreateGuardedObjectTemplate.php, CreateGuardedObject.latte). Množství souborů vůbec neroste. Mám v plánu (ne)mapování dostat přímo do Nette, pak o tom napíšu článek.

2.Hele já nevím. BasePresenter mám ve dvou úrovních. Obecný BasePresenter a pak Base(Admin|Public)Presenter a víc jsem nepotřeboval. Jestliže se ve dvou presenterech týkajících se stejných dat (např. článku) vyskytuje stejný kód pracující s tím článkem, tak je třeba se ptát, jestli nejde přesunout do nějaké služby. A zda se verze kódu pro vytvoření a editaci článku nebudou měnit a nebudou se časem lišit, to pak mít duplikát dává smysl.
Zrovna login komponentu mám v BasePublicPresenter, protože je jen zlomek webů, na kterých bych ji neměl v layoutu a v LoginPresenteru pro administraci, protože administrace je vždy za heslem.
Jiné obecné komponenty lze udělat globální a registrovat je na úrovni DI, v BasePresenter je to celkem snadná úprava.

Editoval Marek Bartoš (7. 5. 2022 17:41)

Polki
Člen | 553
+
0
-

@MarekBartoš
Díky. Nějaké nejasnosti pořád mám, ale to si případně nechám na vlastní vlákno.

Editoval Polki (7. 5. 2022 18:23)

Felix
Nette Core | 1189
+
0
-

Mám v plánu (ne)mapování dostat přímo do Nette, pak o tom napíšu článek.

My v Nette se tesime na pro a proti. :)