Problém s related() přes více než 2 tabulky v Nette 2.1-dev

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

Zdarec.

Narazil jsem na jeden problém v Nette/Database.

Uvažujme následující schéma databáze:

Tedy řádek z X může mít potomky v tabulce Y a řádek z Y zase další potomky v Z, kde jsou pak uloženy SPZ.

Data vypadají takto:

INSERT INTO `tabulkaX` (`id`) VALUES
(1),
(2);


INSERT INTO `tabulkaY` (`id`, `idvx`) VALUES
(1,	1),
(2,	2),
(3,	2);


INSERT INTO `tabulkaZ` (`id`, `idvy`, `spz`) VALUES
(1,	1,	'8B8 8888-1'),
(2,	2,	'8B8 8889'),
(4,	3,	'8B8 7777');

Uvažme kód v prezenteru:

foreach($db->table('tabulkaX') as $x)
{
	foreach($x->related('tabulkaY') as $y)
	{
		foreach($y->related('tabulkaZ') as $z)
		{
			dump($z->spz);
		}
	}
}

Chci tedy vypsat postupně všechna X a k nim jejich SPZ.

Nevidím v tom kódu chybu, avšak nefunguje.

Vypíší se jen SPZ prvního X, SPZky druhého X už nikoli.

Na databázi jsou přitom položeny tyto dotazy:

SELECT `id` FROM `tabulkaX`

SELECT * FROM `tabulkaY` WHERE (`tabulkaY`.`idvx` IN (1, 2))

SELECT * FROM `tabulkaZ` WHERE (`tabulkaZ`.`idvy` IN (1, 2, 3))
SELECT * FROM `tabulkaZ` WHERE (`tabulkaZ`.`idvy` IN (NULL))

Ano, problém zřejmě způsobuje to, že je položen dotaz na tabulku Z se špatným kritériem. Proč tento dotaz ale Nette pokládá nejsem schopen zjistit. Zkoušel jsem se pídit přímo v kódu ActiveRow, ale nedaří se mi to rozluštit.

Používám vývojovou verzi 2.1-dev. Ve stabilní 2.0.3 to funguje bez problémů.

EDIT:
V 2.0.3 (revision eb558ae) je pro změnu divné, že se nepokládají dotazy s klauzulí IN, ale tahají se data z celé tabulky…

SELECT `id` FROM `tabulkaX`
SELECT * FROM `tabulkaY`
SELECT * FROM `tabulkaZ`

To je očekávané chování?

Editoval Eda (25. 7. 2012 22:28)

jtousek
Člen | 951
+
0
-

Co to dělá na této testovací větvi? Hrach už opravil spoustu bugů, jen to ještě není dost dobře otestované.

hrach
Člen | 1834
+
0
-

Doporucuji spis tuto vetev :)
https://github.com/…its/f-db-wip
Vetev f-db-dev je zatim spise experimentalni, ackoliv bez nejakych BC breaku.

Edit: tady ta f-db-wip je celkem otestovana uz dobre :)

Editoval hrach (26. 7. 2012 10:29)

Eda
Backer | 220
+
0
-

Zkoušel jsem obě větve:

Větev f-db-dev funguje tak, jak má – vypíše opravdu 8B8 8888–1, 8B8 8889 a 8B8 7777. Háže mi to pro změnu ale chyby v debugBaru – nevypíší se dotazy, které byly položeny. Místo toho:

Error: Nette\Database\Diagnostics\ConnectionPanel

exception 'Nette\MemberAccessException' with message 'Call to undefined method Nette\Database\Statement::fetchAll().' in /home/eda/Plocha/Server/testy/libs/Nette/common/ObjectMixin.php:74
Stack trace:
#0 /home/eda/Plocha/Server/testy/libs/Nette/common/Object.php(81): Nette\ObjectMixin::call(Object(Nette\Database\Statement), 'fetchAll', Array)
#1 /home/eda/Plocha/Server/testy/libs/Nette/Database/Diagnostics/ConnectionPanel.php(102): Nette\Object->__call('fetchAll', Array)
#2 /home/eda/Plocha/Server/testy/libs/Nette/Database/Diagnostics/ConnectionPanel.php(102): Nette\Database\Statement->fetchAll()
#3 /home/eda/Plocha/Server/testy/libs/Nette/Diagnostics/Bar.php(63): Nette\Database\Diagnostics\ConnectionPanel->getPanel()
#4 /home/eda/Plocha/Server/testy/libs/Nette/Diagnostics/Debugger.php(377): Nette\Diagnostics\Bar->render()
#5 [internal function]: Nette\Diagnostics\Debugger::_shutdownHandler()
#6 {main}

Větev f-db-wip, co doporučuje hrach, nefunguje. Položí se opět tyto dotazy:

