Dynamické načítání modelů (Nette 2 + Nette\DI + Dibi/Nette\Database)

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

Do kuchařky jsem sepsal pár slov na toto téma: https://pla.nette.org/…cy-injection.
Snad to někomu pomůže.
Připomínky/chyby atd. pište tam nebo přímo sem (tady je to přehlednější).

Oggy
Člen | 306
+
0
-

Ahoj, trošku se zkouším zorientovat v nette 2 s DI ..

Použil jsem tvůj modelloader.. a chtěl bych se zeptat na způsob volání metody modelu v callbacku? ..

např. ve stylu routy

<?php
Route::setStyleProperty('#articleId',
Route::setStyleProperty('#articleId', Route::FILTER_IN, callback(array($container->getService('modelLoader')->ArticleModel, 'getIdByUrl')));
?>

je tohle „správný“ způsob v DI ? :-)

Editoval Oggy (30. 7. 2011 16:11)

Majkl578
Moderator | 1364
+
0
-

V podstatě proč ne, jen by to šlo napsat i kratšeji:

callback($container->modelLoader->articleModel, 'getIdByUrl')
  1. Můžeš místo getService používat přetěžování přes __get (kratší, hezčí).
  2. Funkci callback se nemusí dávat pole, stačí ji jako pole použít, v tom je její krása.
Zdeno1981
Člen | 114
+
0
-

Ahoj, s DI teprve začínám a pro začátek jsem využil tento modelLoader a vše funguje jak má, je mám dotaz, přidal jsem službu do bootstrapu:

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

pro příklad jsem si vzal ze sandboxu signPresenter a model Authenticator ale nevím jak s ti dál

Editoval Zdeno1981 (6. 8. 2011 10:42)

norbe
Backer | 405
+
0
-

Zdeno1981: Předně by to asi chtělo napsat, čeho chceš docílit :)

Zdeno1981
Člen | 114
+
0
-

norbe napsal(a):

Zdeno1981: Předně by to asi chtělo napsat, čeho chceš docílit :)

Potřebuju zjistit jak přes ten modelLoader udělat přihlášování uživatelů z příkladů které jsou v sandboxu (signPresenter a model Authenticator)

předpokládal jsem že nejprve to budu muset zaregistrovat jako službu.

Droid
Člen | 92
+
0
-

Zdeno1981 napsal(a):

norbe napsal(a):

Zdeno1981: Předně by to asi chtělo napsat, čeho chceš docílit :)

Potřebuju zjistit jak přes ten modelLoader udělat přihlášování uživatelů z příkladů které jsou v sandboxu (signPresenter a model Authenticator)

předpokládal jsem že nejprve to budu muset zaregistrovat jako službu.

V Bootstrapu mám:

$context->addService('authenticator', function ($context) {
            return $context->modelLoader->loadModel('UsersModel')->getAuthenticatorService();
        });

UsersModel:

namespace Model;

use Nette\Object;

class UsersModel extends Object {

    /** @var Nette\Database\Connection */
    public $database;

    public function __construct($connection) {
        $this->database = $connection;
    }

    public function getAuthenticatorService() {
        return new \Authenticator($this->database->table('users'));
    }
}

ModelLoader mám svůj, využívá to Nette\DB…
Viz. NanoCMS

Editoval Droid (7. 8. 2011 0:19)

EarlGrey
Člen | 14
+
0
-

Zdravím, chystám se na první projekt v nette a abych se zorientoval, hraju si s různýma příkladama. Rád bych používal dibi, ale nemůžu se dobrat k tomu, jak správně rozchodit modelLoader z článku s dibi a zapojenim Authenticatoru. Nemáte prosím někdo funkčí příklad, na kterém bych se na to mohl podívat? Dělám něco blbě a pořád ne a ne tomu přijít na kloub (databáze v modelech už jinak funguje jak má).

Předem děkuju!

Majkl578
Moderator | 1364
+
0
-

EarlGrey napsal(a):

Zdravím, chystám se na první projekt v nette a abych se zorientoval, hraju si s různýma příkladama. Rád bych používal dibi, ale nemůžu se dobrat k tomu, jak správně rozchodit modelLoader z článku s dibi a zapojenim Authenticatoru. Nemáte prosím někdo funkčí příklad, na kterém bych se na to mohl podívat? Dělám něco blbě a pořád ne a ne tomu přijít na kloub (databáze v modelech už jinak funguje jak má).

Předem děkuju!

