Dotaz ohledně změny údajů uživatele – okamžitá změna

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
tatyalien
Člen | 239
+
0
-

V nette zatím jen pár dní zkouším různé věci, rozchodil jsem si acl a teď zkouším rozchodit změny údajů přihlášeného uživatele a nějak se v tom ztrácím.. Uživatel se mě do databáze zapíše (změny), ale potřeboval bych to, aby se to projevilo rovnou i na přihlášeném uživateli…

Presenter:

<?php
final class Admin_EditUserPresenter extends Admin_SecuredPresenter
{
    protected function createComponentEditName($name)
    {
        /* získání dat o uživateli */
        $user = NEnvironment::getUser();
        $this->template->user = ($user->isLoggedIn()) ? $user->getIdentity() : null;
        /* pro kontrolu v šabloně $loggedUser->isAllowed('Admin_PagePresenter', $user->roles) */

        $userPole = NEnvironment::getUser()->getIdentity()->getData();
        $user_id = $user->getIdentity()->getId();
        $user_name = $userPole[0];
        $user_email = $userPole[1];
        $user_password = $userPole[2];

        $msg = 'Jméno musí obsahovat VELKÁ a malá písmena!';
        $form = new NAppForm($this, $name);
        /* nastaveni identifikace ve form */
        $form->getElementPrototype()->id = 'editace-jmena';
        $form->addText('email', 'E-mail')
            ->setDefaultValue($user_email)
            ->addRule(NForm::FILLED, "Vyplňte email.")
            ->addRule(NForm::EMAIL, 'Vyplňte platný email');
            ->addRule(callback('app\models\UserModel', 'existujeEmailVdb'),
            "Tento email je již použit, prosím zvolte jiný email.", $user_id);
        $form->addText('name', 'Jméno:')
            ->setDefaultValue($user_name)
             ->addRule(NForm::FILLED, 'Prosím zadajte jméno.')
             //->addRule(array('CustomFormValidators','isInDatabase'), "Zadejte unikátní e-mail")
             ->addRule(NForm::MIN_LENGTH, 'Jméno musí mít minimálně %d znaků', 4)
	     ->addRule(NForm::REGEXP, $msg, '/[A-Z]+/')
             ->addRule(NForm::REGEXP, $msg, '/[a-z]+/')
            ->addRule(callback('app\models\UserModel', 'existujeJmenoVdb'),
            "Toto jméno je již použito, prosím zvolte jiné jméno.", $user_id);


        $form->addProtection('Prosím odešlet přihlašovací údaje znovu (vypršela platnost tzv. bezpečnostního tokenu).');
        $form->addSubmit('send', 'Uložit');
        // tlačítko přeskočí veškerou validaci
        $form->addSubmit('delete', 'Vymazat')->setValidationScope(NULL);;
        $form->onSubmit[] = array($this, 'editNameSubmitted');
        return $form;
    }

    public function editNameSubmitted($form)
    {
        /* pokud je vše vyplněno, pokusím se uložit změny uživatele */
        try {
        	if($form['delete']->isSubmittedBy()) {
                	// vrátím se zpět na editaci
                        //$this->flashMessage('Vyresetování formuláře!');
                	$this->redirect(":Admin:EditUser:default");
                } else {
                	// akce co se má udělat

                       $values = $form->getValues();
                        // // EditUser models/UserEditaceModel
		        $editace = new EditUser;
		        $editace->name = $values['name'];
                        $editace->email = $values['email'];
			// zjištění ID uživatele
                        $user = NEnvironment::getUser();
                        $user_id = $user->getIdentity()->getId();
			// editace v DB
		        $editace->edit($editace, $user_id);
                        $this->redirect(':Admin:EditUser:save');

                }
        }
        /* Jakou exception sem dát? */
        catch (NIOException $e) {
            $form->addError($e->getMessage());
        }
    }
...
?>

Zde nevím jakou exception volat, ale to není to nejhorší..

Model (models/UserEditaceModel):

<?php
class EditUser extends DibiRow
{
    public function __construct($arr = array())
    {
        parent::__construct($arr);
    }

