nextras join table bez lazy

tomasde
Člen | 6
+
0
-

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 | 73
+
0
-

„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 | 6
+
0
-

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 | 6
+
0
-

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 | 73
+
0
-

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 | 73
+
+1
-

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 | 6
+
0
-

jasne ze jsou ale nic jinyho mi nezbyva

Editoval tomasde (30. 4. 11:04)

tomasde
Člen | 6
+
0
-

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 | 1181
+
0
-

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 | 6
+
0
-
/**
 * @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 | 59
+
+1
-

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)