MVC – Jak postupovat při výpisu a filtrování produktů na e-shopu?

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

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)

ali
Člen | 342
+
0
-
  • 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]
David Kregl
Člen | 52
+
0
-

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)

iNviNho
Člen | 352
+
0
-

Injectovanie v modele je vypnute. Vytvor si to cez constructor a nechaj di container nech to vytvori :)

F.Vesely
Člen | 369
+
+1
-

Inject pouzivej pouze u Presenteru, vsude ostatne si to predavej pres constructor.

	muj.model:
		class: App\Model\MujModel
		arguments: [@session.session]

Tohle delat nemusis, staci jen:

	- App\Model\MujModel
ali
Člen | 342
+
+2
-

@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
+
+5
-

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

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)