Dotrine a spočítání ceny polozek v kosiku, rychlost

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

Ahoj, četl jsem članek https://ondrej.mirtes.cz/…-neni-pomala , ale nevím jestli je to tam v pořádku, píše tam, že pro zobrazování dat píše DQL dotazy(specifikuje slooupce), kde data jsou mu vráceny do prostého pole, mě to tedy přijde dost špatne, bo tím pádem přestává smysl používat Dotrine ne?

Každopádně k otázce
když vyberu třeba košík objednávky a entita basket má vlastnost items, a každá item má svou price. Jak to spočítat celkovou cenu kosiku? teď to dělám tak, že mám v entitě basket metodu celkova cena a tam mám

public function getPrice()
    {
        $price = ['sum' => 0];
        /** @var Item $item */
        foreach($this->items as $item)
        {
            $price['sum'] += $item->getPrice() * $item->getCount();
        }
        return (object)$price;
    }

protože teď mi prijde dost divné tahat vše o items když chci třeba jen cenu.

A jak prosím aktivuji APC cache, ted mi to píše chybu

Call to undefined function Doctrine\Common\Cache\apc_fetch()

Děkuji

Editoval zoool (1. 8. 2016 8:12)

newPOPE
Člen | 648
+
0
-

Ako vo vela pripadoch aj tu je moznosti ako to riesit viac. Zalezi len na Tebe a stave daneho projektu (navstevnost, vytazenost zdrojov…) Pri 3och navstevach za den naozaj nemas co riesit a proste to budes robit ako doteraz…

Dalsim stupnom je to nejak cachovat. Napr tak, ze order bude mat property $price a ta sa bude aktualizovat pri pridani, odobrati kosika. Cize si potom len prepises getPrice() na

public function getPrice() {
  return $this->price;
}

a pri add/delete z kosika upravis logiku napr.

public function add(Item $item) {
  // ... pridas item do $items ...

  // upravis aktualny stav
  $this->price += $item->getPrice() * $item->getCount();
}

Cize citanie mas vybavene.

Svaťa Šimara
Člen | 98
+
+2
-

kde data jsou mu vráceny do prostého pole, mě to tedy přijde dost špatne, bo tím pádem přestává smysl používat Dotrine ne?

Doctrine je obrovský ekosystém, každý použije co se mu hodí. Osobně za hlavní benefit považuji zápisový model. Pokud potřebuju něco číst, Doctrine nabízí několik řešení. Pokud potřebuji rychlost, musím hold něco obětovat – pro čtení obětuji objektovost.

protože teď mi prijde dost divné tahat vše o items když chci třeba jen cenu.

Záleží, jak máš definovaný use-case, univerzální odpověď nebude.

Osobně bych ale optimalizaci košíku řešil až úplně nakonec, jestli vůbec. Jde o kritickou část e-shopu, kde ceny musí být správně. Ceny se můžou měnit za pochodu, a košík, v našem případě, musí on-line změny cen reflektovat.

zoool
Člen | 89
+
0
-

Fafin

Zcela souhlasím, on už to je konec jen to zlepšují keší,, každopádně je tedy to řešení správné? Pokud chci objekty tak jinak to z té databáse asi nevytáhnu než takto ne? a pak třeba na to nasadit tu cache? Uvažoval jsem že celou entitu zakešuji na ID košku a pak když bude nějaká změna tak si vytahnu entitu provedu změny, změním cache a je to? A nebo se to dá tahat nějak jinak? a lépe?

Svaťa Šimara
Člen | 98
+
0
-

@zoool Správné řešení? No, to bych se dostal do polemizování o správnosti vs. vhodnosti…

Z kódu, co tu máš nevidím, jak objekty z DB vytahuješ. Používáš něco jako:

$cart = $entityManager->find(Cart::class, $id);
//nebo
$cart = $entityManager->getRepository(Cart::class)->find($id);

Pokud jo, tak se jednotlivé položky vytahují až když k nim přistupuješ (a používá se proxy objektů). Efektivnější je použít QueryBuilder (nebo Query), a vytáhnout všechno na jednou:

$cart = $entityManager->createQueryBuilder()
			->from(Cart::class, 'c')
			->leftJoin('c.items', 'i')
			->select('c')
			->where('c.id = :id')
			->setParameter(':id', $id)
			->getQuery()
			->getSingleResult();

Samozřejmě, že strukturu objektů jenom odhaduju

zoool
Člen | 89
+
0
-

@Fafin
Tak jak jsi napsal

$cart = $entityManager->createQueryBuilder()
            ->from(Cart::class, 'c')
            ->leftJoin('c.items', 'i')
            ->select('c')
            ->where('c.id = :id')
            ->setParameter(':id', $id)
            ->getQuery()
            ->getSingleResult();