Když budeš postupovat přesně podle mého článku (první příspěvek), výsledkem bude funkční model loader využívající dibi. Na konci je i jak jednoduše zaregistrovat službu, která je zároveň modelem. Pokud bys měl Authenticator jako třídu zvlášť, která model pouze používá, jednoduše bys udělal továrničku na službu authenticator, ve které bys předal kontext nebo přímo model. S předáním kontextem bys ji mohl dát přímo do configu.

joe
Člen | 313
+
0
-

@Majkl578 – tvůj návod je dobrý, jen by mě zajímalo, proč používat například autentikátor jako službu? Mám to v configu nastavené

services:
	authenticator:
		class: \Authenticator
		arguments: [@container]

a zdá se, že všechno funguje tak, jak má.

Nox
Člen | 378
+
0
-

Vždyť to přece máš jako službu – services.authenticator

joe
Člen | 313
+
0
-

Nox – jistě že mám, jen jsem zas napsal pitomost. Tak tedy měním otázku, proč v bootstrapu a ne v configu?

Nox
Člen | 378
+
0
-

Nevim jestli by v configu fungovalo to $container->modelLoader->user … každopádně bootstrap není nutnost – osobně se mi víc líbí si podědit Configurator a dát to tam.

Nevýhoda je že z mě neznámého důvodu nejde odstraňovat z Containeru služby zapsané stylem metoda, místo addService nebo configem (kvůli https://api.nette.org/…ner.php.html#58 2. podmínka) … šlo by to možná obejít, ale imho krkolomně a zas tak často potřeba tak odregistrace asi není

Editoval Nox (27. 8. 2011 21:51)

joe
Člen | 313
+
0
-

Jo už tomu rozumím, vlastně je jen náhoda, že mi to funguje, já předávám celý kontejner a ne jen ten vytvářený v model loaderu.

Majkl578
Moderator | 1364
+
0
-

Nox napsal(a):

Nevýhoda je že z mě neznámého důvodu nejde odstraňovat z Containeru služby zapsané stylem metoda, místo addService nebo configem (kvůli https://api.nette.org/…ner.php.html#58 2. podmínka) … šlo by to možná obejít, ale imho krkolomně a zas tak často potřeba tak odregistrace asi není

Nevím co myslíš stylem metoda, každopádně pokud chceš přepsat existující službu, musíš existující odstranit přes removeService, jak byla z vnějšku tvořená by nemělo hrát roli (o tom přece Container neví).

joe napsal(a):

Jo už tomu rozumím, vlastně je jen náhoda, že mi to funguje, já předávám celý kontejner a ne jen ten vytvářený v model loaderu.

Proč to je v bootstrapu vysvětlil Nox, jak jednoduše bys to zapsal přímo v configu?

Nox
Člen | 378
+
0
-

[OT] Majkl578 „styl metoda“ myslim místo ->addService('someService použít public function createServiceSomeService … jelikož se kontroluje i pomocí method_exist tak remove fungovat nebude

[edit] v aktuální verzi už neplatí

Editoval Nox (29. 11. 2011 22:31)

Majkl578
Moderator | 1364
+
0
-

To ani nevím, že tam je. Přijde mi to trochu divný, že Container něco takového vůbec kontroluje a přitom s tím nepracuje. Tohle chování totiž umí Configurator, do kterého to IMHO patří.

Tomato
Člen | 9
+
0
-

Okej,pokrocila hodina ma dohnala az sem a prosim o radu…

Podla navodu som rozbehal ModelLoader a napr funguje mi Model na pridavanie uzivatelov do DBcka.
Ale netusim,vazne netusim ako ho prinutit zaregistrovat tu sluzbu authenticator.
Mozem pouzit ten model Authenticator zo sandboxu nejako? Alebo proste musim napisat svoj Model co bude implementovat IAuthenticator(co je v podstate copy-paste funkciu authenticate). Pripadne by mi mohol niekto poslat kod jeho authenticatoru aj s tym ako ho zaregistroval?

Ja mam v boostrap-e toto

<?php

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

?>

a v Models\User.php mam

<?php

namespace Model;

use Nette\Object;

class User extends Base implements NS\IAuthenticator {

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 !== $password) {
	    throw new NS\AuthenticationException("Invalid password.", self::INVALID_CREDENTIAL);
	}

	unset($row->password);
	return new NS\Identity($row->id, $row->role, $row->toArray());
    }
}
?>

a po snahe o prihlasenie sa do systemu dostanem:

InvalidArgumentException

Model ‚Models\User‘ not found

Velmi pekne dakujem za akukolvek radu,lebo ocividne mi tu nieco velmi podstatne unika.

Editoval Tomato (29. 11. 2011 7:51)

h4kuna
Backer | 740
+
0
-

Tomato napsal(a):
a v Models\User.php mam

<?php

namespace Model;

use Nette\Object;

Model ‚Models\User‘ not found

Velmi pekne dakujem za akukolvek radu,lebo ocividne mi tu nieco velmi podstatne unika.

Vidíš to?

Namespace máš Model… a hledádáš v Models

Tomato
Člen | 9
+
0
-

ah ano, diky H4kuna tam bol problem ktory mi nasiel teda konecne model User ktory je uz konecne v spravnom namespace a z toho mi vyplynul novy problem a to ten,ze chce hladat aj interface NS\IAuthenticator v Models co som nakoniec odstranil cez use. A kedze sa stale v nette hladam a niektore veci fakt povazujem za magiu,ale dakujem… :)

