Problém s přechodem na nette 2.1 – Nette\Security\User $user a Circular reference

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

Ahoj, právě převádím jeden ze starších projektů z nette 2.0.xx na 2.1, ale mám zásek na jedné chybě:

Nette\InvalidStateException

Circular reference detected for services: user, authenticator, 27_Todo_EmailRepository, 29_Todo_LogRepository.

public function createService__29_Todo_LogRepository()
168:        {
169:            $service = new Todo\LogRepository(new Nette\Database\Context($this->getService('nette.database.default'), NULL, $this->getService('cacheStorage')), $this->getService('session'), $this->getService('user'));
170:            return $service;
171:        }

Zjistil jsem, že to je tím, že v LogRepository mám v konstruktoru Nette\Security\User $user.

Nevíte někdo, jak to rozchodit? V LogRepository potřebuji totiž zjišťovat, zda je user přihlášen a jeho další atributy (jméno a pod…)

Díky

jiri.pudil
Nette Blogger | 1032
+
0
-
  • do Nette\Security\User se v konstruktoru předává autentikátor, pozor na kruhové závislosti

https://phpfashion.com/…na-nette-2-1

n.u.r.v.
Člen | 485
+
0
-

jiri.pudil napsal(a):

  • do Nette\Security\User se v konstruktoru předává autentikátor, pozor na kruhové závislosti

https://phpfashion.com/…na-nette-2-1

Hmm, to trochu nepobírám – co to pro mě znamená? Jak mám tedy dostat usera do modelu?

Zjistil jsem ale ještě jeden problém – při insertu do DB se automaticky provede select tohoto posledního záznamu, ale problém je, že moje DB tabulka á primární klíč nepojmenovaný jako id, takže mi vznikne následující chyba:

PDOException #42S22

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'mail_udalost.id' in 'where clause
SELECT *
FROM `mail_udalost`
WHERE (`mail_udalost`.`id` = '10')

označen řádek 109:            $this->connection->table("mail_udalost")->insert($data);

To bude asi velký problém -že? Protože s DB nic neudělám…

Editoval n.u.r.v. (16. 1. 2014 13:13)

enumag
Člen | 2118
+
0
-

Ve 2.1 jsou DI accessory přes které by to mělo jít, netuším ale jak se to v configu zapisuje a zdokumentované jsem to nikde neviděl.

n.u.r.v.
Člen | 485
+
0
-

enumag napsal(a):

Ve 2.1 jsou DI accessory přes které by to mělo jít, netuším ale jak se to v configu zapisuje a zdokumentované jsem to nikde neviděl.

Ahoj, to je odpověď pro tu DB, nebo pro předání usera? Díky…

enumag
Člen | 2118
+
0
-

Pro předání usera.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Takováto potíž je skvělým nasměrováním k vhodnému refaktoringu :). Proč je LogRepository potřeba v autentikátoru? (Kvůli tomu předpokládám vzniká ona kruhová závislost.)

enumag
Člen | 2118
+
0
-

Nejspíš kvůli logování pokusů o přihlášení (což se dá nejspíš řešit přes události). Osobně mi ale více v nepořádku připadá potřeba Usera v modelu.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

enumag: Osobně mi ale více v nepořádku připadá potřeba Usera v modelu.

Proč ti přijde User v modelu špatně?

n.u.r.v.
Člen | 485
+
0
-

vojtech.dobes napsal(a):

Takováto potíž je skvělým nasměrováním k vhodnému refaktoringu :). Proč je LogRepository potřeba v autentikátoru? (Kvůli tomu předpokládám vzniká ona kruhová závislost.)

Mno LogRepository vůbec v authenticatoru nemám…do logu zapisuji pomocí presenteru.

Log repository je injectovaný v base:

public function injectRepository(...,Todo\LogRepository $logRepository){
...;
        $this->logRepository = $logRepository;
    }

a v prezenterech volám $this->logRepository->insertLog();

  • přidávám se k dotazu uživatele vojtech.dobes

Teď se spíš děsím té DB, protože pokud nevyřeším jak nette 2.1 zprovoznit na DB, kde primary key není pojmenován jak „id“, tak budu muset zůstat u staršího nette…

