MVC – Jak postupovat při výpisu a filtrování produktů na e-shopu?
- David Kregl
- Člen | 52
Zdravím,
řeším teď konkrétní problém s výpisem produktů pro e-shop a uvědomuji si, že vlastně vůbec nevím, která vrstva má v aplikaci zařizovat co.
Konkrétní problém:
Snažím se vypsat všechny produkty z tabulky products, které jsou
v dané kategorii a ještě na výběr musím aplikovat uživatelem zvolené
filtry a v poslední řadě ještě IPub/VisualPaginator.
Bohužel se trápím tím, že nevím, která vrstva MVC by měla dělat co a
vlastně si vůbec nejsem jistý jestli je můj postup správný.
Současná situace:
ProductPresenter.php
/**
* @inject
* @var FrontModule\Model\Product
*/
public $product;
public function renderDefault($category = null)
{
$filters = $this->session->getSection('filters')->filters;
$products = $this->product->findAllProducts($category, $filters);
$this->template->products = $this->setupVisualPaginator($products);
}
public function setupVisualPaginator($products) {
//černá magie
return $products;
}
Product.php
public function findAllProducts($category, $filters)
{
$products = $this->db->table('products')
->where('status', 'active')
->having('COUNT(:warehouse.product_id) > 0')
->group('products.id');
//pokud se jedná o kategorii NOVÉ (id 4), tak vyberu produkty, které byly přidány v posledních 14 dnech – vím, je to velmi nešikovné.
if ($category != 4) {
$products->where(':products_in_categories.category_id', $category);
} else {
$products->where('products.ins_date > DATE_SUB(curdate(), INTERVAL 2 WEEK)');
}
$this->applyFilters($products, $filters);
return $products;
}
public function applyFilters($products, $filters)
{
$order = $filters['order'];
$search = $filters['search'];
$size = array_keys($filters['sizes']);
$color = array_keys($filters['colors']);
$brand = array_keys($filters['brands']);
//order
if ($order) {
switch ($order) {
case 'new';
$order = 'products.ins_date DESC';
break;
case 'cheap';
$order = 'price_retail';
break;
case 'expensive';
$order = 'price_retail DESC';
break;
}
$products->order($order);
} else {
$products->order('products.ins_date DESC');
}
if ($search) {
$products->where('title LIKE CONCAT("%", ? ,"%")', $search);
}
//size
if (!empty($size)) {
$products->where(':warehouse.size_id', $size);
}
//color
if (!empty($color)) {
$products->where(':warehouse.color_id', $color);
}
//brand
if (!empty($brand)) {
$products->where('brand', $brand);
}
//
return $products;
}
Konkrétní dotazy:
- Je řešení filtrů přes session v pořádku? A jak řešíte filtry vy? Nebylo by nejlepší, abych si napsal na filtry vlastní třídu?
- Prosím o všechny menší i větší připomínky k celému řešní – funkce, vrstvy, …
- Jak by šel elegantněji vyřešit problém s kategorií NOVÉ? (viz. PHP komentář)
- Jakým způsobem mohu přistoupit k Nette\Http\Session přímo z modelu?
Předem velice děkuji za Váš čas,
David
Editoval David Kregl (15. 11. 2015 20:33)
- David Kregl
- Člen | 52
ali napsal(a):
- osobne resim filtry pres persistentni parametr
- k session pristoupis velice jednoduse a to tak, ze si tuhle sluzbu injektnes do modelu pres config
muj.model: class: App\Model\MujModel arguments: [@session.session]
A můžu si ji tam vstříknout přímo v samotném modelu? Je následující řešení v pořádku?
/**
* @inject
* @var Session
*/
public $session;
Jak vypadá takové řešení přes persistentní parametry? Máš nějaký příklad nebo mi postačí dokumentace?
Děkuji Ti,
David
Editoval David Kregl (15. 11. 2015 20:54)
- ali
- Člen | 342
@DavidKregl
/** @persistent */
public $filter = [];
public function render()
{
$this["filterForm"]->setDefaults($this->filter);
...
}
/**
* @return Nette\Application\UI\Form
*/
protected function createComponentFilterForm()
{
$form = new Application\UI\Form;
$form->addText("text");
...
...
$form->addSubmit("send", "Filter");
$form->addSubmit("cancel", "Cancel");
$form->onSuccess[] = [$this, "filterSuccess"];
return $form;
}
/**
* @param Nette\Application\UI\Form
* @param array
*/
public function filterSuccess(Application\UI\Form $form, $values)
{
if ($form->isSubmitted()->name === "send") {
$this->filter = array_filter((array) $values, function ($s) {return ($s === "" || $s === NULL || $s === [] ? FALSE : TRUE);});
$this->redirect("this");
} else {
$this->filter = [];
$form->setValues([], TRUE);
$this->redirect("this");
}
}
- Pavel Macháň
- Člen | 282
David Kregl napsal(a):
Zdravím,
řeším teď konkrétní problém s výpisem produktů pro e-shop a uvědomuji si, že vlastně vůbec nevím, která vrstva má v aplikaci zařizovat co.
Konkrétní problém:
Snažím se vypsat všechny produkty z tabulky products, které jsou v dané kategorii a ještě na výběr musím aplikovat uživatelem zvolené filtry a v poslední řadě ještě IPub/VisualPaginator.
Bohužel se trápím tím, že nevím, která vrstva MVC by měla dělat co a vlastně si vůbec nejsem jistý jestli je můj postup správný.
...........
Konkrétní dotazy:
- Je řešení filtrů přes session v pořádku? A jak řešíte filtry vy? Nebylo by nejlepší, abych si napsal na filtry vlastní třídu?
- Prosím o všechny menší i větší připomínky k celému řešní – funkce, vrstvy, …
- Jak by šel elegantněji vyřešit problém s kategorií NOVÉ? (viz. PHP komentář)
- Jakým způsobem mohu přistoupit k Nette\Http\Session přímo z modelu?
Předem velice děkuji za Váš čas,
David
@DavidKregl 1] Je řešení filtrů přes session v pořádku? A jak řešíte filtry vy? Nebylo by nejlepší, abych si napsal na filtry vlastní třídu?
Určitě bych neřešil filtry pomocí session. Pokud bude chtít někdo zaslat někomu seznam produktů dle daného filtru tak mu nebude stačit zkopírovat URL a to následně zaslat. Tlačítko pro vygenerování url dle nastavených filtrů považuji jako fail.
Filtry bych dal přímo do url pomocí @persistent
2] Prosím o všechny menší i větší připomínky k celému řešní – funkce, vrstvy, …
Aplikaci filtrů bych upravil na foreach + switch abych se vyhnul těm podsebou vypsaných ifů. Něco ve smyslu:
public function applyFilters($products, $filters)
{
foreach($filters as $type => $value) {
switch($type){
case 'brands':
$products->where('brand', $value);
break;
case 'colors':
$products->where(':warehouse.color_id', $value);
break;
// atd...
}
}
return $products;
}
Výpis dat, třeba listu produktů bych nedával přímo do presenteru, ale vytvořil bych si na to komponentu. Přeci jen budeš mít výpis produktů i v administraci, kde bude stačit nastavit jinou šablonu a přidat funkce.
3] Jak by šel elegantněji vyřešit problém s kategorií NOVÉ? (viz. PHP komentář)
Nové produkty bych nedával do kategorie Nové (pokud to je kategorie zboží), ale přiřadil bych jim buď nějaký tag (nové, akce, výprodej) nebo dle času kdy se produkt dostane do prodeje (published_at nebo tak něco).
4] Jakým způsobem mohu přistoupit k Nette\Http\Session přímo z modelu?
Injectni si to přez konstruktor a nemusíš se o nic starat.
services:
- MyModel
use Nette\Http\Session;
class MyModel
{
/** @var Session */
private $session;
public function __construct(Session $session){
$this->session = $session;
}
}
Editoval Pavel Macháň (16. 11. 2015 11:53)
- David Kregl
- Člen | 52
Děkuji za vaše postřehy, přípomínky a návrhy řešení.
Hezký den,
Dejv
Editoval David Kregl (24. 11. 2015 16:25)