Oznámení
před 6 lety
- kudlajz
- Člen | 70
@Tharos:
Nikdy si neresil cachovani? Jde mi o to proto, ze web bude navstevovat spousta lidi a bez cache by to ten server urcite zabilo, ne? :)
Prave premyslim, jestli ma nakonec smysl vubec pouzit ORM nebo jestli si napisu vlastni modely ciste v dibi..
před 6 lety
- Šaman
- Člen | 2275
Kešování se dá řešit i jen na úrovni šablon, takže bys nekešoval
dotazy, ale až výsledná a už zpracovaná data.
Co je spousta lidí? Kolik za minutu a očekáváš nějaké koncentrace
v určitých časech? (Např. sázkový systém je extrémně vytížený
hodinu po skončení zápasu, tam má smysl kešovat hodně.)
před 6 lety
- kudlajz
- Člen | 70
Pokud bych cachoval na urovni sablon, jak je to potom s invalidaci dat? A pokud plnim template v metode render, pri cachovani v sablone se nebude volat dotaz na data stale znovu? Jde o socialni sit, s moznosti realtime konverzaci atp., Facebook-like projekt, jen s mensi navstevnosti.
EDIT: I tak mi prijde cachovat jako dobry napad, kdyz jde o data, ktera se nemeni, nebo se meni jen velmi malo, tak mi prijde zbytecne pri kazdem requestu je tahat z DB, kdyz to muzu nacist z disku, jeste navic, kdyz ma VPS jede na SSD.
Editoval kudlajz (24. 12. 2013 11:12)
před 6 lety
- sKopheK
- Člen | 207
Invalidaci dat udelas klasicky pres tagy: https://doc.nette.org/cs/2.4/caching#…
$templateCache = \Nette\Environment::getCache('Nette.Template.Cache');
$templateCache->clean(array(Cache::TAGS => $tags));
před 6 lety
- kudlajz
- Člen | 70
Cachovani v sablone ale asi neni uplne idealni.
Uvedme priklad – mejme metodu findUser($id), ktera fetchne z DB uzivatele
dle id a ulozi do cache, pri pristim volani teto metody, pokud je cache s timto
id validni, tak se pouzije cache, jinak se provede opet krok c. 1.
Pokud to cachuju v te metode napr. v presenteru, tak je to ok a kdyz se ta metoda bude volat treba 10× behem vykonani skriptu, tak se to nacte z DB jednou a z cache 9×.
Pokud ale budu chachovat na urovni sablon, tak se ten vysledek bude cachovat jen v te urcite sablone a vsechna ostatni pouziti nebudou ovlivnena.
před 6 lety
- Mesiah
- Člen | 242
kudlajz napsal(a):
pokud ti jen o to snížit počet dotazů do db tak si teoreticky takovou
cache můžeš napsat sám, v podstatě jen potřebuješ nějakou Dictionary,
kde klíč může být složenina názevEntity-ID a hodnota samotná entita.
Pokud entitu v dictionary nenajdeš, tak ji dočteš a vložíš.
Zajímavější ale budou dotazy kde je klauzuje
where whatever in (1, 2, 3)
tady je už je více strategií jak se
zachovat. :)
Editoval Mesiah (27. 12. 2013 18:42)
před 6 lety
- jsvelta
- Člen | 39
Ahoj, mám jeden problém. Rieším to už zopár hodín.
Mám entitu:
/**
* @property-read int $id
* @property Paper $paper m:hasOne
* @property User $reviewer|NULL m:hasOne(reviewer_id)
* @property int $originality|NULL
* @property int $theme|NULL
* @property int $technicalquality|NULL
* @property int $linguisticquality|NULL
* @property int $recommendation|NULL
* @property string $note|NULL
*/
class Review extends Entity {
}
v latte niečo takéto:
{foreach $papers as $paper}
{foreach $paper->reviews as $review}
{dump $review->reviewer}
{/foreach}
{/foreach}
Ako v latte detekovať, či má review vyplnené reviewer?
Nijak sa mi to nedarí. Čakal som, že $review->reviewer bude NULL, ale to
nefunguje, dokonca to padá na výnimku.
Editoval jsvelta (28. 12. 2013 23:21)
před 6 lety
- Šaman
- Člen | 2275
Změň to to z: @property User $reviewer|NULL
m:hasOne(reviewer_id)
na tohle: @property User|NULL $reviewer
m:hasOne(reviewer_id)
Null je typ (Objekt uživatel, nebo null), ne název. To samé máš u těch intů, ale tam ti to nespadne na výjimku, jen se přetypuje na 0.
před 6 lety
- jsvelta
- Člen | 39
Šaman napsal(a):
Změň to to z: @property User $reviewer|NULL m:hasOne(reviewer_id)
na tohle: @property User|NULL $reviewer m:hasOne(reviewer_id)
Ďakujem, funguje to.
Prehliadol som, že to dávam úplne inam, než chcem :-)
před 6 lety
- Michal III
- Člen | 84
Ahoj, pokusil jsem napsat „něco jako Query Object“, který by mi vyhovoval při práci s repozitáři. Zajímaly by mě vaše názory a podněty.
LeanMapperQuery (GitHub)
Je to inspirováno „backjoin“ z NDB (NotORM). Přistoupím k ukázkám použití:
Mějme následující následující repozitáře (schéma databáze převzato z quickstartu LeanMapperu):
abstract class BaseRepository extends \LeanMapper\Repository implements IQueryable
{
/**
* @implements IQueryable
*/
public function createFluent()
{
return parent::createFluent();
}
/**
* @implements IQueryable
*/
public function getTable()
{
return parent::getTable();
}
public function query()
{
return new BaseQuery($this, $this->mapper);
}
public function find(IQuery $query)
{
return $this->createEntities($query->createQuery());
}
}
class AuthorRepository extends BaseRepository
{
}
class BookRepository extends BaseRepository
{
}
a entity:
class BaseEntity extends \LeanMapper\Entity
{
}
/**
* @property int $id
* @property string $name
*/
class Tag extends BaseEntity
{
}
/**
* @property int $id
* @property string $name
*/
abstract class Person extends BaseEntity
{
}
/**
* @property Book[] $books m:belongsToMany
* @property Book[] $reviewedBooks m:belongsToMany(reviewer_id)
* @property string|null $web
*/
class Author extends Person
{
}
/**
* @property int $id
* @property Author $author m:hasOne
* @property Author|null $reviewer m:hasOne(reviewer_id)
* @property Borrowing[] $borrowings m:belongsToMany
* @property Tag[] $tags m:hasMany
* @property string $pubdate
* @property string $name
* @property string|null $description
* @property string|null $website
* @property bool $available
*/
class Book extends BaseEntity
}
Dále si nadefinujeme BaseQuery
objekt:
class BaseQuery extends Query
{
public function find()
{
return $this->sourceRepository->find($this);
}
}
Pro znázornění spolupráce s implicitními filtry ještě uvedu
Mapper
:
class Mapper extends DefaultMapper
{
public function getImplicitFilters($entityClass, Caller $caller = NULL)
{
if ($entityClass === "$this->defaultEntityNamespace\\Book") {
return new \LeanMapper\ImplicitFilters(function (\LeanMapper\Fluent $statement) {
$statement->where('[available] = 1');
});
}
return array();
}
}
Poté můžeme pracovat s repozitáři následujícím způsobem:
$bookRepository = new Repository\BookRepository($connection, $mapper, $entityFactory);
$books = $bookRepository->query()
->where('@tags.name', 'popular')
->where('DATE(@pubdate) > ?', '1998-01-01')
->find();
Veškeré @...
výrazy se překládají na sloupce
příslušných entity a tečková notace slouží k automatickému
„left-joinování“. Položený dotaz potom vypadá takto:
SELECT [book].*
FROM [book]
LEFT JOIN [book_tag] ON [book].[id] = [book_tag].[book_id]
LEFT JOIN [tag] ON [book_tag].[tag_id] = [tag].[id]
WHERE [available] = 1 AND ([tag].[name] = 'popular') AND (DATE([book].[pubdate]) > '1998-01-01')
Pro backjoin se tedy používá pouze tečka (netřeba dvojtečky), protože příslušná vazba se vyčte ze třídy entity. Jak je vidět v dotazu, implicitní filtry se provedou.
Ukázka trochu komplikovanějšího dotazu:
$authorRepository = new Repository\AuthorRepository($connection, $mapper, $entityFactory);
$authors = $authorRepository->query()
->where('@books.tags.name', 'ebook')
->orderBy('@books.available')->desc()
->orderBy('@books.pubdate')
->find();
SELECT [author].*
FROM [author]
LEFT JOIN (
SELECT [book].*
FROM [book]
WHERE [available] = 1) book ON [author].[id] = [book].[author_id]
LEFT JOIN [book_tag] ON [book].[id] = [book_tag].[book_id]
LEFT JOIN [tag] ON [book_tag].[tag_id] = [tag].[id]
WHERE ([tag].[name] = 'ebook')
ORDER BY [book].[available] DESC , [book].[pubdate]
Pokud se má tedy „přijoinovat“ tabulka, na které se mají provést implicitní filtry, provede se složený dotaz, jak je vidět u joinování tabulky book.
Celý projekt je zatím ve fázi experimentování, proto budu vděčný za veškeré názory, podněty a bug reporty. Zajímá mě také kritika tohoto pohledu na Query Objecty, protože si sám nejsem zatím jistý, jestli to až příliš neporušuje některé principy, nicméně z mých dosavadních zkušeností mohu říct, že se mi takto pracuje s repozitáři zdaleka nejlépe ze všech mnou zatím vyzkoušených možností.
Editoval Michal III (29. 1. 2014 10:50)
před 6 lety
- Tharos
- Člen | 1042
kudlajz napsal(a):
@Tharos:
Nikdy si neresil cachovani? Jde mi o to proto, ze web bude navstevovat spousta lidi a bez cache by to ten server urcite zabilo, ne? :)
Prave premyslim, jestli ma nakonec smysl vubec pouzit ORM nebo jestli si napisu vlastni modely ciste v dibi..
Jsem zastáncem myšlenky, že řešit cache má smysl až v momentě, kdy se ukáže, že je to skutečně zapotřebí. A také považuji za důležité měřením ověřit, že zamýšlené řešení je skutečně funkční. Máš změřené, že se data skutečně načtou rychleji z cache než z databáze? (Samozřejmě v produkčním prostředí.)
Říct si na začátku vývoje, že tohle a tamto asi bude nutné cacheovat a rovnou nějakou cache naimplementovat, považuji za premature optimization jak z učebnice.
K Tvému konkrétnímu dotazu:
- Víš určitě, že si s tím server neporadí?
- Pokud ne, víš určitě, že aplikace je nejlepší místo, kde tu cache řešit? Nebylo by lepší spíše mít nějakou cache na úrovni databáze? Nebo memcache? Nebo optimalizovat dotazy? Či minimalizovat délku round tripu do databáze?
Nemáš-li zdopovězené tyto dotazy, může se Ti klidně stát, že naimplementuješ nějakou cache, která může být „suboptimální“, anebo v nejhorším případě může být pomalejší, než načítání dat z úložiště…
Editoval Tharos (30. 12. 2013 0:14)
před 6 lety
- Jan Suchánek
- Backer | 403
@Tharos: Určitě cache na začátku vývoje a provozu aplikace nemá smysl řešit, ale pro testovaní možností zrychlení při zátěži by se nějaký ten příklad použítí asi mohl hodil nebo ne?
Je možné že Nette\Database je v posledním RC4 o hodně rychlejší než Dibi?
Je něco nového s dokumentací?
před 6 lety
- kudlajz
- Člen | 70
Muze mi nekdo poradit, jak jednoduse vlozit relaci pri m:n asociaci?
Mam v entite definovano:
@property Like[] $likes m:hasMany(post_id:post_like:like_id:like)
a zkousel jsem neco jako
$this->likes[] = new Like;
ale to nefunguje, muze mi nekdo poradit, jak na to? :)
Editoval kudlajz (4. 1. 2014 13:17)
před 6 lety
- Michal III
- Člen | 84
@kudlajz: Existují magické metody
addTo<Name>
, removeFrom<Name>
(viz
zde).
Takže ve Tvém případě:
$this->addToLikes(new Like);
před 6 lety
- kudlajz
- Člen | 70
Dekuji :)
před 6 lety
- kudlajz
- Člen | 70
Muze mi jeste nekdo poradit bliz s tim cachovanim? Jelikoz nelze serializovat entitu, jak mam postupovat? Radek se z db fetchuje pred volanim createEntity().
před 6 lety
- Michal III
- Člen | 84
@kudlajz: Nestačila by místo serializování entity
její metoda getData()
?
před 6 lety
- Pavel Macháň
- Člen | 285
kudlajz napsal(a):
Muze mi jeste nekdo poradit bliz s tim cachovanim? Jelikoz nelze serializovat entitu, jak mam postupovat? Radek se z db fetchuje pred volanim createEntity().
Cachovat data co dostaneš přímo z databáze před vytvořením entity.
Cache, pokud není tak load z DB > createEntity
Nebo jsem cachoval celé komponenty (v render jsem bud nahral nové data z DB a klasicky vykreslil a nebo sem si načetl z cache celé html co jsem si do ní narval a vykreslil jej)
Editoval EIFEL (7. 1. 2014 21:08)
před 6 lety
- Mesiah
- Člen | 242
Ahoj, prosím Vás jen drobnost.
Rád bych měl u entity property, kterou budu vytvářet na straně PHP (url
vytvořená na základě hodnot aktuální entity), existuje v LM řešení ve
stylu filter/passThru? Díky :)
před 6 lety
- David Grudl
- Nette Core | 6806
Nebylo by lepší, kdybych celé toto vlákno přesunul na dibi fórum?
před 6 lety
- Tharos
- Člen | 1042
Na dibi fóru by to byl podobný offtopic jako tady, protože téměř 100 % dotazů nesouvisí přímo s dibi, ale s onou nadstavbou nad ní…
Během několika týdnů by měl mít projekt své vlastní fórum, a tak prosím ještě o chvilku strpení… Pak si klidně všechny zdejší příspěvky přemigruji.
Do té doby už rozhodně nevyjde žádná další verze, takže předpokládám, že tohle vlákno už nebude ani zdaleka tak živé, jako bylo ještě nedávno.
Díky.
Editoval Tharos (8. 1. 2014 0:02)
před 6 lety
- tomas.lang
- Člen | 54
Mesiah napsal(a):
Ahoj, prosím Vás jen drobnost.
Rád bych měl u entity property, kterou budu vytvářet na straně PHP (url vytvořená na základě hodnot aktuální entity), existuje v LM řešení ve stylu filter/passThru? Díky :)
Nestačili by ti na to normální metody ve stylu:
/**
* @property string $name
* @property string $surname
* @property-read string $fullName
*/
class User extends \LeanMapper\Entity
{
public function getFullName()
{
return $this->name . ' ' . $this->surname;
}
}
před 6 lety
- Mesiah
- Člen | 242
tomas.lang napsal(a):
Mesiah napsal(a):
Ahoj, prosím Vás jen drobnost.
Rád bych měl u entity property, kterou budu vytvářet na straně PHP (url vytvořená na základě hodnot aktuální entity), existuje v LM řešení ve stylu filter/passThru? Díky :)Nestačili by ti na to normální metody ve stylu:
/** * @property string $name * @property string $surname * @property-read string $fullName */ class User extends \LeanMapper\Entity { public function getFullName() { return $this->name . ' ' . $this->surname; } }
Právě že ne. Jak jsem zmínil, tak bych chtěl generovat URL a to takovým
způsobem, že i když změním v budoucnu routy, tak budu dostávat URL pro
tyhle nové routy, tolik k myšlence.
Říkal jsem si, že by se mi to líbilo takhle: budu mít službu, která mi
generuje URL. V LM jsou filtr, což v podstatě může být normální
třída, takže teoreticky i služba a pokud by LM měl podporu pro volání
metod pro sestavení nějaké složené property – podobně jako passThru
nebo filters, pak by mi nic nebránilo zavolat tu svou službu na generování
URL a vygenerovat URL pro každou entitu.
před 6 lety
- Tharos
- Člen | 1042
@Mesiah: Jako optimální mi v takovém případě
připadá mít službu pro generování odkazů (jak píšeš) a tu prostě
předávat dané entitě jako závislost ve vlastní EntityFactory
.
V property (realizované pomocí metody) bys pak o tu URL žádal tu
službu.
O pár stránek dříve (v době, kdy jsem představoval právě
EntityFactory
) najdeš pár ukázek, od kterých se dá dobře
odrazit.
Kdybys tápal, klidně se ozvi přes soukromou zprávu.
Editoval Tharos (7. 1. 2014 23:58)
před 6 lety
- Mesiah
- Člen | 242
Tharos napsal(a):
@Mesiah: Jako optimální mi v takovém případě připadá mít službu pro generování odkazů (jak píšeš) a tu prostě předávat dané entitě jako závislost ve vlastní
EntityFactory
. V property (realizované pomocí metody) bys pak o tu URL žádal tu službu.O pár stránek dříve (v době, kdy jsem představoval právě
EntityFactory
) najdeš pár ukázek, od kterých se dá dobře odrazit.Kdybys tápal, klidně se ozvi přes soukromou zprávu.
Tak to je mazec! Tohle je přesně co jsem potřeboval, skvělá skvělá práce! Mockrát díky! :)
před 6 lety
- Pavel Macháň
- Člen | 285
@Tharos Nějaké nové info ohledně dokumentace ? :)
Editoval EIFEL (13. 1. 2014 17:46)
před 6 lety
- Tharos
- Člen | 1042
Michal III napsal(a):
Ahoj, pokusil jsem napsat „něco jako Query Object“, který by mi vyhovoval při práci s repozitáři. Zajímaly by mě vaše názory a podněty.
Konečně jsem se dostal k tomu projít si to. Neprocházel jsem „vnitřnosti“ úplně do detailu, ale jako celek se mi to osobně velmi líbí!
Jinak při hraní si s tím mě napadlo, že by vlastně šlo postavit nad Lean Mapperem robustní LQL.
Nechystáš se na Poslední sobotu?
před 6 lety
- Michal III
- Člen | 84
@Tharos:
LQL
mě také v souvislosti s tím napadlo. Je to sice věc,
které by se asi spousta lidí bránila, že se „nechtějí učit další
jazyk“, ale na spoustu případů by se velice hodila.
Na Poslední sobotu se právě vůbec poprvé chystám :-).
před 6 lety
- Tharos
- Člen | 1042
@Michal III: Fajn, tak to se uvidíme. :) Pokud budeš chtít, můžeme porovnat různé přístupy. Rád Ti předvedu, co v poslední době ponejvíc používám já.
před 6 lety
- Michal III
- Člen | 84
@Tharos: Jj, rád s Tebou tyhle věci proberu. Těším se :-).
před 6 lety
- blur
- Člen | 15
Jak pomocí mapperu dokážu automaticky řadit Modely do různých namespace? Zkoušel jsem pro každý repository vytvořit v konstruktoru mapper ale to mi vynadá, že nejsou totožné. Modely mám zvlášť pro každý modul a každá tabulka má v názvu jeho prefix. S tím zase souvisí odstranění anotace entity. Jak bez ní můžu repository nastavit Název entity kupříkladu tabulka security_users ⇒ User, tabulka content_articles ⇒ Article.
<?php
class AutoMapper extends DefaultMapper
{
public function __construct($namespace)
{
$this->defaultEntityNamespace = $namespace;
}
}
?>
před 6 lety
- honos
- Člen | 109
Tharos napsal(a):
Jinak při hraní si s tím mě napadlo, že by vlastně šlo postavit nad Lean Mapperem robustní LQL.
Sice LM nepoužívám ale také by mě zajímalo co chystáš.. A také na kdy tak vidíš aktualizaci dokumentace pro verzi 2.x.x
před 6 lety
- Nobody.guy
- Člen | 22
Neocenil by někdo generování struktury databáze z vlastností v anotacích?
Editoval Nobody.guy (27. 1. 2014 11:42)
před 6 lety
- Pavel Macháň
- Člen | 285
Nobody.guy napsal(a):
Neocenil by někdo generování struktury databáze z vlastností v anotacích?
@Tharos už něco napsaného na to měl jen to myslím nebylo dotažené aby to zveřejnil.
před 6 lety
- Nobody.guy
- Člen | 22
EIFEL napsal(a):
@Tharos už něco napsaného na to měl jen to myslím nebylo dotažené aby to zveřejnil.
To je škoda :)
před 6 lety
- Filip111
- Člen | 244
Ahoj, mám dotaz ohledně práce s M:N vazbami.
Mám entitu Schedule:
/**
* @property int $id
* @property string|null $title
* @property Classroom[] $classrooms m:hasMany(schedule:gaudeo_classrooms_schedule:classroom:gaudeo_classrooms) m:filter(orderBySorting)
*/
Jak přidám nebo odeberu prvek z $classrooms ?
Doposud mi fungovalo
$schedule->addToClassrooms($classroom);
$schedule->removeFromClassrooms($classroom);
Jenže co jsem přidal filter nad property classrooms
tak to
nejde použít (Chyba: ‚Only properties without filters can be managed
this way.‘)
Zkoušel jsem:
$cl = $schedule->classrooms;
unset($schedule->classrooms[$classroom->id]);
unset($schedule->classrooms[$classroom]);
unset($cl[$classroom->id]);
unset($cl[$classroom]);
$cl->remove($classroom);
ale nic z toho nefunguje – vždy se dostanu k nějaké chybové hlášce.
Díky, Filip
před 6 lety
- Etch
- Člen | 404
@Tharos
Nešlo by přidat do konstruktoru entity volání nějaké protected metody
jako je initDefaults()
, ale pro případ, kdy je entita vytvářena
z Row
?
Občas by se mi hodilo s takto vytvořenou entitou ještě něco provést.
Občas se mi například hodí si uložit kopii hodnot sloupců z Row, abych je
mohl v budoucnu porovnat s getModifiedRowData()
atd.
před 6 lety
- Mesiah
- Člen | 242
Filip111 napsal(a):
Jak přidám nebo odeberu prvek z $classrooms ?
Jenže co jsem přidal filter nad property
classrooms
tak to nejde použít (Chyba: ‚Only properties without filters can be managed this way.‘)
Myslím, že chybová hláška je celkem výmluvná, každopádně odpověď
jak přidávat a odebírat je prostá – přes repository classroom(s). Teď
by asi byla vhodná otázka: „Proč to nejde?“.
Na tohle neumím dostatečně správně a přesně odpovědět, ale řekl bych
že to souvisí s filtry – to že při čtení se ke query připojí filtr
je zřejmě, ale jak by se to mělo chovat při mazání?
Mělo by se to prohnat filtr? Nemělo? – Dokážu si představit oba
scénáře.
Myslím, že Tharosovo rozhodnutí vyhodit chybu je na místě – tímhle by
se aplikace chovala nepředvídatelně a mohlo by to vést ke špatně
odhalitelným chybám – zvlášť ve vícečlenných týmech…
Víc by ti ale asi napsal Thahos. :)
Editoval Mesiah (28. 1. 2014 16:15)
před 6 lety
- Filip111
- Člen | 244
@Mesiash:
Jít na to z druhý strany mě nenapadlo – snad to pomůže.
Můj dotaz byl zaměřený spíš jestli existuje nějaká alternativa
k použití pseudofunkcí addToClassrooms a removeFromClassrooms.
Tharos o něčem uvažoval, ale nevím co z toho by nakonec mělo fungovat (podle toho co jsem zkoušel, tak nic), viz. https://forum.dibiphp.com/…orm-nad-dibi#…
Dík.
před 6 lety
- Tharos
- Člen | 1042
@blur:
Jak pomocí mapperu dokážu automaticky řadit Modely do různých namespace? Zkoušel jsem pro každý repository vytvořit v konstruktoru mapper ale to mi vynadá, že nejsou totožné. Modely mám zvlášť pro každý modul a každá tabulka má v názvu jeho prefix. S tím zase souvisí odstranění anotace entity. Jak bez ní můžu repository nastavit Název entity kupříkladu tabulka security_users ⇒ User, tabulka content_articles ⇒ Article.
To, aby v celé aplikaci existoval pouze jeden Mapper, je poměrně důležité. Jde o to, že Mapper se předává při traverzování mezi entitami (úplně nenápadně) a pokud by existovalo více Mapperů, téměř by nešlo zabránit nechtěným chybám, kdy se najednou objeví entita A s Mapperem X (který získala při traverzování), namísto očekávaného Y (který by měla získat z repositáře).
Překlad security_users
⇒ User
je přece
jednoduchý, nebo ne? Pokud máš pevně danou logiku, můžeš odstřihnout tu
část před potržítkem a upravit velikost prvního písmena. V Mapperu se
ale nemusíš omezovat pouze na takovéto operace. Pokud máš například
modulární aplikaci, Mapper klidně může interně používat nějaké
překladové tabulky. Proměnnou $defaultEntityNamespace
vůbec
nemusíš používat.
Řeší má odpověď Tvůj problém?
před 6 lety
- Nobody.guy
- Člen | 22
Nic na generování SQL z entit teda není a můžu se do toho pustit?
před 6 lety
- Tharos
- Člen | 1042
@honos:
Tharos napsal(a):
Jinak při hraní si s tím mě napadlo, že by vlastně šlo postavit nad Lean Mapperem robustní LQL.
Sice LM nepoužívám ale také by mě zajímalo co chystáš.. A také na kdy tak vidíš aktualizaci dokumentace pro verzi 2.x.x
Dokumentace má rozhodně prioritu před podobnými experimenty. LQL by vytvořit šel, neexistuje žádný principiální problém, proč by nešel. Ale pokud zbude jeho realizace na mě, nějaký pátek ještě asi světlo světa nespatří. Ale třeba Michal III má nakročeno velmi hezky.
Editoval Tharos (29. 1. 2014 9:06)
před 6 lety
- Tharos
- Člen | 1042
Nobody.guy napsal(a):
Nic na generování SQL z entit teda není a můžu se do toho pustit?
Nástřel generování databázového schéma z entit bych měl. Je to i docela dobře funkční, ale nedokončené.
Pokud bys měl zájem to dotáhnout nebo vytvořit něco analogického (ale dotaženého), bylo by to skvělé. Klidně se mi ozvi soukromou cestou (v.kohout, gmail) a můžu Ti poskytnout, co je již hotové, a taky Ti můžu poskytnout pár myšlenek a nápadů.
před 6 lety
- Tharos
- Člen | 1042
@Filip111:
Ahoj, mám dotaz ohledně práce s M:N vazbami.
API pro správu jednoduchých M:N vazeb je záměrně nedostupné u položek s filtry.
Důvod je poměrně jednoduchý. Filtr s tou vazbou může udělat dost
radikální věci. Například může k vazební tabulce přijoinovat nějaká
data a programátor by se pak divil, že se mu pak entity „špatně“
persistují. Nebo může zavést restrikci, pomocí které se nějaká data
vypustí. Mám-li vyfiltrované takové tagy knihy, které začínají na
„S“ (třeba v položce tagsStartingByS
), bylo by matoucí,
kdyby do takové kolekce šlo přiřadit pomocí
$book->addToTags
nějaká tag začínající na „R“.
Problém je třeba i s jednoduchým řazením. Pokud mám tagy nějak
seřazené (třeba podle abecedy), spoléhám na to a někde v aplikaci si do
takové property přiřadím nějaký další tag, mohu tím to seřazení
narušit. No a dalo by se dlouze pokračovat.
Z výše uvedených důvodů je tedy to M:N API u takových vazeb uměle omezené. Dalo by se říct, že zavedením filtru ty vazby přestávají být jednoduché.
Principiálně tohle dost dobře vyřešit nelze. Alternativním řešením je například spravovat ty vazby přes repositář nebo mít v entitě ještě jednu ekvivalentní položku, jen bez toho filtru. Dále by to mohlo jít vyřešit i nějak „nízkoúrovňově“ v těle entity, což by mohlo být nejelegantnější řešení.
Edit: Tak „nízkoúrovňově“ by to šlo. V
Row::(addTo|removeFrom)Referencing
se už filtr nekontroluje, což
je logické. Takže by v pohodě šlo v entitě si metodu například
addToTags
přetížit a vložení umožnit – ale na vlastní
nebezpečí.
Editoval Tharos (29. 1. 2014 9:27)
před 6 lety
- Tharos
- Člen | 1042
@Etch:
Nešlo by přidat do konstruktoru entity volání nějaké protected metody jako je
initDefaults()
, ale pro případ, kdy je entita vytvářena zRow
?Občas by se mi hodilo s takto vytvořenou entitou ještě něco provést. Občas se mi například hodí si uložit kopii hodnot sloupců z Row, abych je mohl v budoucnu porovnat s
getModifiedRowData()
atd.
Pokud by mělo jít o další prázdnou protected metodu přímo v
LeanMapper\Entity
, tak jí moc nakloněn nejsem. Přijde mi, že
využití je dost specifické.
Co třeba udělat si na to nějakou base Entity?
abstract class Entity extends LeanMapper\Entity
{
public function __construct($arg = null)
{
parent::__construct($arg);
if ($arg instanceof LeanMapper\Row) {
$this->touchRow($arg);
}
}
protected function touchRow(LeanMapper\Row $row)
{
}
}
před 6 lety
- Etch
- Člen | 404
@Tharos
Samozřejmě jen dotaz. :D
BTW: Jak kdyby si mi koukal do zrojáků :D Mám to teď napsané velmi podobně. :D
před 6 lety
- Michal III
- Člen | 84
@Tharos @honos: Co se týče LQL
,
napadají mě 2 různá řešení:
- Nějaká dotažená verze toho, o co jsem se již pokoušel zde,
což je jen jakýsi preprocesor nad dotazy do konkrétní databáze (testováno
jen na
MYSQL
), - nebo opravdový
LQL
ve vyšší vrstvě, která by se posléze překládala dle použitého typu databáze – zatím si ani nedovedu představit, kolik by to stálo práce něco takového vytvořit. Kolik času by stálo parsování a překlad jednotlivých dotazů (měly by se z toho důvodu cachovat?). Šlo by tedy o ambicióznější, ale nepoměrně náročnější projekt.
Jakou cestou se vydat?
BTW hrozně se mi líbí, co vše lze z LeanMapperu zužitkovat –
v dotazu by se např. dalo místo %*
parametrů využívat pouze
?
, který by se poté nahradil za příslušný %*
parametr dle typu property
v entitě…
před 6 lety
- Filip111
- Člen | 244
@blur, @Tharos:
Taky jsem narazil na problém s více mappery – konkrétně jsem
přetraverzoval přes pár entit, upravil získanou entitu a nepodařilo se mi
ji uložit, protože repository, které ji ukládá používalo jiný mapper
než ten předaný při traverzování.
Dost mi to zkomplikovalo situaci.
Jak jsem se k tomu dostal?
Základem všech aplikací co dělám je zjednodušené CMS, která samo
o sobě poskytuje některé „služby“ (ne nutně z kontaineru). Některé
tyto „služby“ mají repository, entity a vlastní mapper.
Pak mám modul s nějakým firemním systémem, který ale používá
služby cmska (práce s uživately, maily, logy, nějaká úložiště apod.).
Tento modul má vlastní repository a entity a protože nechci aby o nich
věděl standardní mapper cmska, mají i vlastní mapper se svými
specialitami.
Entity tohoto firemního systému ale úplně klidně mohou být provázané na
entity cmska (na uživatele, nějaký typové číselníky, tagy atd).
Pravidlo jediného mapperu je tedy dost svazující při snaze rozdělit
aplikaci na více modulů.
(řešení v LM mě nenapadá, jen chci upozornit, že to není tak
černobílé)
@Tharos, @honos, @Michal III
Vážně potřebujeme LQL a převod entit na tabulky, resp. SQL?
LeanMapper je Lean a proto ho používám, kolos který umí všechno už tu
máme (Doctrine2) a přijde mi zbytečný vytvářet ho znovu.
před 6 lety
- Michal III
- Člen | 84
@Filip111: To je právě to, LQL
nemusí
být a také nejspíše nikdy nebude pevnou součástí LeanMapperu. Je to
myšleno spíš jako rozšíření (nadstavba). Rozdíly mezi LeanMapperem a
Doctrine nejsou jen ve „velikosti“, ale především v některých
principech, každopádně se ale ani o jednom projektu nedá říct, že by byl
nějak špatný nebo horší než ten druhý.
Řekl bych, že to jsou různé prostředky či „zbraně různého
kalibru“, které je třeba používat na různé projekty. Někomu stačí
dibi, někomu LeanMapper s repozitáři v prezentérech, někdo to požene
přes fasádu a jsem si jist, že někdo by LQL
či něco
podobného také využil.
před 6 lety
- Tharos
- Člen | 1042
@Filip111:
Pravidlo jediného mapperu je tedy dost svazující při snaze rozdělit aplikaci na více modulů.
(řešení v LM mě nenapadá, jen chci upozornit, že to není tak černobílé)
Důvod, proč to tak ale je, je věřím jasný. Vůbec si nemyslím, že by to bylo nějak svazující. Jen je zapotřebí nevnímat Mapper jenom jako nějaký „tupý“ aplikátor nějakých konvencí. Může fungovat úplně dynamicky:
class DynamicMapper extends DefaultMapper
{
private $dictionary = array();
public function registerModule($modulePrefix, array $names)
{
foreach ($names as $name) {
$this->dictionary[$modulePrefix . '_' . $name] = 'Addon\\' . ucfirst($modulePrefix) . '\\Entity\\' . ucfirst($name);
}
}
public function getEntityClass($table, Row $row = null)
{
if (isset($this->dictionary[$table])) {
return $this->dictionary[$table];
}
return parent::getEntityClass($table, $row);
}
}
$mapper = new DynamicMapper;
$mapper->registerModule('news', array('item', 'comment', 'rating'));
$mapper->registerModule('content', array('page', 'text'));
$entityClass = $mapper->getEntityClass('news_item'); // $entityClass === 'Addon\News\Entity\Item'
$entityClass = $mapper->getEntityClass('news_rating'); // $entityClass === 'Addon\News\Entity\Rating'
$entityClass = $mapper->getEntityClass('content_page'); // $entityClass === 'Addon\Content\Entity\Page'
Pak není problém, aby si každý modul CMS při svém zavádění zaregistroval do Mapperu entity, které do „ekosystému“ přidává. Uvedená ukázka je záměrně triviální, v praxi by byl kód asi o něco složitější, ale věřím, že je z toho patrné, co chci říct.
Vážně potřebujeme LQL a převod entit na tabulky, resp. SQL?
LeanMapper je Lean a proto ho používám, kolos který umí všechno už tu máme (Doctrine2) a přijde mi zbytečný vytvářet ho znovu.
Jde o věci, které by v žádném případě nebyly nějakou nativní součástí Lean Mapperu, ale jen nadstavbou nad ním.
Generování tabulek z anotací (respektive z EntityReflection
)
je lákavé, protože díky existenci EntityReflection
je to
hračka a takové generování prostě při vývoji ušetří dost času.
Bez LQL se dá obejít. Je to prostě jeden ze způsobů, jak vyjádřit to,
jaké entity a jakým způsobem se mají z úložiště načíst. Pokud by jej
někdo naprogramoval, věřím, že by si své uživatele našel. Z Lean
Mapperu to kolos ale určitě neudělá, do něj to nijak nezasáhne. Půda už
je v podstatě připravená (EntityReflection
,
IMapper
).
Editoval Tharos (30. 1. 2014 8:49)