Tahle nějak to tahám(používám query builder), a to je správné? Bo to tahá vše co může a mě to přijde nepraktické, ale pokud se člověk na to podívá jako na objekty, tak to dává smysl.
No a můžu tedy celou tu entitu pak zakešovat? Nebo se to už cachuje samo na produkci a nemusím to řešit? Bo na produkci mi to prijde rychlé. A co ta cache APC? proč mi to píše tu chybu?

David Matějka
Moderator | 6445
+
+1
-

A co ta cache APC? proč mi to píše tu chybu?

potrebujes https://pecl.php.net/package/APCu

Jiří Nápravník
Člen | 710
+
0
-

Můžeš si nadefinovat, které sloupce vytaháš, mrkni na partial entity, ale pozor partial entita je to pořád v tom requestu. Nemůžeš tahat nahoře pro košík jen cenu a pak někde vypsat celou entitu, protože tam budeš mít null. Na druhou stranu, pokud chceš jenom nějaký základní přehled a ceny, tak nemá smysl tahat třeba celé popisky, komentáře a já nevím co vše dalšího.

Nicméně projet si všechny itemy a sečíst ceny, v tom nevidím žádný problém. Pokud však potřebuje pouze cenu použííl bych query na SUM().

zoool
Člen | 89
+
0
-

@JiříNápravník Děkuji, jsem právě něvěděl jestli je to ok, samozrejmě vedle toho zobrazuji i cenu bez dph a počet polozek. Když použiji sum tak už mě štve že mi to vlastně vráti něco jiného, normálně mi to vrací entitu, ale když tam přidám sum musím už změnit všude kody a řict, že entita je v poli na nule a ten sum je na 1, a nechce se mi to pořád měnit, v tom mi native sql přijde lepsi, že se to tam jen přidá jako by další proměnná a je to. to pak vypíši kde chci. dá se to nějak zakomponovat do entity?

newPOPE
Člen | 648
+
+2
-
Svaťa Šimara
Člen | 98
+
+1
-

@JiříNápravník Partial entity? No, jako tuto techniku jsem nepochopil. Doctrine mi tímto dovoluje vytvořit nevalidní entity. Nevalidní entita se navenek tváří, že je úplně ok – existuje, lze volat její veřejné metody, ale její chování může být v rozporu s očekávaným (a otestovaným).

Pokud potřebuju super optimalizovaně vytáhnout jenom nějaká data, použiju hydrataci skaláru, pole, anebo si vytvořím vlastní DTO (na což tady naráží @newPOPE)

Jiří Nápravník
Člen | 710
+
0
-

@Fafin partial entity používám. Když člověk ví, na co je použije, kdy, a ví, co způsobuje, tak nevidím větší problém… Třeba vytahuji články v přehledu v kategorii a fakt tam nepotřebuji, všech třeba dvacet atributu, z kterých některé jsou třeba jen pro administraci.

Hydrataci do pole ja osobne zase nerad, chci objekty. DTO jsem poradne nepouzil, nicmene pokud to chápu, pak zase přijdu o veřejné metody v entitě, není to entita vlastně atd.

Svaťa Šimara
Člen | 98
+
+1
-

@JiříNápravník Ale, konečně zajímavá debata!

Když člověk ví, na co je použije, kdy, a ví, co způsobuje, tak nevidím větší problém…

Dotaz: K projektu se dostane další programátor, bude vědět, že pracuje s objektem daného typu, použije jeho veřejnou metodu (která využívá nehydratovaných atributů), co se stane?
Nebo se zeptám jinak: Splňuje objekt rozhraní, které zveřejňuje, když není plně hydratován?

… z kterých některé jsou třeba jen pro administraci

Tady bych se taky rád zastavil – pokud jeden objekt plní v administraci určitou roli, která je pro frontend nepodstatná, nesmíchal jsem náhodou více zodpovědností?

Hydrataci do pole ja osobne zase nerad, chci objekty.

Proč?

DTO …, nicmene pokud to chápu, pak zase přijdu o veřejné metody v entitě, není to entita vlastně atd.

DTO není entita, DTO nemá ambice být entitou. DTO je data transfer object – objekt s konstruktorm a gettery. DTO je zcela anemický, protože postrádá chování.

fizzy
Backer | 49
+
0
-

U nas na shope ukladame cenu kosika priamo do entity a pri zmene produktov sa prepocitava. V session mame ulozene len id kosika, ked user nema vytvoreny tak sa vytvara az po vlozeni produktu inak sa do databazy neodosiela ziadny dotaz, vytvori sa len prazdny objekt kosika.

Inak kosik najmenej vplyva na rychlost aplikacie a optimalizoval by som radsej ine casti shopu :)

Jiří Nápravník
Člen | 710
+
0
-

