Jump to content

How To Change Position Of Products Category At Product Page?


Recommended Posts

At product page, Products Category (Display products of the same category on the product page) appears below the product and over the "More Info/Data Sheet/Comments" tab. Want to move this below them. Any idea how to change the position of this?

 

I tried by accessing Live Edit but this displays Home only. If try Product Page, page redirect ot "Fatal Error".

 

Appreciate if you can help on this.

Link to comment
Share on other sites

Hi mehmoodhamid,

 

I Understand you didn't understand. My error. I didn't read your Q well enough.

Can you provide me a link to your website? What version of Prestashop do you use?

 

I think we need to solve this in the css file.

Maybe we need to add some height to the <div productscategory_list> block in the productscategory.css file

 

 

#productscategory_list {

overflow: hidden;

float: left;

width: 96%;

height: 200px; //<-- add this line and see if it solves the overlapping... Maybe 200 should be more/less...

}

 

If it works, we need to make a neat 'override' file instead of editing the original file, but for now just add this line to the file

/modules/productscategory/productscategory.css

(around line#15 in the file)

 

Please give me a link to your site, and try the above adjustment. Let me know if it does anything...

(P.S. Did you try other browsers, if it gives the same overlapping text errors?? What browser/browserversion/Operating System do you use?)

 

Pascal.

Link to comment
Share on other sites

Hi Pascal, Thank you for your resonse.

 

Website is not up yet and as it will be up I will provide you the url. I was trying this at Demo.

 

Like positioning of the modules at home page, can we move the position of Product Category at Product Page?

 

*P.S. This is not overlapping errors. This is the position of the "Product Category" to display the products in the same category. And, yes I tried multiple browser and it appears same to all.

 

Once again thanks for your time and help.

Link to comment
Share on other sites

Hi Mehmood,

 

Sorry, this one took some time...

I believe I found what you're looking for.

As I understand well, you want the modules of "Product footer" (in your case, Products Category) UNDER the 'more info' tab of the product (i.e the contents of the 'detail'-ed info field of the product), right?

 

To do this, Go to the file 'product.tpl', normally found in:

/themes/<your them folder>/product.tpl

 

Find the following line (probably the only line that has the word 'footer' in it, so search for 'footer'):

{if isset($HOOK_PRODUCT_FOOTER) && $HOOK_PRODUCT_FOOTER}{$HOOK_PRODUCT_FOOTER}{/if}

 

 

If you look carefully, just below this line, you'll see that they start with the 'more info' tab code.:

 

 

<!-- description and features -->

{if (isset($product) && $product->description) || (isset($features) && $features) || (isset($accessories) && $accessories) || (isset($HOOK_PRODUCT_TAB) && $HOOK_PRODUCT_TAB) || (isset($attachments) && $attachments) || isset($product) && $product->customizable}

<div id="more_info_block" class="clear">

...

 

 

So, to fix your problem, we have to move the 'footer line' we found before all the way below this <div> block.

 

So cut the footer line:

 

{if isset($HOOK_PRODUCT_FOOTER) && $HOOK_PRODUCT_FOOTER}{$HOOK_PRODUCT_FOOTER}{/if} // cut this line

 

and scroll all the way down to the end of the file. Here you find the following code:

 

...

 

</div> // <-- this is the closing tag of our 'more_info_block' div

{/if}

 

{if isset($HOOK_PRODUCT_FOOTER) && $HOOK_PRODUCT_FOOTER}{$HOOK_PRODUCT_FOOTER}{/if} // <-- add here!!

 

{if isset($packItems) && $packItems|@count > 0}

<div id="blockpack">

<h2>{l s='Pack content'}</h2>

{include file="$tpl_dir./product-list.tpl" products=$packItems}

</div>

{/if}

 

{/if}

// end of the file

 

 

So add our 'footer line' as indicated. This should do the trick!

 

Give it a try, and let us know if this worked for you.

 

Cheers,

Pascal

  • Like 6
Link to comment
Share on other sites

  • 2 months later...
  • 2 months later...
  • 2 months later...
  • 3 weeks later...
  • 1 month later...
  • 3 months later...

Hello, Pascal, I took the permission to use your solution for the above problem in our shop, too.

It seems more logical to have every data related to a product, like short description and detailed description in one visual block, and then only after these to have the other related products, if the case. 

Your solution works fine in most cases, however if the detailed description is too long, the page looks like this - see attachement.

1. - 29 products in the same category

2. - should be a list of other products, but only 2 of them shows up.

 

post-759375-0-76203800-1393940749_thumb.jpg

 

Have any ideeas how to solve this? Thanks in advance.

Edited by Salus (see edit history)
Link to comment
Share on other sites

Hi Salue,

can you add my IP address to your maintenance IP list, so that I can have a quick look inside?

 

IP: 114.109.116.41, 124.121.140.179

 

(not fully sure which one you need, believe the first one, otherwise, just add both...)

 

Thx,

pascal

Edited by PascalVG
added IP address (see edit history)
Link to comment
Share on other sites

Hi, Pascal, I solved this problem by myself. I re-edited those products description, and it`s OK now. The problem above could have two possible reasons: it was either because of the table inserted at the end of the description section, either because the product was a duplicated record generated from an already existing one. I deleted the duplication and created a new product, deleted/reedited the table from the end of description/more info section and now it works fine. Thank you :)

Link to comment
Share on other sites

  • 1 month later...

Hi Salus, thanks for the feedback. Good to hear it's solved.

 

Happy selling,

pascal.

 

Hi Pascal,

 

you are SUPER , and I hope you can help me with this module: productscategory

 

I would to see the product of the same category and also the same style ( the style is a features of the product) 

 

I think it's necessary to change this function in productscategory.php

 

public function hookProductFooter($params)
{
$id_product = (int)$params['product']->id;
$product = $params['product'];
 
$cache_id = 'productscategory|'.$id_product.'|'.(isset($params['category']->id_category) ? (int)$params['category']->id_category : $product->id_category_default);
 
if (!$this->isCached('productscategory.tpl', $this->getCacheId($cache_id)))
{
 
$category = false;
if (isset($params['category']->id_category))
$category = $params['category'];
else
{
if (isset($product->id_category_default) && $product->id_category_default > 1)
$category = new Category((int)$product->id_category_default);
}
 
if (!Validate::isLoadedObject($category) || !$category->active)
return false;
 
......
 
thanks
Angela
Link to comment
Share on other sites

Hi Angela,

can you tell me a little about your features structure?

 

- In your shop, does EVERY product have a feature?

- Do (some) products have more than one feature? (like size, colour etc)

- Do (some) products have more than one feature VALUE for a specific feature (like for feature 'colour': 'gray' AND 'blue' ,  green AND white or so)

 

I have it basically already working (for first feature, first value), but not sure if you would want to show all products that are associated with ALL the current product's features/all it's feature values.

 

Let me know,

pascal

Link to comment
Share on other sites

Hi Angela,

can you tell me a little about your features structure?

 

- In your shop, does EVERY product have a feature?

- Do (some) products have more than one feature? (like size, colour etc)

- Do (some) products have more than one feature VALUE for a specific feature (like for feature 'colour': 'gray' AND 'blue' ,  green AND white or so)

 

I have it basically already working (for first feature, first value), but not sure if you would want to show all products that are associated with ALL the current product's features/all it's feature values.

 

Let me know,

pascal

 

Hi Pascal,

 

My situation is this:

 

- we sell shirts  ( shop.7camicie.com)

- the products have a lot of features ( example: color, style, sleeve, fit ecc.. )

- in the product page now there is the block: productscategory

- but i would like to show in this block all the products of the same category and the same style. ( the style is the feature with id_feature= 8

 

 

Thanks very much.

 

Angela

Link to comment
Share on other sites

Ok, so, we should find the VALUE of the feature "style' (with feature ID =8) of the current product, (say, "girly" or so) and find all products with the same feature-value (style='girly') within that same category, right?

Link to comment
Share on other sites

Ok, so, we should find the VALUE of the feature "style' (with feature ID =8) of the current product, (say, "girly" or so) and find all products with the same feature-value (style='girly') within that same category, right?

Yes, right!

 

thanks

Link to comment
Share on other sites

Hi Angela,

sorry for late continuation. Pretty busy and traveling lately...

Think I found what you need. Let's try:

 

 

override or Edit file classes/Category.php (Code sample taken from PS 1.6.0.5):   (Make backup, just in case!)

Add a new function:

 

    public function getProductsWithFeature($featureValue = null, $id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)

 
	/**
	  * Return current category products that have the given Feature Value
	  *
	  * @param integer $id_lang Language ID
	  * @param integer $p Page number
	  * @param integer $n Number of products per page
	  * @param boolean $get_total return the number of results instead of the results themself
	  * @param boolean $active return only active products
	  * @param boolean $random active a random filter for returned products
	  * @param int $random_number_products number of products to return if random is activated
	  * @param boolean $check_access set to false to return all products (even if customer hasn't access)
	  * @param Context $context
	  * @param integer $featureValue
	  * @return mixed Products or number of products
	  */
	public function getProductsWithFeature($featureValue = null, $id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)
	{
		if (!$context)
			$context = Context::getContext();
		if ($check_access && !$this->checkAccess($context->customer->id))
			return false;
		
		$front = true;
		if (!in_array($context->controller->controller_type, array('front', 'modulefront')))
			$front = false;
			
		if ($p < 1) $p = 1;

		if (empty($order_by))
			$order_by = 'position';
		else
			/* Fix for all modules which are now using lowercase values for 'orderBy' parameter */
			$order_by = strtolower($order_by);

		if (empty($order_way))
			$order_way = 'ASC';
			
		$order_by_prefix = false;
		if ($order_by == 'id_product' || $order_by == 'date_add' || $order_by == 'date_upd')
			$order_by_prefix = 'p';
		elseif ($order_by == 'name')
			$order_by_prefix = 'pl';
		elseif ($order_by == 'manufacturer')
		{
			$order_by_prefix = 'm';
			$order_by = 'name';
		}
		elseif ($order_by == 'position')
			$order_by_prefix = 'cp';

		if ($order_by == 'price')
			$order_by = 'orderprice';

		if (!Validate::isBool($active) || !Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way))
			die (Tools::displayError());

		$id_supplier = (int)Tools::getValue('id_supplier');

		/* Return only the number of products */
		if ($get_total)
		{
			$sql = 'SELECT COUNT(cp.`id_product`) AS total
					FROM `'._DB_PREFIX_.'product` p
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON p.`id_product` = cp.`id_product`
					LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON p.`id_product` = fp.`id_product`
					WHERE cp.`id_category` = '.(int)$this->id.
					($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').
					($active ? ' AND product_shop.`active` = 1' : '').
					(!empty($featureValue) ? ' AND fp.`id_feature_value` = '.(int)$featureValue : '').
					($id_supplier ? 'AND p.id_supplier = '.(int)$id_supplier : '');
			return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
		}

		$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, MAX(product_attribute_shop.id_product_attribute) id_product_attribute, product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity, pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image,
					il.`legend`, m.`name` AS manufacturer_name, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new, product_shop.price AS orderprice, fp.`id_feature_value`
				FROM `'._DB_PREFIX_.'category_product` cp
				LEFT JOIN `'._DB_PREFIX_.'product` p
					ON p.`id_product` = cp.`id_product`
				'.Shop::addSqlAssociation('product', 'p').'
				LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON (cp.`id_product` = fp.`id_product`)
				LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
				ON (p.`id_product` = pa.`id_product`)
				'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
				'.Product::sqlStock('p', 'product_attribute_shop', false, $context->shop).'
				LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
					ON (product_shop.`id_category_default` = cl.`id_category`
					AND cl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('cl').')
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
					ON (p.`id_product` = pl.`id_product`
					AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'image` i
					ON (i.`id_product` = p.`id_product`)'.
				Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il
					ON (image_shop.`id_image` = il.`id_image`
					AND il.`id_lang` = '.(int)$id_lang.')
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
					ON m.`id_manufacturer` = p.`id_manufacturer`
				WHERE product_shop.`id_shop` = '.(int)$context->shop->id.'
					AND cp.`id_category` = '.(int)$this->id
					.($active ? ' AND product_shop.`active` = 1' : '')
					.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
					.' AND fp.`id_feature_value` = '.(int)$featureValue
					.($id_supplier ? ' AND p.id_supplier = '.(int)$id_supplier : '')
					.' GROUP BY product_shop.id_product';

		if ($random === true)
			$sql .= ' ORDER BY RAND() LIMIT '.(int)$random_number_products;
		else
			$sql .= ' ORDER BY '.(!empty($order_by_prefix) ? $order_by_prefix.'.' : '').'`'.bqSQL($order_by).'` '.pSQL($order_way).'
			LIMIT '.(((int)$p - 1) * (int)$n).','.(int)$n;

echo '<br />(Category.php): sql = <br />';
print_r($sql);


		$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
		if ($order_by == 'orderprice')
			Tools::orderbyPrice($result, $order_way);

		if (!$result)
			return array();

		/* Modify SQL result */
		return Product::getProductsProperties($id_lang, $result);
	}




