Post by suhanto » Wed Sep 30, 2009 9:43 am

Currently I'm using OpenCart version 1.3.2. Thanks for the improvements made from the version 1.3.0, that now OpenCart produces canonical URLs for products, categories, CMS, and manufacturers. Now I can enjoy that improvement by having a single URL for a product.

However, when I'm hovering my mouse over the store logo, why the SEF URL does not rewrite it? I mean the link still shows like this:

http://opencart.local/index.php?route=common/home

I think it should be

http://opencart.local/home

I didn't do anything else more than enabling SEF URL in admin screen. Is there any configuration I miss?

Thanks in advance for any possible explanation or solution.
Last edited by OSWorX on Fri Jan 15, 2010 11:12 pm, edited 1 time in total.
Reason: Editing title for better finding through search

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by suhanto » Thu Oct 01, 2009 1:08 pm

Looks like I was wrong. The version 1.3.2 still has canonical URL problems. I mean, there should be only 1 URL for a product. In OpenCart version 1.3.2, a product can have more than one URLs. For example:

http://opencart.local/ipod-nano, if the product is accessed from the home screen
and
http://opencart.local/mp3-players/ipod-nano, if the product is accessed from the category menu

I'd prefer to have either one, wherever the product is accessed. Kindly please have this one problem resolved :) . I prefer the SEO URL model of OpenCart compared to PrestaShop, except for this problem.

(The Magento model of SEO URL offers more flexibility, and it doesn't have canonical URL problem. I like Magento model but Magento itself is 'heavy', so I'm looking for the alternative. Either OpenCart or PrestaShop)

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by suhanto » Thu Oct 01, 2009 3:35 pm

I couldn't barely await even a day for this 'canonical URL' problem to be resolved (for me it is a problem, but if you feel it is NOT a problem then ignore this thread please). Because I almost certain to choose OpenCart over PrestaShop. I don't like the way PrestaShop handle SEF URL: it is cheating. In PrestaShop, the 'product name' part of the SEF URL does NOT matter at all. Therefore, we can access a single product with the following URL:

http://prestashop.local/music-ipods/7-ipod-touch.html

but we also get the same page with the following URL:

http://prestashop.local/music-ipods/7-y ... -here.html

or we can replace the category name as we like and still get the same page:

http://prestashop.local/cipika-cipiki/7-z.html

So, Daniel, your OpenCart is better than PrestaShop in this particular area.

Now, I'm going to provide a temporary, quick-and-dirty solution for canonical URL problem I described in my previous post. Here is the solution:

Edit the /catalog/model/tool/seo_url.php, and change some code from line 15:

Code: Select all

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

// THE CHANGE STARTS HERE
$id_available = false;
$category_url = '';
foreach ($data as $key => $value) {
	if (($key == 'product_id') || ($key == 'manufacturer_id') || ($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'];
			$id_available = true;
			
			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) {
				$category_url .= '/' . $query->row['keyword'];
			}							
		}
		
		unset($data[$key]);
	}
}

if (!$id_available)
	$url .= $category_url;

// THE CHANGE ENDS HERE

if ($url) {
This will give us single URL for a single product, without category prefix, wherever we access the product. This solution works fine in my installation, but of course I'm waiting for the permanent solution by OpenCart team.

However, I still haven't got the answer for my orignal problem: how to rewrite the route to common/home, product/special, account/login, account/account etc ???

Although the URL only has a single query string, but for me, it is still ugly. So, Daniels, I hope the next version of OpenCart already resolved this issue as well.

Thanks...

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by suhanto » Fri Oct 02, 2009 7:09 am

Still no one wanna discuss about this with me?

hmmm, that's OK. I'll continue my 'discovery' phase on learning OpenCart, and write anything I need to communicate here. I hope someone will accompany me on this journey :)

... and I'm a little bit surprised that the 'canonical URL' problem does not only happen on product's URL in relation with its categories but:

1. product's URL in relation with manufacturer
2. product's URL in search result

To resolve the first problem, I've modified the /catalog/model/tool/seo_url.php again. Here is my recent modification:

Code: Select all

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

// CHANGES STARTS HERE

$id_available = false;
$manufacturer_url = '';
$category_url = '';
foreach ($data as $key => $value) {
	if (($key == 'product_id') || ($key == 'manufacturer_id') || ($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) {
			if ($key == 'product_id' || $key == 'information_id') {
				$url .= '/' . $query->row['keyword'];
				$id_available = true;
			} else {
				$manufacturer_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) {
				$category_url .= '/' . $query->row['keyword'];
			}							
		}
		
		unset($data[$key]);
	}
}

if (!$id_available) {
	if ($manufacturer_url)
		$url .= $manufacturer_url;
	else if ($category_url)
		$url .= $category_url;
}