Když člověk ví, na co je použije, kdy, a ví, co způsobuje, tak nevidím větší problém…

Dotaz: K projektu se dostane další programátor, bude vědět, že pracuje s objektem daného typu, použije jeho veřejnou metodu (která využívá nehydratovaných atributů), co se stane?
Nebo se zeptám jinak: Splňuje objekt rozhraní, které zveřejňuje, když není plně hydratován?

Uznávám pro nového člověka to může být komplikace všimnout si, zda jde o partial, bylo by jistě lepší kdyb to šlo z entity přímo poznat nějakým příznakem třeba. Já však neříkám, že partial objekty jsou super věc. Tazatel se ptal, že nechce tahat všechny věci, co nepotřebuje, a jedno z řešení je právě partial objekt.

… z kterých některé jsou třeba jen pro administraci

Tady bych se taky rád zastavil – pokud jeden objekt plní v administraci určitou roli, která je pro frontend nepodstatná, nesmíchal jsem náhodou více zodpovědností?

Ne protože to furt patří k jedné entitě. Pořád je to článek, jenom v administraci mě zajímá, kdo jej vložil, kdy jej vložil, kdo jej stáh třeba z TOPu a kdy. Tyhle věci mě však už na frontendu nezajímají. Ano jde tyhle věci třeba přichytit do nějaký specialní entity, ale v případě statisíců článků už je každý join poznat…

Hydrataci do pole ja osobne zase nerad, chci objekty.

Proč?

protože pole je mi k ničemu a je to low-end, pak mám entitu v podstatě na nic, je to jen přepravka nad atributy. Metody co mohu dávám rád do entit a volám ,to nad nimi, ne až někde v servisní třídě. Pokud budu hydratovat do polí, přicházím dle mě o jednu z hlavních výhod Doctrine. Nemluvě o tom, když jendou použiju pole a jednou entitu, tak to taky případnému novému programátorovi neusnadním.

DTO …, nicmene pokud to chápu, pak zase přijdu o veřejné metody v entitě, není to entita vlastně atd.

DTO není entita, DTO nemá ambice být entitou. DTO je data transfer object – objekt s konstruktorm a gettery. DTO je zcela anemický, protože postrádá chování.

Platí v podstatě to samé co u pole výše, jak píšu. V podstatě taky jen přepravka na data…

Editoval Jiří Nápravník (2. 8. 2016 11:55)

mkoubik
Člen | 728
+
+1
-

Jiří Nápravník napsal(a):
Ne protože to furt patří k jedné entitě. Pořád je to článek, jenom v administraci mě zajímá, kdo jej vložil, kdy jej vložil, kdo jej stáh třeba z TOPu a kdy. Tyhle věci mě však už na frontendu nezajímají. Ano jde tyhle věci třeba přichytit do nějaký specialní entity, ale v případě statisíců článků už je každý join poznat…

No evidentně to jedna entita není, když to tady i píšeš. Pokud je entita validní a funkční i bez nějakých atributů, pak do té entity nepatří (většinou to není stav entity, ale jen nějaká data).

Problém je že doctrine neumožňuje mít víc druhů entit nad jednou tabulkou, ale to neznamená že bys nad tím při návrhu neměl přemýšlet jako o oddělených entitách.

Většinou potřebuješ entitu pro zápis (v administraci), aby ti hlídala konzistenci. V požadavcích které nemění stav aplikace (většinou frontend) ale entitu nepotřebuješ a stačí ti nějaká immutable (takže nepotřebuješ identity mapu) view třída s helper metodama. Tu si můžeš právě krásně vytvořit z toho pole.

To že lidi používají entity v požadavcích které jen čtou data je IMHO jedno z nejrozšířenějších nepochopení doctriny a pramení z něj pak spousta „problémů“.


Edit: „view třídou s helper metodama“ jsem myslel něco jako:

class Article
{
	public function getFullAuthorName()
	{
		return $this->author->firstName . ' ' . $this->author->lastName;
	}

	public function getCommentsCount
	{
		return count($this->comments);
	}
}

ne helpery v šablonách.

Vypadá to jako entita, ale nemění to svůj stav – jen slouží k pohodlnějšímu vypsání než z pole.

Tím že objekt navrhuješ přímo pro konkrétní view, tak tam nepotřebuješ nějaké obecné traverzování, práci s kolekcemi apod. jen to co opravdu použiješ v šabloně.

Editoval mkoubik (2. 8. 2016 17:18)

Jiří Nápravník
Člen | 710
+
0
-

