Jak pracovat s cookies (resp. dostat se ke containeru)?
- kahi
- Člen | 32
Potřeboval bych v modelu načítat a ukládat cookies, ale…
- dle https://doc.nette.org/cs/http/request
bych na to měl jít takto:
$httpRequest = $container->getService('httpRequest'); $cart = $httpRequest->getCookie('cart');
- tím ale narážím na to, jak získat $container. Jak?
- 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
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
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
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
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)
- Filip Procházka
- Moderator | 4668
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
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
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?
- Tharos
- Člen | 1030
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)