Obálka nad Nette\Database pro lepší entity
- jansfabik
- Člen | 193
Udělal jsem si takovou jednoduchou nadstavbu nad Nette\Database, která
používá pro řádky každé tabulky jinou třídu (potomka
ActiveRow
). Myslíte si, že je to dobrý nápad?
Přináší to spoustu výhod:
- našeptávání sloupců
- možnost přidávat nové metody (např.
$user->createIdentity()
apod.) - možnost přidávat nové properties (např.
$user->articles
místo$user->related('articles')
)
A zároveň to nepřichází o žádnou z výhod Nette\Database.
Takhle to vypadá v praxi: presenter, config, modely
Asi k tomu udělám ještě extension pro config (podobně jako má nette/database) a generátor modelů a konfigurace ze schémat.
repozitář: https://github.com/fabik/database
composer: fabik/database
Editoval jansfabik (22. 5. 2012 20:56)
- jansfabik
- Člen | 193
Napadlo mě, že by se tím daly i čistě vyřešit many-to-many relace (hlavně pro šablony by to byla obrovská úleva).
$items = $this->items->findBy(...);
foreach ($items as $item) {
echo $item->name;
foreach ($item->tags as $tag) {
echo $tag->name;
}
}
Ale zatím nevím, jak udělat tu metodu getTags()
. Tohle jsem
zatím zkoušel: (Vrací to ale nejenom tagy aktuálního itemu, ale
i ostatních itemů ve výběru).
public function getTags()
{
return $this->related('item_tags')->getReferencedTable('tags', 'tag_id');
}
Nevíte někdo, jak by to šlo udělat?
- jansfabik
- Člen | 193
něco takového jsem hledal, škoda, že je to závislé na https://github.com/…refactoring/ snad se to brzo objeví v nette
- juzna.cz
- Člen | 248
Hrach to v NDAB nema lazy a ani jeho syntaxe se mi moc nelibi ;) Ja jsem si ruzne formy relaci udelal pekne lazy. Navic vsecko to vraci Selection, takze je mozne nad relaci provadet dalsi sekekce nebo razeni (hrach ma eager loading a vraci obyc pole).
Ale jeste to neni stable kvuli bugum v nette db. Zkousel jsem refactoring od Hracha, kde mi to zase delalo problemy s pameti. A navic vcera hosiplan nasel bug v samotnem PHPku, ktere dela memory leaky.
No, pozvu hracha na pivo a snad to doresime. Uz me to taky hodne tlaci. Stay tuned.
- jansfabik
- Člen | 193
jtousek:
$connection->onQuery[] = callback(new \Nette\Database\Diagnostics\ConnectionPanel(), 'logQuery');
Pokud se databáze konfiguruje v config.neon pomocí sekce
nette: database: ...
, tak se to defaultně doplní samo.
juzna: Zkoušel jsem to a myslím si, že řazení nefunguje. Ale to řešení M:N relací přes Selection mi přijde mnohem elegantnější a čistší než přes pole.
hrach: Výhodu lazy přístupu nevidím v tom, že pokud ta
data nebudu číst, tak se ten dotaz neprovede, ale že si můžu ty prvky
ještě dodatečně profiltrovávat, řadit apod. (např. bych měl metodu
getTags()
a někde bych chtěl vypsat tagy podle názvu a někde
jinde podle priority tags->order('name')
,
tags->order('priority')
).
- jansfabik
- Člen | 193
hrach: Nikde jsem nenapsal, že se u toho nebudou používat gettery ;-), klidně můžou. Ale i při jejich použití se mi juznovo řešení jeví jako mnohem elegantnější.
function getTags()
{
return $this->relatedMN('book_tag', 'tag');
}
function getTagsSortedByName()
{
return $this->tags->order('name');
}
function getTagsSortedByPriority()
{
return $this->tags->order('priority');
}
vs.
function getTags()
{
return $this->getSubRelation('book_tag:tag');
}
function getTagsSortedByName()
{
return $this->getSubRelation('book_tag:tag', function ($related) {
$related->order('tag.name');
});
}
function getTagsSortedByPriority()
{
return $this->getSubRelation('book_tag:tag', function ($related) {
$related->order('tag.priority');
});
}
Škoda jen, že to juznovo nefunguje. Myslím, že vím, jak by to šlo opravit, ale nemám čas to teď psát. Jestli by se do toho někomu chtělo, tak tady je pseudokód:
booksTags = tagsBooks = tags = []
rows = fetchAll("SELECT * FROM book_tags WHERE book_id IN [book_ids]")
foreach (rows as row)
tagsBooks[row.tag_id][] = row.book_id
rows = fetchAll("SELECT id FROM tags
WHERE id IN [array_keys(tagsBooks)] AND [where]
ORDER BY [order]")
foreach (rows as row)
foreach (tagsBooks[row.id] as book_id)
booksTags[book_id][] = row.id
foreach (booksTags as &bookTags)
bookTags = array_slice(bookTags, offset, limit)
foreach (bookTags as tag_id)
tags[tag_id] = NULL
rows = fetchAll("SELECT * FROM tags WHERE id IN [array_keys(tags)]")
foreach (rows as row)
tags[row.id] = row
Pro každou knihu mám id všech tagů v booksTags[id]
a můžu
si je proto vytáhnout z pole tags
.
- hrach
- Člen | 1838
Spatny uhel pohledu.
function getTags($order = NULL)
{
return $this->getSubRelation('book_tag:tag', function ($related) use ($order) {
$related->order($order);
});
}
Nicmene ten zasadni rozdil je v tom, kdy se provadi filtrovani potrebnych dat. To jsem dnes vysvetloval prave @juznovi. Nakonec pochopil :D Oba pristupy maji neco do sebe. Nicmene napr. toto je (v nekterych pripadech vyrazne) efektivnejsi v mem podani (jde o to where, ktere filtruje data nad tabulkou book_tag, ne az na tagu):
function getTags($order = NULL)
{
return $this->getSubRelation('book_tag:tag', function ($related) {
$related->where("usedCount > ?", 100);
});
}
- jtousek
- Člen | 951
Dívám se teď detailněji na implementaci, protože bych rád něco takového použil. Váhám mezi tímto a ndab.
Mnoho kódu je zkopírováno z Nette\Database pouze s úpravou
new ActiveRow($data, $this);
⇒
$this->createRow($data);
. Nebylo by lepší udělat to pomocí
forku Nette než takto? Jakékoli změny v Nette\Database by bylo nutné
ručně kopírovat i sem, což se mi vůbec nelíbí.