Pages

Sunday, September 6, 2015

How to solve the duplicate content issue in OpenCart?

Imagine this - you are the administrator of the imaginary OpenCart store http://myshinystore.com and you have just set up your store to use SEO URL’s.
As a result your category links look something like this:
http://myshinystore.com/parent_category/child_category (instead of http://myshinystore.com/index.php?route=product/category&path=11_3)
Similarly, the product links look something like this:
http://myshinystore.com/product_link (instead of http://myshinystore.com/index.php?route=product/product&product_id=7)
Looks great, right? No more ugly long URLs - only meaningful paths from now on.

So what?

Well, there is one small thing which could impact your website ranking in search engines like Google or Bing. Take for example the category link above. The same page can be accessed from:
http://myshinystore.com/child_category (instead of http://myshinystore.com/parent_category/child_category)
This is a problem because even though both links lead to absolutely the same content, the search engine will regard them as different pages on your website - the so-called “Duplicate content” issue. More information about it here:

So, how do we solve this?

Since there is no setting in OpenCart to resolve this, we will need to get our hands dirty and modify a bit of code in your store. The modification will be made as an OCMOD extension to avoid changes to your core files.
Note: Please keep in mind that the changes we are about to make might cause conflicts with other third-party extensions on your store. If this happens, feel free to disable the modifications in order to return to the previous behavior.
Note: Also keep in mind that these modifications are developed for OpenCart 2.x.

Step 1 - Prepare the file.

Without further ado, let’s begin! Using your favorite text editor, create a new file calledduplicate_url_fix.ocmod.xml.
There are generally two ways in which the duplicate issue can be resolved. Use only one of the approaches below, depending on your preference.

Step 2, Approach 1 - Modify the store to use only the short versions of the links.

This will make OpenCart convert all of the SEO links to only a single word (without any paths). So as a result all links to child categories and products will look like this:
http://myshinystore.com/child_category (instead of http://myshinystore.com/parent_category/child_category)
Add the following contents to the newly created file duplicate_url_fix.ocmod.xml:
<?xml version="1.0" encoding="UTF-8"?>
<modification>
    <name><![CDATA[Duplicate Content Fix]]></name>
    <code><![CDATA[duplicate_content_fix]]></code>
    <version><![CDATA[1.0]]></version>
    <author><![CDATA[iSenseLabs]]></author>
    <link><![CDATA[http://isenselabs.com]]></link>
    <file path="catalog/controller/common/seo_url.php">
        <operation>
            <search><![CDATA[parse_str($url_info['query'], $data);]]></search>
            <add position="after"><![CDATA[
                $has_product_id = false;
                $has_path = false;               
​                foreach ($data as $query_key => $query_value) {
                    if ($query_key == 'product_id' && !empty($data['route']) && $data['route'] == 'product/product') {
                        $has_product_id = true;
                    }                    if ($query_key == 'path') {
                        $has_path = true;
                    }
                if ($has_product_id && $has_path) {
                    unset($data['path']);
                } else if (!$has_product_id && $has_path) {
                    $path_parts = explode('_', $data['path']);
                    $data['path'] = $path_parts[count($path_parts) - 1];
                }
            ]]></add>
        </operation>
    </file>
</modification>

 

Step 2, Approach 2 - Modify the store to always use the long versions of the links.

This will make OpenCart convert all of the SEO links to the longest path possible. So as a result all links to child categories and products will look like this:
http://myshinystore.com/parent_category_1/parent_category_2/child_category (instead of http://myshinystore.com/child_category)
Add the following contents to the newly created file duplicate_url_fix.ocmod.xml:
<?xml version="1.0" encoding="UTF-8"?>
<modification>
    <name><![CDATA[Duplicate Content Fix]]></name>
    <code><![CDATA[duplicate_content_fix]]></code>
    <version><![CDATA[1.0]]></version>
    <author><![CDATA[iSenseLabs]]></author>
    <link><![CDATA[http://isenselabs.com]]></link>
    <file path="catalog/controller/product/category.php">
        <operation>
            <search><![CDATA[$category_info = $this->model_catalog_category->getCategory($category_id);]]></search>
            <add position="before"><![CDATA[
                $this->session->data['last.entered.category'] = $category_id;
            ]]></add>
        </operation>
    </file>    
    <file path="catalog/controller/common/seo_url.php">
        <operation>
            <search><![CDATA[class ControllerCommonSeoUrl extends Controller {]]></search>
            <add position="after"><![CDATA[
                public function findParentPath($category_id) {
                    $found_path = array($category_id);                   
                    do {
                        $category_result = $this->db->query("SELECT * FROM " . DB_PREFIX . "category WHERE category_id = '" . $category_id . "'");
                        $category_id = (int)$category_result->row['parent_id'];
                        if ($category_id > 0 && !in_array($category_id, $found_path)) {
                            array_unshift($found_path, $category_id);
                        }
                    } while ($category_id != 0);                   
                    return $found_path;
                }
            ]]></add>
        </operation>        
        <operation>
            <search><![CDATA[parse_str($url_info['query'], $data);]]></search>
            <add position="after"><![CDATA[
                $has_product_id = false;
                $has_path = false;               
                foreach ($data as $query_key => $query_value) {
                    if ($query_key == 'product_id' && !empty($data['route']) && $data['route'] == 'product/product') {
                        $has_product_id = true;
                    }                   
                    if ($query_key == 'path') {
                        $has_path = true;
                    }
                }               
                // Calculate full path
                $parent_categories_paths = array();               
                if ($has_product_id) {
                    // Find the true path based on the product_id                   
                    $parent_categories_result = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_to_category WHERE product_id='" . (int)$data['product_id'] . "'");                   
                    foreach ($parent_categories_result->rows as $parent_category) {
                        $parent_categories_paths[] = $this->findParentPath($parent_category['category_id']);
                    }
                } else if ($has_path) {
                    // Find the true path based on the last category_id                   
                    $path_parts = explode('_', $data['path']);
                    $category_id = $path_parts[count($path_parts) - 1];                   
                    $parent_categories_paths[] = $this->findParentPath($category_id);
                }               
                if (!empty($parent_categories_paths)) {
                    $last_entered_category = !empty($this->session->data['last.entered.category']) ? (int)$this->session->data['last.entered.category'] : 0;                   
                    $data['path'] = implode('_', $parent_categories_paths[0]);                   
                    $has_path = true;                   
                   foreach ($parent_categories_paths as $parent_categories_path_candidate) {
                        if (in_array($last_entered_category, $parent_categories_path_candidate)) {
                            $data['path'] = implode('_', $parent_categories_path_candidate);
                            break;
                        }
                    }
                }
            ]]></add>
        </operation>
    </file>
</modification>

 

Step 3 - Uploading the file

Almost there. Now save your file and install it with the OpenCart Extension Installer. Make sure after you upload the file to click Refresh in Admin > Extensions > Modifications in order for the changes to get applied.

That’s it!

Congratulations! The changes you made will help avoid the duplicate content issue. Note that this is not the only way to resolve this issue - another totally different approach would be to use canonical URL’s in your pages. More information about canonical URL’s can be found here:
If you want to use canonical URL’s in your website, there are a few ready modules in the OpenCart Extension store:
I hope you found the information above useful. Let us know if you have any questions in the comments below.

No comments:

Post a Comment