Nedaří se přejít z nette 2.0b na nette 2.0 stable (2.0.13)

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

Bohužel jsem nenašel jasný popis přechodu (či změn) z 2.0b na 2.0 stable nezbývá mi než se zeptat. Nedaří se mi převést stávající (poměrně rozsáhlou) aplikaci z dřívější struktury config.neon a bootstrap.php na nový (viz aktuální sandbox). Budu rád, když mi s tím někdo pomůže.
Nejprve naznačím dosavadní podobu souborů config a bootstrap a pak co jsem „zplodil“.
Stávající config.neon:

common:
	php:
		date.timezone: Europe/Prague
#nasledujici parametry nemzat - jen nastavit todos: true/false
	myvar:
		todos: true
#název a adresa firmy uživatele
	company:
		name: NameOfCompany
		street: Street 123
		city: Town
		zip: 123 45
		country: Czech Republic

	services:
		robotLoader:
			run: true
		model:
			class: Model
			arguments: [@database]
		database:
			class: DibiConnection
			arguments: [%database%]
		authenticator:
			create: [@model, createAuthenticatorService]
		authorizatorFactory:
			class: AuthorizatorFactory
		authorizator:
			create: [@authorizatorFactory, create]
		session:
			arguments: [ [ expiration: "+ 2 days" ] ]
production < common:
development < common:

Nyní config.local.neon:

common:
	database:
		driver: sqlsrv
		host: SQLSERVER
		username: admin
		password: password
		database: DB
		charset: utf-8
		profiler: TRUE
production < common:
development < common:
	database:
		host: .\SQLEXPRESS
		username: user
		password: pass

A nyní bootstrap.php (neděste se prosím – je to můj první projekt více než 2 roky starý):

use Nette\Diagnostics\Debugger,
	Nette\Application\Routers\Route,
	Nette\Application\Routers\RouteList,
	Nette\Application\Routers\SimpleRouter;
// Load Nette Framework
require $params['libsDir'] .'/Nette/loader.php';
// Load Dibi library
require $params['libsDir'] .'/Dibi/Dibi.php';
// Enable Nette Debugger for error visualisation & logging
Debugger::$strictMode = TRUE;
Debugger::$logDirectory = $params['logDir'] ;
Debugger::enable(Debugger::DEVELOPMENT);

// 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 = $configurator->loadConfig(__DIR__ . '/config.local.neon');
// Setup router using mod_rewrite detection
if (function_exists('apache_get_modules') && in_array('mod_rewrite', apache_get_modules())) {
	$container->router = new RouteList;
	$container->router[] = new Route('index.php', 'Homepage:default', Route::ONE_WAY);
	$container->router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
} else {
	$container->router = new SimpleRouter('Homepage:default');
}
// create DB connection
dibi::connect($container->params['database']);
$application = $container->application;
$application->errorPresenter = 'Error';
//DateInput register
Vodacek\Forms\Controls\DateInput::register();
// Run the application!
$application->run();

Aktuálně jsem se pokusil zjednodušit config.neon takto (local jsem zatím vypustil):

common:
	parameters:
#	Sem napsat vlastní konstanty - nevím jak?
	php:
		date.timezone: Europe/Prague
	nette:
		application:
			errorPresenter: Error
		session:
			expiration: 14 days
		model:
			class: Model
			arguments: [@database]
		database:
			class: DibiConnection
			host: .\SQLEXPRESS
			username: user
			password: pass
			charset: utf-8
			profiler: TRUE
#	pokus o nové services - nefunkční
	services:
		routerFactory: RouterFactory
		router: @routerFactory::createRouter
		authenticator: Authenticator
		authorizator: AuthorizatorFactory
		create: @authorizator::create

		database:
			class: dibi
			create: dibi::connect
			arguments: [@database]

		model:
			class: Model
#			arguments: [@database]
#	původní services - nefunkční
#		authenticator:
#			create: [@model, createAuthenticatorService]
#
#		authorizatorFactory:
#			class: AuthorizatorFactory
#
#		authorizator:
#			create: [@authorizatorFactory, create]

	factories:

production < common:
development < common:

A nakonec nový bootstrap.php:

// Load Nette Framework or autoloader generated by Composer
require __DIR__ . '/../../lib/autoload.php';
$configurator = new Nette\Config\Configurator;
// Enable Nette Debugger for error visualisation & logging
$configurator->enableDebugger(__DIR__ . '/../log');
// Specify folder for cache
$configurator->setTempDirectory(__DIR__ . '/../temp');
// Enable RobotLoader - this will load all classes automatically
$configurator->createRobotLoader()
	->addDirectory(__DIR__)
	->addDirectory(__DIR__ . '/../../lib')
	->register();
