Cenové hladiny, aneb „kam s nima“

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

Dobrý den,
vytvářím si eshopík v nette a mám dotaz, jak byste řešili cenové hladiny. Shopík už mě funguje, ceny pro uživatele se mě načtou z tabulky zboží, momentálně bez cenových hladin. Teď bych je tam chtěl nějak dostat. Zatím jsem přišel na 2 možnosti:

  1. V basePresenteru bych si ve startup vytáhl, pokud je uživatel přihlášený, jeho cenové hladiny a připravil si pro něj registerHelper, kde bych ze šablony volal tento helper a změnil cenu. (náročnost: udělat toto jen v basepresenteru a v šablonách volat tento helper, při ukládání objednávky jen natáhnout správnou cenu za zboží).
  2. Upravit všechny dotazy, kde se renderuje zboží, přehled, košík tak, aby se načetly už ceny dle jeho cenové hladiny (náročnost: oprava všech dotazů do DB se zohledněním přihlášeného uživatele a jeho cenové hladiny.)

Nebo je ještě nějaká jiná možnost?
Tento shopík mám pouze jako učící pomůcku, tak to prosím tak berte ;)

blacksun
Člen | 177
+
0
-

Řešil bych to už na úrovni databáze, například funkcí vracející zboží najoinované na ceny, kde by parametrem byl uživatel nebo rovnou nějaké id cenové hladiny.

tatyalien
Člen | 239
+
0
-

Takže „b“, do toho se mě moc nechce, ale asi to bude nejčistější řešení.

petr.pavel
Člen | 535
+
0
-

Přikláněl bych se k nějaké podobě (a), tj. mít třídu, která má na starosti stanovení ceny. Řešil bych tedy na úrovni PHP a ne databáze. Teď sice můžeš mít situaci, kdy by to čistě přes db vyřešit šlo, ale radši bych si nechal volnější cestu do budoucna a řešil v PHP.

tatyalien
Člen | 239
+
0
-

No třídu na stanovení ceny, abych pravdu řekl, ani nevím jak bych si jí představil, ani kde bych jí přesně volal. V presenteru načtu zboží co se má zobrazit a v renderu ho vykresluji… tak jestli pak v render metodě volat tuto třídu, nebo v šabloně? (do třídy by se předalo například katalogové číslo a id zákazníka, pokud se jedná o nepřihlášeného, vrátila by se základní cena, pokud je přihlášenej, už by se rozhodovalo. To si představit umím, ale kde to přesně volat?)

petr.pavel
Člen | 535
+
0
-

Možná mám špatnou představu o tom, co myslíš pod pojmem cenová hladina. Předpokládám, že některý zákazník má 5% slevu na všechno, jiný 10% na spotřební materiál, ale na zbytek jen 5%, nebo slevu 15%, ale jen do konce roku, prostě cokoliv, co je prodejní oddělení schopno vymyslet :-)
Takže třída CenaVyrobku by měla metodu spocitej(), které bys předával všechno, co potřebuje. Jestli si navíc uděláš helper, proč ne. Určitě ale budeš někdy potřebovat získat cenu už v presenteru, takže bych logiku nedával jen do helperu.

h4kuna
Backer | 740
+
0
-

Jen navazuji na to co psal petr.pavel

petr.pavel napsal(a):

Předpokládám, že některý zákazník má 5% slevu na všechno, jiný 10% na spotřební materiál, ale na zbytek jen 5%, nebo slevu 15%

tomto doplňku je 4. parametr procentní upravení ceny viz ukázka

{!$money|currency:eur:usd:1.1}

a aby se ti nepřepočítávalo tak zachovej výchozí parametry metody format.

{!$money|currency:NULL:NULL:1.1} není potřeba přepočítávat

Přidaná hodnota je že ti to vyřeší formátování měny, oproti helperu number který je v nette má výhodu že si to globálně nastavíš pro celý web a nemusíš pokaždé znovu nastavovat viz ukázka .

Ale já bych to řešil na úrovni databáze a využil jen formátování čísla.

Editoval h4kuna (1. 8. 2012 10:47)

tatyalien
Člen | 239
+
0
-

petr.pavel:
Ano máš pravdu, prostě jakékoliv přepočítání základní ceny dle parametrů. Nejspíš bych dodal metodu do ShopModel kde si beru hodnoty z dtb na výpis zboží. Jen nevím jak bych měl postupovat. V presenteru si vytáhnu zboží například:

$this->zbozi = $this->modelShop->getGoods()->select('*')->where('skryteZbozi',0)->order('katalog')->limit($paginator->itemsPerPage, $paginator->offset);

Nyní mám v $this->zbozi například 10 produktů se základní cenou, toto můžu narvat do template a v template vykreslit.
Měl bych tedy nyní přenastavit v $this->zbozi například

foreach($this->zbozi as $key => $val) {
	$this->zbozi[$key]['cena'] = $this->modelShop::nastavCenu(this->zbozi[$key]['cena'], $this->getUser()->getId());
}

(kde by nastavCenu si skontrolovalo, jestli je na daný produkt nějaká pevná sleva, nebo procentuální, případně žádná)

petr.pavel
Člen | 535
+
0
-

Jestli vždycky pracuješ se slevou, tak místo nastavCenu() bych napsal zjistiSlevu() a cenu počítal až třeba v šabloně. Cílem je vyčlenit do metody jen to, co je potřeba pro odlišení ceny.

Taky záleží na tom, podle čeho cenu/slevu sestavuješ. Jestli podle kategorie výrobku a sazby zákazníka, předávej metodě tyto dvě, ne prostředníky. Tj. ne cenu a id zákazníka, ale kategorii a sazbu. Bude to logicky průhlednější a líp se ti pak bude metoda testovat.