Edit: Dočasně vyřešeno tak, že jsem v nette/databse/context.php přepsal v konstruktoru
$this->reflection = $reflection ?: new Reflection\ConventionalReflection;
na
$this->reflection = $reflection ?: new Reflection\DiscoveredReflection($this->connection);

Ale toto neberu jako řešení… na foru jsem se dočetl, že by to mělo jít přepnout v neon pomocí reflection: discovered, ale to mi nefunguje

Editoval n.u.r.v. (16. 1. 2014 14:20)

enumag
Člen | 2118
+
0
-

@vojtech.dobes: Protože důvodem je obvykle automatické filtrování v závislosti na $user->getId(), což dle mého názoru není starost modelu ale controlleru/presenteru (nemluvím jen o Nette). Tedy controller by měl předat ID uživatele při volání konkrétního dotazu – už proto aby se např. admin mohl dívat na data ostatních uživatelů. Že se nikdo neoprávněný nedostane k cizím datům je starost presenteru a autorizátoru.

Editoval enumag (16. 1. 2014 14:48)

n.u.r.v.
Člen | 485
+
0
-

enumag napsal(a):

@vojtech.dobes: Protože důvodem je obvykle automatické filtrování v závislosti na $user->getId(), což dle mého názoru není starost modelu ale controlleru/presenteru (nemluvím jen o Nette). Tedy controller by měl předat ID uživatele při volání konkrétního dotazu – už proto aby se např. admin mohl dívat na data ostatních uživatelů. Že se nikdo neoprávněný nedostane k cizím datům je starost presenteru a autorizátoru.

Ok, problém je, že v mém případě může nastat situace že se user smaže (úplně, ne příznak) ale já potřebuji v logu vidět i potom jak byl user aktivní…tedy když budu zapisovat do logu jen id usera, tak po smazání tohoto usera mi bude id k „píp“… Proto musím zapisovat do logu i jeho jméno a příjmení a další informace.

Proto mám v metodě pro zápis do logu toto:

public function insertLog(...){
...
...
            if ($this->user->isLoggedIn()) {
                $data = array(
                    "CV" => $this->session->id_visitor,
                    "CU" => $this->us->getIdentity()->getId(),
                    "JMENO" => $this->us->getIdentity()->JMENO,
                    "PRIJMENI" => $this->us->getIdentity()->PRIJMENI,
                    "CLT" => $udalost,
                    "DATUM" => $this->datum,
			...
			...
                );
            } else {
                $data = array(
                    "CV" => $this->session->id_visitor,
                    "CU" => 0,
                    "JMENO" => "",
                    "PRIJMENI" => "",
                    "CLT" => $udalost,
                    "DATUM" => $this->datum
			...
			...
                );
            }
            $this->connection->table("log")->insert($data);
        }
}

Tedy se nabízí dvě řešení:

  1. v prezenteru metodě předat pole s daty pro zápis již připravené
  2. v prezenteru předat metodě $this->user…
enumag
Člen | 2118
+
0
-

@n.u.r.v.: Ani 1, ani 2. Předal bych $user->getIdentity().

EDIT: Nebo bych si vytáhl entitu uživatele z databáze dle ID a předal tu. Identita totiž ne vždy obsahuje aktuální data o uživateli. Děje se to v případech kdy se data uživatele změnila poté co se přihlásil. Samozřejmě jen pokud to nemáš ošetřené např. pomocí vlastního UserStorage.

Editoval enumag (16. 1. 2014 15:07)

n.u.r.v.
Člen | 485
+
0
-

enumag napsal(a):

@n.u.r.v.: Ani 1, ani 2. Předal bych $user->getIdentity().

EDIT: Nebo bych si vytáhl entitu uživatele z databáze dle ID a předal tu. Identita totiž ne vždy obsahuje aktuální data o uživateli. Děje se to v případech kdy se data uživatele změnila poté co se přihlásil. Samozřejmě jen pokud to nemáš ošetřené např. pomocí vlastního UserStorage.

Hmm, vlastní userstorage nemám, ale to načtení dat z db podle id je docela dobré řešení -id usera dokážu předat…zkusím…

Milo
Nette Core | 1283
+
0
-

Databáze a konfigurace v neonu.
https://doc.nette.org/cs/configuring#…

n.u.r.v.
Člen | 485
+
0
-

