Přihlášení a DI – problém

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

Ahoj, máp problém s aplikaci přihlašení ve spojeni s DI. Jde o to, že po odeslani přihlašovacích údajů se mi objeví hláška laděnky Fatal Error: Call to a member function where() on a non-object:

Model\User.php

<?php
public function authenticate(array $credentials){

list($username, $password) = $credentials;
**$row = $this->users->where('username', $username)->fetch();** // jedná se o tento řádek

if (!$row) {
	throw new NS\AuthenticationException("User '$username' not found.", self::IDENTITY_NOT_FOUND);
		}

if ($row->password !== $this->calculateHash($password)) {
	throw new NS\AuthenticationException("Invalid password.", self::INVALID_CREDENTIAL);
		}
unset($row->password);
	return new NS\Identity($row->id, $row->toArray());
	}
?>

určitě to bude zase nějaká banalita, ale já ji nevidím. Díky

Editoval johnyx (5. 12. 2011 0:11)

Jan Endel
Člen | 1016
+
0
-

Chtělo by to i zbytek, ale něják chudákovi Model\Userovi nepředáváš $this->users, sám ti tam nenakluše.

duke
Člen | 650
+
0
-

Ta chyba ti říká, že $this->users neobsahuje objekt, takže chyba je buď právě v tom, že tam nemá být $this->users, nebo v tom, že $this->users není správně nastaveno.

johnyx
Člen | 11
+
0
-

pilec napsal(a):

Chtělo by to i zbytek, ale něják chudákovi Model\Userovi nepředáváš $this->users, sám ti tam nenakluše.

Rozumím. Problem je v tom, že když jsem používal přihlašování bez DI, tak to fungovalo, s přechodem na DI to hlasí právě tuto hlášku. Oki, přidám sem zbytek:

Models\User.php:

<?php
namespace Models;
use Nette\Security as NS;
use Nette\Object;

class User extends Base implements NS\IAuthenticator{
	private $users;
	public function authenticate(array $credentials){

		list($username, $password) = $credentials;
		$row = $this->users->where('username', $username)->fetch();

		if (!$row) {
			throw new NS\AuthenticationException("User '$username' not found.", self::IDENTITY_NOT_FOUND);
		}

		if ($row->password !== $this->calculateHash($password)) {
			throw new NS\AuthenticationException("Invalid password.", self::INVALID_CREDENTIAL);
		}

		unset($row->password);
		return new NS\Identity($row->id, $row->toArray());
	}
		public function calculateHash($password)
	{
		return sha1($password);
	}

}
?>

Models\BaseModel.php

<?php
namespace Models;

abstract class Base extends \Nette\Object{

	private $context;

	public function __construct(\Nette\DI\Container $container){

	$this->context = $container;
	    }
	final public function getContext(){
		  return $this->context;
	   }
	final public function getDatabase(){
          return $this->context->database;
		  }
	 }
?>

ModelLoader.php

<?php
use Nette\DI\Container;

final class ModelLoader{
	private $modelContainer;
	private $models = array();

	public function __construct(Container $container){

		$modelContainer = new Container;
		$modelContainer->addService('database', $container->database);
		$modelContainer->addService('casheStorage', $container->cacheStorage);
		$modelContainer->addService('session', $container->session);
		$modelContainer->params = $container->params['models'];
		$modelContainer->freeze();
		$this->modelContainer = $modelContainer;

	}

	public function getModel($name){

		$lname = strtolower($name);
		if( !isset($this->models[$lname] )){
			$class = "\Models\\" . ucfirst($name);
				if( !class_exists($class) ){
					throw new \InvalidArgumentException("Model '$class' not found");
				}
				$this->models[$lname] = new $class($this->modelContainer);
				}
			return $this->models[$lname];
		}