Editoval Tomato (29. 11. 2011 16:08)

h4kuna
Backer | 740
+
0
-

Mělo by pomoci toto když přidáš

<?php
use Nette\Security as NS;
?>

NS je alias pro Nette\Security pokud by to nepomohlo tak tam dej.

<?php
class User extends Base implements \Nette\Security\IAuthenticator
?>
h4kuna
Backer | 740
+
0
-

Tomato napsal(a):

A kedze sa stale v nette hladam a niektore veci fakt povazujem za magiu,ale dakujem… :)

Nejpodobnější na představu je adresářová struktura. Představ si adresáře a soubory, kde soubor reprezentuje třídu a adresář je reprezentovaný pomocí namespace. Pomocí use si nalinkuješ potřebný „adresář“ a můžeš tak používat jeho „soubory“ v tomto případě třídy. Nic těžkého používání namespace neni.

Editoval h4kuna (29. 11. 2011 20:56)

johnyx
Člen | 11
+
0
-

Zdravím,
snažím se rozchodit DI a Authetificator a narážím na stejný problém jako Tomato. Ať dělam co dělám, tak se mm furt hlásí InvalidArgumentException: Model ‚User‘ not found a přitom model User mám. Zkusí mi prosím sem hodit nějakou radu – přikládám raději i kódy:
boostrap.php:

<?php
use Nette\Diagnostics\Debugger,
	Nette\Application\Routers\Route,
	Nette\Application\Routers\RouteList,
	Nette\Application\Routers\MultiRouter;


// Load Nette Framework
$params['libsDir'] = __DIR__ . '/../libs';
require $params['libsDir'] . '/Nette/loader.php';


// Enable Nette Debugger for error visualisation & logging
Debugger::$logDirectory = __DIR__ . '/../log';
Debugger::$strictMode = TRUE;
Debugger::enable();

// Load configuration from config.neon file
$configurator = new Nette\Configurator;
$configurator->container->params += $params;
$configurator->container->params['tempDir'] = __DIR__ . '/../temp';
$container = $configurator->loadConfig( __DIR__ . '/config.neon');
$container->addService('authenticator', function ($container) { return $container->modelLoader->user;});

$router = $container->router;
$adminRouter[] = new Route('administrace/<presenter>/<action>', 'Administrace:administrace');
$router[] = new Route('index.php', 'Homepage:default', Route::ONE_WAY);
$router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');

// Configure and run the application!
$application = $container->application;
//$application->catchExceptions = TRUE;
$application->errorPresenter = 'Error';
dibi::connect( $container->params['database'] );

$application->run();
?>

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 =  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);
	}
}
?>

User.php:

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

class User extends Base implements NS\IAuthenticator
{
	/** @var Nette\Database\Table\Selection */
	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);
	}

}
?>

?>

Editoval johnyx (3. 12. 2011 17:25)

h4kuna
Backer | 740
+
0
-

johnyx napsal(a):

Zdravím,
InvalidArgumentException: Model ‚User‘ not found
ModelLoader.php:

<?php
use Nette\DI\Container;

final class ModelLoader{
...
	public function getModel($name){

		$lname = strtolower($name);
		if( !isset($this->models[$lname] )){
			$class =  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);
	}
}
?>

User.php:

<?php
namespace Models;
?>

Jasný protože ModelLoader je v root namespace. A když hledá třídu User kterou máš v namespace Models, tak ji nemůže najít.

  1. ModelLoader přidej řádek
<?php
namespace Models;
?>

nebo

  1. $class = ucfirst($name); nahraď $class = ‚\Models\‘. ucfirst($name);
johnyx
Člen | 11
+
0
-

h4kuna napsal(a):

  1. $class = ucfirst($name); nahraď $class = ‚\Models\‘. ucfirst($name);

Diky … prvne jsem to zkousel, jak jsi sam psal a za boha jsem na to nemohl prijit a po celem dnu brejleni do toho byla zahada odhalena – zapomenute jedno lomitko .....