Tag Archives: PrestaShop

Caching Modules on PrestaShop

Let’s see an important examples: the category tree block (/modules/blockcategories.php)

i will show only the function that does the sql queries (that we are going to cache!).
Watch out for the Tools::getCache and Tools::setCache!

function hookLeftColumn($params)
{
	global $smarty, $cookie;

	/*  ONLY FOR THEME OLDER THAN v1.0 */
	global $link;

	//cache
	if (!$catOldTheme=Tools::getCache('blockCatOldTheme'.((!is_null($cookie->id_lang))?'_'.$cookie->id_lang:''))) //read cache
	{
		$catOldTheme=Category::getHomeCategories(intval($params['cookie']->id_lang), true);
		Tools::setCache('blockCatOldTheme'.((!is_null($cookie->id_lang))?'_'.$cookie->id_lang:''),$catOldTheme); //set cache
	}


	$smarty->assign(array(
		'categories' => $catOldTheme,
		'link' => $link
	));
	/* ELSE */

	$id_customer = intval($params['cookie']->id_customer);
	$maxdepth = Configuration::get('BLOCK_CATEG_MAX_DEPTH');

	//cache
	if (!$result=Tools::getCache('blockCatNewTheme'.((!is_null($cookie->id_lang))?'_'.$cookie->id_lang:''))) //read cache
	{
		$result=Db::getInstance()->ExecuteS('
		SELECT DISTINCT c.*, cl.*
		FROM `'._DB_PREFIX_.'category` c
		LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` AND `id_lang` = '.intval($params['cookie']->id_lang).')
		LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = c.`id_category`)
		WHERE 1'
		.(intval($maxdepth) != 0 ? ' AND `level_depth` <= '.intval($maxdepth) : '').'
		AND (c.`active` = 1 OR c.`id_category`= '.Configuration::get('PS_DB_macroCatIdHome').')
		AND cg.`id_group` '.(!$cookie->id_customer ?  '= 1' : 'IN (SELECT id_group FROM '._DB_PREFIX_.'customer_group WHERE id_customer = '.intval($cookie->id_customer).')').'
		ORDER BY `level_depth` ASC, cl.`name` ASC');
		Tools::setCache('blockCatNewTheme'.((!is_null($cookie->id_lang))?'_'.$cookie->id_lang:''),$result); //set cache
	}


	if (!$result)
		return;
	$resultParents = array();
	$resultIds = array();

	foreach ($result as $row)
	{
		$row['name'] = Category::hideCategoryPosition($row['name']);
		$resultParents[$row['id_parent']][] = $row;
		$resultIds[$row['id_category']] = $row;
	}

	$blockCategTree = $this->getTree($resultParents, $resultIds, Configuration::get('BLOCK_CATEG_MAX_DEPTH'));
	$isDhtml = (Configuration::get('BLOCK_CATEG_DHTML') == 1 ? true : false);
	$smarty->assign('isDhtml', $isDhtml);

	$this->currentCategoryId = NULL;
	if (isset($_GET['id_category']))
	{
		$cookie->last_visited_category = intval($_GET['id_category']);
		$this->currentCategoryId = intval($_GET['id_category']);
	}
	if (isset($_GET['id_product']))
	{
		if (!isset($cookie->last_visited_category) OR !Product::idIsOnCategoryId(intval($_GET['id_product']), array('0' => array('id_category' => $cookie->last_visited_category))))
		{
			$product = new Product(intval($_GET['id_product']));
			if (isset($product) AND Validate::isLoadedObject($product))
				$cookie->last_visited_category = intval($product->id_category_default);
		}
		$this->currentCategoryId = intval($cookie->last_visited_category);
	}

	$htmlOutput = $this->htmlTree($blockCategTree);
	$smarty->assign('htmlOutput', $htmlOutput);
	return $this->display(__FILE__, 'blockcategoriesnew.tpl');
}

with the first call, the getCache, we are checking if there is cached content on disk, and if it’s true, the cached content will be returned inside the $result variable.
If there is no cache content, the prestashop’s original code will run and the saved to disk with setCache method! easy uh?
The only thing that is missing is the deleting of this cached content (needed to update cached content with updated database data). I found to way to do this.

  • crontab: put a script that directly delete cached file on disk every hour/day (as you like)
  • right after database changes: put a delCache call inside postProcess() method definition on administration class (the ones that extends AdminTab) eg.: /admin/tabs/AdminCategories.php (we will see that more in details on last caching tutorial)
  • manually: delete cache manually when you have updated the interested records (in this example, the categories tables)

See you on next post to learn how cache modules!

Caching PrestaShop

hello everybody. actually i’m working for a webagency and i used prestashop to develop an ecommerce with over 3700 products and over 250 categories. pretty huge uh?

well prestashop it’s easy to develop and extend, but it’s not so optimized.. so it’s easy to run into slow queries and server freezes with over 1300 visitors per day. So i had to cache some of biggest queries, like categories tree and subcategories, blocks and products. With those expedients i lowered the number of queries from 200 per page to nearly 40 (there are a lot, i know.. but it’s prestashop, not my fault.)
so let’s start with the fixes! 🙂

first of all, let’s define two variable for our caching system, like the directory path of our cached content. open defines.inc.php and declare a variable, like i did

define('_SQL_CACHING_',  true); //set it to true to enable caching!
define('_PS_CACHE_DIR_',  __PS_BASE_URI__.'cache/'); //cache path!

then make that directory and give it write permissions.

then, let’s introduce some cache functions inside the Tools class file.

	static public function setCache($var,$data,$dir='sql/')
	{
		if (!_SQL_CACHING_) return;

		$path=_PS_CACHE_DIR_;

		$dir.=($var%100).'/'.$var.'/';
		$data=serialize($data);

		$path.=$dir;
		@mkdir($path,0777,true);
		@chmod($path,0777);
		$file=$path.$var.'.cache';

		@file_put_contents($file,$data);
		@chmod($file,0777);
	}

	static public function getCache($var,$dir='sql/')
	{
		if (!_SQL_CACHING_) return false;

		$path=_PS_CACHE_DIR_;

		$dir.=($var%100).'/'.$var.'/';
		$path.=$dir;
		$file=$path.$var.'.cache';

		if (file_exists($file))
		{
			$data=@file_get_contents($file);
			return unserialize($data);
		}
		else return false;
	}

	static public function delCache($var,$dir='sql/')
	{
		if (!_SQL_CACHING_) return false;

		if(!function_exists('rmdirr'))
		{
			function rmdirr($dir)
			{
				if (@file_exists($dir))
				{
					if (@is_dir($dir))
					{
						foreach(glob($dir."/*") as $obj)
							@is_dir($obj)? rmdirr($obj) : @unlink($obj);
						@rmdir($dir);
					}
					else @unlink($dir);
				}
			}
		}

		$path=_PS_CACHE_DIR_.$dir.($var%100).'/'.$var.'*';
		foreach(glob($path) as $filename)
			rmdirr($filename);
	}

let’s examine that functions.. we have:

  • setCache($var,$data,$dir=’sql/’): write cached data to disk
  • getCache($var,$dir=’sql/’): read cached data from disk
  • delCache($var,$dir=’sql/’): delete cached data from disk

how to use those function (parameters explaination)

  • $var: it’s the variable/file-on-disk name. it must be unique or a cached content will replace another one!
  • $data: it’s the data that we want to cache. it can be anything: simple variable, array, object..
  • $dir: it’s the subdirectory of cache path. I use it to differentiate caching path of blocks from main pages, for example.

where to use those functions?
well we have exactly three points where to use these functions: modules (on blocks), pages, single objects.

  • modules: we have to act inside the module files. normally the main file (same name of module’s directory)
  • pages: we have to act inside the files of prestashop’s root, like product.php or category.php and interact with smarty (the template engine)
  • objects: we have to act inside the ObjectModel class

On next posts i will show some examples that describes how to apply these kind of cache!