Přihlášení a DI – problém
- johnyx
- Člen | 11
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)
- johnyx
- Člen | 11
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)
- johnyx
- Člen | 11
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
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
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??
- Nox
- Člen | 378
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
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.
- Nox
- Člen | 378
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
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
@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
- johnyx
- Člen | 11
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?
- Nox
- Člen | 378
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
Jestli je to NotORM dotaz, není to náhodou tím, že do tabulky se přistupuje pomocí
->users()
a ne
->users