Jump to content
cracked

[PS1.6.1.5] Link::getProductLink not working properly?

Recommended Posts

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:

post-1353562-0-11379600-1485944858_thumb.png

 

This is the configuration of product with id 384:

post-1353562-0-58325500-1485944891_thumb.png

 

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:

post-1353562-0-39568600-1485945305_thumb.png

 

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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:
 
post-1353562-0-04183200-1485946276_thumb.png

 

PS: you really think I would code this and open up this topic, if I weren't desperate? ;)

Regards,

Lennart

post-1353562-0-04183200-1485946276_thumb.png

Edited by cracked (see edit history)

Share this post


Link to post
Share on other sites

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 by Gabriel Perez (see edit history)

Share this post


Link to post
Share on other sites

Hello Gabriel Perez,

 

thanks again for your advice buddy, but I already came up with that idea too. Problem is, it ain't working:

post-1353562-0-50466200-1485947071_thumb.png

 

 

Regards,

Lennart

Share this post


Link to post
Share on other sites

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 by Gabriel Perez (see edit history)

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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 by Gabriel Perez (see edit history)

Share this post


Link to post
Share on other sites

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:

post-1353562-0-60742100-1485956215_thumb.png

Share this post


Link to post
Share on other sites

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 ...

Share this post


Link to post
Share on other sites

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

  • Like 1

Share this post


Link to post
Share on other sites
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 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

Cookies ensure the smooth running of our services. Using these, you accept the use of cookies. Learn More