If you compare the functions getProducts and this new function getProductsWithFeature, you will see that they are almost the same. We added a parameter, which is the FeatureValue we want to be an additional requirement/restriction for all selected products. I added some additional restrictiion in both the SQLs:

 

First I added an extra  SELECT field: , fp.`id_feature_value`

 

Then I added an extra FROM table to select from:

                LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON (cp.`id_product` = fp.`id_product`)

 
and finally an extra WHERE restriction:

       .' AND fp.`id_feature_value` = '.(int)$featureValue

 

Where we compare the feature value of products with the added parameter $featureValue

 

 

Save the file.

 

 

Then go to /modules/productscategory folder and edit the file: productscategory.php (Make backup!)

 

and find the function:

      public function hookProductFooter($params)
 

Here we need to add some code to find the Feature Value of the desired Feature of the current Product. We then want to use this value to restrict the products shown here: (code sample of 1.6.0.5)

 

if (!Validate::isLoadedObject($category) || !$category->active)
return false;
 
// Get infos
 
$productFeatures = $product->getFeatures();   // EDIT: Forgot this line, to get all features of current product....
$productFeatureValue = 0;
foreach ($productFeatures  as $productFeature) {  // find Feature value
  if ($productFeature['id_feature'] == 8) {       // N.B. 8 was the ID you mentioned of the Feature you wanted to restrict on. Change as needed.
  $productFeatureValue = $productFeature['id_feature_value'];
  break;
  }
}
$category_products = $category->getProductsWithFeature($productFeatureValue,
       $this->context->language->id, 1, 100); /* 100 products max. */
