Jak spravne vytvaret instanci cache?

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

Dobry den,
chtel bych pouzivat spravne cache.

Dnes jsem si cetl chat a nekdo tam nekomu jinemu nadaval, protoze vytvarel instanci Cache a FileStorage primo v Presenteru. Ja to tak delam take.

Proc to tak nemuze byt? Jak by to spravne melo vypadat? Zrovna ted resim, jak bych to mel udelat na sandboxu nette verze 2.0.11

Editoval moonlol (6. 8. 2013 20:29)

h4kuna
Backer | 740
+
0
-

V nette je připravená factory ke které se dostaneš

<?php
$this->context->nette->createCache('name');
?>

Případně si udělej alias metodu

<?php
public function getCache($name = NULL) {
    return $this->context->nette->createCache($name);
}
?>

Popravdě nevzpomenu si na případ, kdy použít cache v presenteru, to by měl řešit model. Do modelu si to nadrátuj pomocí DI a autowire.

Editoval h4kuna (6. 8. 2013 21:24)

moonlol
Člen | 19
+
0
-

Tohle mi nestaci. :(
Potreboval bych to, prosim, podrobneji.

Kdyby to bylo kdekoliv napsane, precetl bych si to, ale nikdo to nikde nerozebral.(Alespon jsem to nenasel) V quickstartu nic, v dokumentaci o cachovani taky ne. Tam je to prave pro zacatecnika popsane tak, aby to svadelo vytvaret kdekoliv.

Nevim, jak mam nastavit config.neon a pouzit DI a autowire.

V presenteru pouzivam ted tohle

 $storage = new Nette\Caching\Storages\FileStorage('../temp');
 $cache = new Cache($storage);

 $multi_arr=null;
$vvalue = $cache->load('xxx');
if($vvalue === NULL)
	//zavolej model a cachuj co mi vrati.

opravdu bych ocenil, kdyby nekdo demonstroval vsechny kroky, ktere je treba udelat abych ekvivalent tohodle kodu mohl pouzivat v modelu ale spravne.

.....

Jeste dodavam, ze v config neon, musim mit nastaveno v services nyni

	cacheStorage:
			class: Nette\Caching\Storages\DevNullStorage

aby se mi nevytvarela databazova cache, protoze kdyz se vytvari, tak to hazi nahodne undefined offset.

Editoval moonlol (7. 8. 2013 6:02)

bazo
Člen | 620
+
0
-

normalne si votvor konstruktor v modeli

use Nette\Caching\Cache;
use Nette\Caching\IStorage;

class Model
{
  /** @var Cache */
  private $cache;

  public function __construct(IStorage $cacheStorage)
  {
    $this->cache = new Cache($cacheStorage);
  }
}

a ides

frosty22
Člen | 373
+
0
-

No bohužel přistupovat ke contextu z presenteru není ideální, ale ještě nikdo nepřišel z žádnou efektivnější metodou pro továrničky, takže přesně tak jak píše h4kuna.

V preenteru (není best-practice, a mělo by být v modelu):

<?php
public function actionDefault()
{
	$cache = $this->context->nette->createCache('mojeCache');
	$value = $cache->load("nahodneCislo");
	if ($value === NULL) {
		$value = rand(1,100000);
		$cache->save("nahodneCislo", $value, array(...));
	}
	var_dump($value);
}
?>

Z kontajneru je připravená továrnička pro cache od nette, ta využívá default FileStorage právě temp, pokud bys chtěl vlastní či jiný storage, musíš si vytvořit v neon jinou instanci Cache a předat Storage a poté si třeba udělat vlastní továrničku.

Jinak mnohem hezčí je to samozřejmě v modelu, kde by to mělo být a vyhneš se přístupu ke kontextu, díky autowiringu se ti předá závislost sama, čili stačí mít ve své službě:

<?php
class MojeSluzba {

	public function __construct(\Nette\Caching\Cache $cache)
	{
		var_dump($cache); // Tady již je cache
	}

}
?>

A v NEONu tedy klasicky služby:

services:
	mojeSluzba:
		class: MojeSluzba(@nette.createCache("mojeCache"))
frosty22
Člen | 373
+
0
-

@bazo myslím si že je lepší předávat do modelu již přímo cache, než-li pouze storage, závislosti by se měli předávat – DI.

A zároveň je hezčí, když názvy daných cache objektů se definují v neonu, kde se vytváří tedy instance Cache, než-li poté v modelu, kvůli kolizi jmenných prostor.

ViPEr*CZ*
Člen | 818
+
0
-

Tak ono by mělo jít si zaregistrovat službu v konfigu, která vznikne z @nette.createCache("mojeCache") a tu si potom přes inject* injectovat do Presenteru jestli se nepletu (případně mě opravte, mysl mám teď převážně jinde… jen takovej rychlý nápad jak se vyhnout přímo použití contextu pro předání instance Cache do presenteru).

moonlol
Člen | 19
+
0
-

hlasi mi to

Service ‚mojeSluzba‘: Reference to missing service ‚nette.createCache‘.

takze muj neon.config

#
# SECURITY WARNING: it is CRITICAL that this file & directory are NOT accessible directly via a web browser!
#
# If you don't protect this directory from direct web access, anybody will be able to see your passwords.
# https://nette.org/en/security-warning
#
common:
	dibi:
		host: 127.0.0.1
		username: root
		password:
		database: db
		lazy: TRUE
	parameters:

	php:
		date.timezone: Europe/Prague
		# zlib.output_compression: yes

	nette:
		application:
			errorPresenter: Error


		session:
			expiration: 14 days


	services:
		routerFactory: RouterFactory
		router: @routerFactory::createRouter
		dataRepository: data
		mojeSluzba:
			class: MojeSluzba(@nette.createCache("mojeCache"))




	factories:


production < common:

development < common:

muj model

<?php

use Nette\Security,
	Nette\Utils\Strings,
    Nette\Caching\Cache,
    Nette\Caching\IStorage;



class Data extends Nette\Object
{
	/** @var \DibiConnection */
	private $database;



	public function __construct(\DibiConnection $database)
	{
		$this->database = $database;
	}

    public function findAll()
    {

        return $this->database->select('*')->from('data')->fetchAll();

    }


}

a ted jak bych chtel Model mit…
Nevim, jak upravit config.neon aby to jelo.

<?php

use Nette\Security,
	Nette\Utils\Strings,
    Nette\Caching\Cache,
    Nette\Caching\IStorage;



class Data extends Nette\Object
{
	/** @var \DibiConnection */
	private $database;
    /** @var Cache */
    private $cache;


	public function __construct(\DibiConnection $database,\Nette\Caching\Cache $cache)
	{
		$this->database = $database;
        $this->cache = $cache;
	}

    public function findAll()
    {

        return $this->database->select('*')->from('data')->fetchAll();

    }


}
config.neon je pro me neco uplne nepochopitelnyho, nevim jak se ho naucit.

Editoval moonlol (7. 8. 2013 10:35)

moonlol
Člen | 19
+
0
-

myslim si, ze by melo stacit nejak predat ty dva argumenty v config.neonu k radku dataRepository: data .. ale absolutne netusim jak.. :\

Editoval moonlol (7. 8. 2013 10:54)

frosty22
Člen | 373
+
0
-

Konfig je jádro celé aplikace – definuješ tam všechny služby, továrničky, a parametry z toho se pak vygeneruje kontajner (ten si může prohlédnout v temp/cache/_Nette.Configuration ..

Podstatné pod sekcí „parameters“ máš dané parametry, hodnoty .. do sekce „services“ přidáváš své služby tj. i tento objekt „Data“ co máš .. a záložka „factories“ je pro továrničky, tj. objekty, které při vytváření mohou chtít různé parametry – viz například u cache právě její název.

Neon, co bys mohl mít v tomto případě – raději použiji vlastí instanci Nette/Cache namísto, té co již má v sobě – tohle si tam tedy můžeš přidat:

services:
		cacheStorage:
			class: Nette\Caching\Storages\FileStorage("%tempDir%/cache")

		data:
			class: Data(..., @cache("muj-nazev-cache"))

factories:
		cache:
			class: Nette\Caching\Cache
			arguments: [@cacheStorage, %namespace%]
			parameters: [namespace]

A tohle ti přidá do toho vygenerovaného kontajneru přidá metody:

  1. CreateServiceCacheStorage() – tato metoda bude vytvářet instanci FileStorage a předájí cestu k tempu
  2. CreateServiceData() – tato metoda bude vytvářet instanci tvého objektu Data, ta chce v kontruktoru třídu DibiConnection a druhý parametr cache. Jelikož DibiConnection již v kontejneru máš, tak to tomu modelu předá samo (autowiring) a tudíž tam stačí napsat ty 3 tečky.

Objekt ale typu Cache tam není, jelikož je to továrnička nikoliv pouze služba a tím pádem to tam musíš napsat ručně, což @cache(něco) je pro volání továrničky s názvem „cache“ a předání daných argumentů.

  1. createCache($namespace) – to již je metoda, která přijímá parametr, tudíž je to z hlediska toho kontajneru továrnička – a ta vytváří instanci Cache s tím, že prvím argumentem předá službu FileStorage (= zde jsem definovat tu služu přes název tj @cacheStorage) + právě ten název cache (= namespace), který je ale volitelný, čili je to jako parametr a ten je volaný viz bod 2.

No nevím jak lépe to popsat, snad pomohlo.

moonlol
Člen | 19
+
0
-

Diky Tvemu vysvetleni jsem asi pochopil, jak to mam nastavit(i kdyz pochybuji, ze tomu skutecne rozumim)

Zkoumal jsem nyni obsah cache v modelu a vypada to ze je v nem presne, co ma byt. Jeste jednou moc dekuji!

Kdyby v tom nyni nekdo videl neco co delam spatne, tak me prosim upozornete. Hodlam to takhle pouzivat! Dekuji

config.neon

<?php

#
# SECURITY WARNING: it is CRITICAL that this file & directory are NOT accessible directly via a web browser!
#
# If you don't protect this directory from direct web access, anybody will be able to see your passwords.
# https://nette.org/en/security-warning
#
common:
	dibi:
		host: 127.0.0.1
		username: root
		password:
		database: db
		lazy: TRUE
	parameters:

	php:
		date.timezone: Europe/Prague
		# zlib.output_compression: yes

	nette:
		application:
			errorPresenter: Error


		session:
			expiration: 14 days


	services:
		cacheStorage:
			class: Nette\Caching\Storages\FileStorage("%tempDir%/cache")
		routerFactory: RouterFactory
		router: @routerFactory::createRouter
		dataRepository:
			class: Data (@dibi.connection,@cache("mojeCache"))





	factories:
		cache:
					class: Nette\Caching\Cache
					arguments: [@cacheStorage, %namespace%]
					parameters: [namespace]


production < common:

development < common:

?>

a muj model

<?php



use Nette\Utils\Strings;




class Data extends Nette\Object
{
	/** @var \DibiConnection */
	private $database;
    /** @var Cache */
    private $cache;


	public function __construct(\DibiConnection $database,\Nette\Caching\Cache $cache)
	{
		$this->database = $database;
        $this->cache = $cache;
    var_dump($this->cache);

	}

    public function findAll()
    {

        return $this->database->select('*')->from('data')->fetchAll();

    }


}


?>

Editoval moonlol (7. 8. 2013 11:45)

frosty22
Člen | 373
+
0
-

Je to tak jak má být, až na ta pojmenování – to „mojeCache“ bych si pojmenovat nějak hezčejc, osobně používám název pro namespace cache podle názvu objektu, která s ní pracuje, čili v tvém případě „Data“

.. akorát tady se naráží na druhé pojmenování, nelíbí se mi to „Data“, víceméně už je to že jsi si asi pojmenovat tabulku „data“ tak je to docela zvláštní – pokud to máš pro test, budiž, jinak ty „data“ asi něco reprezentují. A potom tedy tedy když už je to repozitář a dokonce jsi ho tak pojmenovat v configu „dataRepository“, tak bych se tak i držel u třídy čili „DataRepository“ a pak tedy namísto „mojeCache“ ⇒ „DataRepository“.

bazo
Člen | 620
+
0
-

frosty22 napsal(a):

@bazo myslím si že je lepší předávat do modelu již přímo cache, než-li pouze storage, závislosti by se měli předávat – DI.

A zároveň je hezčí, když názvy daných cache objektů se definují v neonu, kde se vytváří tedy instance Cache, než-li poté v modelu, kvůli kolizi jmenných prostor.

Cache ako sluzba nie je, a v roznych castiach aplikacie mozes potrebovat rozny namespace. skus autowirovat 20 roznych cache

moonlol
Člen | 19
+
0
-

Pojmenovani mam nevhodne, to napravim a diky za pripominku.

frosty22
Člen | 373
+
0
-

@bazo: Asi jsme se nepochopili a nebo spíše nechápu jak to teď myslíš.

Tys nahoře ukazoval příklad kde jsi předal Storage a univř modelu jsi vytvářel až novou instanci new Cache .. což jsme uvedl že se mi nelíbí protože, můžu si univř udělat namespace třeba „user“ .. a potom si stáhnu nějakou knihovnu a nebo na to zapomenu a udělám si další třídu, která si také vytvoří namespace „user“ a už je to možná kolize.

Když budeš mít továrničku v NEONu a předávat jen danou Cache, tak to máš na jednom místě, a není co řešit – viz ten příklad co jsem uvedl výše – https://forum.nette.org/…stanci-cache#…

Editoval frosty22 (7. 8. 2013 14:52)

bazo
Člen | 620
+
0
-

tvoje riesenie je pekne. ale ako zabranis koliziam pri pouziti s kniznicami 3stran? tie sa asi tvojho postupu drzat nebudu

a este to ma dalsiu chybu, ze sa neda s tym pouzit autowire.

musis to kazdej sluzby dopisovat rucne, a drzat to na konci vsetkych argumentov

frosty22
Člen | 373
+
0
-

Ano, samozřejmě že při použití cizích knihoven, které to budou mít implementované pomocí Storage, tak kolizi nezabráním, alespoň však mám menší pravděpodobnost – museli by se sejít dvě knihovny se stejným namespace. Ale na druhou stranu, já obhajoval řešení s implementací Cache namísto Storage a to že se setkám s knihovnou implementující Storage s tím nic nenadělám.

Jinak ano bohužel autowire je škoda, ale s tím se holt u továrniček s argumenty počítá, tomu se člověk v konfigu nevyhne a to nejen v tomto případě.

A potom co se týče předávání závislostí, tak objekt využívá rozhraní třídy Cache, čili je závislý na této třídě a tu by měl dostat nikoliv Storage a z něj si závislost vytvořit. Stejně tak bych mu mohl předat cestu do tempu a rovnou by si mohl vytvořit i Storage, když bych se v závislosti posunul zase o úroveň výš. Sice chápu, že v tomto případě nemá Cache žádné rozhraní, čili bych bez modifikace stejně objekt nezaměnil, ale co kdyby se rozšířil konstruktor Cache o nějaké další parametry, musel bych modifikovat všechny knihovny, které tuto instanci vytváří, což právě od toho tu je kontajner, který by měl vytvářet instance či obsahovat továrničky, které je budou vytvářet.