Já beru entitu článek. Ty na to pohlížíš jako entita – článek na frontu a v adminu. Popravdě, nedovedu si moc představit udržovat to na dvakrát. A to oddělení do více entit např. nějaký základ článku, pak věci specifické pro admin, a třeba věci specifické pro front, to mi nepřijde příliš přehledné, snadno udržovatelné a myslím, že v případě stovky tisíc takových článků, tam můžou být opravdu výkonostní problémy. Lepší bude jestli máš nějaký příklad, ať se podívám, rád se přiučím, ale opravdu jsem nikde neviděl, že by se v podstatě na jeden objekt koukalo pohledem více entit…

Ano muže na frontu být jen nějaká DTO, ale vy nepoužíváte žádné metody uvnitř entit? Například první kategorie, pod který článek padá, hlavní fotka z více, počet komentářů, poslední komentář atd.

DTO by měla být jen tupá přepravka ne? Ano jde to view helpery, ale těch by bylo spousta a v případě, když mám ještě API pro mobilní appku, tak je rozhodně pohodlnější mít to přímo v entitě, než i tady to vykreslovat přes view helpery ne?

Svaťa Šimara
Člen | 98
+
0
-

Já beru entitu článek. Ty na to pohlížíš jako entita – článek na frontu a v adminu. Popravdě, nedovedu si moc představit udržovat to na dvakrát. A to oddělení do více entit např. nějaký základ článku, pak věci specifické pro admin, a třeba věci specifické pro front, to mi nepřijde příliš přehledné, snadno udržovatelné a myslím, že v případě stovky tisíc takových článků, tam můžou být opravdu výkonostní problémy.

Moc děkuji za tento příspěvěk.

Přehlednost – více jednodužších entit je přehlednějších než jedna velká

Udržovatelnost – tady jsi uhodil hřebíček na hlavičku – více menších entit se daleko lépe udržuje než jedna velká. Opravdu, pokud mám ve 3 entitách po 10 atributech a 15 metodách – tak se mi 3 entity udržují lépe než 1 entita s 30 atributy a 45 metodami

Výkonnostní problémy – opět krásný příklad – je výkonově výhodnější mít v jedné tabulce věci jenom pro frontend a v jiné pro administraci. Pokud vytahuji data z jedné široké tabulky, jde to pomalej než z jedné poloviční. Asi jsi narážel na JOIN, tomu se dá vhodným návrhem (spíš šikovným s trochou štěstí) vyhnout

Zdroj:
Evans, Domain-driven design, část 4. Strategic design
Případně hodně zkráceně: http://martinfowler.com/…Context.html

Jiří Nápravník
Člen | 710
+
0
-

Přehlednost – u mě je to tedy spíše naopak. Výše jsi argumentoval novým programátorem. To samé zde, přijde nový programátor, bude čekat, že je jeden článek v jedné entitě, nebo rozhozený ve třech?

Výkonost – tak ono už teď tam mám asi sedm joinů, už teď je výběr 50 článků v adminu na 200ms databáze. A nezlob se na mě, ale výběr s joinem je určitě pomalejší než pokud jde o rozměrnější tabulku a ty sloupce ani nevybírám (partial).

V tom zdroji co uvádíš, jde přeci jen o komplexnější věc, tady mám třeba pro admin asi deset atributu (do nedavna asi jen ctyri), momentálne to predelavat nebudu. Chtěl bych to ale spíše vidět v nějaké praxi, nevíš o nějakém systému, co by to tak používal. Ono teorie je často pěkná věc, ale …:-)

Nicméne tohle jsme se dostali přeci jen trochu mimo. Ano možná článek pro admin má smysl, ale tady jde o další use casy, článek má spoustu atributů, co třeba v přehledu v kategorii nepotřebuji, ale v jiném výpisu ano, v detailu článku potřebuji vše. Nebo potřebuji nějaký atribut jenom pro řazení, filtrování, ale už jej nepotřebuji vybírat. Tak tam asi nemá smysl tvořit nový kontext, ale pro mojí potřebu je lepší partial, případně ti co nemají nic zajímavého v entitě DTO

Editoval Jiří Nápravník (2. 8. 2016 16:37)

Svaťa Šimara
Člen | 98
+
+2
-

To samé zde, přijde nový programátor, bude čekat, že je jeden článek v jedné entitě, nebo rozhozený ve třech?

Proč? Argumetoval jsem tím, že nový programátor bude čekat, že entita je validní. Pokud bude v různých kontextech systému koncept článku různým objektem, kde je problém?

Výkon – souhlas, pokud potřebuju data z více kontextů. Pokud ale na frontendu nidky nepotřebuju nic z administrace, pak další JOIN potřeba není, a užší tabula bude rychlejší.

Šlo o to, že ve svém článku máš nejspíš smíchané 2 koncepty, které by šlo rozdělit. Snažím se ukázat, že to lze a že to má i výhody. No, jsem rád, že vidím, k čemu se používají partial entity.