How can I create custom SEO-friendly URLs in OpenCart?

A simple "no code" method is to install vQmod. Instructions here: vQmod install wiki

And then upload the xml file available in this Opencart forum thread to the vqmod/xml/ folder.

The xml contains a relatively easy to read script that maps the urls similar to the two answers above, but without modifying core files. So site updates will not kill it.


Put this near the top of your index.php file. It was the only solution that worked for me.

switch($_SERVER["REQUEST_URI"])
{
    case '/old-url': $three01 = "/new-url"; break;
}

if($three01)
{
    header("HTTP/1.1 301 Moved Permanently"); header("Location: ".$three01); exit;
}

I'm using Opencart version 1.5.5.1 and this is the exact code that worked for me:

<?php 
class ControllerCommonSeoUrl extends Controller {
    /* SEO Custom URL */
    private $url_list = array (
        'common/home'       => '',
        'checkout/cart'     => 'cart',
        'account/register'  => 'register',
                    'account/wishlist'  => 'wishlist',
                    'checkout/checkout' => 'checkout',
                    'account/login'     => 'login',
                    'product/special'   => 'special',
                    'affiliate/account' => 'affiliate',
                    'checkout/voucher'  => 'voucher',
                    'product/manufacturer' => 'brand',
                    'account/newsletter'   => 'newsletter',
                    'account/order'        => 'order',
                    'account/account'      => 'account',
                    'information/contact'  => 'contact',
                    'account/return/insert' => 'return',
                    'information/sitemap'   => 'sitemap',
        );
    /* SEO Custom URL */

public function index() {
    // Add rewrite to url class
    if ($this->config->get('config_seo_url')) {
        $this->url->addRewrite($this);
    }

    // Decode URL
    if (isset($this->request->get['_route_'])) {
        $parts = explode('/', $this->request->get['_route_']);

        foreach ($parts as $part) {
            $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE keyword = '" . $this->db->escape($part) . "'");

            if ($query->num_rows) {
                $url = explode('=', $query->row['query']);

                if ($url[0] == 'product_id') {
                    $this->request->get['product_id'] = $url[1];
                }

                if ($url[0] == 'category_id') {
                    if (!isset($this->request->get['path'])) {
                        $this->request->get['path'] = $url[1];
                    } else {
                        $this->request->get['path'] .= '_' . $url[1];
                    }
                }   

                if ($url[0] == 'manufacturer_id') {
                    $this->request->get['manufacturer_id'] = $url[1];
                }

                if ($url[0] == 'information_id') {
                    $this->request->get['information_id'] = $url[1];
                }   
            } else {
                $this->request->get['route'] = 'error/not_found';   
            }
        }
                    /* SEO Custom URL */
                    if ( $_s = $this->setURL($this->request->get['_route_']) ) {
                            $this->request->get['route'] = $_s;
                    }/* SEO Custom URL */

        if (isset($this->request->get['product_id'])) {
            $this->request->get['route'] = 'product/product';
        } elseif (isset($this->request->get['path'])) {
            $this->request->get['route'] = 'product/category';
        } elseif (isset($this->request->get['manufacturer_id'])) {
            $this->request->get['route'] = 'product/manufacturer/info';
        } elseif (isset($this->request->get['information_id'])) {
            $this->request->get['route'] = 'information/information';
        }

        if (isset($this->request->get['route'])) {
            return $this->forward($this->request->get['route']);
        }
    }
}

public function rewrite($link) {
    $url_info = parse_url(str_replace('&amp;', '&', $link));

    $url = ''; 

    $data = array();

    parse_str($url_info['query'], $data);

    foreach ($data as $key => $value) {
        if (isset($data['route'])) {
            if (($data['route'] == 'product/product' && $key == 'product_id') || (($data['route'] == 'product/manufacturer/info' || $data['route'] == 'product/product') && $key == 'manufacturer_id') || ($data['route'] == 'information/information' && $key == 'information_id')) {
                $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = '" . $this->db->escape($key . '=' . (int)$value) . "'");

                if ($query->num_rows) {
                    $url .= '/' . $query->row['keyword'];

                    unset($data[$key]);
                }                   
            } elseif ($key == 'path') {
                $categories = explode('_', $value);

                foreach ($categories as $category) {
                    $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = 'category_id=" . (int)$category . "'");

                    if ($query->num_rows) {
                        $url .= '/' . $query->row['keyword'];
                    }                           
                }

                unset($data[$key]);
            }
                                    /* SEO Custom URL */
                                    if( $_u = $this->getURL($data['route']) ){
                                        $url .= $_u;
                                        unset($data[$key]);
                                    }/* SEO Custom URL */       


        }
    }

    if ($url) {
        unset($data['route']);

        $query = '';

        if ($data) {
            foreach ($data as $key => $value) {
                $query .= '&' . $key . '=' . $value;
            }

            if ($query) {
                $query = '?' . trim($query, '&');
            }
        }

        return $url_info['scheme'] . '://' . $url_info['host'] . (isset($url_info['port']) ? ':' . $url_info['port'] : '') . str_replace('/index.php', '', $url_info['path']) . $url . $query;
    } else {
        return $link;
    }
}   
    /* SEO Custom URL */
    public function getURL($route) {
            if( count($this->url_list) > 0) {
                 foreach ($this->url_list as $key => $value) {
                    if($route == $key) {
                        return '/'.$value;
                    }
                 }
            }
            return false;
    }
    public function setURL($_route) {
            if( count($this->url_list) > 0 ){
                 foreach ($this->url_list as $key => $value) {
                    if($_route == $value) {
                        return $key;
                    }
                 }
            }
            return false;
    }/* SEO Custom URL */
}
?>

It turns out this can be done with a relatively simple change to a single file. No .htaccess rewrite rules, simply patch the catalog/controller/common/seo_url.php file and add your pretty URLs to an existing database table.


The patch to seo_url.php:

Index: catalog/controller/common/seo_url.php
===================================================================
--- catalog/controller/common/seo_url.php   (old)
+++ catalog/controller/common/seo_url.php   (new)
@@ -48,7 +42,12 @@
                $this->request->get['route'] = 'product/manufacturer/product';
            } elseif (isset($this->request->get['information_id'])) {
                $this->request->get['route'] = 'information/information';
-           }
+           } else {
+                $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE keyword = '" . $this->db->escape($this->request->get['_route_']) . "'");
+                if ($query->num_rows) {
+                    $this->request->get['route'] = $query->row['query'];
+                }
+           }

            if (isset($this->request->get['route'])) {
                return $this->forward($this->request->get['route']);
@@ -88,7 +87,15 @@
                        }

                        unset($data[$key]);
-                   }
+                   } else {
+                        $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = '" . $this->db->escape($data['route']) . "'");
+
+                        if ($query->num_rows) {
+                            $url .= '/' . $query->row['keyword'];
+
+                            unset($data[$key]);
+                        }
+                   }
                }
            }

There are two edits required. The first extends the index() function to look in the url_alias table for any keyword matching $this->request->get['_route_'].

The second extends the rewrite() function to look in the url_alias table for all routes, not just those for products, manufacturers, and information pages.


Adding entries to the database:

INSERT INTO `url_alias` (`url_alias_id`, `query`, `keyword`) VALUES
(NULL, 'checkout/cart', 'cart');

That's it. http://example.com/cart should return the same thing that http://example.com/index.php?route=checkout/cart does, and OpenCart should recognize $this->url->link('checkout/cart'); and return a link to the pretty URL http://example.com/cart