I'm going to answer my own question (again)

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