Jump to content

PHP: Create SEO-friendly URL titles (slugs)

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
13 replies to this topic

#1
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,124 posts
Intro:
Last time I had talked about using .htaccess to make automatic seo-friendly URLs here: http://forum.codecal...access-php.html. In my tutorial requests (at my subdomain) somebody requested that I write about URL slugs, or rather generating an URL-friendly name out of a title a user may have entered.

This is a complementary tutorial to the linked post as that uses a custom mod_rewrite and this implements a friendly GET request (the two can be used together or apart), so I will link the two together for convenience.

Start:
To make an URL out of a small title the user submits can be hard, imagine the case where they submit a piece of code to a site. The site may throw back an automatic ID, and the submission result is this:
ID: 24141
Title: How to get INT_MAX
Desc: This is how to get it when it is not known
Fixed URL: snippet.php?id=24141
This URL ID is bad because of two things.

  • The user can not distinguish the page by number alone
  • Search engines do not know what the ID number means
Instead of that we can make the title into a slug, or SEO friendly title.

/** 
  * Return URL-Friendly string slug
  * @param string $string 
  * @return string 
  */

function seoUrl($string) {
    //Unwanted:  {UPPERCASE} ; / ? : @ & = + $ , . ! ~ * ' ( )
    $string = strtolower($string);
    //Strip any unwanted characters
    $string = preg_replace("/[^a-z0-9_\s-]/", "", $string);
    //Clean multiple dashes or whitespaces
    $string = preg_replace("/[\s-]+/", " ", $string);
    //Convert whitespaces and underscore to dash
    $string = preg_replace("/[\s_]/", "-", $string);
    return $string;
}
Tests:
These unfriendly titles:
?id=get int_max
?id=return INT_MAX
?id=This does a lot of things.
?id='#define test - $define (here), + @three_functions test!
?id=Already --d-ashes in here-here!
Become these URL-friendly titles:
?id=get-int-max
?id=return-int-max
?id=this-does-a-lot-of-things
?id=define-test-define-here-three-functions-test
?id=already-dashes-in-here-here
You can store these in a simple `idname` VARCHAR(50) column along with the numeric ID. You can retrieve your SQL results the same as using your numeric ID, except we use the `idname` column. Simply:
$res = 
    mysql_query("SELECT foobar FROM `tbl_posts` WHERE `idname` = '" . mysql_real_escape_string($_GET['id']) . "'";
Hints:
You will see things like vBSEO or Wordpress append a random number at the end. This can be the numeric ID or a random number. Personally the numeric ID is a good choice, as this means no two posts will have the same name accidentally, therefor 'introduction-to-foo2014' and 'introduction-to-foo2-12495' will be different and distinguishable. Feel free to experiment.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#2
DEViANT

DEViANT

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 358 posts
Ah, nice tutorial. Reminds me of a little function I wrote a while back to slugify hyperlinks as well. It's a bit shorter, but I thik it gives the same end result... More or less.

function slugify($text)
{
  // replace all non letters or digits with -
  $text = preg_replace('/\W+/', '-', $text);

  // trim and lowercase
  $text = strtolower(trim($text, '-'));
  return $text;
}

:D You should rep+ me so that I can win :D

My Blog | Ask me!
Error : Satan did it

#3
John

John

    Writes binary right handed and hex left handed

  • Moderators
  • 6,321 posts
Very nice!

#4
CyberCoder

CyberCoder

    Newbie

  • Members
  • Pip
  • 1 posts

John said:

Very nice!

Excellent little function!

#5
slownage

slownage

    Newbie

  • Members
  • Pip
  • 4 posts
F1 :crying: please!

I tried implementing the code but i bumped into some errors,(the database and the rest are properly setup-ed) actually when i load the page(which values are in the database)
- web.com/post.php?id=some-thing - it showed just the template, nothing dynamic, not even errors

<?php

$mysql = new MySQLi('*', '*', '*', '*') or die('There was a problem connecting to the database');

$stmt = $mysql->prepare("SELECT id, date, title, category, image, body FROM posts WHERE url_slug = '" . mysql_real_escape_string($_GET['id']) . "'") or die('Problem preparing query');

$stmt->execute();

$stmt->bind_result($id, $date, $title, $category, $image, $body );


while($row = $stmt->fetch()) :

?>

                    <?php echo $category; ?>

                    <?php echo $title; ?>

                    <?php echo $date;?>

                    <img src="<?php echo $image; ?>

            	    <?php echo $body; ?>

               <?php endwhile; ?>

if you have any solutions please don't hesitate to contact me: silviu_q12(at)yahoo.com
BTW: i used url_slug instead of idname;

#6
slownage

slownage

    Newbie

  • Members
  • Pip
  • 4 posts
"SELECT url_slug FROM posts ORDER BY rand() LIMIT 2"

i did something like this /\ and it worked, it displayed 2 of the url_slug

but which is good?
"SELECT id, date, title, category, image, body FROM posts WHERE url_slug = '" . mysql_real_escape_string($_GET['id']) . "'"

or
"SELECT id, url_slug, date, title, category, image, body FROM posts WHERE url_slug = '" . mysql_real_escape_string($_GET['id']) . "'"

and thank you for the reply!
--------------------------------------------------------
But the thing is that on the home page(index.php) i have this link:
<a class="posttitle" href="post.php?id=<?php echo $url_slug;?>"><?php echo $title; ?></a>
which goes to /post.php?id=some-thing, but there it displays nothing but the template ....

#7
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,124 posts
Hi slownage, the first one seems correct.

If you can try debugging the script, such as checking $stmt->num_rows [1] after execution to see if there is anything to return after the $stmt->execute(). If no rows are returned then your id does not match what is actually in the database.

[1]: PHP: mysqli_stmt::num_rows - Manual
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#8
slownage

slownage

    Newbie

  • Members
  • Pip
  • 4 posts
it outputs / after i did what you suggested / : Number of rows: 0.

what should i do? :c-blink:

but when i do so:
													

$id = (int)$_GET['id'];

$mysql = new MySQLi('*', '*', '*', '*') or die('There was a problem connecting to the database');

$stmt = $mysql->prepare("SELECT id, date, title, category, image, body FROM posts WHERE id = $id") or die('Problem preparing query');

it works, but of course with the ugly link: /post.php?id=1

#9
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,124 posts
Can you dump your rows and show me the example contents?
SELECT url_slug FROM posts

Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#10
slownage

slownage

    Newbie

  • Members
  • Pip
  • 4 posts

Alexander said:

Can you dump your rows and show me the example contents?
SELECT url_slug FROM posts

it will output: the-title-of-the-first-post the-title-of-the-second-post
//the output is good!

#11
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,124 posts
Hi, try to debug your application

For example, /post.php?id=some-thing will populate $_GET['id'] with "some-thing", therefore some-thing must be in the url_slug column and also be exactly "some-thing" for your SQL query to correctly before.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#12
JonBeluga

JonBeluga

    Newbie

  • Members
  • Pip
  • 2 posts
would it be out of the question for someone to provide an sql dump and sample files so this could all be dropped into a test environment?