nextras join table bez lazy
- tomasde
- Člen | 8
Ahoj,
pokosim se v nextras nacist entitu filmy ale chci aby mi to automaticky vypsalo
s entitou filmy i entitu Evets (entita s casy kdy se film bude promitat)
pokud nejake budou.
V nextras se nacita vsechno lazy a a ja nechci aby mi pro kazdej film vytvarel
sql dotaz na events.
Chtel bych aby to vypadalo nejak takto
SELECT m.*, e.*
FROM movies m
INNER JOIN events e ON m.movie_id = e.movie_id
ORDER BY e.time;
Aha takze napisu Sql do MoviesMapper , ale jak vytahnout v repository tu metodu z maperu
Editoval tomasde (30. 4. 10:43)
- Infanticide0
- Člen | 107
„aby mi pro kazdej film vytvarel sql dotaz na events“
tomu se řiká N+1 problem a Nextras ho řeší jedním
selectem všech potřebných events, nefunguje ti to tak?
- tomasde
- Člen | 8
nene mam to namapovany 1:m jeden film ma spousta eventu a events to mam
m:1
Ale ted to skousim v mapper ze napisu cistej sql dotaz ale nevim jak z mapper
to dostat do repository a z toho pak ven. Sem z tech trid uplne zmatenej.
Ja nevim z Orm nemam vubec zadny skusenosti. Asi zustanu u cistych SQl
dotazu.
Sice me ty array pole ..... Ale klasika je klasika
Editoval tomasde (30. 4. 10:48)
- tomasde
- Člen | 8
ty jo navic je to pomaly jak svina u jednoho sql dotazu findAll()
SELECT `movies`.* FROM `movies` AS `movies`
240 ms se vsim vsudy
A to je jen 1 dotaz pak chci jeste menu a vice veci to bych se pak dostal na
500ms- 1s
a to mam jen 5 filmu v tablce na test
Tak nic, zustavam u cistyho mysql
to se dostanu zpatky na 40 ms
Davat orm na nette je jako tahnout parni valec porschem
Editoval tomasde (30. 4. 11:01)
- Infanticide0
- Člen | 107
brzo zjistíš, že ty čistý sql dotazy jsou jenom zlo. Do template neposílej data ale ICollection (return find/get metod)
= nic sám nefetchuj, pokud data potřebuješ až v šabloně. Dej tomu trochu času.
- Infanticide0
- Člen | 107
5 řádků za 240 ms bude problém někde jinde, ORM sice má nějakou režii, ale neni zásadní. Tahám 1800 řádků za 0.37ms.
Těch 240ms ti ukazuje tracy db panel nebo to je čas celýho requestu?
- tomasde
- Člen | 8
Cely request, ukazuje tracy. Sql dotaz 0,3ms a zatim to zkousim bez nete
vytahuju si jen
orm z kontaineru do php souboru a tam si to dumpuju.
Ja si vybral nette protoze je ze vsech frameworku nejrychlejsi, jeste yii je
rychly ale ty array pole tam to fuj.
Laravel 1 reqest bez db 150ms, nette 40ms , cake 210ms , codeIgniter myslim
nejak kolem 100, yii 60ms
Editoval tomasde (30. 4. 11:21)
- Marek Bartoš
- Nette Blogger | 1274
Bez ukázky kódu těžko říct, co děláš špatně.
Určitě je lepší sestavení dotazu nechat na nextras a neextrahovat data
z collection, ale spíš pracovat přímo s ní. Díky kolekci nextras ví,
že ta data patří k sobě a má získávat vazby současně. Navázaná data
by pak mělo získat jedním dotazem.
Pokud chceš data z relace opravdu vytáhnout všechna společně s daty
hlavní entity, tak se na ně v getBy/findBy dotaž. Místo dvou dotazů se
získají
v jednom. findBy(['related->data->id!=' => null]);
- tomasde
- Člen | 8
/**
* @property int $id {primary}
* @property string $title
* @property string $link
* @property string|NULL $trailer
* @property string|NULL $image
* @property OneHasMany<MarsEvents> $events {1:m MarsEvents::$movie, exposeCollection=true, orderBy=[time=ASC], cascade=[persist, remove]}
*/
class MarsMovies extends Entity{}
/**
* @property int $id {primary}
* @property DateTimeImmutable $time
* @property string|NULL $lang
* @property bool $is3d {default 0}
* @property DateTimeImmutable|NULL $preview
* @property int $saal {default 0}
* @property MarsMovies $movie {m:1 MarsMovies::$events}
*
*/
class MarsEvents extends Entity{}
No a To prave nevim jak, rikam s orm nemam zkusenosti. Takze potrebuju
trochu nakopnout
tady mam ty entity. Kdyz zadam
$orm->marsMovies->findAll();
vygeneruje to jen tenhle SQL dotaz SELECT mars_movies
.* FROM
mars_movies
AS mars_movies
V mapper a repository zatim nic nemam.
Tohle je zatim takova jednoducha vec, pak budu potrebovat slozitejsi dotazy jako
vypsat menu kategorie filmu a pokud nebude zadnej event pro kategorii a bude
autohide=1 tak se ta kategorie v menu nezobrazi.
Editoval tomasde (30. 4. 12:13)
- Lumeriol
- Generous Backer | 63
Musíš brát v potaz, že pokud cokoliv změníš v kódu, tak se to ukládá do cache a první spuštění tak trvá déle. Navíc na produkci je pak čas úplně někde jinde než na lokálním prostředí, takže se nelze řídit tím, že ti něco trvá déle a něco kratší dobu.
Pokud máš něco v Mapperu, dosáhneš na to třeba skrze @method v PhpDoc repozitáře. Např.:
// někde v modelu, presenteru a podobně
/** @var \Nextras\Dbal\Result\Row[] $movies */
$movies = $orm->marsMovies->findMoviesByEvents();
bdump($movies);
// MarsMoviesRepository
/**
* @method array findMoviesByEvents()
* @extends Repository<MarsMovies>
*/
final class MarsMoviesRepository extends Repository
{
}
// MarsMoviesMapper
public function findMoviesByEvents(): array
{
$builder = $this->connection->createQueryBuilder();
$builder
->select('[m].[id], [e].[id] AS [eventId], COUNT([m.id]) AS [pocet_filmu]')
->from('%table', 'm', $this->getTableName())
->joinLeft('[events] AS [e]', '[m.id] = [e.movie_id]')
->where('[m].[id] > %i', 2)
->andWhere('[m].[id] < %i', 5)
->addOrderBy('e.time ASC');
$result = $this->connection->queryByQueryBuilder($builder);
return $result->fetchAll();
}
Nextras ORM funguje na bázi Identity map. Tedy že pokud si vytáhneš 5 záznamů, máš v dané mapě připravené všechny sloupce ihned k použití. Proto je neefektivní to používat pro velké množství záznamů. Dá se to řešit, ale primárně není dobré vytáhnout milion záznamů do foreach cyklu, když potřebuješ jen třeba sloupce ID, jméno filmu a datum premiéry, navíc na stránce zobrazené po 20 záznamech. Tam je asi lepší použít Mapper, ale k tomu si postupně dojdeš. V Mapperu se pak dá složit prakticky jakýkoliv dotaz, ORM je pak jen nadstavba pro ulehčení psaní jednodušších dotazů (nelze brát doslovně, ale pro zjednodušení situace). ORM dotaz pak může vypadat např. takto:
// MarsEventsRepository
/**
* @return ICollection<MarsEvents>
*/
public function findEvents(): ICollection
{
return $this
->findBy(
[
'is3d' => true,
'movie->id>' => 2,
'movie->id<' => 18,
'movie->title~' => LikeExpression::startsWith('Godzilla'),
]
);
}
Použít čisté SQL je sice také možné, ale právě Mapper a jeho QueryBuilder jej nahrazuje snazším, přehlednějším a asi i bezpečnějším zápisem. Čisté SQL je pak v podstatě jako čisté PHP místo použití frameworku. Ano, je to rychlejší, ale za jakou cenu.
EDIT: použitý příklad Mapperu fungovat sice nebude (kvůli tvým vazbám 1:m místo m:1, kterou jsem použil já, když jsem přehlédl v příkladu entit), ale to už si pak můžeš doladit podle potřeby.
Editoval Lumeriol (30. 4. 16:23)