	public function __get($name){
		return $this->getModel($name);
	}

?>

boostrap.php:

<?php
$container->addService('authenticator', function ($container) { return $container->modelLoader->user;});

?>

Fakt už si s tím nevím rady. Díky

Editoval johnyx (5. 12. 2011 10:06)

Nox
Člen | 378
+
0
-

Když tomu Modelu teda hodíš celý kontejner… stejně nikde nevidim, proč by mělo fungovat $this->users … tak kdyžtak $this->getContext()->users

johnyx
Člen | 11
+
0
-

Nox napsal(a):

Když tomu Modelu teda hodíš celý kontejner… stejně nikde nevidim, proč by mělo fungovat $this->users … tak kdyžtak $this->getContext()->users

Chápu nicméne tím jsem se nikam neposunul – lépe řečeno posunul k hlášc: Nette\DI\MissingServiceException: Service ‚users‘ not found.. Jestli jsem dobře pochopil DI co popisoval Majkl578, tak jsem do ModelLoader.php přidal službu:

<?php
$modelContainer->addService('users', $container->user);
?>

mi laděnka hlásí: Nette\MemberAccessException: Call to undefined method Nette\Http\User::where().

Nox
Člen | 378
+
0
-

Do Autenticátoru posíláš „users“ – to je něco co má umět „where()“ a tudíž je to asi nějaká access layer, co vyhledává/zpřístupňuje data. Posíláš tam Http\User, to nebude to pravé

Nevim co používáš k DB, jestli Nette\Database … nepracuji s tím, ale možná teda dát za ‚users‘ nějaké $container->database->users() (vyzkoušej jestli budou fungovat 2 požadavky za sebou, když getService vrací furt stejný objekt)

johnyx
Člen | 11
+
0
-

Nox napsal(a):

Do Autenticátoru posíláš „users“ – to je něco co má umět „where()“ a tudíž je to asi nějaká access layer, co vyhledává/zpřístupňuje data. Posíláš tam Http\User, to nebude to pravé

Nevim co používáš k DB, jestli Nette\Database … nepracuji s tím, ale možná teda dát za ‚users‘ nějaké $container->database->users() (vyzkoušej jestli budou fungovat 2 požadavky za sebou, když getService vrací furt stejný objekt)

No jde o to, že zkouším podle kuchařky ktreou popsal Majkl578 nastavení DI a modelLoaderu s tím, že tam popisuje velmi stručně zpřístupnění služby authenticator. Pakliže postupuji dle jeho návodu tzn. vytvořím tuto službu a v bootstrapu ji aktivuji:

<?php
$container->addService('authenticator', function ($container) { return $container->modelLoader->user;});
?>

tak mi to nechodí – někde musí být nějaký trik, jak to rozchodit a já na něj nemohu za boha přijít. Neví někdo jak to rozchodit??

JuniorJR
Člen | 181
+
0
-

A čeho by si vlastně rád docílil?

$container->addService('authenticator', function ($container) {
    return $container->modelLoader->user;
});

Tímto si zaregistruješ službu authenticator, která je pak v presenteru přístupná.

Nox
Člen | 378
+
0
-

Proč řešíš authenticator, ten ti přece jede, nejede users, ne?

Musíš jako users nastavit něco co umí ::where(), což Http\User (který je standardně v $container->user) není. Podle struktury to bude NotORM nebo Nette, tak musíš asi poslat příslušný přednastavený objekt (tabulka users a select id, pass), případně to trochu upravit a poslat toto, ale zapouzdřeno

johnyx
Člen | 11
+
0
-

JuniorJR napsal(a):

A čeho by si vlastně rád docílil?

$container->addService('authenticator', function ($container) {
    return $container->modelLoader->user;
});

Tímto si zaregistruješ službu authenticator, která je pak v presenteru přístupná.

Noi rád bych docílil přihlášení a přesměrování na danou stránku. V presenteru pak k tomu mám tento kód:

AdministracePresenter.php

<?php

use Nette\Application\UI\Form as AppForm,
	Nette\Security as NS;

public $backlink = '';

	public function renderDefault()
	{ }

	 function createComponentForm($name){
        $form = new AppForm($this, $name);

        $form->addText('username', 'Uživatelské jméno');
		$form->addPassword('password', 'Heslo');
        $form->addSubmit('login', 'Přihlásit');
        $form->onSuccess[] = callback($this, 'loggedIn');
	}

	public function LoggedIn($form){

		try{
			$values = $form->getValues();
			$this->user->login($values->username, $values->password);
			$this->application->restoreRequest($this->backlink);
			$this->redirect('Administrace:administrace');
			}
		catch(NS\AuthenticationException $e){
			$form->addError($e->getMessage());
			}
	}
}
?>

když jsem to měl psané standardně – tj. bez použití DI, tak mi to fungovalo. Chtěl jsem použít DI a úplně jsem se v tom ztratil.

JuniorJR
Člen | 181
+
0
-

Chybu v presenteru nevidím. Snad jen překlep v názvu handle metody.

johnyx
Člen | 11
+
0
-

JuniorJR napsal(a):

Chybu v presenteru nevidím. Snad jen překlep v názvu handle metody.

jsem asi slepej … kterou metodu máš na mysli?

