Jak pracovat s cookies (resp. dostat se ke containeru)?

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

Potřeboval bych v modelu načítat a ukládat cookies, ale…

  1. dle https://doc.nette.org/cs/http/request bych na to měl jít takto:
    $httpRequest = $container->getService('httpRequest');
    $cart = $httpRequest->getCookie('cart');
  2. tím ale narážím na to, jak získat $container. Jak?
  3. odpověď bude nejspíš někde v https://doc.nette.org/…introduction, ale zeptám se takle: Opravdu musím prvně pochopit k čemu je dobré DI a kontejner, než si budu moct načíst a uložit cookie? Nebyl by nějaký příklad s čistým řešením tohoto problému? Díky!

Editoval kahi (4. 9. 2011 20:36)

Tharos
Člen | 1030
+
0
-

Předně: model by o existenci nějakých cookies vůbec neměl vědět. Nevím, jakou přesně řešíš situaci, ale určitě bys informace uložené v cookies měl zpracovávat v presenter vrstvě a modelu bys je měl předávat pouze pomocí jeho API.

Editoval Tharos (4. 9. 2011 23:27)

kahi
Člen | 32
+
0
-

Ukládám nákupní košík. Beru to tak, že forma skladování dat není starostí presenteru, takže mám model pro košík.

Chceš říct, že v presenteru se ke cookies přistupuje jinak než v modelu?

Tharos
Člen | 1030
+
0
-

Chci tím říct, že v modelu by se vůbec nemělo přistupovat k něčemu, jako jsou cookies. Samozřejmě MVC/MVP není zapotřebí dodržovat úplně striktně – když se ví, proč se co porušuje – ale zrovna tohle je porušení IMHO docela hrubé a neobhajitelné. :)

V cookies se většinou ukládají velmi specifické a stručné informace (jako například session ID uživatele) a nelze je vnímat jako nějaké datové úložiště, se kterým by měl pracovat model. Na to skutečně nejsou určeny.

V Tvém případě bych osobně zvážil, zda aktuální stav košíku nemít v databázi (adresovaný podle loginu uživatele či session ID návštěvníka). Vytváří to samozřejmě o něco větší zátěž na databáze, ale má to značné výhody. A pokud opravdu chceš mít stav košíku čistě jenom v cookies, uchovával bych jej jen jako kombinaci například ID produktu:počet (tj. stručně) a to bych zpracovával v Presenter vrstvě (třeba v nějaké vykreslitelné komponentě). Model bych pak skrze jeho API jen žádal o názvy těch produktů podle jejich ID.

Jinak určitě bych nezapisoval data do cookies přímo, ale ukládal bych si je jako session proměnné (a v tom bych naplno využil Nette).

Editoval Tharos (4. 9. 2011 21:04)

kahi
Člen | 32
+
0
-

Omlouvám se, v prvé řadě bych rád vyřešil svůj problém, poté bych se zabývat ideální architekturou, přestože je to uznávám krátkozraké.

Ať už bych to řešil přes cookies nebo přes sessions, potřebuji se (předpokládám) dostat (nebo ho vytvořit) ke kontejneru. Opakuji, chápu, že článek o DI to asi vysvětluje, ale momentálně bych raději šel cestou převzetí existujícího funkčního řešení. Můžete nějaké takové odkázat? V dokumentaci jsem našel jen řešení dílčích problémů.

Promiň, přestože mi není jasné, v čem je Tvé řešení (db + session id) lepší (než mít v cookie pole id=>count), proč by model neměl přistupovat ke cookies atd., v prvé řadě bych chtěl vyřešit to, co mě pálí. (Tzn. jak dostat kontejner do presenteru nebo modelu.)

Tharos
Člen | 1030
+
0
-

Když si odmyslím nejpřimočařejší řešení vůbec, totiž přístup do superglobální proměnní $_COOKIE, můžeš postupovat následovně:

Někde inicializuješ model a v tom místě mu potřebuješ předat (třeba přes konstruktor) z DI kontejneru službu httpRequest. Tato služba (reprezentovaná třídou Nette\Http\Request) obsahuje metody pro přístup ke cookies.

// kdesi v Presenteru
$basked = new MyApp\Basket($this->context->httpRequest); // do this at your own risk

Pokud bys model inicializoval přímo v DI kontejneru, stačí mu onu službu předat nějakým vhodným mechanismem (auto-wiringem, ručním zápisem v konfiguračním souboru a podobně…).

