Migrace na Nette 2.1 – getReferencedTable

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

Ahoj,
postupně se snažím migrovat ze staré verze Nette 2.0.17 na 2.1.8 a narazil jsem na problém, který nevím, jak jednoduše vyřešit.

Missing argument 3 for Nette\Database\Table\Selection::getReferencedTable(), called in XXX on line 53 and defined

Když kouknu do staré verze, tak getReferencedTable měla jako 3. parametr „$checkReferenced = FALSE“, ale nová verze tam má „$checkPrimaryKey“ (čili nyní je hodnota povinná), ale vypadá to, že nebude stačit jenom všude nastavit na FALSE.

Největší problém vidím v tom, že ať čtu jak chci, tak nikde o tom nevidím ani zmínku: https://doc.nette.org/…tions/to-2-1

Takže co s tím?

mpis
Člen | 65
+
0
-

S řešením Ti bohužel nepomůžu, ale z důvodů čistě studijních se ptám, proč nejdeš rovnou na 2.2.6?

kolsi
Člen | 131
+
0
-

Tak důvodů je víc, ale ten hlavní je, že to chceme upgradovat postupně (těch věcí, co se rozbilo je víc než bych čekal).

Problém jsem vyřešil zatím tak, že jako třetí parametr getReferencedTable() jsem dal všude „id“ – nevím, jestli jsem pochopil správně, že tam má být název sloupce odkazované tabulky, ale vypadá to, že to funguje.

Zatím se ale nemůžu zbavit pocitu, že Nette 2.1 je prostě pomalejší než předchozí verze a každá stránka generuje i o pár desítek více DB dotazů, např.

$not_added_roles = $this->model->getRoles()->fetchPairs('name', 'parent');

vygeneruje v Nette 2.0.17 jeden dotaz:

SELECT *
FROM `pd2_acl_role`
WHERE (`id` IN (1, 3, 4))
.../app/modules/CoreModule/model/Security/Authorizator.php

ale v Nette 2.1.8 vygeneruje 3 úplně stejné dotazy:

SELECT `id`, `name`
FROM `pd2_acl_role`
WHERE (`id` IN (1, 3, 4))
.../app/modules/CoreModule/model/Security/Authorizator.php:32

SELECT `id`, `name`
FROM `pd2_acl_role`
WHERE (`id` IN (1, 3, 4))
.../app/modules/CoreModule/model/Security/Authorizator.php:32

SELECT `id`, `name`
FROM `pd2_acl_role`
WHERE (`id` IN (1, 3, 4))
.../app/modules/CoreModule/model/Security/Authorizator.php:32

Editoval kolsi (27. 11. 2014 10:22)

Ot@s
Backer | 476
+
0
-

kolsi napsal(a):
ale v Nette 2.1.8 vygeneruje 3 úplně stejné dotazy:

A není to náhodou jen duplicita? Podívej se na časy, jak dlouho dotaz běžel. Že jsou stejné…

kolsi
Člen | 131
+
0
-

Časy jsou právě různé – 0.109, 0.079 a 0.078

hrach
Člen | 1834
+
0
-

Používás privátní metodu, tak se nediv. Mas používat ref a related.

kolsi
Člen | 131
+
0
-

Pokud jsem něco nepřehlídnul, tak ref a related jsou pro ActiveRow a getReferencedTable pro Selection, takže každý z nich dělá něco jiného.

P.S. duplicitně generované dotazy je jiný problém a s getReferencedTable to nemá souvislost.

kolsi
Člen | 131
+
0
-

Tak s tím fetchPairs jsem to vyzkoumal takto:
Pokud je druhá hodnota cizí klíč (resp. sloupec je „parent_id“ a já uvedu „parent“), tak fetchPairs všechny cizí klíče fetchne a pole obsahuje rovnou ActiveRow. To je dobře. Ale v Nette 2.0 se provede 1 x SELECT .. IN (všechny hodnoty cizích klíčů), zatímco v Nette 2.1 se ten stejný SELECT provede tolikrát, kolik těch cizích klíčů je. A to není úplně dobře.

hrach
Člen | 1834
+
0
-

A nededis náhodou od selection? Ta duplcita by mohla byt kvůli tomu třetímu parametru…

kolsi
Člen | 131
+
0
-

Connection (teď už vlastně Context) a Selection máme sice vlastní, ale dělá to, i když se nepoužijou.

EDIT: tak to vypadá, že dotaz se generuje znovu vždy, když ten cizí klíč má hodnotu NULL.

Editoval kolsi (27. 11. 2014 15:34)

kolsi
Člen | 131
+
0
-

Ještě se zeptám takhle – když je getReferencedTable tedy privátní metoda (ačkoli to nikde není uvedeno), tak pokud mám 2 tabulky (např. projects a users) spojené vazbou M:N (mám tedy spojovací tabulku projects_users), jak tedy optimálně (= co nejmenší množství dotazů + co nejrychleji + co nejméně kódu) vytáhnu data z jedné tabulky pro hodnotu z druhé tabulky?

Např. chci všechny projekty pro uživatele s ID $user_id. Nyní dělám:

$this->connection->table("projects_users")->where("user_id", $user_id)->getReferencedTable("projects", "project_id");

Předpokládám, že Nette vytvoří pouze jeden dotaz a vrátí mi Selection obsahující všechny vyhovující záznamy z tabulky „projects“. Pokud místo getReferencedTable() použiju related():

$this->connection->table("users")->get($user_id)->related("projects_users", "user_id")

Tímto ale dostanu Selection obsahující záznamy z tabulky projects_users a pak musím volat ještě $record->project->…, což mi vytvoří další dotazy navíc.

Takže dotaz je jasný.

norbe
Backer | 405
+
+2
-

A proč porovnáváš 2 různé věci?

Jednou vycházíš z tabulky projects_users a podruhé z users. Osobně bych použil ten druhý kód, schválně se podívej, kolik ti ten dotaz navíc sežere času…

Pokud chceš nejefektivněji získat projekty daného uživatele uděláš to takhle: $this->connection->table('projects')->where(':projects_users.user_id', $userId).

kolsi
Člen | 131
+
0
-

První případ je, jak to děláme teď. Ten druhý jsem sestavil teď narychlo podle dokumentace s využitím „related“ (jak radil hrach o pár příspěvků výše). related se musí volat nad ActiveRow, proto jsem vyšel z jiné tabulky.

Každopádně jednoduché vypsání názvu všech projektů je s „related“ skoro 2× pomalejší než s getReferencedTable. Ale tvůj návrh vypadá použitelně, protože je zase naopak asi 2× rychlejší než getReferencedTable :) Ještě musím projít, jestli to půjde použít ve všech našich případech a pokud jo, tak problém bude vyřešen. Díky!

norbe
Backer | 405
+
0
-

Jasné, každopádně ActiveRow si ale můžeš vytáhnout i z tabulky projects_users ;)

kolsi
Člen | 131
+
0
-

To si právě nemyslím, protože table(„projects_users“)->where(„user_id“, $user_id) vrací více záznamů. Pokud tedy následně nechci pomocí foreach přes všechny iterovat…

norbe
Backer | 405
+
0
-

Ano, table(„projects_users“)->where(„user_id“, $user_id) vrací více záznamů, proto existuje metoda fetch