cracked Posted February 1, 2017 Share Posted February 1, 2017 Greetings dear Presta-Devs, I have a question for y'all, regarding the generation of product urls. I my case, it seems that links generated by Link::getProductLink() are generated falsely, but so far I can't exactly tell why (am I doing something wrong?!). Well, we use a module "Advanced SEO Friendly URL - von ModuleFactory" to remove the IDs from the categories and products, but it seems as if the problem does not lie within this module, as it doesn't override any of the functionality used by original Link::getProductLink(). So I wrote the following Testcode to further investigate the problem: <?php include_once(dirname(__FILE__).'/config/config.inc.php'); include_once(dirname(__FILE__).'/init.php'); if (!defined('_PS_VERSION_')) exit; echo "And then... i got in: "._DB_USER_." "._DB_PREFIX_."<br>"; echo "PS_HOME_CATEGORY: ".(new Category(Configuration::get('PS_HOME_CATEGORY')))->getName()."<br>"; echo "PS_ROOT_CATEGORY: ".(new Category(Configuration::get('PS_ROOT_CATEGORY')))->getName()."<br>"; class MyTestClass extends Module { function getProductLink($id_lang, $id_product = 0) { $id_product = 384;//599; $id_lang = 2; $link = new Link(); if (method_exists('ShopUrl', 'resetMainDomainCache')) ShopUrl::resetMainDomainCache(); echo 'SELECT id_product, id_shop FROM `'._DB_PREFIX_.'product_shop` WHERE `id_product` = '.intval($id_product).' AND `active` = 1 AND `visibility` != \'none\' ORDER BY `id_product` ASC <br>' ; $products_id = Db::getInstance()->ExecuteS('SELECT id_product, id_shop FROM `'._DB_PREFIX_.'product_shop` WHERE `id_product` = '.intval($id_product).' AND `active` = 1 AND `visibility` != \'none\' ORDER BY `id_product` ASC'); $i = 0; foreach ($products_id as $product_id) { echo "============"; echo "<br>"; $product = new Product((int)$product_id['id_product'], false, $id_lang); echo $product->name."<br>"; echo "id_category_default: ".$product->id_category_default."<br>"; echo "product->category: ".$product->category."<br>"; $category = $product->category; if ($i++ >= 1) $category = "getreide-und-getreideprodukte"; echo "Using Category: ".$category."<br>"; echo "product->link_rewrite: ".$product->link_rewrite."<br>"; echo "<br>"; echo "Generated Link with id_shop (".$product_id['id_shop']."): <br>"; $url = $link->getProductLink($product, $product->link_rewrite, htmlspecialchars(strip_tags($category)), $product->ean13, $id_lang, (int)$product_id['id_shop'], 0, true); echo $url."<br>"; } } } MyTestClass::getProductLink(); echo "The end."; producing the output in picture1: This is the configuration of product with id 384: As you can see, home-b2b is the home_category of one of our shops in a multi-store. It is not selected for this product, but is rather a parent category to "boden". So take a look what Link::getProductLink() does: It excludes categories, which id's are in $category_disable_rewrite. In this, there are the id's of the PS_HOME_CATEGORY and PS_ROOT_CATEGORY which is "Home" and "Root" in our case, and as you can see in my test-script output, first two lines. Well, but the problem is, there isn't only ONE "PS_HOME_CATEGORY" in multi-store. "home-b2b" and "home-l2l" in our case, doesn't get filtered out, producing wrong URLs. if I add this to line 148 in Link.php, the links come out right. if ($dispatcher->hasKeyword('product_rule', $id_lang, 'categories', $id_shop)) { $params['category'] = (!$category) ? $product->category : $category; $cats = array(); foreach ($product->getParentCategories($id_lang) as $cat) { if (!in_array($cat['id_category'], Link::$category_disable_rewrite)) { if ($cat['link_rewrite'] == 'home-b2b' || $cat['link_rewrite'] == 'home-l2l') continue; //remove root and home category from the URL $cats[] = $cat['link_rewrite']; } } $params['categories'] = implode('/', $cats); Well, still not completly right, because whatever I give into getProductLinks $category-Parameter, it still takes "boden", see test-script, again. But that is another problem. What are you guys saying? Thanks in advance & regards, Lennart Link to comment Share on other sites More sharing options...
Gabriel Perez Posted February 1, 2017 Share Posted February 1, 2017 Hi, Perhaps, before digging so much into the code, you may have a look in Seo & Urls Menu, in the Routes section, how is exactly the product link construction. Link to comment Share on other sites More sharing options...
cracked Posted February 1, 2017 Author Share Posted February 1, 2017 (edited) Hi Gabriel Perez, yeah, you probably right. I did that first, of course. But theres not much to see, our settings are pretty much standard, except for the product-link where the reference code is added in the end: PS: you really think I would code this and open up this topic, if I weren't desperate? Regards, Lennart Edited February 1, 2017 by cracked (see edit history) Link to comment Share on other sites More sharing options...
Gabriel Perez Posted February 1, 2017 Share Posted February 1, 2017 (edited) Well, then if you want the link of the product to have only the category you choose, then you just have to replace {categories:/} for {category:/} "categories" will put into your url all the categories the product is in, while "category" will put only the parent (or actual) category of the product. Edited February 1, 2017 by Gabriel Perez (see edit history) Link to comment Share on other sites More sharing options...
cracked Posted February 1, 2017 Author Share Posted February 1, 2017 Hello Gabriel Perez, thanks again for your advice buddy, but I already came up with that idea too. Problem is, it ain't working: Regards, Lennart Link to comment Share on other sites More sharing options...
Gabriel Perez Posted February 1, 2017 Share Posted February 1, 2017 (edited) The problem is then the route restrictions that has the module you use. By default, prestashop only request you to put the id on the url, but seems that the module now requires the "categories" instead. One solution that will work for you is the one you came from, checking if the category is "home-b2b" PD : we're using another module that alows you to remove the id from the product url, perhaps you should check other modules to do that. Edited February 1, 2017 by Gabriel Perez (see edit history) Link to comment Share on other sites More sharing options...
cracked Posted February 1, 2017 Author Share Posted February 1, 2017 Yeah, that could absolutely be the case, that this "Advanced SEO Friendly URL"-Module is causing all my problems. I'm in contact with one of their devs too, we'll see if we get an update. But as far as I can tell, Link::getProductLink() isn't overwritten by this module, neither any of the functions used by getProductLinkt(); The question remains if this is a general misconcept of this standard class/function that it just checks for "Configuration::get('PS_HOME_CATEGORY')" which will always return just one home category - but there can be many (in multistore). What do you think? Regards, Lennart Link to comment Share on other sites More sharing options...
Gabriel Perez Posted February 1, 2017 Share Posted February 1, 2017 (edited) As you said, the module doesnt need to overwrite the Link class to do that. It is problably, in this case, just overwriting the Dispatcher. In second place, Configuration::get has a optional parameter with the shop id, that should make the work for multistore too, making it posible to have various PS_HOME_CATEGORY. The question is : are the categories marked as root categories to their corresponding shops? Edited February 1, 2017 by Gabriel Perez (see edit history) Link to comment Share on other sites More sharing options...
cracked Posted February 1, 2017 Author Share Posted February 1, 2017 Hi again, yes they are indeed marked as root categories. (Is there a way to see this in Prestashops Backend?) This SQL-output should provide proof to the case: Link to comment Share on other sites More sharing options...
Gabriel Perez Posted February 1, 2017 Share Posted February 1, 2017 You can check it in the admin panel : Advanced Parameters -> Multistore. In each store, you can view info like the shop name, the root category, theme selected ... Link to comment Share on other sites More sharing options...
cracked Posted June 20, 2017 Author Share Posted June 20, 2017 Hello Guys, months later but I finally found out what exactly the problem is - and got the solution. Please keep in mind that we use a multi-store, so this problem only applys when you want to get product links for different stores inside a multistore. So, there are several ways to generate a product link in prestashop. The constructor of class Product looks like this: public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null) So you would create a product object like this: $product = new Product(826, false, 2, 1); Now you could get the product-link for this product by simply using $product->getLink(); or you could do it this way: #getProductLink header: #public function getProductLink($product, $alias = null, $category = null, $ean13 = null, $id_lang = null, $id_shop = null, $ipa = 0, $force_routes = false, $relative_protocol = false, $add_anchor = false) #this is how you could also generate the ProductLink Context::getContext()->link->getProductLink($product,null,null,null,$id_lang,1); which works fine as far as you want to create the link for shop id = 1. But as you can see, Link::getProductLink's 6th parameter is $id_shop. So one, like me, could get the idea that you can simply create the product link for lets say, shop id = 3 like this: #the 6th parameter is shop_id. Here I try to create the link for shop id = 3 Context::getContext()->link->getProductLink($product,null,null,null,$id_lang,3); Which doesn't work, when your shops have different home categories. But why? It's because this part of code inside of getProductLink: if ($dispatcher->hasKeyword('product_rule', $id_lang, 'categories', $id_shop)) { $params['category'] = (!$category) ? $product->category : $category; $cats = array(); foreach ($product->getParentCategories($id_lang) as $cat) { if (!in_array($cat['id_category'], Link::$category_disable_rewrite)) { //remove root and home category from the URL $cats[] = $cat['link_rewrite']; } } $params['categories'] = implode('/', $cats); } Precisely line 146 $product->getParentCategories is the problem. As $product was generated with shop_id = 1, getParentCategories will find and use all parentCategories of shop_id = 1, even though you wanted to create a link for shop_id = 3. Now the solution could be simple. Generate a new $product object with shop_id = 3. But i think this is very inefficient and bothering, first because we use multi shop and every time I want the product link I have to generate a new object for each shop and SECOND, because Link::getProductLink already has the parameter id_shop BUT IT DOESN'T WORK AS EXPECTED. For me, this is a bug in prestashop. I fixed this for myself by overwriting Link.php and altering the code like this: #overwritten Link.php: ... if ($dispatcher->hasKeyword('product_rule', $id_lang, 'categories', $id_shop)) { $params['category'] = (!$category) ? $product->category : $category; $cats = array(); foreach (Link::getParentCategories($product->id, $id_lang, $id_shop) as $cat) { if (!in_array($cat['id_category'], Link::$category_disable_rewrite)) { //remove root and home category from the URL $cats[] = $cat['link_rewrite']; } } $params['categories'] = implode('/', $cats); } ... function getParentCategories($id_product, $id_lang = null, $id_shop = null) { if (!$id_lang) { $id_lang = Context::getContext()->language->id; } //taken from Shop.php addSqlRestrictionOnLang if (isset(Context::getContext()->shop) && is_null($id_shop)) { $id_shop = (int)Context::getContext()->shop->id; } if (!$id_shop) { $id_shop = (int)Configuration::get('PS_SHOP_DEFAULT'); } $defaultCatSql = 'select id_category_default from '._DB_PREFIX_.'product_shop where id_product = '.$id_product.' and id_shop = '.$id_shop; $idDefaultCat = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($defaultCatSql)[0]['id_category_default']; $interval = Category::getInterval($idDefaultCat); $sql = new DbQuery(); $sql->from('category', 'c'); $sql->leftJoin('category_lang', 'cl', 'c.id_category = cl.id_category AND id_lang = '.(int)$id_lang.' AND cl.id_shop = '.$id_shop); $sql->where('c.nleft <= '.(int)$interval['nleft'].' AND c.nright >= '.(int)$interval['nright']); $sql->orderBy('c.nleft'); return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); } Now I can use Link:getProductLink as expected, with a $product object which doesn't matter with which shop_id it was generated. Hope this helps someone else. Regards, cracked 1 Link to comment Share on other sites More sharing options...
amir hossein Posted October 17, 2019 Share Posted October 17, 2019 On 6/20/2017 at 4:57 PM, cracked said: Hello Guys, months later but I finally found out what exactly the problem is - and got the solution. Please keep in mind that we use a multi-store, so this problem only applys when you want to get product links for different stores inside a multistore. So, there are several ways to generate a product link in prestashop. The constructor of class Product looks like this: public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null) So you would create a product object like this: $product = new Product(826, false, 2, 1); Now you could get the product-link for this product by simply using $product->getLink(); or you could do it this way: #getProductLink header: #public function getProductLink($product, $alias = null, $category = null, $ean13 = null, $id_lang = null, $id_shop = null, $ipa = 0, $force_routes = false, $relative_protocol = false, $add_anchor = false) #this is how you could also generate the ProductLink Context::getContext()->link->getProductLink($product,null,null,null,$id_lang,1); which works fine as far as you want to create the link for shop id = 1. But as you can see, Link::getProductLink's 6th parameter is $id_shop. So one, like me, could get the idea that you can simply create the product link for lets say, shop id = 3 like this: #the 6th parameter is shop_id. Here I try to create the link for shop id = 3 Context::getContext()->link->getProductLink($product,null,null,null,$id_lang,3); Which doesn't work, when your shops have different home categories. But why? It's because this part of code inside of getProductLink: if ($dispatcher->hasKeyword('product_rule', $id_lang, 'categories', $id_shop)) { $params['category'] = (!$category) ? $product->category : $category; $cats = array(); foreach ($product->getParentCategories($id_lang) as $cat) { if (!in_array($cat['id_category'], Link::$category_disable_rewrite)) { //remove root and home category from the URL $cats[] = $cat['link_rewrite']; } } $params['categories'] = implode('/', $cats); } Precisely line 146 $product->getParentCategories is the problem. As $product was generated with shop_id = 1, getParentCategories will find and use all parentCategories of shop_id = 1, even though you wanted to create a link for shop_id = 3. Now the solution could be simple. Generate a new $product object with shop_id = 3. But i think this is very inefficient and bothering, first because we use multi shop and every time I want the product link I have to generate a new object for each shop and SECOND, because Link::getProductLink already has the parameter id_shop BUT IT DOESN'T WORK AS EXPECTED. For me, this is a bug in prestashop. I fixed this for myself by overwriting Link.php and altering the code like this: #overwritten Link.php: ... if ($dispatcher->hasKeyword('product_rule', $id_lang, 'categories', $id_shop)) { $params['category'] = (!$category) ? $product->category : $category; $cats = array(); foreach (Link::getParentCategories($product->id, $id_lang, $id_shop) as $cat) { if (!in_array($cat['id_category'], Link::$category_disable_rewrite)) { //remove root and home category from the URL $cats[] = $cat['link_rewrite']; } } $params['categories'] = implode('/', $cats); } ... function getParentCategories($id_product, $id_lang = null, $id_shop = null) { if (!$id_lang) { $id_lang = Context::getContext()->language->id; } //taken from Shop.php addSqlRestrictionOnLang if (isset(Context::getContext()->shop) && is_null($id_shop)) { $id_shop = (int)Context::getContext()->shop->id; } if (!$id_shop) { $id_shop = (int)Configuration::get('PS_SHOP_DEFAULT'); } $defaultCatSql = 'select id_category_default from '._DB_PREFIX_.'product_shop where id_product = '.$id_product.' and id_shop = '.$id_shop; $idDefaultCat = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($defaultCatSql)[0]['id_category_default']; $interval = Category::getInterval($idDefaultCat); $sql = new DbQuery(); $sql->from('category', 'c'); $sql->leftJoin('category_lang', 'cl', 'c.id_category = cl.id_category AND id_lang = '.(int)$id_lang.' AND cl.id_shop = '.$id_shop); $sql->where('c.nleft <= '.(int)$interval['nleft'].' AND c.nright >= '.(int)$interval['nright']); $sql->orderBy('c.nleft'); return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); } Now I can use Link:getProductLink as expected, with a $product object which doesn't matter with which shop_id it was generated. Hope this helps someone else. Regards, cracked great , thanks for sharing your results Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now