// CHANGES ENDS HERE

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

and to resolve the second problem, I've changed the /catalog/controller/product/search.php on line 140, removing some 'unwanted' parameters:

Code: Select all

'href'    => $this->model_tool_seo_url->rewrite($this->url->http('product/product&product_id=' . $result['product_id'])),
OK, I hope there's no more modification :-\

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by suhanto » Fri Oct 02, 2009 10:53 am

I'm going to answer my own question (again) ;D ... it's just coincidence that somehow I'm able to understand Daniel's OpenCart source code...

Thanks Daniel for the brilliant and clear programming model of OpenCart. I like it 8)

So, to rewrite some of the OpenCart links in the homepage (notably in the Header menu), first we have to understand that OpenCart uses a mapping table to match between IDs and NAMEs. For example, OpenCart knows that ipod-classic match to product_id=20 because it is explicitly mapped in the mapping table.

... and we can use the same mapping table for our solution. We need the following mapping entries:

account/login => login
product/special => special
account/account => account
account/logout => logout
checkout/shipping => checkout
checkout/cart => cart

so, now construct the SQL to populate the mapping. The following code shows a sample SQL to do this:

Code: Select all

INSERT INTO url_alias(`query`, `keyword`) VALUES('account/login', 'login');
Next step, we have to make the ControllerCommonSeoUrl understands the URL like http://opencart.local/login. Change the code of ControllerCommonSeoUrl (in /catalog/controller/common/seo_url.php) to become:

Code: Select all

class ControllerCommonSeoUrl extends Controller {
	public function index() {
		if (isset($this->request->get['_route_'])) {
			$parts = explode('/', $this->request->get['_route_']);

			$is_shortcut = false;
			foreach ($parts as $part) {
				$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE keyword = '" . $this->db->escape($part) . "'");
				
				if ($query->num_rows) {
					
					if (strstr($query->row['query'], '/')) {
						
						$is_shortcut = true;
						$this->request->get['route'] = $query->row['query'];
						break;
						
					} else {
						
						$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];
						}
						
					}	
				}
			}
			
			if (!$is_shortcut) {
				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';
				} 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']);
			}
		}
	}
}
Next, we have to make the 'engine' generates proper URL for the header menus. Open the /catalog/controller/common/header.php, and change some code:

Code: Select all

$this->data['text_cart'] = $this->language->get('text_cart'); 
$this->data['text_checkout'] = $this->language->get('text_checkout');

// CHANGE STARTS HERE

$this->load->model('tool/seo_url');

$this->data['home'] = $this->url->http('common/home');
$this->data['special'] = $this->model_tool_seo_url->rewrite($this->url->http('product/special'));
$this->data['account'] = $this->model_tool_seo_url->rewrite($this->url->https('account/account'));
$this->data['logged'] = $this->customer->isLogged();
$this->data['login'] = $this->model_tool_seo_url->rewrite($this->url->https('account/login'));
$this->data['logout'] = $this->model_tool_seo_url->rewrite($this->url->http('account/logout'));
$this->data['cart'] = $this->model_tool_seo_url->rewrite($this->url->http('checkout/cart'));
$this->data['checkout'] = $this->model_tool_seo_url->rewrite($this->url->https('checkout/shipping'));

// CHANGE ENDS HERE

$this->id       = 'header';
$this->template = $this->config->get('config_template') . 'common/header.tpl';
... and then, change the SEO URL tool (/catalog/model/tool/seo_url.php) to become:

Code: Select all

class ModelToolSeoUrl extends Model {
	public function rewrite($link) {
		if ($this->config->get('config_seo_url')) {
			$url_data = parse_url(str_replace('&', '&', $link));
		
			$url = ''; 
			
			$data = array();
		
			parse_str($url_data['query'], $data);
			
			$id_available = false;
			$manufacturer_url = null;
			$category_url = null;
			foreach ($data as $key => $value) {
				if (strstr($value, '/')) {
					$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = '" . $value . "'");
					if ($query->num_rows) {
						$url .= '/' . $query->row['keyword'];
					}
				}				
				else if (($key == 'product_id') || ($key == 'manufacturer_id') || ($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) {
						if ($key == 'product_id' || $key == 'information_id') {
							$url .= '/' . $query->row['keyword'];
							$id_available = true;
						} else {
							$manufacturer_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) {
							$category_url .= '/' . $query->row['keyword'];
						}							
					}
					
					unset($data[$key]);
				}
			}
			
			if (!$id_available) {
				if (isset($manufacturer_url))
					$url .= $manufacturer_url;
				else if (isset($category_url))
					$url .= $category_url;
			}
		
			if ($url) {
				unset($data['route']);
			
				$query = '';
			
				if ($data) {
					foreach ($data as $key => $value) {
						$query .= '&' . $key . '=' . $value;
					}
					
					if ($query) {
						$query = '?' . str_replace('&', '&', trim($query, '&'));
					}
				}

				return $url_data['scheme'] . '://' . $url_data['host'] . (isset($url_data['port']) ? ':' . $url_data['port'] : '') . str_replace('/index.php', '', $url_data['path']) . $url . $query;
			} else {
				return $link;
			}
		} else {
			return $link;
		}		
	}
}
That's all...

