I'd like to like to rewrite my product url's from my opencart webshop. Opencart itself has a seo implementation which really sucks. I've updated the seo implementation to be able to use the same keyword for multiple categories see: Opencart duplicate URL keywords But this is only working for the categories. For the products i'd just need a htaccess rewrite rule i think.
The original url looks like this:
http://domain.com/index.php?route=product/product&path=25_28_93&product_id=1759
my url looks like this at the moment:
http://domain.com/In-Stock/Retreaded-Tires/Agricultural?product_id=1759
As you can see the categories did change already.
And I want it to be like this:
http://domain.com/In-Stock/Retreaded-Tires/Agricultural/1759/1050-50R32-Mega-X-Bib
Then for pagination (within a category) I have this url:
http://domain.com/index.php?route=product/category&path=36_70_67&page=2
I've already made this into:
http://domain.com/Tire-Retreading/Equalizing/&page=2
But i'd like this to be
http://domain.com/Tire-Retreading/Equalizing/2
My htaccess file looks like this:
Options +FollowSymlinks
Options -Indexes
<FilesMatch "(?i)((\.tpl|\.ini|\.log|(?<!robots)\.txt))">
Order deny,allow
Deny from all
</FilesMatch>
RewriteEngine On
RewriteBase /
RewriteRule ^sitemap.xml$ index.php?route=feed/google_sitemap [L]
RewriteRule ^googlebase.xml$ index.php?route=feed/google_base [L]
RewriteRule ^download/(.*) /index.php?route=error/not_found [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]
My seo_url.php file which is a bit altered looks like this:
public function index() {
$this->load->model('catalog/category');
// 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_']);
// remove any empty arrays from trailing
if (utf8_strlen(end($parts)) == 0) {
array_pop($parts);
}
$categories = array();
for ($i = 0; $i < count($parts); $i++) {
$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE keyword = '" . $this->db->escape($parts[$i]) . "'");
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') {
$categories[$i] = $this->model_catalog_category->getCategory($url[1]);
if (!isset($this->request->get['path'])) {
$this->request->get['path'] = $categories[$i]['category_id'];
} else {
foreach ($query->rows as $row) {
$url = explode('=', $row['query']);
$category_id = $url[1];
$category = $this->model_catalog_category->getCategory($category_id);
if ($category['parent_id'] == $categories[$i - 1]['category_id']) {
$this->request->get['path'] .= '_' . $category['category_id'];
}
}
}
}
if ($url[0] == 'manufacturer_id') {
$this->request->get['manufacturer_id'] = $url[1];
}
if ($url[0] == 'information_id') {
$this->request->get['information_id'] = $url[1];
}
if ($query->row['query'] && $url[0] != 'information_id' && $url[0] != 'manufacturer_id' && $url[0] != 'category_id' && $url[0] != 'product_id') {
$this->request->get['route'] = $query->row['query'];
}
} else {
$this->request->get['route'] = 'error/not_found';
break;
}
}
if (!isset($this->request->get['route'])) {
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 new Action($this->request->get['route']);
}
}
}
public function rewrite($link) {
$url_info = parse_url(str_replace('&', '&', $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 && $query->row['keyword']) {
$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 && $query->row['keyword']) {
$url .= '/' . $query->row['keyword'];
} else {
$url = '';
break;
}
}
unset($data[$key]);
} else {
$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = '" .$data['route'] . "'");
if ($query->num_rows && $query->row['keyword']) {
$url .= '/' . $query->row['keyword'];
unset($data[$key]);
}
}
}
}
if ($url) {
unset($data['route']);
$query = '';
if ($data) {
foreach ($data as $key => $value) {
$query .= '&' . rawurlencode((string)$key) . '=' . rawurlencode((string)$value);
}
if ($query) {
$query = '?' . str_replace('&', '&', 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;
}
}
The Pagination code is like this:
class Pagination {
public $total = 0;
public $page = 1;
public $limit = 20;
public $num_links = 8;
public $url = '';
public $text_first = '|<';
public $text_last = '>|';
public $text_next = '>';
public $text_prev = '<';
public function render() {
$total = $this->total;
if ($this->page < 1) {
$page = 1;
} else {
$page = $this->page;
}
if (!(int)$this->limit) {
$limit = 10;
} else {
$limit = $this->limit;
}
$num_links = $this->num_links;
$num_pages = ceil($total / $limit);
$this->url = str_replace('%7Bpage%7D', '{page}', $this->url);
$output = '<ul class="pagination">';
if ($page > 1) {
$output .= '<li><a href="' . str_replace('{page}', 1, $this->url) . '">' . $this->text_first . '</a></li>';
$output .= '<li><a href="' . str_replace('{page}', $page - 1, $this->url) . '">' . $this->text_prev . '</a></li>';
}
if ($num_pages > 1) {
if ($num_pages <= $num_links) {
$start = 1;
$end = $num_pages;
} else {
$start = $page - floor($num_links / 2);
$end = $page + floor($num_links / 2);
if ($start < 1) {
$end += abs($start) + 1;
$start = 1;
}
if ($end > $num_pages) {
$start -= ($end - $num_pages);
$end = $num_pages;
}
}
for ($i = $start; $i <= $end; $i++) {
if ($page == $i) {
$output .= '<li class="active"><span>' . $i . '</span></li>';
} else {
$output .= '<li><a href="' . str_replace('{page}', $i, $this->url) . '">' . $i . '</a></li>';
}
}
}
if ($page < $num_pages) {
$output .= '<li><a href="' . str_replace('{page}', $page + 1, $this->url) . '">' . $this->text_next . '</a></li>';
$output .= '<li><a href="' . str_replace('{page}', $num_pages, $this->url) . '">' . $this->text_last . '</a></li>';
}
$output .= '</ul>';
if ($num_pages > 1) {
return $output;
} else {
return '';
}
}
}
EDIT:
All of my pages are redirected to http://domain.com/index.php
From there it decides what directory/file to use from the route parameter.
So route=product/product
is telling go to the product.php
within the directory product. The directory product contains also categories.php
which leads to a route: route=product/category
The Path variable from the string represents the id of a category. In my example 25
stands for In-Stock
. And 25_28
stands for In-Stock/Retreaded-Tires
etc.
The product_id
variable represents the corresponding id for the product.
The page variable represents pagination and is used on the list of products within a category. This list can have a variable length as it calculates how many products are within a category and how many he should show on 1 page.
So if the original url has &page=2
with the route route=product/category
it should make an url like this: http://domain.com/Tire-Retreading/Equalizing/2
Redirects are not bad for SEO, but — as with so many things — only if you put them in place correctly. A bad implementation might cause all kinds of trouble, from loss of PageRank to loss of traffic. Redirecting pages is a must if you make any changes to your URLs.
URL rewriting is a technique that is often used in SEO to improve the ranking of a website. By changing the text of a website's URL, SEO experts can make it easier for web browsers to find and index the site. This can help improve the visibility of the site in search engine results pages (SERPs).
OpenCart SEO, also known as OpenCart search engine optimization, is a digital marketing strategy used to improve the search engine visibility of OpenCart websites.
I still find the added explanations confusing and it sounds to me like you don't quite know how URL rewriting works, so I'll try to explain some URL rewriting basics.
What you can do with URL rewriting is to translate 'pretty' URL segments (of URLs shown to the user) into segments of your actual, long, 'non-pretty' URLs (which use variables to serve certain content). You do that by using regular expressions to match the pretty segments, and then giving either pre-defined values or those matched values to your php variables.
So, what you do first is figure out how your pretty URLs should look, and how their individual segments translate to your variables. (Though you can of course also use any number of new variables, whose values you can then convert and assign to already existing or pre-defined variables in your php scripts.)
You gave this example: http://example.com/Tire-Retreading/Equalizing/2
This URL seems to be made up of three parts that you want to translate to variables:
Tire-Retreading
(variable route
)Equalizing
(variable path
)2
(variable page
)You'll have to build your RegEx for matching your segments based on all possible ways the individual segments can be spelled, including any special characters that are allowed etc. Once that is done, you can pass the matched values to your variables using a backreference (or you could define your own values to be used by your variables).
Using backreferences, you could possibly use this:
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/([0-9]+)$ index.php?route=$1&path=$2&page=$3 [NC,L]
You'll have to put the individual RegEx for the segment matching (like [A-Za-z-]+
) in round brackets to be able to assign the results to your php variables in the form of $1
, $2
etc.
Depending on if users should also be allowed to browse 'overview' pages for your products/categories or paths or not, you might have to start your rewriting from the smallest possible 'pretty' URL to the longest one.
E.g.
RewriteRule ^([A-Za-z-]+)$ $1/ [NC,R]
RewriteRule ^([A-Za-z-]+)/$ index.php?route=$1 [NC,L]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)$ $1/$2/ [NC,R]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/$ index.php?route=$1&path=$2 [NC,L]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/([0-9]+)$ $1/$2/$3/ [NC,R]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/([0-9]+)/$ index.php?route=$1&path=$2&page=$3 [NC,L]
The Rewriting for the domain itself goes before all the 'path' rewriting.
You'll also have to figure out which flags – the part in square brackets at the end – you need to use, meaning you'll have to read up on URL rewriting in more detail. In the above example, all URLs the user types in that do not end with a slash are automatically redirected (that's what the R
in square brackets stands for) to the same URL but with an added slash. So, http://example.com/blah
would redirect to http://example.com/blah/
etc.
Based on the info you provided, you'll have URLs starting with one of two possible segments:
http://example.com/In-Stock/...
vs.
http://example.com/Product-or-Category/....
So, you'll have to take extra care these two don't get confused. If In-Stock
gets funnelled into its own variable and is always spelled exactly like that, you'll probably want to make your first rules deal with that and use the RegEx for matching the products/categories only later (all rewrite rules are dealt with chronologically, one after the other).
I hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With