    public function edit(EditUser $data, $id)
    {
        return dibi::query('UPDATE [users] SET', (array) $data, 'WHERE [id]=%i', $id);
    }
    public function save(EditUser $data)
    {
        return dibi::query('INSERT INTO [users]', (array) $data);
    }
}
?>

Data propisující do šablony řeším pomocí BasePresenter:

<?php
abstract class BasePresenter extends NPresenter
{
	protected function beforeRender()
	{
		$this->template->viewName = $this->view;
		$this->template->root = dirname(APP_DIR);

        $user = NEnvironment::getUser();
        $this->template->user = ($user->isLoggedIn()) ? $user->getIdentity() : NULL;
        /* pro kontrolu v šabloně $loggedUser->isAllowed('Admin_PagePresenter', $user->roles) */
	$this->template->loggedUser = $user;
        // pokud je uživatel přihlášený, dodám si údaje do šablony
        if ($user->isLoggedIn()) {
        	$userPole = $user->getIdentity()->getData();
	        $this->template->user_id = $user->getIdentity()->getId();
	        $this->template->user_name = $userPole[0];
	        $this->template->user_email = $userPole[1];
	        $this->template->user_password = $userPole[2];
        } else {
	        $this->template->user_id = '';
	        $this->template->user_name = '';
	        $this->template->user_email = '';
	        $this->template->user_password = '';
        }

		$a = strrpos($this->name, ':');
		if ($a === FALSE) {
			$this->template->moduleName = '';
			$this->template->presenterName = $this->name;
		} else {
			$this->template->moduleName = substr($this->name, 0, $a + 1);
			$this->template->presenterName = substr($this->name, $a + 1);
		}
	}
}
?>
RadH
Člen | 23
+
0
-

Zkus v metodě editNameSubmitted použít po editaci uživatele následující kód:

<?php
// editace v DB
$editace->edit($editace, $user_id)

if (dibi::getAffectedRows()) {
	if ($user->identity instanceof IIdentity) {
		$user->identity->name = $values['name'];
		$user->identity->email = $values['email'];
	}
}
?>

U exception bych zachytával DibiException. Rozhodně bych nevypisoval $e->getMessage(), ale na produkci vypsal info o neúspěchu uložení do DB, v debug módu si $e->getMessage() vypsat můžeš pro lepší lazení.

Jinak tento kód ti vyvolá Fatal Error pokud není uživatel přihlášen a není vytvořena identita a uživatel dostane hlášení „Call to a member function getId() on a non-object“.

<?php
// Muze vyvolat Fatal Error
$user = Environment::getUser();
$user_id = $user->getIdentity()->getId();
?>

na začátek editNameSubmitted zpracování bych dal test na identitu:

<?php
$user = NEnvironment::getUser();

if ($user->identity instanceof IIdentity) {
	// zpracovat form
} else {
	$this->flashMessage('Měnit údaje smí pouze přihlášený uživatel.', 'warning');
	$this->redirect(':Admin:EditUser:default');
}
?>
tatyalien
Člen | 239
+
0
-

Tak jsem to testnul:
if ($user->identity instanceof IIdentity)
A stejně se nic nezměnilo, když si ale vypšíu před redirectem:

<?php
$this->flashMessage("|".$user->identity->name."|".$user->identity->email."|");
?>

Dostanu změněné údaje, ale nepromítne se to do přihlášeného uživatele (v db je vše ok).

Ohledně Fatal Error to vím, ale to by mělo být kryté tím, že přístup na Admin_EditUserPresenter je podmíněný přihlášeným uživatelem, pokud není, je přesměrován na přihlášení. Má to tedy cenu to tam dodávat?

Editoval tatyalien (14. 1. 2011 9:50)

RadH
Člen | 23
+
0
-

Nekoukl jsem se do beforeRender, ty tam máš číselné klíče k datům, to je trochu nešikovné, protože z nich není přesně poznat co v kterém je.

Lepší je při přihlášení v metodě authenticate uložit do dat identity pole se jmennými klíči např.:

<?php
array(
	'name' => 'Jméno',
	'email' => 'muj@email.cz',
	'password' => 'XXX',
)
?>

Pak v metodě beforeRender()BasePresenter uprav přiřazení proměnných pro template třeba takto:

<?php
if ($user->isLoggedIn()) {
        $this->template->user_id = $user->getId();
        $this->template->user_name = $user->identity->name;
        $this->template->user_email = $user->identity->email;
        $this->template->user_password = $user->identity->password;
} else {
        ....
}
?>

Proč dáváš do template user_password?

tatyalien
Člen | 239
+
0
-

Oki, jdu to přepsat a testnout.

Ad: user_password. Na starjech webech co jsem dělal jsem si vždy při přihlášení uživatele uložil věci do session a při změně údajů to kontroloval oproti nim, pkud uživatel zadával stejné data, hodil jsem mu text, že data odpovídají záznamům uloženým v db, tak není potřeba provádět změny.
Tady myslím to pak odendám, protože co jsem koukal v jednom případě, tak dám jen kontrolu, pokud se rovná staré heslo s novým…

Tak po testu vše fachá, díky moc ;)
Stačilo tam pak dát jen:

