Jak zobrazovat chybové hlášky v šabloně? (Nette 3.0)
- vb76
- Člen | 16
Ahoj všem,
seznamuji se s Nette 3.0 a tvořím přihlašování uživatelů s ověřováním v databázi. Vycházím ze Sandboxu https://github.com/nette/sandbox Mám problém s tím, jak zobrazovat v šabloně chybové hlášky, uložené přes $form->addError. Pokud přidám do šablony toto:
<ul class="errors" n:if="$form->hasErrors()">
<li n:foreach="$form->errors as $error">{$error}</li>
</ul>
zahlásí mi to Undefined variable: form. Poradíte mi, prosím, kde dělám chybu? Děkuji předem za informace.
App\Config\common.neon
parameters:
php:
date.timezone: Europe/Prague
application:
errorPresenter: Error
mapping:
*: App\*Module\Presenters\*Presenter
session:
expiration: 14 days
autoStart: true
services:
- App\Model\UserModel
- App\Forms\FormFactory
- App\Forms\PrihlaseniFormFactory
router: App\Router\RouterFactory::createRouter
App\Forms\FormFactory.php
<?php
declare(strict_types=1);
namespace App\Forms;
use Nette;
use Nette\Application\UI\Form;
final class FormFactory
{
use Nette\SmartObject;
public function create(): Form
{
$form = new Form;
return $form;
}
}
?>
App\Forms\PrihlaseniFormFactory.php
<?php
declare(strict_types=1);
namespace App\Forms;
use Nette;
use Nette\Application\UI\Form;
use Nette\Security\User;
final class PrihlaseniFormFactory
{
use Nette\SmartObject;
/** @var FormFactory */
private $factory;
/** @var User */
private $user;
public function __construct(FormFactory $factory, User $user)
{
$this->factory = $factory;
$this->user = $user;
}
public function create(callable $onSuccess): Form
{
$form = $this->factory->create();
$form->addText('inputEmail', 'E-mail:')
->setRequired('Zadejte e-mailovou adresu.')
->setHtmlAttribute('placeholder', 'E-mail');
$form->addPassword('inputPassword', 'Heslo:')
->setRequired('Zadejte heslo.')
->setHtmlAttribute('placeholder', 'Heslo');
$form->addSubmit('buttonLogin', 'Přihlásit');
$form->onSuccess[] = function (Form $form, \stdClass $values) use ($onSuccess): void {
try {
$this->user->login($values->inputEmail, $values->inputPassword);
} catch (\Nette\Security\AuthenticationException $e) {
$form->addError('Neplatné uživatelské jméno nebo heslo.');
return;
}
$onSuccess();
};
return $form;
}
}
?>
App\Model\UserModel.php
<?php
declare(strict_types=1);
namespace App\Model;
use Nette;
use Nette\Security\Passwords;
final class UserModel implements Nette\Security\IAuthenticator
{
use Nette\SmartObject;
private const
TABLE_NAME = 'bm_users',
COLUMN_ID = 'id',
COLUMN_ACTIVE = 'active',
COLUMN_NAME = 'username',
COLUMN_PASSWORD_HASH = 'password',
COLUMN_ROLE = 'role';
/** @var Nette\Database\Context */
private $database;
/** @var Passwords */
private $passwords;
public function __construct(Nette\Database\Context $database, Passwords $passwords)
{
$this->database = $database;
$this->passwords = $passwords;
}
/**
* Performs an authentication.
* @throws Nette\Security\AuthenticationException
*/
public function authenticate(array $credentials): Nette\Security\IIdentity
{
[$username, $password] = $credentials;
$row = $this->database->table(self::TABLE_NAME)
->where(self::COLUMN_NAME, $username)
->fetch();
$row = $this->database->table(self::TABLE_NAME)
->where(self::COLUMN_NAME, $username)
->fetch();
if(!$row)
{
throw new Nette\Security\AuthenticationException('The username is incorrect.', self::IDENTITY_NOT_FOUND);
}
elseif(!$this->passwords->verify($password, $row[self::COLUMN_PASSWORD_HASH]))
{
throw new Nette\Security\AuthenticationException('Heslo není správné!', self::INVALID_CREDENTIAL);
}
elseif($row[self::COLUMN_ACTIVE] == 1)
{
throw new Nette\Security\AuthenticationException('Uživatelský účet není aktivní!', self::NOT_APPROVED);
}
elseif($this->passwords->NeedsRehash($row[self::COLUMN_PASSWORD_HASH]))
{
$row->update(array(self::COLUMN_PASSWORD_HASH => $this->passwords->hash($password)));
}
$arr = $row->toArray();
unset($arr[self::COLUMN_PASSWORD_HASH]);
return new Nette\Security\Identity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);
}
}
?>
App\Presenters\templates\Components\Forms.latte
{define form-1 $formName}
<div n:foreach="$flashes as $flash" class="alert {$flash->type} alert-dismissible fade show" role="alert">
{$flash->message|noescape}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form n:name=$formName class=form-horizontal>
<div class="form-group mb-2">
{input inputEmail class => 'form-control form-control-sm'}
</div>
<div class="form-group mb-2">
{input inputPassword class => 'form-control form-control-sm'}
</div>
<div class="text-left">
{input buttonLogin class => 'btn btn-sm btn-success mb-0 font-weight-bold'}
<a n:href="Prihlaseni:novyUcet" class="btn btn-sm btn-danger mb-0 float-right"><strong>Nový účetet</strong></a>
</div>
<div class="clearfix text-center mt-2">
<a n:href="Prihlaseni:obnoveniHesla" class="fsize-3">Nepamatuji si heslo</a>
</div>
</form>
{/define}
App\Presenters\templates\@layout.latte
{import 'Components/Forms.latte'}
<!DOCTYPE html>
<html lang="cs-cz">
.
.
.
</html>
App\Presenters\templates\Homepage\default.latte
{block title}{/block}
{block description}{/block}
{block keywords}{/block}
{block content}
{include form-1 prihlaseniForm}
{/block}
App\Presenters\BasePresenter.php
<?php
declare(strict_types=1);
namespace App\Presenters;
use Nette;
use Nette\Application\UI\Form;
use App\Forms;
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
/** @persistent */
public $backlink = '';
/** @var Forms\PrihlaseniFormFactory */
private $prihlaseniFactory;
public function __construct(Forms\PrihlaseniFormFactory $prihlaseniFactory)
{
$this->prihlaseniFactory = $prihlaseniFactory;
}
/**
* Prihlaseni form factory.
*/
protected function createComponentPrihlaseniForm(): Form
{
return $this->prihlaseniFactory->create(function (): void {
$this->redirect('Homepage:');
});
}
public function actionOdhlaseni(): void
{
$this->getUser()->logout();
}
}
?>
- Marek Bartoš
- Nette Blogger | 1280
createComponentPrihlaseniForm()
→ proměnná se jmenuje
prihlaseniForm
Edit: bullshit, proměnnou $form vytváří makro formuláře n:name
Editoval Mabar (25. 9. 2019 11:57)
- vb76
- Člen | 16
Mabar napsal(a):
createComponentPrihlaseniForm()
→ proměnná se jmenujeprihlaseniForm
Děkuji za informaci. Jen pro upřesnění, v prihlaseniForm
budou informace uložené do proměnné form
?
Jako konkrétně v šabloně vypíšu chybovou hlášku?
Musím to předat do šablony přes $this->template->xxx
a
definovat v presenteru?
- Marek Bartoš
- Nette Blogger | 1280
Z createComponent se ti vkládají proměnné do šablony automaticky, název proměnné se vytváří z názvu metody. A $prihlaseniForm je instance Form co si vracíš v createComponentPrihlaseniForm(), jen $form v šabloně nahraď za $prihlaseniForm
- vb76
- Člen | 16
Mabar napsal(a):
Z createComponent se ti vkládají proměnné do šablony automaticky, název proměnné se vytváří z názvu metody. A $prihlaseniForm je instance Form co si vracíš v createComponentPrihlaseniForm(), jen $form v šabloně nahraď za $prihlaseniForm
Děkuji za vysvětlení. Už to chápu.
- vb76
- Člen | 16
vb76 napsal(a):
Mabar napsal(a):
Z createComponent se ti vkládají proměnné do šablony automaticky, název proměnné se vytváří z názvu metody. A $prihlaseniForm je instance Form co si vracíš v createComponentPrihlaseniForm(), jen $form v šabloně nahraď za $prihlaseniForm
Děkuji za vysvětlení. Už to chápu.
Tak jsem našel chvilku se na to ještě mrknout. Přidal jsem do default.latte následující kód
<ul class="errors" n:if="$prihlaseniForm->hasErrors()">
<li n:foreach="$prihlaseniForm->errors as $error">{$error}</li>
</ul>
{include form-1 prihlaseniForm}
a stále mi hlásí Undefined variable: prihlaseniForm. Vše ostatní je nezměněno viz zaslané informace výše. Zkoušel jsem dát také {$prihlaseniForm} a výsledek je stejný. Nějak si s tím nevím rady. Nakopnutí pomohlo pochopit logiku, ale proč se ta proměnná neplní nebo nepředává netuším.
Věděl by někdo čím by to mohlo být? Děkuji předem za pomoc!
Editoval vb76 (24. 9. 2019 20:44)
- Marek Bartoš
- Nette Blogger | 1280
Šablona default.latte, ve které se snažíš chyby vypsat, patří k té samé komponentě/presenteru, ve které vytváříš formulář? Každá šablona má dostupné jen proměnné komponenty, ke které patří.
- vb76
- Člen | 16
Mabar napsal(a):
Šablona default.latte, ve které se snažíš chyby vypsat, patří k té samé komponentě/presenteru, ve které vytváříš formulář? Každá šablona má dostupné jen proměnné komponenty, ke které patří.
Aha, v tom bude ten problém. Patří k Homepage Presenteru, který dědí od Base Presenteru, ve kterém je vytvářen formulář. Chtěl bych použít přihlašování ve všech presenterech /šablonách. Struktura Homepage:default má umístěn formulář jinde než ostatní šablony. Pro ty je umístěn v @layout.latte. Proto jsem to vložil do Base Presenteru. Viz struktura výše.
Půjde to?
Editoval vb76 (24. 9. 2019 22:28)
- Šaman
- Člen | 2667
Jestli HomepagePresenter,
dědí od Base Presenteru, ve kterém je vytvářen formulář
, pak
je formulář prakticky vytvářen v HomepagePresenter. (To je věc OOP.
Neexistuje žádná instance BasePresenter
, ale každý jeho
potomek ho zároveň obsahuje.) Takže jestli jde jen o to, tak
to půjde.
- vb76
- Člen | 16
Konkrétně mi nefunguje předávání proměnné $prihlaseniForm, která by měla obsahovat chybové hlášky přidané přes $form->addError() v PrihlaseniFormFactory. Formulář vytvářím v BasePreneter a v šabloně se snažím zobrazit chybová hlášení. Nicméně šablona se neváže k presenteru, ve kterém se formulář vytváří. Když si chci proměnnou $prihlaseniForm (chybovou hlášku) vypsat v Homepage:default, hlásí mi to Undefined variable: prihlaseniForm. S tímto konkrétně potrebuji poradit.
- Šaman
- Člen | 2667
Podařilo se ti v úvodním příspěvku poslat spoustu kódu, kromě toho
jednoho důležitého :) Jak to do té šablony posíláš?
Samotná proměnná tam jen tak neexistuje, pro přístup k formuláři
(obecně k libovolné vlastní komponentě v presenteru) použij
$this['prihlaseniForm']
.
Teda, Mabar psal že se v šabloně vytváři automaticky, ale o tom moc
nevím. Já si je předávám, pokud je tam potřebuji jako proměnné (jinak
bych teda předpokládal, že pokud se předají automaticky, tak do proměnné
$prihlaseniFormControl
)
Takže jestli nefunguje ani $prihlaseniFormControl
, tak si to
zkus předat v render metodě jako
$this->template->prihlaseniForm = $this['prihlaseniForm'];
To předání můžeš zařídit i v BasePresenteru v metodě
beforeRender
a používat to v šabloně layoutu. Pak máš
zaručené, že každá šablona (rozšiřující layout) a každý presenter
(dědící od BasePresenteru) to bude umět vypsat.
- Marek Bartoš
- Nette Blogger | 1280
Tak se moc omlouvám, jak manuální renderování formulářů používám
automaticky, tak jsem si zafixoval, že je ta proměnná vždycky dostupná :/
Raději jsem teď kouknul do kódu a proměnnou form vytváří makro
n:name
.
https://github.com/…rmMacros.php#…
<form n:name="form">
<div n:if="$form->hasErrors()" n:foreach="$form->getErrors() as $error">
<span>{$error}</span>
</div>
Editoval Mabar (25. 9. 2019 11:56)