přesměrování při špatném přihlašení na presenter
- vlkodlak
- Člen | 175
Zdravím,
řeším, pro mně takovou zapeklitost a nedaři se, chci vytvořit možnost pro přihlašení uživatele a účet neexistuje, aby byl přesměrován na presenter, kde bude moci provést registraci, jenže se mi nedaří předat chybu / povel do SingPresenteru, který by mohl dal vše přesměrovat nebo rovnou ze SingInFormFactory přenést „děj“ na presenter VytvorUzivatele.
**SingInFormFactory **
<?php
declare(strict_types=1);
namespace App\Forms;
use Nette;
use Nette\Application\UI\Form;
use Nette\Security\User;
use Nette\Application\UI\Control;
final class SignInFormFactory
{
use Nette\SmartObject;
/** @var FormFactory */
private $factory;
/** @var User */
private $user;
/** $var file */
private $infofile;
public function __construct(FormFactory $factory, User $user, $infofile = '')
{
$this->factory = $factory;
$this->user = $user;
$this->infofile = $infofile;
}
public function create(callable $onSuccess): Form
{
$form = $this->factory->create();
$form->addText('username', 'Username:')
->setHtmlAttribute('class', 'bigNumbers')
->setRequired('Please enter your username.');
$form->addPassword('password', 'Password:')
->setRequired('Please enter your password.');
$form->addCheckbox('remember', 'Keep me signed in');
$form->addSubmit('send', 'Sign in')
->setHtmlAttribute('class', 'button btn btn-outline-primary')
->setHtmlAttribute('style', ' color: #212529; border-color: #28a745;');
$form->onSuccess[] = function (Form $form, \stdClass $values) use ($onSuccess): void {
try {
$this->user->setExpiration($values->remember ? '14 days' : '20 minutes');
$this->user->login($values->username, $values->password);
} catch (Nette\Security\AuthenticationException $e) {
$form->addError($e->getMessage());
return;
}
$onSuccess();
};
return $form;
}
}
zkoušel jsem experimentovat i s onError, ale neuspešně
$form->onError[] = function() use ($form) {
$this->redirect('Offlineuser:default');
};
- Marek Bartoš
- Nette Blogger | 1280
V onSuccess je pozdě na přidávání errorů do formuláře. K tomu aby došlo na událost onError musí být chyba přidaná skrze validační pravidla nebo při onSubmit.
- vlkodlak
- Člen | 175
@MarekBartoš David má pravdu, ten onError opravdu zachytí špatnou událost dřív než dojde k viz. výjimce
catch (Nette\Security\AuthenticationException $e) {
$form->addError($e->getMessage());
return;
@DavidGrudl pak mám dotaz na člověka nejznalejšího a nejzkušenějšího ;-)
- Laděnka mi říká, že nezná
Call to undefined method App\Forms\SignInFormFactory::redirect()
a já v klauzuli Use nevidím, co by jí mělo chybět?
use Nette;
use Nette\Application\UI\Form;
use Nette\Security\User;
use Nette\Application\UI\Control;
- pokud jsem pochopil správně: když událost proběhne přes onError tak bych měl řešit i situaci, kdy uživatel prostě zadá špatně heslo, to co mi teď řeší ta část catch (Nette\Security\AuthenticationException $e)
Mohu pak znovu vyvolat Exeption? ve větvi onError provedu ověření zda podmínka pro přesměrování je splněna, pak přesměrují, pokud ne nechám doběhnout výjimku / znovu ji vyvolám? něčím takovým
...
throw $e;
...
- nightfish
- Člen | 519
vlkodlak napsal(a):
- Laděnka mi říká, že nezná
Call to undefined method App\Forms\SignInFormFactory::redirect()
a já v klauzuli Use nevidím, co by jí mělo chybět?
@vlkodlak Bude to tím, že SignInFormFactory
na sobě
žádnou metodu redirect()
nemá.
Osobně bych to celé řešil úplně jinak – v SignInFormFactory bych
nechal jen vytvoření formuláře a navěšení callbacku
$onSuccess
a veškerý kód, který volá službu User
nechal v presenteru:
protected function createComponentSignInForm(): Form {
return $this->signInFormFactory->create(function(Form $form): void {
$values = $form->getValues();
try {
$this->getUser()->setExpiration($values->remember ? '14 days' : '20 minutes');
$this->getUser()->login($values->username, $values->password);
} catch (\Nette\Security\AuthenticationException $e) {
// tady se nejak rozhodni, jestli presmerovat, ci nikoliv
// ja si v Authorizatoru vyhazuju ruzne vyjimky podle nastalych situaci
// (spatne udaje, zablokovany ucet, nedokoncena aktivace),
// takze tady bych mel vic vetvi `catch` a bud presmeroval, nebo pridal formularovou chybu
$canRedirect = ...;
if ($canRedirect) {
$this->redirect('Offlineuser:default');
} else {
$form->addError($e->getMessage());
}
}
});
}
- Bulldog
- Člen | 110
Souhlasím s @dakur
My to tedy děláme ještě tak, že komponenta je jen ‚vykreslitelem‘
formuláře, takže samotná FormFactory jako závislost bere něco, co
umožňuje uživatele přihlásit (typicky Nette\Security\User) a rovnou na
formulář navěsí onSuccess metodu, která se o přihlášení pokusí,
takže ohledně fungování formuláře se už komponenta ani Presenter
nestarají.
Komponenta se nám stará o jeho vykreslení/překreslení a Presenter navěšuje pouze, jak píšeš, přesměrování po úspěšném vykonání formuláře.
Tedy náš formulář je komplet přenositelný a nezávisí na to, jak jej
zpracuje Presenter, nebo komponenta.
Tzn. pokud ho hodíme do projektu, kde nemáme Presentery, bude pořád
fungovat. Pokud jej hodíme do projektu, kde nemáme komponenty, pořád
můžeme překopírovat factory, která bude pracovat pořád stejně a nic
dalšího se nemusí přidávat.
A pokud jej hodíme do projektu, kde není Nette\Security, tak jen změníme, co se posílá továrně jako závislost přes DI.
Už jsem ale viděl i způsoby od větších firem, kdy to bylo dost
podobné, ale FormFactory jen stavěla strukturu formuláře, a
nenastavovala mu callbacky. Tato FormFactory se injectovala
přímo do Presenteru a ten už nastavoval v určitých callbacích potřebné
věci.
Pak měli jednu helper třídu pro každý formulář, která v sobě
definovala všechny callbacky a v Presenteru pak dělali to, že navěsili při
vykreslení dané callbacky na formulář.
Takže to vypadalo takto: (Pseudokód, ale opravdu reálný. Nic není
vymyšleno. Napsáno podle předlohy.)
FormFactory:
class UserFormFactory
{
public function create(): Nette\Application\UI\Form
{
$form = new Nette\Application\UI\Form();
$form->addText('name', 'Jmeno');
$form->addText('surname', 'Prijmeni');
$form->addEmail('email', 'Email');
$form->addPassword('pass', 'Heslo');
$form->addSubmit('save', 'Uložit');
return $form;
}
public function setDefaultsByEntity(Nette\Application\UI\Form $form, ?UserEntity $user): void
{
if ($user === null) {
return;
}
$data = $user->toArray();
$form->setDefaults($data);
}
}
Helper s metodami na process formuláře:
class UserService
{
public function __construct(
private App\Model\Orm $orm,
private Nette\Security\Passwords $passwords,
) { }
public function processForm(Form $form, ArrayHash $values, ?UserEntity $user): UserEntity
{
if ($user === null) {
$user = new UserEntity();
$user->createdAt = new DateTime();
}
$user->name = $values->name;
$user->surname = $values->surname;
$user->email = $values->email;
$user->pass = $this->passwords->hash($values->pass);
$user->updatedAt = new DateTime();
$this->orm->persistAndFlush($user);
return $user;
}
}
Presenter:
class UserPresenter
{
#[Inject] public App\Model\Orm $orm;
#[Inject] public UserService $userService;
#[Inject] public UserFormFactory $userFormFactory;
private UserEntity $userEntity;
public function actionEdit(int $id): void
{
$this->userEntity = $this->getUserEntity($id);
}
protected function createComponentUserForm(): Nette\Application\UI\Form
{
$form = $this->userFormFactory->create();
$form->onAnchor[] = function (Nette\Application\UI\Form $form): void {
$this->userFormFactory->setDefaultsByEntity($form, $this->userEntity);
};
$form->onSuccess[] = function (Nette\Application\UI\Form $form, ArrayHash $values): void {
$this->userEntity = $this->userService->processForm($form, $values, $this->userEntity);
};
$form->onSuccess[] = function (Nette\Application\UI\Form $form, ArrayHash $values): void {
$this->flashMessage('Uživatel uložen.', 'success');
$this->redirect('default');
};
return $form;
}
private function getUserEntity(int $id): UserEntity
{
// Chyba. Jsme závislí na ORM. Lepší je injectovat rovnou repozitáře, které by neměly být nazvané tak, aby se nepoznalo, že to jsou repozitáře
// Díky tomu můžeme měnit ORM podle potřeby, nebo ho vyměnit v určitých místech za jiný způsob získávání dat.
$user = $this->orm->user->getById($id);
if ($user === null) {
// Další chyba.. Nenastavuje se 404 stránka, ale jen se přesměrovává, což může vést ke zmatení vyhledávačů například a změlnění PageRank
$this->flashMessage('Uživatel nenalezen.', 'danger');
$this->redirect('default');
}
return $user;
}
}
Což mě osobně přijde přehnané a taky jsi závislý na tom, aby ten, kdo si překopíruje formulář se musel najednou starat o překreslování, vykreslování, zpracování, ověření chyb vůči databázi atp., takže nemáš jeden soubor, co zkopíruješ, ale musíš kopírovat 2 a v presenteru je mezi sebou propojovat, což je více práce, ale hej. Když se jim takto vyvíjí v pohodě, tak proč ne že.