// Create Dependency Injection container from config.neon file
$configurator->addConfig(__DIR__ . '/config/config.neon');
$container = $configurator->createContainer();
return $container;

Problém je aktuálně ten, že se mi nedaří spustit aplikaci, ta končí následující chybou:
Service of type Nette\Security\IAuthorizator not found přičemž v knihovně samozřejmě interface IAuthorizator uložen je.

Editoval mr.mac (30. 11. 2013 21:07)

enumag
Člen | 2118
+
0
-

Tohle

services:
    authorizator: AuthorizatorFactory
    create: @authorizator::create

změň na

services:
    authorizatorFactory: AuthorizatorFactory
    authorizator: @authorizatorFactory::create

Tohle samozřejmě nevyřeší problém, jen máš ty služby divně pojmenované. Mohl bys ukázat AuthorizatorFactory?

Editoval enumag (30. 11. 2013 19:54)

mr.mac
Člen | 87
+
0
-

enumag napsal(a):
Tohle samozřejmě nevyřeší problém, jen máš ty služby divně pojmenované. Mohl bys ukázat AuthorizatorFactory?

Díky za snahu o pomoc, trochu se v tom inovovaném config.neon topím (je někde popáno „co všechno“ tam může být definováno?).
K tvému dotazu: Mám udělaný vlastní authorizator v DB asi tam chyba nebude, to mi ve 2.0b funguje dobře, zde je část kódu:

use Nette\Security\Permission;
class AuthorizatorFactory extends Nette\Object
{
  private $permission;
  public function create(){
	$permission = new Permission;
        // roles
	$prava = new Prava;
	$roles = $prava->getRole();
	$permission = $this->setRolesDB($roles);
	$this->permission = $permission;
        // resources sets
	$zdroje = $prava->getResources();
	$this->setResourcesDB($zdroje);
	// privileges sets
	$this->setPrivilegesDB();
	$permission->allow('Admin', Permission::ALL, Permission::ALL);
	$this->permission = $permission;
	return $permission;
  }
	... dále už jen definice výše použitých metod
}

Editoval mr.mac (30. 11. 2013 20:31)

enumag
Člen | 2118
+
0
-

Zkus tam přidat anotaci:

use Nette\Security\Permission;
class AuthorizatorFactory extends Nette\Object
{
  //...
  /**
   * @return \Nette\Security\IAuthorizator
   */
  public function create(){
	//...
  }
}

Ta anotace musí být fully-qualified. V Nette 2.1 final to možná půjde i přes use statementy, na GitHubu se o tom právě vede diskuse.

Editoval enumag (30. 11. 2013 20:55)

mr.mac
Člen | 87
+
0
-

enumag napsal(a):

Zkus tam přidat anotaci:

Díky moc, pomohlo to, teď mám asi nesprávně načtené parametry z neonu, neboť mi laděnka hlásí Dibi is not connected to database, což se ani nedivím, (používám Dibi), protože určitě mám špatně v neonu službu k vytvoření connection k databázi. Jak by to tam mělo být správně?
Konstruktor v modelu mám totiž tento:

use Nette\DI\Container;