SELECT `id` FROM `tabulkaX`
SELECT * FROM `tabulkaY` WHERE (`tabulkaY`.`idvx` IN (1, 2))
SELECT * FROM `tabulkaZ` WHERE (`tabulkaZ`.`idvy` IN (1, 2, 3))
SELECT * FROM `tabulkaZ` WHERE (`tabulkaZ`.`idvy` IN (NULL))

Co mám zkusit dál? :-)

Dále také v té verzi wip nefunguje tohle (na stejném schématu DB jako výše), na čtvrtý zápis jsem chtěl upozornit v samostatném vláknu, ale nakonec to hodím sem…:

foreach($db->table('tabulkaC') as $c)
{
	// nefunguje, hází No reference found for $tabulkaC->tabulkaB.
	echo $c->ref('tabulkaB')->id;
	echo $c->tabulkaB->id;

	// tohle funguje...
	//echo $c->ref('tabulkaB','idvb')->id;

	/* takto mi to funguje ve verzi 2.0.3, řekl bych, že je to dost velká chyba,
	protože všude (např. v hrachově prezentaci z PS...) je uvedeno,
	že v argumentu ref() má být tabulka, na kterou se chci odkázat,
	nikoliv sloupec, na přes který se chci odkázat. Nicméně to takto
	funguje a při troše snahy tomu lze aplikaci přizpůsobit. Než jsem
	na to ale přišel, málem jsem zešedivěl. Špatná implementace
	getBelongsToReference způsobuje i to, že například musím zapsat
	do where() parametr "tabulka1:tabulka2:column2.column3", abych
	vyhledával v tabulce 3 přes column3 - také velmi matoucí, nehledě
	na to, že to nikde není pořádně zdokumentované a v podstatě jediný
	zdroj informací a ukázek, jak vybírat přes další tabulky, je hrachova
	prezentace z PS...
	//echo $c->ref('idvb')->id;

}

Neberte to prosím jako rýpání, byl bych jen velmi rád, kdyby se nám podařilo v dohledné době odladit všechny chybky a rád s tím nějak pomůžu. Nechtěl bych jen kvůli pár bugům zbytečně celý projekt předělávat na NotORM, byť ten je asi bez závažnějších chyb…

Editoval Eda (26. 7. 2012 13:30)

jtousek
Člen | 951
+
0
-

Ještě jsem zapomněl říct… vždy když tam dáš jinou verzi Nette, je nutné smazat cache a raději i btfj.dat.

Eda
Backer | 220
+
0
-

Cache vždycky mažu, snad jsem na to nikdy nezapomněl.

btfj.dat nemažu, netušil jsem, že může mít vliv.

Teď jsem zkusil smazat i btfj a stejně to nepomohlo. Ani echo $c->ref('tabulkaB')->id; ani vypisování v těch třech cyklech stále nefunguje (verze wip).

Editoval Eda (26. 7. 2012 13:33)

hrach
Člen | 1834
+
0
-
  • Kluci, ale to ste meli rict hned, ze ten registr vozidel jede na nette\database. To bych ty bugfixoval rychleji :D
  • Predem bych chtel moc podekovat, ze to resis na foru a nevykasles se na nas :) Dale ti doporucuji opravdu neodchazet k notormu, to je mnohem vice zabugovane ;)
  • f-db-dev sem opravil. Diky za report.
  • f-db-wip moc netusim, proc nefunguje. Mohl bys ji prosim zkusit jeste jednou a urcite smazat cache? :) Diky.
  • pro „čtvrtý zápis“ prosím založ nové vlákno
  • prosím, postni sem (i do nového vlákna) testovací sql definice. tzn. včetně cizích klíčů.
Eda
Backer | 220
+
0
-

…registr :-D
Nj, dneska jsem tam nasadil -dev a už to aspoň trochu jede :-D

Ok, dám ještě N\DB šanci :-)

Všechny testy provádím na této databázi. Je tam 6 tabulek, v souboru je kompletní dump z Admineru:
http://leteckaposta.cz/986180118

f-db-dev: Díky, dbPanel už frčí.

f-db-wip: Ještě jednou jsem to zkoušel a fakt to nefunguje. Zkus si naimportovat můj dump databáze a do Nette sandboxu si dej tento kód:

foreach($db->table('tabulkaX') as $x)
{
	foreach($x->related('tabulkaY') as $y)
	{
		foreach($y->related('tabulkaZ') as $z)
		{
			dump($z->spz);
		}
	}
}

Očekávaný výpis je „8B8 8888–1“ „8B8 8889“ „8B8 7777“.
Vypíše se však jen „8B8 8888–1“.

Položí se následující dotazy:

SELECT `id` FROM `tabulkaX`
SELECT * FROM `tabulkaY` WHERE (`tabulkaY`.`idvx` IN (1, 2))
SELECT * FROM `tabulkaZ` WHERE (`tabulkaZ`.`idvy` IN (1, 2, 3))
SELECT * FROM `tabulkaZ` WHERE (`tabulkaZ`.`idvy` IN (NULL))

Já už s tím asi víc nehnu, je to na Tobě :-)

Jinak: Zkus si ten stejný kód spustit i pro tabulky A, B a C. Mají stejné schéma jako X, Y, Z, jen jiná data. A, světe div se, na nich to funguje. Tzn.

foreach($db->table('tabulkaA') as $a)
{
	foreach($a->related('tabulkaB') as $b)
	{
		foreach($b->related('tabulkaC') as $c)
		{
			dump($c->spz);
		}
	}
}

Položené dotazy:

SELECT `id` FROM `tabulkaA`
SELECT * FROM `tabulkaB` WHERE (`tabulkaB`.`idva` IN (1, 2, 3))
SELECT * FROM `tabulkaC` WHERE (`tabulkaC`.`idvb` IN (1, 2, 3))

Tedy žádný NULL dotaz… = bez problémů.

A vypíší se očekávané „1“ „2“ „3“ „4“ „5“ „6“ „7“ „8“ „9“

Zajímavé :-)

Bohužel už asi nebudu moct reagovat (a založit vlákno o „čtvrtém zápisu“), zítra odjíždím na měsíc pryč :-( Až se ale vrátím, můžu pokračovat v analýze :-)

Díky za snahu.

hrach
Člen | 1834
+
0
-
  • nefungovalo mi to ani na f-db-dev – musel sem udelat dalsi patch na velikost pismen :)
  • po patchi aplikovanem na f-db-dev i na f-db-wip mi obe vetve fungovaly naprosto bez problemu :)
  • mazes patnou cache nebo nevim :D kazdopadne to jede uz. diky. A jsem rad, ze se mi potvrdilo to, co sem rikal, ze f-db-wip by mela fungovat taky, f-db-dev ma zmeny uplne nekde jinde :)
Eda
Backer | 220
+
0
-
  • Kterou verzi teda mám testovat a používat? Větev f-db-wip byla zrušena, zbývá tedy f-db-dev a f-database-refactoring.
  • Zkusil jsem tedy větev f-databaze-refactoring, stále stejné schéma DB, stejný kód. Výsledkem byla chyba SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.tabulkax' doesn't exist. Patch velikosti písmen spíš nezabral :-)
  • Hm, a ve větvi f-db-dev je výsledek stejný.
  • Když tabulky přejmenuju na malopísmenné (tabulkax, tabulkay, tabulkaz), vše funguje bez problémů.

Editoval Eda (27. 7. 2012 1:51)

hrach
Člen | 1834
+
0
-

Lol :) je to nejaky zacarovany kruh.

  • pouzivat jen f-database-refactoring
  • chyba bude imo v konfiguraci mysql. dokud sem dany path neudelal, nejelo zase nic mne
  • spus prosim na dane vetvi takto testy RunTests.bat -j 1 Nette\Database, uvidime, co to da.
ajda2
Člen | 66
+
0
-

Je teda prosím někde na Githubu funkční řešení? Tohle mi přijde jako dost zásadní věc.
Nebo se mám u takovýchto „složitějších“ konstrukcí na Nette Database vyakšlat a napsat si Joiny ručně?
Případně to obejít ještě nějak jinak? Nenapadá mě jak.

hrach
Člen | 1834
+
0
-

@ajda2: tak jeste jednou, tohle vsechno funguje, jsou na to napsane testy, ktere prochazeji.
Tuto funkcni cast nette si stahni zde: https://github.com/…-refactoring
Doufam, ze bude brzy mergnuta.

ajda2
Člen | 66
+
0
-

@hrach: Díky za rychlou reakci, ale bohužel… Stále se mi nakonec vykoná dotaz

<?php
SELECT *
FROM `image_lang_data`
WHERE (`image_lang_data`.`image_id` IN (NULL))
?>

Což jednak končí chybou a jednak nenačte správně záznamy.
Případně můžu dodat ještě dump databáze, zda není problém u mě.

Editoval ajda2 (30. 7. 2012 17:31)

hrach
Člen | 1834
+
0
-

Pokud mas trochu casu, muzes prosim zalozit nove vlakno? Dej tam prosim dump sve db a cast kodu, kterou volas. Diky moc. (Ps: doufam, ze si promazal cache ;))

ajda2
Člen | 66
+
0
-

@hrach: Samozřejmě sem cache vymazal :)
Založil jsem nové vlákno, kde je podrobný popis, schéma DB i s dumpem včetně dat + kód
https://forum.nette.org/…ated-problem#…