Nikomu ale neříkej, že Ti tohle bylo poraděno zde na fóru, protože něco jako instance Nette\Http\Request by se do modelu správně nikdy nemělo dostat.

Editoval Tharos (4. 9. 2011 23:29)

pave.kucera
Člen | 122
+
0
-

Kontejner je v presenteru přístupný přes

<?php
$this->getContext();
?>

ale konkrétně na službu httpRequest je tam i protected getter.

Do modelu si pak předej httpRequest v konstruktoru, ulož ho do nějaké proměnné a pak s ním můžeš vesele pracovat.

<?php
class MyModel {
	private $httpRequest;

	public function __construct(\Nette\Http\Request $request)
	{
		$this->httpRequest = $request;
	}

	public function something()
	{
		// práce s cookie přes $this->httpRequest
	}
}
?>

// Edit: Tharos byl rychlejší :)

Editoval pave.kucera (4. 9. 2011 21:59)

uestla
Backer | 799
+
0
-

Nejdou náhodou cookies ručně měnit?

Filip Procházka
Moderator | 4668
+
0
-

Jak už tady padlo, nevidím důvod proč by to nemohlo být v session nebo v databázi. Je to bezpečnější, systémovější, čistější.

kahi
Člen | 32
+
0
-

Tharos, pave.kucera – děkuji!

Nejdou náhodou cookies ručně měnit?

V tom nevidím problém, nákupní košík se musí tak či tak před objednávkou zkontrolovat.

nevidím důvod proč by to nemohlo být v session nebo v databázi. Je to bezpečnější, systémovější, čistější.

Protože je to více práce, větší složitost, více kódu, více potenciálních chyb, alespoň v mém případě určitě. :-)

V tomto případě mě nenapadá, jaké bezpečnostní riziko plyne z použití cookies pro uložení košíku.

Filip Procházka
Moderator | 4668
+
0
-

Protože je to více práce, větší složitost, více kódu

class Basket extends Nette\Object
{
	/** @var SessionSection */
	private $storage;

	public function __construct(SessionSection $session)
	{
		$this->storage = $session;
	}

	public function getItemsList()
	{
		return $this->storage->getIterator()->getArrayCopy();
	}

	public function add($item, $count = 1)
	{
		if (isset($this->storage->$item)) {
			$count += $this->storage->$item;
		}
		$this->storage->$item = $count;
	}

	public function remove($item)
	{
		unset($this->storage->$item);
	}
}

$this->basket = new Basket($this->getSession('basket')); // v presenteru

Chtěl bych vidět, jak napíšeš kratší řešení s cookies.

více potenciálních chyb

A to kde prosím?

redhead
Člen | 1313
+
0
-

OT: Ne basket, ale cart (via perfekcionista)

Tharos
Člen | 1030
+
0
-

Ještě doplním, že já, coby „programující teoretik sedmého druhu“ (a purista) , bych doporučil ještě pracovat v té session v nějaké vykreslitelné komponentě a do třídy Cart už bych ji nepředával. V mé aplikaci bys narazil zhruba na následující řešení:

namespace MyApp\Application\UI\Control;

use Nette\Http\SessionSection;
use MyApp\Model\Cart as CartModel

class Cart extends \Nette\Application\UI\Control
{
	private $sessionSection;

	private $cartModel;

	public function __construct(SessionSection $sessionSection, CartModel $cartModel, \Nette\ComponentModel\IContainer $parent = null, $name = null)
	{
		parent::__construct($parent, $name);
		$this->sessionSection = $sessionSection;
		$this->cartModel = $cartModel;
	}

	public function renderCart()
	{
		$template = $this->template->setFile(__DIR__ . DIRECTORY_SEPARATOR . 'cart.latte');
		try {
			$template->cart = $this->cartModel->findItemsInCart($this->sessionSection->cartId);
			$template->render();
		} catch (\MyApp\Model\CartException $e) {
			// zalogování + nějaké vyřešení chyby
		}
	}

Psáno úplně z hlavy (a trochu i paty), určitě v tom budou překlepy (už teď vidím, že mi nahoře chybí jeden středník), ale myšlenka by z toho měla být jasná.

Editoval Tharos (5. 9. 2011 13:23)

kahi
Člen | 32
+
0
-

Chtěl bych vidět, jak napíšeš kratší řešení s cookies. (HosipLan)

Aha. No, nenapíšu :-). Děkuji.

V mé aplikaci bys narazil zhruba na následující řešení: (Tharos)

Inspirativní, děkuji.