class Model extends DibiRow
{
	/** @var Dibi\Connection */
        public $CONN;
        public function __construct($arr = array())
        {
            parent::__construct($arr);
            $this->CONN = dibi::getConnection();
        }
    ...

Editoval mr.mac (30. 11. 2013 21:01)

enumag
Člen | 2118
+
0
-

Správně bys měl asi použít DibiExtension. Instaluje se to v bootstrapu takhle: https://doc.nette.org/…n/extensions. V Nette 2.1 to půjde instalovat přes neon.

S dibi konkrétně ti ale neporadím protože ji nepoužívám. Každopádně to statické getConnection zřejmě bude vadit.

Editoval enumag (30. 11. 2013 21:07)

mr.mac
Člen | 87
+
0
-

No s tím moc zkušenosti nemám, dříve jsem měl v bootstrapu na tvrďáka toto:

// create DB connection
dibi::connect($container->params['database']);

Jenomže teď mi to hlásí chybu, Undefined index: database, když si dumpnu ten $container, tak tam nějak nevidím pole database načtený z neonu, nevíš jak se na něj v bootstrapu odkázat, to by mi zatím stačilo a nechal bych to „po staru“, ale (snad) už s novou verzí nette a pak se budu postupně snažit to doladit. V každém případě díky.
Dump $container (nevidím ty načtené objekty z neonu):

SystemContainer(8) ▼ {
   classes => array(38) ►
   meta => array(0)
   parameters => array(8) ►
   params => array(8) ▼ {
      appDir => "C:\\xampp\htdocs\ems2\app" (24)
      wwwDir => "C:/xampp/htdocs/ems2/www" (24)
      debugMode => TRUE
      productionMode => FALSE
      environment => "development" (11)
      consoleMode => FALSE
      container => array(2) ▼ {
         class => "SystemContainer" (15)
         parent => "Nette\DI\Container" (18)
      }
      tempDir => "C:\\xampp\htdocs\ems2\app/../temp" (32)
   }
	...

Editoval mr.mac (30. 11. 2013 21:44)

enumag
Člen | 2118
+
0
-

No jestli jde jen o vytvoření té servicy tak takhle:

$container->getService('database');

Někde jsem viděl možnost že se přímo v neonu přidá nějaký tag se kterým se ta služba vytvoří automaticky. Nevím ale zda to funguje i ve 2.0, už dlouho jedu na dev.

mr.mac
Člen | 87
+
0
-

To nefunguje, chyba Circular reference detected for services: database. Jen mi jde o to, že v systémovém kontejneru nevidím ty parametry, které jsou uloženy v config.neon a měly by být načteny/vytvořeny v kontejneru metodou $container = $configurator->createContainer(). Tedy alespoň si to myslím.

enumag
Člen | 2118
+
0
-

Heh ty máš blbě tu definici, zkus tohle:

services:
    database:
        class: dibi
        create: dibi::connect
        arguments: %database%

A ty ten parametr database nemá co dělat v sekci nette:, musí být v parameters:.

Editoval enumag (30. 11. 2013 23:52)

mr.mac
Člen | 87
+
0
-

enumag napsal(a):

Díky, zase jsem o kousek dál. Opravil jsem to dle tvé rady, ještě jsem to musel pozměnit a nakonec jsem se dopracoval k „relativně funkčnímu“ stavu. Database jako služba zatím nic.
V bootsatrapu „zatím bohužel“ mám připojení natvrdo:

dibi::connect($container->params['database']);

A v config.neon toto – a chodí to:

	services:
		routerFactory: RouterFactory
		router: @routerFactory::createRouter
		authenticator: @model::createAuthenticator
		authorizatorFactory: AuthorizatorFactory
		authorizator: @authorizatorFactory::create
		model:
			class: Model
			arguments: [@database]
		database:
			class: DibiConnection
			arguments: [%database%]

Jenom pořád nevím kam uložit v config.neonu svoje proměnné v sekci constants to řve Constants may only evaluate to scalar values, což chápu, je někde požadovaná nová struktura config.neon popsaná?

Editoval mr.mac (1. 12. 2013 12:50)

enumag
Člen | 2118
+
0
-

Do parameters přece.

Btw. tohle nefunguje? Takhle se ti to spojení dost možná vytváří 2×.

mr.mac
Člen | 87
+
0
-

enumag napsal(a):
Do parameters přece.

Btw. tohle nefunguje? Takhle se ti to spojení dost možná vytváří 2×.

Nevím proč, ale v sekci parameters mi to řvalo, potom jsem si config.neon trochu „pročistil“ a je to ok, díky, už fakt blbnu.
To s tím dvojím připojením k DB mě taky napadlo, ale zatím ten tvůj postup opravdu stále hlásí, že spojení není vytvořeno.
Mám tady však ještě ještě jeden dotaz, zatím bez odezvy – myslíš, že by to mohlo být tím dvojím připojením (vytváří se duplicity v záznamech)?

Editoval mr.mac (1. 12. 2013 13:30)

enumag
Člen | 2118
+
0
-

Netuším.

mr.mac
Člen | 87
+
0
-

enumag napsal(a): Netuším.

Nevadí, v každém případě moc děkuji za pomoc. Jsem rád, že se mi to podařilo rozchodit a mohu „jít s dobou“.

mr.mac
Člen | 87
+
0
-

Tak jsem se pokusil zprovoznit svou aplikaci přímo pod Nette 2.1 RC4 a nedaří se mi autentikace, laděnka hlásí Authenticator has not been set. po odeslání přihlašovacího formuláře v SignPresenteru (na předposledním řádku při volání metody login):

public function signInFormSubmitted($form){
	try {
		$values = $form->values;
		$this->getUser()->login($values->username, $values->password);
		$this->application->restoreRequest($this->backlink);
	...

V config.neon mám to co uvádím výše:

...
	services:
		routerFactory: RouterFactory
		router: @routerFactory::createRouter

		authenticator: @model::createAuthenticator

		authorizatorFactory: AuthorizatorFactory
		authorizator: @authorizatorFactory::create
...

V modelu mám:

public function createAuthenticator() {
	return new Authenticator($this->CONN->dataSource('SELECT u.*, r.nazev [nrole]
		FROM users u LEFT JOIN role r ON u.role=r.id'));
}

Může mi prosím někdo pomoci? Díky předem!

Šaman
Člen | 2666
+
0
-

Předpokládám, že ta tvoje třída Authenticator implementuje rozhraní IAuthenticator? (V novém Nette jsou ty názvy služeb zbytečné, pokud si je sám netaháš podle jména. Důležité je jejich rozhraní.)


Add: Ještě si do configu do sekce nette můžeš přidat ladicí panel s výpisem celého kontejneru, aspoň budeš vědět, jestli se ti ta služba vytvořila, nebo ne:

nette:
	container:
		debugger: true

Editoval Šaman (22. 12. 2013 19:38)

mr.mac
Člen | 87
+
0
-

Díky za reakci a tip. Authenticator mám implementovaný „snad“ standardně:

use Nette\Object,
	Nette\Diagnostics\Debugger,
	Nette\Security as NS,
 	Nette\Security\Identity,
	Nette\Security\AuthenticationException;
/**
 * Users authenticator.
 */
class Authenticator extends Object implements NS\IAuthenticator
{
	private $users;
	public function __construct($users = array())
	{
		$this->users = $users;
	}
...

Bohužel v systémovém kontejneru mám u položky authenticator: Autowired: no, např. AuthorizatorFactory mi běží (viz můj config.neon). Bohužel autenticator stále nic.

EDIT: Tak authenticator mi už běží, změnil jsem v config.neon toto:

	services:
		authenticator: Authenticator

Bohužel na posledním řádku následujícího kódu mi laděnka hlásí Call to a member function where() on a non-object, přitom pokud jsem se z aplikace neodhlásil, tak mi připojení k DB pomocí Dibi fungovalo dobře.

public function authenticate(array $credentials){
   list($username, $password) = $credentials;
   $row = $this->users->where('username = %s', $username)->fetch();

Pole users je totiž prázdné, asi se neprovedla v modelu createAuthenticator (viz výpis configu výše). Nějak se v tom topím … bohužel v quick startu toho moc nenačtu a asi ani jinde, změny jsou rychlejší než návody :-(.

Už to funguje – viz níže.

Editoval mr.mac (23. 12. 2013 12:48)

mr.mac
Člen | 87
+
0
-

Tak nevím, jestli jsem neměl vyprázděnou cache, ale když jsem vrátil v config.neonu službu asi jak měla být authenticator: @model::createAuthenticator tak v contejneru vidím, že běží, pole users existuje a je naplněno.
Avšak teď mi laděnka hlásí: Nette\Application\UI\Presenter::getApplication() is deprecated; use dependency injection instead. v SignPresenteru na řádku 43:

38:   public function signInFormSubmitted($form)
39:   {
40:     try {
41:        $values = $form->values;
42:        $this->user->login($values->username, $values->password);
43:        $this->application->restoreRequest($this->backlink);
44:        $this->getPresenter()->getApplication()->restoreRequest($this->getPresenter()->backlink);

EDIT:
Potřebuji se dostat na původní stránku, kam zněl odkaz (musel jsem být „odkloněn“ na přihlášení). V Nette 2.0b mi tyto 2 řádky (43, 44) fungovaly bezchybně – něco se změnilo?

Editoval mr.mac (23. 12. 2013 12:48)

Robyer
Člen | 74
+
0
-

Ten restoreRequest zavolej na presenteru ;-)

mr.mac
Člen | 87
+
0
-

Kde? V SignPresenteru mám jen továrničku přihlašovacího formuláře createComponentSignInForm, pak jeho odeslání (viz výše signInFormSubmitted) a pak ještě akci na odhlášení, nic víc…?

Robyer
Člen | 74
+
0
-

Takhle jsem to myslel:

$this->getPresenter()->restoreRequest($this->backlink);
$this->getPresenter()->restoreRequest($this->getPresenter()->backlink);
mr.mac
Člen | 87
+
0
-

Díky, chodí to! Omlouvám se za tupost.