tatyalien
Člen | 239
+
0
-

Ok, nějak si to rozvrhnu a zbastlím jak bude čas ;)

Ohledně počítání, nejspíš bych měl předávat identitu uživatele (kde bude uložena jeho hladina), id produktu). Jen, pokud to bude muset aplikace kontrolovat oproti DB, tak například u výpisu zboží se stránkováním by byl například při 10 položkách 10× dotaz do db pro každý produkt zvlášť :(.

To už by bylo lepší předat získaná data a přenastavit to najednou.

Editoval tatyalien (1. 8. 2012 13:42)

petr.pavel
Člen | 535
+
0
-

Proč chceš předávat celou identitu, když už teď víš, že potřebovat budeš jen hladinu? Znáš ten vtip o klavíru?

Jestli používáš Nette Database, tak by to nemuselo být 10 dotazů, ale jen jeden. Tohle lazy zpracování je jedna z jeho hlavních předností.

Existuje takové doporučení, nejdřív programovat čistě z hlediska návrhu, a teprve když to všechno funguje, tak začít optimalizovat výkon.

tatyalien
Člen | 239
+
0
-

Napsal jsem to blbě, myslel jsem předávat hodnotu z identity uživatele (jeho cenovou hladinu) ;). Nette database používám, tak to prubnu ;) nejdřív sesmolit a až pak optimalizovat ;).

tatyalien
Člen | 239
+
0
-

Něco jsem včera večer ještě spíchl:

/**
 * Vrátí přepočítanou cenu zboží. Nepřihlášený uživatel, nebo uživatel bez cenové hladiny obdrží zpět počáteční cenu.
 * Pokud má uživatel nastavenou cenovou hladinu, cena se nastaví:
 * a) pokud je v tabulce self::TABLE_SLEVAZBOZI zadané katalogové číslo se zvolenou cenovou hladinou, přiřadí se zadaná cena
 * b) vytáhne se procentní sleva z tabulky self::TABLE_CENOVAHLADINA a vynásobí se aktuální cena
 * c) pokud cenová hladina neexistuje, vrátí se počáteční cena
 * @param int|NULL $cenovaHladina
 * @param string $katalog
 * @param double $cena
 * @return double
*/
public function getProduktCena($cenovaHladina, $katalog, $cena)
{
    // pokud neexistuje cenová hladina, vracím zpět nezměněnou cenu
    if(!isset($cenovaHladina) || is_null($cenovaHladina)) {
        return $cena;
    }
    // pokud existuje pevná sleva na zboží se zvolenou cenovou hladinou
    $row = $this->getSlevaZbozi()->select('cena')->where('goods_katalog', $katalog)->where('cenovehladiny_id', $cenovaHladina)->limit(1)->fetch();
    if($row) {
        return $row['cena'];
    }
    // pokud neexistuje pevná cena, načtu si cenovou hladinu a přenastavím cenu
    $row = $this->getCenovaHladina()->select('sleva')->where('id', $cenovaHladina)->limit(1)->fetch();
    if($row) {
        return $cena * $row['sleva'];
    } else {
        // pokud hladina neexistuje, vracím nezměněnou cenu
        return $cena;
    }
}

v basePresenteru si dodám helper:

$_this = $this;
$this->template->registerHelper('nastavCenu', function($cena, $katalog) use ($_this) {
    if($_this->user->isLoggedIn()) {
        return $_this->modelShop->getProduktCena($_this->getUser()->getIdentity()->data['cenovehladiny_id'], $katalog, $cena);
    } else {
        return $cena;
    }
});

Kde následně mohu volat v šabloně:

přepočítáno: {$ceny[$value['goods_katalog']]['cenaSdani']|nastavCenu, $value['goods_katalog']}

Editoval tatyalien (2. 8. 2012 8:02)

petr.pavel
Člen | 535
+
0
-

Za mě dobrý :-)

Jen mě napadá pár poznámek:

  • $_this->getUser()->getIdentity()->data['cenovehladiny_id'] jde myslím zapsat jako $_this->user->identity->cenovehladiny_id. Ostatně, překvapuje mě, že se dostaneš na data, když jsou private.
  • if(!isset($cenovaHladina) || is_null($cenovaHladina)) by šlo zapsat jako if (empty($cenovaHladina)), pokud bych nechtěl vyloučit 0 a ''.
  • Je nějaký důvod, proč si předpřipravuješ ceny do pole $ceny? Jestli jsou v samostatné db tabulce a jen je linkuješ, můžeš linkovat rovnou při zobrazování – a lazy zpracování Nette DB ti to spojí do jediného dotazu.
tatyalien
Člen | 239
+
0
-
  • $_this->getUser()->getIdentity()->data['cenovehladiny_id'] – nějak toto používám, v data mám ostatní údaje po autorizaci uživatele. Ale prubnu to tvoje $_this->user->identity->cenovehladiny_id
  • if(!isset($cenovaHladina) || is_null($cenovaHladina)), zde by „0“ neměla být nastavená, ale raději jsem testoval jestli existuje hladina, a není NULL tak šlapej dál, ale asi bz fakt stačilo empty($cenovaHladina)
  • To pole ceny je pouze ve vykreslení košíku. V sessionu/DB košíku si pouze držím: users_id, goods_katalog, goods_nazev, ks, poznamka, datum. Pokud uživatel klikne na košík tak si jen vytáhnu aktuální ceny z DB proto je zde samostatné pole „ceny“. Kdekoliv jinde na stránkách je jinak cena přímo při výpisu z tabulky goods.

Editoval tatyalien (2. 8. 2012 11:23)