PHP Classes
elePHPant
Icontem

How to create a simple PHP CMS with SEO friendly links

Recommend this page to a friend!
  Blog PHP Classes blog   RSS 1.0 feed RSS 2.0 feed   Blog How to create a simpl...   Post a comment Post a comment   See comments See comments (15)   Trackbacks (0)  

Author: Ashraf Gheith

Posted on:

Categories: PHP Tutorials

Nowadays search engines like Google value more and more the user experience provided by the sites to rank their site pages well. So, SEO (Search Engine Optimization) often means making the sites more user friendly.

One aspect that contributes to Web site pages being ranked well is to have friendly URLs that allow the user to know what pages are about just looking at the URLs.

Read this tutorial to learn how to create a simple content management system that uses user friendly URLs for the content pages, so you can implement in your own projects without having to use other large CMS systems.




Contents

Introduction

The Concepts Explained

All Roads Lead to Rome: Handling all Requests with One Script

Welcome to index.php

Getting the URL Details

Creating the Links in the Administration Interface

Conclusion


Introduction

Search Engine Optimization (SEO) is very important for any web site today. if you do not optimize your web site no one will find you on search engines. And there is no use for a web site if no one can find it.

This tutorial explains just one aspect of SEO, which is to have pages with friendly URLs. Many years ago we were all mad about phpNuke or Joomla, but they had one nasty problem, the page links looked to ugly: "index.php?id=653&page_type=blog&lang=en". They were very hard to type and they all looked the same. Search engines like Google also do not like these types of URLs.

Then a new generation of CMS came out, some using for instance the CodeIgniter framework, others using Moodle or Drupal. But they also had their own SEO problems. You still can find them on-line today, but a new hero came out very fast after that was Wordpress.

One pitfall any of these CMSs have is that they are made for general purposes sites. For example Wordpress started as a blogging system, now offers plug ins to make any sort of Web sites. But for a system like that you need huge classes, too much code, too much plug-ins and too much maintenance.

Another thing is that you cannot sell GPL licenced code for clients without opening the source code. They will eventually hear somewhere that you made them pay for an open source software, and they may not like that.

Given this, probably the best solution is to make your own CMS, lightweight. You can licence it as ever you want, you know it very well as you wrote it and it is good to have a custom solution with a custom database structure.

The problem we will face is that we will return to the "index.php?id=653&page_type=blog&lang=en" problem. So lets see how to make a simple CMS with SEO friendly URL's.

The Concepts Explained

SEO friendly links must have the page title in it, they need to be readable and they must get rid of index.php. For example "how-to-make-seo-friendly-links" or "making-a-class-for-php-classes-site", as you can see has no index.php in it, there isn't any file name extension what so ever in the end, it is very readable and you can get the title from the link. One important point is that these links must be dynamically generated, as this is the idea of having a CMS.

So let us get back to "index.php?id=653&page_type=blog&lang=en", when we resolve it we have "index.php", it is the main PHP file processing for all requests in the CMS.

We have the ID of the article or the page, we have a page type, in this case "blog", but it can be anything, product, article or blog. Finally we have a language, in this case it is for sites with more than one language, in this tutorial I will skip the language. We need to mimic the same functionality in our "how-to-make-seo-friendly-links" link, but we do not have any element of these up, so this is how we do it:

  1. Redirect all URL requests to one place for processing.
  2. Check a table of links and get the data we need, ID and type.
  3. Depending on the element type we call a suitable plug-in to process and show the data.

All Roads Lead to Rome: Handling all Requests with One Script

If we want all URLs to be handled by one script, let's say index.php, we need then top configure the .htaccess file. We need to have an Apache on our hosting or some other Web server that provides equivalent control of configuration via a .htaccess file. The .htaccess file must enable the URL rewriting module using the RewriteEngine directive. Then just add a rule for all traffic to go to index.php.

RewriteEngine on
RewriteRule !((.php)|(.html)|(.htm)|images\/(.*)|css\/(.*)|js\/(.*))$ index.php [NC]

It is important to set these directives carefully. In the example above I am redirecting all traffic to index.php except someone accesses a PHP or HTML file directly, or if the request path is in folders images, CSS or JS. That is where my images, CSS and JavaScript files usually are.

The use of the [NC] flag causes the RewriteRule to be matched in a case-insensitive manner. That is, it doesn't care whether letters appear as upper-case or lower-case in the matched URI.

Welcome to index.php

Now we got all traffic be handled to index.php, here we need to do a general check: who came here and what he needs. index.php will be like a traffic manager. We need to catch the coming link and query a special table in a MySQL database with all our previously generated links, we get the ID and Type of the page associated with the link URL. Let us see the table structure first.