$nb_category_products = (int)count($category_products);
$middle_position = 0;
 
// Remove current product from the list
if (is_array($category_products) && count($category_products))
{
    foreach ($category_products as $key => $category_product)
   ...
 

 

 

And save the file. (Full function as follows):

	public function hookProductFooter($params)
	{
		$id_product = (int)$params['product']->id;
		$product = $params['product'];

		$cache_id = 'productscategory|'.$id_product.'|'.(isset($params['category']->id_category) ? (int)$params['category']->id_category : $product->id_category_default);

		if (!$this->isCached('productscategory.tpl', $this->getCacheId($cache_id)))
		{

			$category = false;
			if (isset($params['category']->id_category))
				$category = $params['category'];
			else
			{
				if (isset($product->id_category_default) && $product->id_category_default > 1)
					$category = new Category((int)$product->id_category_default);
			}

			if (!Validate::isLoadedObject($category) || !$category->active)
				return false;

			// Get infos
                        $productFeatures = $product->getFeatures(); // EDIT forgot this line, to get all Features of current product
			$productFeatureValue = 0;
			foreach ($productFeatures  as $productFeature) {  // find Feature value
				if ($productFeature['id_feature'] == 5) {
					$productFeatureValue = $productFeature['id_feature_value'];
					break;
				}
			}
			$category_products = $category->getProductsWithFeature($productFeatureValue,$this->context->language->id, 1, 100); /* 100 products max. */
			$nb_category_products = (int)count($category_products);
			$middle_position = 0;

			// Remove current product from the list
			if (is_array($category_products) && count($category_products))
			{
				foreach ($category_products as $key => $category_product)
				{
					if ($category_product['id_product'] == $id_product)
					{
						unset($category_products[$key]);
						break;
					}
				}

				$taxes = Product::getTaxCalculationMethod();
				if (Configuration::get('PRODUCTSCATEGORY_DISPLAY_PRICE'))
				{
					foreach ($category_products as $key => $category_product)
					{
						if ($category_product['id_product'] != $id_product)
						{
							if ($taxes == 0 || $taxes == 2)
							{
								$category_products[$key]['displayed_price'] = Product::getPriceStatic(
									(int)$category_product['id_product'],
									true,
									null,
									2
								);
							} elseif ($taxes == 1)
							{
								$category_products[$key]['displayed_price'] = Product::getPriceStatic(
									(int)$category_product['id_product'],
									false,
									null,
									2
								);
							}
						}
					}
				}

				// Get positions
				$middle_position = (int)round($nb_category_products / 2, 0);
				$product_position = $this->getCurrentProduct($category_products, (int)$id_product);

				// Flip middle product with current product
				if ($product_position)
				{
					$tmp = $category_products[$middle_position - 1];
					$category_products[$middle_position - 1] = $category_products[$product_position];
					$category_products[$product_position] = $tmp;
				}

				// If products tab higher than 30, slice it
				if ($nb_category_products > 30)
				{
					$category_products = array_slice($category_products, $middle_position - 15, 30, true);
					$middle_position = 15;
				}
			}

			// Display tpl
			$this->smarty->assign(
				array(
					'categoryProducts' => $category_products,
					'middlePosition' => (int)$middle_position,
					'ProdDisplayPrice' => Configuration::get('PRODUCTSCATEGORY_DISPLAY_PRICE')
				)
			);
		}

		return $this->display(__FILE__, 'productscategory.tpl', $this->getCacheId($cache_id));
	}

This should do the trick, I believe.

 

So, in summary what we get now is:

- On the product detail page, when you have installed the (modified) productCategory module, the products shown are restricted not only to the (default) category the currently shown product belongs to (this is the default restriction of the basic, unmodified module), but also we restrict now on only those products in this group that also have the same value for the desired Feature (in our case, the feature with ID=8).

 

Hope I didn't forget anything. Please give it a try and let me know if it works.

 

pascal.

 

 

P.S. I hope I understood correctly that the feature_id = 8, NOT id_feature_value = 8)