Nox
Člen | 378
+
0
-

Ale ono to očividně jede když mu to hlásí chybu uvnitř metody authenticatoru

Řešení jsem tu už poslal 2×, ale tak ještě radši jednou:

Call to undefined method Nette\Http\User::where() znamená že objekt ->users neumí ::where().

Ty máš

<?php
$modelContainer->addService('users', $container->user);
?>

Jenže $container->user je Nette\Http\User a ten neumí where

where umí nějaká třeba databázová vrstva

Jednodušší možné řešení:

<?php
$modelContainer->addService('users', $container->database->table('users'));
?>

případně nějaký zapouzdřující objekt (objectrepository, dao…)
(kdo umí Nette\Database nebo NotORM a mám tam chybu, tak mi to prosím opravte)

duke
Člen | 650
+
0
-

Nox napsal(a):
Jednodušší možné řešení:

$modelContainer->addService('users', $container->database->table('users'));

Toto příliš nedoporučuji, protože to pak bude vracet pořád tentýž result, tedy různě filtrovaný podle toho, jaké where(), atp. na něm kdo dříve zavolal. Lepší je to opravdu obalit nějakým repositářem.

johnyx
Člen | 11
+
0
-

@Nox jaj … jsem to ale hňup. Díky, občas je mě třeba pořádně kopnout do hlavy aby se mi rozsvítilo. Celý problém je vyřešen :-) Upravil jsem UserModel takto:

<?php
...
$row = $this->getContext()->users->fetch('SELECT * FROM users WHERE username=%s', $username);
...
?>

a ModelLoader:

<?php
$modelContainer->addService('users', $container->database);
?>

a zdá se, že to šlape, tak jak má. Díky

JuniorJR
Člen | 181
+
0
-

Ano, ale jaksi si ztratil „výhody“ model loaderu (daný model users totiž stejně registruješ ručně) :)

Editoval JuniorJR (6. 12. 2011 14:34)

johnyx
Člen | 11
+
0
-

JuniorJR napsal(a):

Ano, ale jaksi si ztratil „výhody“ model loaderu (daný model users totiž stejně registruješ ručně) :)

rozumím … máš tedy nějaký nápad jak to řešit bez ztráty této výhody? Jestli jsem to vše dobře pochopil, tak tuto „výhodu“ jsem ztratil pouze pro případ přihlášení nebo se pletu?

JuniorJR
Člen | 181
+
0
-

Mnou zmiňovaná ztráta se bude týkat všech případů, kdy se budeš snažit uvnitř jednoho modelu odkazovat na model jiný.

johnyx
Člen | 11
+
0
-

JuniorJR napsal(a):

Mnou zmiňovaná ztráta se bude týkat všech případů, kdy se budeš snažit uvnitř jednoho modelu odkazovat na model jiný.

Aha, takže všechno špatně. Bohužel pak jsem zcela v koncích, protože to co psal Nox nevím a jak udělat a nerozumím tomu.

JuniorJR
Člen | 181
+
0
-

Ještě by si to mohl vyřešit tak, že by si do toho $modelContaineru předal ten daný ModelLoader a pak by si mohl v modelech přistupovat k ostatním modelům asi nějak takto

$this->context->modelLoader->fooModel->find() ...

Editoval JuniorJR (6. 12. 2011 15:05)

Nox
Člen | 378
+
0
-

duke napsal(a):

Nox napsal(a):
Jednodušší možné řešení:

$modelContainer->addService('users', $container->database->table('users'));

Toto příliš nedoporučuji, protože to pak bude vracet pořád tentýž result, tedy různě filtrovaný podle toho, jaké where(), atp. na něm kdo dříve zavolal. Lepší je to opravdu obalit nějakým repositářem.

Jasný, proto jsem psal, ať se na to mrkne někdo, kdo to používá. Já používám Doctrine2, takže bych poslal něco co implementuje ObjectRepository

JuniorJR napsal(a):

Ještě by si to mohl vyřešit tak, že by si do toho $modelContaineru předal ten daný ModelLoader a pak by si mohl v modelech přistupovat k ostatním modelům asi nějak takto

$this->context->modelLoader->fooModel->find() ...

Wow, to je ale ServiceLocator jako vyšitý :P

Editoval Nox (6. 12. 2011 15:49)

PavelJurasek
Člen | 39
+
0
-

Jestli je to NotORM dotaz, není to náhodou tím, že do tabulky se přistupuje pomocí

->users()

a ne

->users