<?php
                        $user = NEnvironment::getUser();
                        $user_id = $user->getId();
		        // uložení
			$editace->edit($editace, $user_id);
			// aktualizace načtených dat
			$user->identity->name = $values['name'];
			$user->identity->email = $values['email'];
?>

Editoval tatyalien (14. 1. 2011 10:23)

RadH
Člen | 23
+
0
-

Ke kontrole stejných dat lze použít právě dibi::getAffectedRows(), pokud nic nezměníš tak ovlivněných řádků bude nula. Podle toho dáš vědět uživateli výsledek.
Osobně hesla (byť hashovaná) nikam jinak než do DB neukládám. Vždy kontroluji jen otisk proti DB.

tatyalien
Člen | 239
+
0
-

RadH: Aha chápu, tak poupraveno :-)

<?php
		        $editace->edit($editace, $user_id);
                        // info
			if (dibi::getAffectedRows()) {
				$user->identity->name = $values['name'];
			        $user->identity->email = $values['email'];
                                $this->flashMessage("uživatel editován.");
                                $this->redirect(':Admin:EditUser:save');
			} else {
                        	$this->flashMessage("Data není potřeba ukládat, odpovídají datům uloženým v DB.");
                                $this->redirect(':Admin:EditUser:default');
                        }
?>
RadH
Člen | 23
+
0
-

Ještě čistší by bylo, kdyby metoda edit modelu vracela dibi::getAffectedRows().
Tak nebude v presenteru žádný DB dotaz a vše bude pěkně v modelu.
Pak bys mohl mít:

<?php
if ($editace->edit($editace, $user_id)) {
....
?>
tatyalien
Člen | 239
+
0
-

Teď nevím jak to myslíš,
já se odazuji v prezenteru na akci z models/UserEditaceModel

Kde je:

<?php
class EditUser extends DibiRow
{
    public function __construct($arr = array())
    {
        parent::__construct($arr);
    }
    public function edit(EditUser $data, $id)
    {
        return dibi::query('UPDATE [users] SET', (array) $data, 'WHERE [id]=%i', $id);
    }
    public function save(EditUser $data)
    {
        return dibi::query('INSERT INTO [users]', (array) $data);
    }
?>

V Nette jsem začátečník / lama, pokud to dělám blbě, je lepší mě nakopnout teď, než časem…

RadH
Člen | 23
+
0
-

Tvá metoda edit v modelu už sama o sobě vrací dibi::getAffectedRows() (INSERT a UPDATE v dibi query vracejí affectedRows).

Tak test po uložení

<?php
$editace->edit($editace, $user_id);
// info
if (dibi::getAffectedRows()) {
....
?>

nahraď tímto

<?php
if ($editace->edit($editace, $user_id)) {
....
?>
tatyalien
Člen | 239
+
0
-

Opraveno, ještě jednou díky.