Milo napsal(a):

Databáze a konfigurace v neonu.
https://doc.nette.org/cs/configuring#…

Ano, toto jsem našel, ale bohužel reflection: discovered nefunguje – tento údaj je ignorován – nemá u mě na nic vliv…

Ale mám jiný problém – jak jsem dočasně natvrdo v nette/database/context.php přepsal v konstruktoru
$this->reflection = $reflection ?: new Reflection\ConventionalReflection;
na
$this->reflection = $reflection ?: new Reflection\DiscoveredReflection($this->connection);

tak to sice funguje, jenže teď při každém dotazu na db si nette nejdříve osahává DB (SHOW FULL COLUMNS), takže mám teď dvojnásobný počet dotazů na db…Což je špatné.

enumag
Člen | 2118
+
0
-

Musíš té DiscoveredReflection předat ICacheStorage. Že to nefunguje přes neon je ale divný.

n.u.r.v.
Člen | 485
+
0
-

enumag napsal(a):

Musíš té DiscoveredReflection předat ICacheStorage. Že to nefunguje přes neon je ale divný.

Hups… v tomhle jsem trochu ještě mimo – jak se to předává, Díky

enumag
Člen | 2118
+
0
-

Jako druhý parametr, podívej se do API… Anebo spíše ukaž co v tom neonu máš protože NetteExtension tam to storage dává automaticky a není důvod to dělat jinak.

n.u.r.v.
Člen | 485
+
0
-

enumag napsal(a):

Jako druhý parametr, podívej se do API… Anebo spíše ukaž co v tom neonu máš protože NetteExtension tam to storage dává automaticky a není důvod to dělat jinak.

toto je můj config.neon

common:
	parameters:

	includes:
		- const.neon

	php:
		date.timezone: Europe/Prague
		# zlib.output_compression: yes

	nette:
		application:
			errorPresenter: Error

		database:
			default:
				dsn: 'mysql:host=localhost;dbname=db1'
				user: user1
				password: heslo1
				reflection: discovered
			db_2:
				dsn: 'mysql:host=ip2;dbname=db2'
				user: user2
				password: heslo2
				reflection: discovered
			db_3:
				dsn: 'mysql:host=ip3;dbname=db3'
				user: user3
				password: heslo3
				reflection: discovered

		session:



	services:
		authenticator: Authenticator
		routerFactory: RouterFactory
		router: @routerFactory::createRouter

		- Todo\FirstsignRepository
		- Todo\FirmaRepository(Nette\Database\Context(@nette.database.db_2), Nette\Database\Context(@nette.database.db_3))
		- Todo\UzivatelRepository
		- Todo\SluzbaRepository
		- Todo\EmailRepository(%email_odesilatel%, %email_smtp%, %email_odkaz%, Nette\Database\Context(@nette.database.default))
		- Todo\MenuRepository
		- Todo\LogRepository(Nette\Database\Context(@nette.database.default))

Pak mám ještě const.neon, kde mám uložené konstanty (parameters:)

koukal jsem do temp/cache a je tam složka co má v názvu mimo jiné i slovo database a v ní je několik souborů takže to asi něco do cache ukládá…

Je také zajímavé, že někdy si to DB ošahá a někdy ne – např vlezu do sekce pro výběr usera k editaci (tedy se provádí select v db tabulce userů – a v tomto případě proběhne jen samotný select. Zase ale na jiných stránkách to provádí SHOW FULL COLUMNS pořád – i když dám reload stránky)

Editoval n.u.r.v. (17. 1. 2014 9:38)

enumag
Člen | 2118
+
0
-

Aha, jasně. Změň Nette\Database\Context(@nette.database.db_2) na @nette.database.db_2.context, podobně pro ostatní databáze.

n.u.r.v.
Člen | 485
+
0
-

enumag napsal(a):

Aha, jasně. Změň Nette\Database\Context(@nette.database.db_2) na @nette.database.db_2.context, podobně pro ostatní databáze.

jů, už to funguje – osahá si to tabulku jen při prvním přístupu, pak už ne – výborně…díky…

Mno ještě by mě zajímalo, zda bude pro nette 2.1 vydán nový BootstrapRenderer – ten starý mi píše u některých metod deprecated…