Although this has done the most part as required, but it still requires some clean up works. For example the SEO URL needs to be applied to all bread-crumbs links. Also, some redirection code need to be adjusted, for example if we try accessing /account before login, the page will be redirected to /route=account/login. Well it should be changed to /login. But, I'm gonna leave these works as your homework :)

Update me if you find some bugs, or better implementation...

Thanks...

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by suhanto » Sat Oct 03, 2009 5:51 pm

I'm going to demonstrate what I've done in my previous posts in this thread.

Currently I've just set up a new online store, powered by OpenCart. I've just uploaded it yesterday, and it hasn't formally published yet. I'm not yet in business :) I'm going to see its stability for one week before I made it public and ready for business.

You can accessed it here : http://suhanto.com/katalog

My new online store is located in one of virtual directories of my domain (suhanto.com). The parent domain is my blog, powered by wordpress (it's in Indonesian, I'm going to create the English version in http://suhanto.com/en in the near future :))

You can see in the online store that the SEO URL has been applied to header links:

http://suhanto.com/katalog => the "Home"
http://suhanto.com/katalog/promo => Special offers
http://suhanto.com/katalog/login => Login
http://suhanto.com/katalog/register => Create new account
http://suhanto.com/katalog/search => Search
http://suhanto.com/katalog/contact => Contact us
http://suhanto.com/katalog/sitemap => The sitemap

Currently there's only one product:
http://suhanto.com/katalog/ubuntu-serve ... -jackalope

that's the SINGLE link of the product, wherever we access it:
* from Home screen
* Inside the "software" category
* search result
* brands (as currently there is no brand, I can't demonstrate it now)

What I like most is: There's no duplicate links for the product, there is only one "canonical link" to a product.

I've also made this change to breadcrumbs links, so that they also "in-sync" with the change I made elsewhere.

And now, my biggest HOPE is: the next version of OpenCart will adopt this approaches. Because if not, I'll be "busy" when upgrading ;D

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by liyen » Tue Oct 06, 2009 3:05 pm

Thanks for sharing, suhanto..

I'm also working on the url rewrite part. I'm using .htaccess method.

I have visited your shopping cart http://suhanto.com/katalog and the rewrite works nicely. :)

May I know is there any reason that you not rewriting the following links?
index.php?route=checkout/cart
index.php?route=account/account
index.php?route=checkout/shipping

I manage to reach http://abc.com/index.php?route=checkout/cart at http://abc.com/catalog/checkout/cart.htm after setting in htaccess.

But now I'm having some problems.

I need to change all the links in the website.
eg. index.php?route=checkout/cart will display as catalog/checkout/cart.htm

I think some thing need to be done at $this->redirect($this->url->https('checkout/cart'));

I think I can save time to adding the url alias into the database.
All I need is to make $this->url->https to create links in the format http://abc.com/catalog/checkout/cart.htm will do.

Do you have any idea?

Newbie

Posts

Joined
Tue Oct 06, 2009 2:55 pm

Post by liyen » Tue Oct 06, 2009 4:21 pm

Ok, I created new functions in system/engine/url.php to generate the url as desire and I mange to get the url created in the format I requested above.

Now still having problem to replace catalog/common/home.htm to catalog/index.htm

Newbie

Posts

Joined
Tue Oct 06, 2009 2:55 pm

Post by suhanto » Tue Oct 06, 2009 4:32 pm

Liyen,

The reason why I don't rewrite the links for account/account, checkout/cart, and checkout/shipping is simply because those pages are not 'indexable'. I mean, I would happily put meta-tag robot="noindex,nofollow" in those pages. In this case I don't care if those pages do not have SEF URL.

Although the login and register pages have the same 'noindex' characteristic, but it will make the customers easier to reach those pages by reducing some key strokes.

However, it's easy to change them to SEF-URL, just follow the same pattern with the others.

About the url->https/url->http functions, once I've also have the same thought. If I can perform the rewrite task inside those functions, it will save as a lot of editing many files. I think we can do that if can instantiate the seo_url model class inside http/https helper functions. I haven't think about it further, and for the time being I still perform 'rewrite' part in every controller.

I'll be glad if you'll investigate this potentially simpler method further :)

http://suhanto.net


User avatar
New member

Posts

Joined
Sat Sep 19, 2009 2:57 pm
Location - Jakarta, Indonesia

Post by liyen » Tue Oct 06, 2009 4:45 pm

For the time being I still perform 'rewrite' part in every controller.
I'm also doing the same way here.

Hopefully we will find a better solution soon. :)