CREATE TABLE urls (
 id int(9) NOT NULL AUTO_INCREMENT,
 url varchar(255) NOT NULL,
 urltype varchar(255) NOT NULL,
 element int(9) NOT NULL,
 PRIMARY KEY (id),
 KEY url (url),
 KEY urltype (urltype)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

In this case element will reflect the ID of the page and urltype as its type. Now let us see the PHP code:

<?php
 $link = str_replace( "/", "", urldecode( $_SERVER[ "REQUEST_URI" ] ));
 $db_connection = new mysqli( "localhost", "dbuser", "pasword123", "cms");
 $db_connection->query( "SET NAMES 'UTF8'" );
 $statement = $db_connection->prepare( "SELECT type, element FROM urls WHERE url = ?" );
 $statement->bind_param( "s", $link);
 $statement->execute();
 $statement->bind_result($type,$id);
 $statement->fetch();
 $statement->close();
 if($id == 0){
  header('Location: /page-not-found');
 }
 $db_connection->close();
?>

As you can see, first we got the request URL, "urlencoded" it and removed the "/" character. Then we queried the database with a prepared statement to get the type and ID of our element. If the element does not exist we redirect the code to a custom "page not found" page.

Getting the URL Details

After getting the type and ID of our element, it is easy to do the rest. We need to make another query to get the element details depending on the URL type.

So let us suppose we have two types of elements in our CMS: products and blogs. If it is a blog we query the blogs table and require the blog.php file to process the page. If it is a product we query the products table and get its details, then we call products.php to show that template. These are the products and blogs tables:

CREATE TABLE blogs (
 id int(9) NOT NULL AUTO_INCREMENT,
 title varchar(255) NOT NULL,
 blog varchar(255) NOT NULL,
 PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

CREATE TABLE products (
 id int(9) NOT NULL AUTO_INCREMENT,
 product varchar(255) NOT NULL,
 details varchar(255) NOT NULL,
 price float(5,2) NOT NULL,
 PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

And now the PHP code where we will use a switch case statement to determine what to do depending on the page type.

If the element type is "product" we perform a query to the products table and get the product details, then call product_show.php file to show the template for product view.

If the element is a "blog" type, we do the same but on blogs table and then call blog_view.php where you will make your blog template.

<?php
 switch($type){
  case "product":
   $db_connection = new mysqli("localhost", "dbuser", "pasword123", "cms");
   $db_connection->query( "SET NAMES 'UTF8'" );
   $statement = $db_connection->prepare( "SELECT id, product, details, price FROM products WHERE id = ?" );
   $statement->bind_param( "i", $id );
   $statement->execute();
   $statement->bind_result( $productid, $product, $details, $price);
   $statement->fetch();
   $statement->close();
   $db_connection->close();
   require_once "product_view.php";
    break;
  case "blog":
   $db_connection = new mysqli( "localhost", "dbuser", "pasword123", "cms");
   $db_connection->query( "SET NAMES 'UTF8'" );
   $statement = $db_connection->prepare( "SELECT id, title, blog FROM blogs WHERE id = ?");
   $statement->bind_param( "i", $id);
   $statement->execute();
   $statement->bind_result( $blogid, $title, $blog);
   $statement->fetch();
   $statement->close();
   $db_connection->close();
   require_once "blog_view.php";
    break;
 }
?>

Creating the Links in the Administration Interface

We have seen how to redirect URL requests to index.php and show the requested product or blog depending on the URL. But now we need to see how in administration we create these links and fill them in "urls" table.

When adding a blog we will insert all data in the "blogs" table, so we will need to get the insert blog post ID. We will use that ID in our "urls" table. We also need the title to create the link for it. That is where we will use create_link() function.

<?php
 function insert_blog(){
  $db_connection = new mysqli("localhost", "dbuser", "pasword123", "cms");
  $db_connection->query("SET NAMES 'UTF8'");
  $statement = $db_connection->prepare( "INSERT INTO blogs( title, blog) VALUES ( ?, ? )");
  $statement->bind_param("ss", $_POST[ 'title' ], $_POST[ 'blog' ]);
  $statement->execute();
  $statement->close();
  $inserted_id = $statement->insert_id;
  
  // Here we generate an URL from the title
  $url = create_link( $_POST['title'] );
  
  // Here we add a link to our urls table
  $statement = $db_connection->prepare( "INSERT INTO urls( url, urltype, element) VALUES (?, 'blog', ?)");
  $statement->bind_param("si", $url, $inserted_id);
  $statement->execute();
  $statement->close();
  $db_connection->close();
 }
?>

As you can see we always insert the blog first, then get it's inserted ID, and get the title. Then we create a new URL from the title using create_link function so we can insert a new row in urls table. As a urltype we set to 'blog' to distinguish between blogs and products in the urls table. Let us do the same for products:

<?php
 function insert_product(){
  $db_connection = new mysqli( "localhost", "dbuser", "pasword123", "cms");
  $db_connection->query("SET NAMES 'UTF8'");
  $statement = $db_connection->prepare( "INSERT INTO products( product, details, price) VALUES (?, ?, ?)");
  $statement->bind_param("ssd", $_POST[ 'title' ], $_POST[ 'details' ], $_POST[ 'price' ]);
  $statement->execute();
  $statement->close();
  $inserted_id = $statement->insert_id;
  
  // Now we generate an URL from the title
  $url = create_link( $_POST[ 'title' ]);
  
  // Now we add a link to our urls table
  $statement = $db_connection->prepare( "INSERT INTO urls( url, urltype, element) VALUES (?, 'product', ?)");
  $statement->bind_param("si", $url, $inserted_id);
  $statement->execute();
  $statement->close();
  $db_connection->close();
 }
?>

And now lets see how "create_link" function looks like:

<?php
 function create_link($title) {
  $title = strtolower($title);
  $title = str_replace('&','and', $title);
  $title = str_replace(';', '-', $title);
  $title = str_replace(':', '-', $title);
  $title = str_replace(',', '-', $title);
  $title = str_replace('–', '-', $title);
  $title = str_replace('/', '-', $title);
  $title = str_replace(' - ', '-', $title);
  $title = str_replace('"', '-', $title);
  $title = str_replace(' ', '-', $title);
  
  $db_connection = new mysqli( "localhost", "dbuser", "pasword123", "cms";
  $db_connection->query("SET NAMES 'UTF8'");
  $statement = $db_connection->prepare( "SELECT id FROM urls WHERE url = ? limit 1");
  $statement->bind_param("s", $title);
  $statement->execute();
  $statement->bind_result($id);
  $statement->fetch();
  $statement->close();
  $db_connection->close();
  
  if($id == 0) {
   return $title;
  } else {
    create_link( $title. "-". strtolower( generateRandomString(7) ));
  }
 }
 
 function generateRandomString( $length = 10) {
  $characters = '0123456789'. 'abcdefghijkl'. 'mnopqrstuvwxyz'. 'ABCDEFGHIJKL'. 'MNOPQRSTUVWXYZ';
  $randomString = '';
  for ($i = 0; $i < $length; $i++) {
   $randomString .= $characters[ rand(0, strlen( $characters ) - 1)];
  }
  return $randomString;
 }
?>

The "create_link" function gets the title of the blog or the product and makes it as a lower case string. Then it removes any special characters with a series of str_replace lines and replaces any whitespaces with "-" character.

The result is a SEO friendly URL, which we will need to check if it is already used in the URLs table. We do not want any duplicates, all URLs need to be unique in that table. If the URL is found, we return the result to be inserted.

In case we already have that URL, we then use generateRandomString function which generates a random string, and we add it to the end of our generated URL. Then we call "create_link" recursively until we get a unique URL for insertion.

This code is written for the tutorial in a simple way so you can understand the concept. It is not to be taken as something well tested in a real CMS.

Conclusion

Creating user and SEO friendly URLs is not hard. Many existing CMS take care of that for you. But if for some reason you need to implement your own solution with needing a full blown CMS, this article presented the basic steps that you can follow to solve this matter without depending on other software.

If you liked this article or you have any questions, just post a comment to the article here.


You need to be a registered user or login to post a comment

Login Immediately with your account on:

FacebookGmail
HotmailStackOverflow
GitHubYahoo


Comments:

4. Thanks For The Pointer - Lee Davis (2015-08-31 19:06)
Well done for advocating modern code usage... - 4 replies
Read the whole comment and replies

2. Or Use Laravel - Kayla Anderson (2015-08-06 20:45)
Or Use Laravel... - 4 replies
Read the whole comment and replies

1. Further... - Till Wehowski (2015-08-06 19:29)
SQL OT... - 2 replies
Read the whole comment and replies

3. You can charge using GPL - Nick (2015-08-06 18:50)
You can charge using GPL... - 1 reply
Read the whole comment and replies



  Blog PHP Classes blog   RSS 1.0 feed RSS 2.0 feed   Blog How to create a simpl...   Post a comment Post a comment   See comments See comments (15)   Trackbacks (0)