“Happy new year – happy to develop all things as you want”. Hello everyone, I decided to write an article to start a new journey for the new year. This article summarizes how to enable “SEO friendly Url in Opencart” feature.
1. Summary What are SEO Friendly URLs?
First of all, we need to understand the concept of “Seo Friendly Urls”?. “Seo friendly url” or “Slug Url” is a concept used to refer to the link structure after the domain name, this structure combines with the domain name and creates a link to the website’s resources (an article). writing, a source page, images, videos…). “Seo friendly url” has the following 2 special features:
- A string that is a combination of the characters aZ and the numbers 0-9 with the delimiter “-” representing a space or the special characters “%*~!”.
- Slug Url character string is always clear, easy to remember and unique in all existing links in a website.
And SEO Friendly is one of the important requirements when implementing a successful SEO plan, so having the website support this feature is a must in the home-made “digital marketing and SEO” era. The content below will hopefully help you – those who are new to Opencart will have a smoother start in the future.
2. Enable SEO Friendly URLs in Opencart.
Unlike WordPress or Pretashop, by default this link beautification feature is not enabled in Opencart but must be done manually through 2 steps as follows:
2.1 Step 1: Enable SEO Urls feature in admin
- Login to the admin panel (you need system permissions to see the next settings).
- Navigate to System > Settings -> Server (System > System Configuration > Server).
- Check the USE SEO Urls feature in the admin as shown below. Remember to clear the system cache if you have a cache plugin installed.
2.2 Step 2: Change the .htaccess file
After completing step 1, basically your website has been activated with SEO features, every time you add a new article or a new product, in the admin section, there will be a section for you to customize the link of the resource. . However, in order for users to be able to access that link, you need to edit the htaccess file (currently on nginx, I still don’t know what the standard configuration file is, a/c/e who knows how to share me with you? ok) as follows:
1 2 3 4 5 6 7 8 9 10 11 | # SEO URL Settings RewriteEngine On # If your opencart installation does not run on the main web folder make sure you folder it does run in ie. / becomes /shop/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] |
To be able to add code 301 in the .htaccess file, you need to add it above the last line in this code.
3. Add SEO Friendly Urls feature to your self-created module.
By default, these Slug Urls strings are stored in a table, every time the user accesses the link, Opencart’s Core will read a record in this table and navigate to the corresponding controller. However, this feature does not automatically detech every time we add a new controller or a new module, so it needs to be activated in another way in the following 2 directions:
3.1 Case 1: Add a controller with methods as the endpoint of the view
An example for this case is that we want to add a contact page for the website to get customer information, or a landing page to run ads. Mindset for this case, we only need to create 1 controller with 1 method to display the view with the contact form combined with this content storage handle. Suppose we create a controller named “contact” with method “index” with the following directory structure:
Now we access the contact page with a link with the following pattern: root-domain/index.php?route=folder/controller/method As in the above example, it will be root-domain/index.php?route=contact-folder /contact/index or root-domain/index.php?route=contact-folder/contact with the default index method if not provided. To convert the above difficult-to-remember url into something like: root-domain/lien-he-voi-chung-toi , go to the admin and search for the item: Design > SEO URL or Design > SEO URL to add a record. into the SEO URL link manager.
Note:
- Query will be equal to contact-folder/contact/index
- Keyword will be equal to lien-he-voi-chung-toi
3.2 Case 2: Add a custom module with render view data dependent on model id
As in Example 3.1, the render data of the view depends on the static code added in the contact.twig file in the code. So what if we want render data to be retrieved from a model mapped from one or more tables in the database? The most visual example for this is the module for posting job advertisements on the website. This simple module has the following basic features:
- A page of listings of posts
- Each job posting on the posting page will render a detailed page of the job posting content that has been entered in the admin page.
On the list page we can create a controller and a method like in Example 3.1. The details page needs to get dynamic data from the get method on the url with a param like Tuyendung=id in the generated url.
To do this we need to add some code in the file:
- Opencart 3 + 4: catalog/controller/startup/seo_url.php
- Opencart 2: catalog/controller/common/seo_url.php
- Opencart 4: update later (I haven’t touched her yet)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | <?php class ControllerStartupSeoUrl extends Controller { 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_']); // remove any empty arrays from trailing if (utf8_strlen(end($parts)) == 0) { array_pop($parts); } foreach ($parts as $part) { $partFormat = str_replace(".html", "", $part); $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE keyword = '" . $this->db->escape($partFormat) . "' AND store_id = '" . (int)$this->config->get('config_store_id') . "'"); 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]; } if ($url[0] == 'blog_id') { $this->request->get['blog_id'] = $url[1]; } if ($url[0] == 'blog_category_id') { $this->request->get['blog_category_id'] = $url[1]; } if ($url[0] == 'video_id') { $this->request->get['video_id'] = $url[1]; } if ($url[0] == 'video_category_id') { $this->request->get['video_category_id'] = $url[1]; } if ($url[0] == 'landing_id') { $this->request->get['landing_id'] = $url[1]; } if ($url[0] == 'branch_id') { $this->request->get['branch_id'] = $url[1]; } if ($query->row['query'] && $url[0] != 'video_id' && $url[0] != 'branch_id' && $url[0] != 'video_category_id' && $url[0] != 'landing_id' && $url[0] != 'blog_id' && $url[0] != 'blog_category_id' && $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'; }elseif (isset($this->request->get['blog_id'])) { $this->request->get['route'] = 'web/blog'; }elseif (isset($this->request->get['blog_category_id'])) { $this->request->get['route'] = 'web/category'; }elseif (isset($this->request->get['video_category_id'])) { $this->request->get['route'] = 'web/video_category'; }elseif (isset($this->request->get['video_id'])) { $this->request->get['route'] = 'web/video'; }elseif (isset($this->request->get['landing_id'])) { $this->request->get['route'] = 'web/landing'; }elseif (isset($this->request->get['branch_id'])) { $this->request->get['route'] = 'web/branch'; } } } } 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') || ($data['route'] == 'web/blog' && $key == 'blog_id') || ($data['route'] == 'web/category' && $key == 'blog_category_id') || ($data['route'] == 'web/video_category' && $key == 'video_category_id') || ($data['route'] == 'web/video' && $key == 'video_id') || ($data['route'] == 'web/landing' && $key == 'landing_id') || ($data['route'] == 'web/branch' && $key == 'branch_id') ) { $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE `query` = '" . $this->db->escape($key . '=' . (int)$value) . "' AND store_id = '" . (int)$this->config->get('config_store_id') . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'"); if ($query->num_rows && $query->row['keyword']) { // $url .= '/' . $query->row['keyword'] $url .= '/' . $query->row['keyword'] . '.html'; unset($data[$key]); } } elseif ($key == 'path') { $categories = explode('_', $value); foreach ($categories as $category) { $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE `query` = 'category_id=" . (int)$category . "' AND store_id = '" . (int)$this->config->get('config_store_id') . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'"); if ($query->num_rows && $query->row['keyword']) { $url .= '/' . $query->row['keyword']; } else { $url = ''; break; } } $url .= '.html'; unset($data[$key]); } elseif( ($data['route'] == 'web/gallery' || $data['route'] == 'web/contact' || $data['route'] == 'web/contact/create' || $data['route'] == 'product/special' || $data['route'] == 'product/newest' || $data['route'] == 'product/bestseller' || $data['route'] == 'product/search' || $data['route'] == 'web/category' || $data['route'] == 'web/store' || $data['route'] == 'common/home' || $data['route'] == 'web/prize' || $data['route'] == 'checkout/cart' || $data['route'] == 'checkout/recurring' || $data['route'] == 'checkout/checkout' || $data['route'] == 'checkout/failure' || $data['route'] == 'checkout/success' || $data['route'] == 'account/account' || $data['route'] == 'account/edit' || $data['route'] == 'account/password' || $data['route'] == 'account/address' || $data['route'] == 'account/wishlist' || $data['route'] == 'account/order' || $data['route'] == 'account/download' || $data['route'] == 'account/reward' || $data['route'] == 'account/return' || $data['route'] == 'account/transaction' || $data['route'] == 'account/recurring' || $data['route'] == 'account/newsletter' || $data['route'] == 'account/logout' || $data['route'] == 'account/login' || $data['route'] == 'account/register' || $data['route'] == 'web/store/search' || $data['route'] == 'web/video_category') and $key == 'route' ) { //my custom $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE `query` = '" . $data['route'] . "'"); if ($query->num_rows && $query->row['keyword']) { // $url .= '/' . $query->row['keyword']; if( $data['route'] == 'web/category' && isset($data['blog_category_id']) || $data['route'] == 'web/video_category' && isset($data['video_category_id']) ) { $url .= '/' . $query->row['keyword']; }else { $url .= '/' . $query->row['keyword'] . '.html'; } } else { $url = ''; break; } } } } if ($url) { unset($data['route']); $query = ''; if ($data) { foreach ($data as $key => $value) { $query .= '&' . rawurlencode((string)$key) . '=' . rawurlencode((is_array($value) ? http_build_query($value) : (string)$value)); } if ($query) { $query = '?' . str_replace('&', '&amp;', 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; } } } |
I sent 1 file seo_url.php after customizing and adding some SEO links. Hope the article will help you.