Newbie

Posts

Joined
Tue Oct 06, 2009 2:55 pm

Post by towerofbabel » Tue Oct 06, 2009 5:03 pm

suhanto wrote: Still no one wanna discuss about this with me?
It is good you are trying for solutions.

I really hope Daniel will implement an integrated solution in future releases, but I am not sure the reality of dup. content issues in general is really understand around these parts yet.

I tried to provide some info and counter misinformation here:
http://forum.opencart.com/viewtopic.php?f=10&t=5764

Currently working on Finance Trails, personal finance software for budget optimization.


New member

Posts

Joined
Fri Aug 28, 2009 2:55 pm

Post by dbstr » Wed Oct 07, 2009 2:18 am

Instead of removing the path from URLs, I decided to add them instead with a small method that pulls the category ids. But it's just a quick fix for my site, which only contains a main category and maximum 1 level of subcategories. ("Desktops -> Mac" would then add '&path=20_27', while "Desktops -> Mac -> Macbooks" would screw me over - but it's not needed for my site).

The reason? Well obviously trying to avoid duplicated content - but also to expand the menu and highlight the correct category. I wrote my own menu which kinda requires this to happen - 1) to make it more user friendly, 2) otherwise it would look stupid.

I got another client tho, where I most likely will have to get it to work with more than 1 subcategory - so I suppose I will have to do a fix for this.. somehow. ;-)

Request Reviews v1.0 released.


Active Member

Posts

Joined
Sun Aug 30, 2009 12:20 am

Post by banane » Thu Dec 17, 2009 2:01 pm

thank you suhanto for your suggestion, anyway I still trying to solve this problem:
if I point to http://suhanto.com/katalog/software/lin ... ktop-9-04s that should be a broken link because there are no products with this name (I've added a "s") the server doesn't send back an error page with 404 error code but instead shows me the category (software in this case).
this happens with search engine that are still indexing same pages about products that I've already deleted, because they look for them through the categories and have back the page concerning the category instead an error page.
I hope that this is clear..
bye

User avatar
New member

Posts

Joined
Fri Jul 31, 2009 6:30 pm


Post by banane » Thu Dec 24, 2009 3:14 pm

does anyone know hot to solve the problem above?
google is indexing a lot of duplicate because when he looks for a product that is no more existing, he receives the page of category and not an error page telling that the product has been deleted..

User avatar
New member

Posts

Joined
Fri Jul 31, 2009 6:30 pm


Post by adelaida » Fri Feb 19, 2010 8:38 pm

i think it is possible to find a solution within .htaccess REWWRITE rools.

opencart shopping saved lots of time for me!
thank you! just another situk mind (c)
simply clear uk granny dating website


Newbie

Posts

Joined
Fri Feb 19, 2010 8:17 pm

Post by yabbayabba » Fri Mar 12, 2010 9:21 am

Hi.
I'm a real novice here and know very little about the code side of things so just wanted something to cut and paste. I wonder if someone could tell me exactly where the sql to populate the mapping actually goes? Does it go in the sql database or in one of the php files? Also what is the mapping table that you are talking about, again is this part of a php file?

Thanks
Glen

Newbie

Posts

Joined
Tue Mar 09, 2010 8:05 pm

Post by rodpuzey » Sat Mar 20, 2010 3:15 am

Hello, i have been reading this post and it has helped me a lot. Thanks you. I went to your site and i like the powered by and link to your site, how and what files do i change to do that same thing on my site?

Thanks again

Newbie

Posts

Joined
Thu Mar 18, 2010 5:48 am

Post by mancourt » Fri May 21, 2010 1:51 am

Has this been added to the new version yet? I see on the demo SEF URLs don't seem to be active?

For example...
http://demo.opencart.com/index.php?rout ... duct_id=48

Newbie

Posts

Joined
Sat Mar 06, 2010 4:26 am

Post by Xsecrets » Fri May 21, 2010 2:31 am

seo urls are included. they are just not active on the demo, but no even in the latest versions things like the homepage and contact sitemap etc are not rewritten. just products/categories/manufacturers

OpenCart commercial mods and development http://spotonsolutions.net
Layered Navigation
Shipment Tracking
Vehicle Year/Make/Model Filter


Guru Member

Posts

Joined
Sun Oct 25, 2009 3:51 am
Location - FL US
Who is online

Users browsing this forum: No registered users and 4 guests