Problém s related() přes více než 2 tabulky v Nette 2.1-dev
- Eda
- Backer | 220
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
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 | 1838
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
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)
- Eda
- Backer | 220
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 | 1838
- 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
…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 | 1838
- 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
- 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 | 1838
@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
@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)
- ajda2
- Člen | 66
@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#…