Edited by PascalVG
forgot to get the product Features of current product. See blue line (see edit history)
  • Like 1
Link to comment
Share on other sites

Hi Angela,

sorry for late continuation. Pretty busy and traveling lately...

Think I found what you need. Let's try:

 

 

override or Edit file classes/Category.php (Code sample taken from PS 1.6.0.5):   (Make backup, just in case!)

Add a new function:

 

    public function getProductsWithFeature($featureValue = null, $id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)

 
	/**
	  * Return current category products that have the given Feature Value
	  *
	  * @param integer $id_lang Language ID
	  * @param integer $p Page number
	  * @param integer $n Number of products per page
	  * @param boolean $get_total return the number of results instead of the results themself
	  * @param boolean $active return only active products
	  * @param boolean $random active a random filter for returned products
	  * @param int $random_number_products number of products to return if random is activated
	  * @param boolean $check_access set to false to return all products (even if customer hasn't access)
	  * @param Context $context
	  * @param integer $featureValue
	  * @return mixed Products or number of products
	  */
	public function getProductsWithFeature($featureValue = null, $id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)
	{
		if (!$context)
			$context = Context::getContext();
		if ($check_access && !$this->checkAccess($context->customer->id))
			return false;
		
		$front = true;
		if (!in_array($context->controller->controller_type, array('front', 'modulefront')))
			$front = false;
			
		if ($p < 1) $p = 1;

		if (empty($order_by))
			$order_by = 'position';
		else
			/* Fix for all modules which are now using lowercase values for 'orderBy' parameter */
			$order_by = strtolower($order_by);

		if (empty($order_way))
			$order_way = 'ASC';
			
		$order_by_prefix = false;
		if ($order_by == 'id_product' || $order_by == 'date_add' || $order_by == 'date_upd')
			$order_by_prefix = 'p';
		elseif ($order_by == 'name')
			$order_by_prefix = 'pl';
		elseif ($order_by == 'manufacturer')
		{
			$order_by_prefix = 'm';
			$order_by = 'name';
		}
		elseif ($order_by == 'position')
			$order_by_prefix = 'cp';

		if ($order_by == 'price')
			$order_by = 'orderprice';

		if (!Validate::isBool($active) || !Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way))
			die (Tools::displayError());

		$id_supplier = (int)Tools::getValue('id_supplier');

		/* Return only the number of products */
		if ($get_total)
		{
			$sql = 'SELECT COUNT(cp.`id_product`) AS total
					FROM `'._DB_PREFIX_.'product` p
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON p.`id_product` = cp.`id_product`
					LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON p.`id_product` = fp.`id_product`
					WHERE cp.`id_category` = '.(int)$this->id.
					($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').
					($active ? ' AND product_shop.`active` = 1' : '').
					(!empty($featureValue) ? ' AND fp.`id_feature_value` = '.(int)$featureValue : '').
					($id_supplier ? 'AND p.id_supplier = '.(int)$id_supplier : '');
			return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
		}

		$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, MAX(product_attribute_shop.id_product_attribute) id_product_attribute, product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity, pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image,
					il.`legend`, m.`name` AS manufacturer_name, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new, product_shop.price AS orderprice, fp.`id_feature_value`
				FROM `'._DB_PREFIX_.'category_product` cp
				LEFT JOIN `'._DB_PREFIX_.'product` p
					ON p.`id_product` = cp.`id_product`
				'.Shop::addSqlAssociation('product', 'p').'
				LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON (cp.`id_product` = fp.`id_product`)
				LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
				ON (p.`id_product` = pa.`id_product`)
				'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
				'.Product::sqlStock('p', 'product_attribute_shop', false, $context->shop).'
				LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
					ON (product_shop.`id_category_default` = cl.`id_category`
					AND cl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('cl').')
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
					ON (p.`id_product` = pl.`id_product`
					AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'image` i
					ON (i.`id_product` = p.`id_product`)'.
				Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il
					ON (image_shop.`id_image` = il.`id_image`
					AND il.`id_lang` = '.(int)$id_lang.')
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
					ON m.`id_manufacturer` = p.`id_manufacturer`
				WHERE product_shop.`id_shop` = '.(int)$context->shop->id.'
					AND cp.`id_category` = '.(int)$this->id
					.($active ? ' AND product_shop.`active` = 1' : '')
					.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
					.' AND fp.`id_feature_value` = '.(int)$featureValue
					.($id_supplier ? ' AND p.id_supplier = '.(int)$id_supplier : '')
					.' GROUP BY product_shop.id_product';

		if ($random === true)
			$sql .= ' ORDER BY RAND() LIMIT '.(int)$random_number_products;
		else
			$sql .= ' ORDER BY '.(!empty($order_by_prefix) ? $order_by_prefix.'.' : '').'`'.bqSQL($order_by).'` '.pSQL($order_way).'
			LIMIT '.(((int)$p - 1) * (int)$n).','.(int)$n;

echo '<br />(Category.php): sql = <br />';
print_r($sql);


		$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
		if ($order_by == 'orderprice')
			Tools::orderbyPrice($result, $order_way);

		if (!$result)
			return array();

		/* Modify SQL result */
		return Product::getProductsProperties($id_lang, $result);
	}




If you compare the functions getProducts and this new function getProductsWithFeature, you will see that they are almost the same. We added a parameter, which is the FeatureValue we want to be an additional requirement/restriction for all selected products. I added some additional restrictiion in both the SQLs:

 

First I added an extra  SELECT field: , fp.`id_feature_value`

 

Then I added an extra FROM table to select from:

                LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON (cp.`id_product` = fp.`id_product`)

 
and finally an extra WHERE restriction:

       .' AND fp.`id_feature_value` = '.(int)$featureValue

 

Where we compare the feature value of products with the added parameter $featureValue

 

 

Save the file.

 

 

Then go to /modules/productscategory folder and edit the file: productscategory.php (Make backup!)

 

and find the function:

      public function hookProductFooter($params)
 

Here we need to add some code to find the Feature Value of the desired Feature of the current Product. We then want to use this value to restrict the products shown here: (code sample of 1.6.0.5)

 

if (!Validate::isLoadedObject($category) || !$category->active)
return false;
 
// Get infos
 
$productFeatures = $product->getFeatures();   // EDIT: Forgot this line, to get all features of current product....
$productFeatureValue = 0;
foreach ($productFeatures  as $productFeature) {  // find Feature value
  if ($productFeature['id_feature'] == 8) {       // N.B. 8 was the ID you mentioned of the Feature you wanted to restrict on. Change as needed.
  $productFeatureValue = $productFeature['id_feature_value'];
  break;
  }
}
$category_products = $category->getProductsWithFeature($productFeatureValue,
       $this->context->language->id, 1, 100); /* 100 products max. */
$nb_category_products = (int)count($category_products);
$middle_position = 0;
 
// Remove current product from the list
if (is_array($category_products) && count($category_products))
{
    foreach ($category_products as $key => $category_product)
   ...
 

 

 

And save the file. (Full function as follows):

	public function hookProductFooter($params)
	{
		$id_product = (int)$params['product']->id;
		$product = $params['product'];

		$cache_id = 'productscategory|'.$id_product.'|'.(isset($params['category']->id_category) ? (int)$params['category']->id_category : $product->id_category_default);

		if (!$this->isCached('productscategory.tpl', $this->getCacheId($cache_id)))
		{

			$category = false;
			if (isset($params['category']->id_category))
				$category = $params['category'];
			else
			{
				if (isset($product->id_category_default) && $product->id_category_default > 1)
					$category = new Category((int)$product->id_category_default);
			}

			if (!Validate::isLoadedObject($category) || !$category->active)
				return false;

			// Get infos
                        $productFeatures = $product->getFeatures(); // EDIT forgot this line, to get all Features of current product
			$productFeatureValue = 0;
			foreach ($productFeatures  as $productFeature) {  // find Feature value
				if ($productFeature['id_feature'] == 5) {
					$productFeatureValue = $productFeature['id_feature_value'];
					break;
				}
			}
			$category_products = $category->getProductsWithFeature($productFeatureValue,$this->context->language->id, 1, 100); /* 100 products max. */
			$nb_category_products = (int)count($category_products);
			$middle_position = 0;

			// Remove current product from the list
			if (is_array($category_products) && count($category_products))
			{
				foreach ($category_products as $key => $category_product)
				{
					if ($category_product['id_product'] == $id_product)
					{
						unset($category_products[$key]);
						break;
					}
				}

				$taxes = Product::getTaxCalculationMethod();
				if (Configuration::get('PRODUCTSCATEGORY_DISPLAY_PRICE'))
				{
					foreach ($category_products as $key => $category_product)
					{
						if ($category_product['id_product'] != $id_product)
						{
							if ($taxes == 0 || $taxes == 2)
							{
								$category_products[$key]['displayed_price'] = Product::getPriceStatic(
									(int)$category_product['id_product'],
									true,
									null,
									2
								);
							} elseif ($taxes == 1)
							{
								$category_products[$key]['displayed_price'] = Product::getPriceStatic(
									(int)$category_product['id_product'],
									false,
									null,
									2
								);
							}
						}
					}
				}

				// Get positions
				$middle_position = (int)round($nb_category_products / 2, 0);
				$product_position = $this->getCurrentProduct($category_products, (int)$id_product);

				// Flip middle product with current product
				if ($product_position)
				{
					$tmp = $category_products[$middle_position - 1];
					$category_products[$middle_position - 1] = $category_products[$product_position];
					$category_products[$product_position] = $tmp;
				}

				// If products tab higher than 30, slice it
				if ($nb_category_products > 30)
				{
					$category_products = array_slice($category_products, $middle_position - 15, 30, true);
					$middle_position = 15;
				}
			}

			// Display tpl
			$this->smarty->assign(
				array(
					'categoryProducts' => $category_products,
					'middlePosition' => (int)$middle_position,
					'ProdDisplayPrice' => Configuration::get('PRODUCTSCATEGORY_DISPLAY_PRICE')
				)
			);
		}

		return $this->display(__FILE__, 'productscategory.tpl', $this->getCacheId($cache_id));
	}

This should do the trick, I believe.

 

So, in summary what we get now is:

- On the product detail page, when you have installed the (modified) productCategory module, the products shown are restricted not only to the (default) category the currently shown product belongs to (this is the default restriction of the basic, unmodified module), but also we restrict now on only those products in this group that also have the same value for the desired Feature (in our case, the feature with ID=8).

 

Hope I didn't forget anything. Please give it a try and let me know if it works.

 

pascal.

 

 

P.S. I hope I understood correctly that the feature_id = 8, NOT id_feature_value = 8)

Hi Pascal!!!

 

I will try just now your suggest and I will keep you in touch!

 

Thanks

Angela

Link to comment
Share on other sites

Hi Angela,

sorry for late continuation. Pretty busy and traveling lately...

Think I found what you need. Let's try:

 

 

override or Edit file classes/Category.php (Code sample taken from PS 1.6.0.5):   (Make backup, just in case!)

Add a new function:

 

    public function getProductsWithFeature($featureValue = null, $id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)

 
	/**
	  * Return current category products that have the given Feature Value
	  *
	  * @param integer $id_lang Language ID
	  * @param integer $p Page number
	  * @param integer $n Number of products per page
	  * @param boolean $get_total return the number of results instead of the results themself
	  * @param boolean $active return only active products
	  * @param boolean $random active a random filter for returned products
	  * @param int $random_number_products number of products to return if random is activated
	  * @param boolean $check_access set to false to return all products (even if customer hasn't access)
	  * @param Context $context
	  * @param integer $featureValue
	  * @return mixed Products or number of products
	  */
	public function getProductsWithFeature($featureValue = null, $id_lang, $p, $n, $order_by = null, $order_way = null, $get_total = false, $active = true, $random = false, $random_number_products = 1, $check_access = true, Context $context = null)
	{
		if (!$context)
			$context = Context::getContext();
		if ($check_access && !$this->checkAccess($context->customer->id))
			return false;
		
		$front = true;
		if (!in_array($context->controller->controller_type, array('front', 'modulefront')))
			$front = false;
			
		if ($p < 1) $p = 1;

		if (empty($order_by))
			$order_by = 'position';
		else
			/* Fix for all modules which are now using lowercase values for 'orderBy' parameter */
			$order_by = strtolower($order_by);

		if (empty($order_way))
			$order_way = 'ASC';
			
		$order_by_prefix = false;
		if ($order_by == 'id_product' || $order_by == 'date_add' || $order_by == 'date_upd')
			$order_by_prefix = 'p';
		elseif ($order_by == 'name')
			$order_by_prefix = 'pl';
		elseif ($order_by == 'manufacturer')
		{
			$order_by_prefix = 'm';
			$order_by = 'name';
		}
		elseif ($order_by == 'position')
			$order_by_prefix = 'cp';

		if ($order_by == 'price')
			$order_by = 'orderprice';

		if (!Validate::isBool($active) || !Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way))
			die (Tools::displayError());

		$id_supplier = (int)Tools::getValue('id_supplier');

		/* Return only the number of products */
		if ($get_total)
		{
			$sql = 'SELECT COUNT(cp.`id_product`) AS total
					FROM `'._DB_PREFIX_.'product` p
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON p.`id_product` = cp.`id_product`
					LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON p.`id_product` = fp.`id_product`
					WHERE cp.`id_category` = '.(int)$this->id.
					($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').
					($active ? ' AND product_shop.`active` = 1' : '').
					(!empty($featureValue) ? ' AND fp.`id_feature_value` = '.(int)$featureValue : '').
					($id_supplier ? 'AND p.id_supplier = '.(int)$id_supplier : '');
			return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
		}

		$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, MAX(product_attribute_shop.id_product_attribute) id_product_attribute, product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity, pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image,
					il.`legend`, m.`name` AS manufacturer_name, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new, product_shop.price AS orderprice, fp.`id_feature_value`
				FROM `'._DB_PREFIX_.'category_product` cp
				LEFT JOIN `'._DB_PREFIX_.'product` p
					ON p.`id_product` = cp.`id_product`
				'.Shop::addSqlAssociation('product', 'p').'
				LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON (cp.`id_product` = fp.`id_product`)
				LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
				ON (p.`id_product` = pa.`id_product`)
				'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
				'.Product::sqlStock('p', 'product_attribute_shop', false, $context->shop).'
				LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
					ON (product_shop.`id_category_default` = cl.`id_category`
					AND cl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('cl').')
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
					ON (p.`id_product` = pl.`id_product`
					AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'image` i
					ON (i.`id_product` = p.`id_product`)'.
				Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il
					ON (image_shop.`id_image` = il.`id_image`
					AND il.`id_lang` = '.(int)$id_lang.')
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
					ON m.`id_manufacturer` = p.`id_manufacturer`
				WHERE product_shop.`id_shop` = '.(int)$context->shop->id.'
					AND cp.`id_category` = '.(int)$this->id
					.($active ? ' AND product_shop.`active` = 1' : '')
					.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
					.' AND fp.`id_feature_value` = '.(int)$featureValue
					.($id_supplier ? ' AND p.id_supplier = '.(int)$id_supplier : '')
					.' GROUP BY product_shop.id_product';

		if ($random === true)
			$sql .= ' ORDER BY RAND() LIMIT '.(int)$random_number_products;
		else
			$sql .= ' ORDER BY '.(!empty($order_by_prefix) ? $order_by_prefix.'.' : '').'`'.bqSQL($order_by).'` '.pSQL($order_way).'
			LIMIT '.(((int)$p - 1) * (int)$n).','.(int)$n;

echo '<br />(Category.php): sql = <br />';
print_r($sql);


		$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
		if ($order_by == 'orderprice')
			Tools::orderbyPrice($result, $order_way);

		if (!$result)
			return array();

		/* Modify SQL result */
		return Product::getProductsProperties($id_lang, $result);
	}




If you compare the functions getProducts and this new function getProductsWithFeature, you will see that they are almost the same. We added a parameter, which is the FeatureValue we want to be an additional requirement/restriction for all selected products. I added some additional restrictiion in both the SQLs:

 

First I added an extra  SELECT field: , fp.`id_feature_value`

 

Then I added an extra FROM table to select from:

                LEFT JOIN `'._DB_PREFIX_.'feature_product` fp ON (cp.`id_product` = fp.`id_product`)

 
and finally an extra WHERE restriction:

       .' AND fp.`id_feature_value` = '.(int)$featureValue

 

Where we compare the feature value of products with the added parameter $featureValue

 

 

Save the file.

 

 

Then go to /modules/productscategory folder and edit the file: productscategory.php (Make backup!)

 

and find the function:

      public function hookProductFooter($params)
 

Here we need to add some code to find the Feature Value of the desired Feature of the current Product. We then want to use this value to restrict the products shown here: (code sample of 1.6.0.5)

 

if (!Validate::isLoadedObject($category) || !$category->active)
return false;
 
// Get infos
 
$productFeatures = $product->getFeatures();   // EDIT: Forgot this line, to get all features of current product....
$productFeatureValue = 0;
foreach ($productFeatures  as $productFeature) {  // find Feature value
  if ($productFeature['id_feature'] == 8) {       // N.B. 8 was the ID you mentioned of the Feature you wanted to restrict on. Change as needed.
  $productFeatureValue = $productFeature['id_feature_value'];
  break;
  }
}
$category_products = $category->getProductsWithFeature($productFeatureValue,
       $this->context->language->id, 1, 100); /* 100 products max. */
$nb_category_products = (int)count($category_products);
$middle_position = 0;
 
// Remove current product from the list
if (is_array($category_products) && count($category_products))
{
    foreach ($category_products as $key => $category_product)
   ...
 

 

 

And save the file. (Full function as follows):

	public function hookProductFooter($params)
	{
		$id_product = (int)$params['product']->id;
		$product = $params['product'];

		$cache_id = 'productscategory|'.$id_product.'|'.(isset($params['category']->id_category) ? (int)$params['category']->id_category : $product->id_category_default);

		if (!$this->isCached('productscategory.tpl', $this->getCacheId($cache_id)))
		{

			$category = false;
			if (isset($params['category']->id_category))
				$category = $params['category'];
			else
			{
				if (isset($product->id_category_default) && $product->id_category_default > 1)
					$category = new Category((int)$product->id_category_default);
			}

			if (!Validate::isLoadedObject($category) || !$category->active)
				return false;

			// Get infos
                        $productFeatures = $product->getFeatures(); // EDIT forgot this line, to get all Features of current product
			$productFeatureValue = 0;
			foreach ($productFeatures  as $productFeature) {  // find Feature value
				if ($productFeature['id_feature'] == 5) {
					$productFeatureValue = $productFeature['id_feature_value'];
					break;
				}
			}
			$category_products = $category->getProductsWithFeature($productFeatureValue,$this->context->language->id, 1, 100); /* 100 products max. */
			$nb_category_products = (int)count($category_products);
			$middle_position = 0;

			// Remove current product from the list
			if (is_array($category_products) && count($category_products))
			{
				foreach ($category_products as $key => $category_product)
				{
					if ($category_product['id_product'] == $id_product)
					{
						unset($category_products[$key]);
						break;
					}
				}

				$taxes = Product::getTaxCalculationMethod();
				if (Configuration::get('PRODUCTSCATEGORY_DISPLAY_PRICE'))
				{
					foreach ($category_products as $key => $category_product)
					{
						if ($category_product['id_product'] != $id_product)
						{
							if ($taxes == 0 || $taxes == 2)
							{
								$category_products[$key]['displayed_price'] = Product::getPriceStatic(
									(int)$category_product['id_product'],
									true,
									null,
									2
								);
							} elseif ($taxes == 1)
							{
								$category_products[$key]['displayed_price'] = Product::getPriceStatic(
									(int)$category_product['id_product'],
									false,
									null,
									2
								);
							}
						}
					}
				}

				// Get positions
				$middle_position = (int)round($nb_category_products / 2, 0);
				$product_position = $this->getCurrentProduct($category_products, (int)$id_product);

				// Flip middle product with current product
				if ($product_position)
				{
					$tmp = $category_products[$middle_position - 1];
					$category_products[$middle_position - 1] = $category_products[$product_position];
					$category_products[$product_position] = $tmp;
				}

				// If products tab higher than 30, slice it
				if ($nb_category_products > 30)
				{
					$category_products = array_slice($category_products, $middle_position - 15, 30, true);
					$middle_position = 15;
				}
			}

			// Display tpl
			$this->smarty->assign(
				array(
					'categoryProducts' => $category_products,
					'middlePosition' => (int)$middle_position,
					'ProdDisplayPrice' => Configuration::get('PRODUCTSCATEGORY_DISPLAY_PRICE')
				)
			);
		}

		return $this->display(__FILE__, 'productscategory.tpl', $this->getCacheId($cache_id));
	}

This should do the trick, I believe.

 

So, in summary what we get now is:

- On the product detail page, when you have installed the (modified) productCategory module, the products shown are restricted not only to the (default) category the currently shown product belongs to (this is the default restriction of the basic, unmodified module), but also we restrict now on only those products in this group that also have the same value for the desired Feature (in our case, the feature with ID=8).

 

Hope I didn't forget anything. Please give it a try and let me know if it works.

 

pascal.

 

 

P.S. I hope I understood correctly that the feature_id = 8, NOT id_feature_value = 8)

 

Hi Pascal!!!

 

It Works!!!!!

 

Many Many Thanks for your SUPER HELP!!!!

 

Angela

Link to comment
Share on other sites

  • 9 months later...

Hello Pascal,
 

I am currently using PS 1.6.0.11, i currently encounter this problem when i use the pre-order module which overlaps the "more info tab" to "product category". i already tried the 2 solutions you gave before but still the problem won't solve the issue. attached is the screenshot.

 

3TOC5HP.png

Link to comment
Share on other sites

  • 9 months later...

Hello,

 

I have also the problem and I modified the code to have "Products in the Same Category" UNDER the "More info" tab of the product.

 
The code works, but I can't clic anymore on "More Info", "Date Sheet" and "Product Reviews"

 

Do you know why ?

 

(here the link : http://www.frogavenue.com/en/perfumes/16-grasse-au-parfum-provence-last-seduction.html )

 

Thank you for your help,

Nicolas

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...