Generate Unique URL Slug in Codeigniter

๐Ÿ‘๏ธ 35 Views
|
๐Ÿ“… Jul 31, 2026
|
โฑ๏ธ 13 min read
Generate Unique URL Slug in Codeigniter

If you have ever visited a well-structured blog or e-commerce site and noticed clean, readable URLs like /post/how-to-build-a-rest-api-in-php instead of something like /post?id=47 โ€” that readable version is called a URL slug. It is better for users, better for SEO, and it is something every serious web application should implement. In this guide we will walk through exactly how to generate unique, SEO-friendly URL slugs dynamically in CodeIgniter using PHP.

What Is a URL Slug and Why Does It Matter?

A URL slug is the human-readable part of a URL that identifies a specific page. It is generated from the title or name of the content โ€” converting spaces to hyphens, stripping special characters, and making everything lowercase. Here is the difference in practice:

  • Generic URL: www.tipsandtricks.dev/post/1
  • SEO-friendly URL: www.tipsandtricks.dev/post/how-to-generate-unique-url-slug-in-codeigniter

The second version tells both the user and search engines exactly what the page is about before they even click it. Google uses the words in your URL as a ranking signal โ€” so a descriptive slug directly contributes to your SEO. It also improves click-through rates because people are more likely to click a URL they can read and trust.

๐Ÿ’ก Want to generate SEO-friendly slugs instantly? Try our free AI-powered Slug Generator tool โ€” it generates 5 optimized slug suggestions from your title in one click: tipsandtricks.dev/tools/slug-generator โ†’

What We Will Build

By the end of this tutorial you will have a working CodeIgniter function that:

  • Converts any post title into a clean, lowercase, hyphenated slug
  • Checks the database to ensure the slug is unique
  • Automatically appends a number (-1, -2) if the slug already exists
  • Stores the slug in the database alongside the post
  • Works for blogs, e-commerce products, portfolios โ€” any content type

Step 1 โ€” Create the Database Table

Your posts table needs at minimum three columns โ€” an auto-increment ID, the post title, and the URL slug. Run this SQL in your database:

CREATE TABLE `posts` (
  `id`       int(11)      NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `title`    varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `url_slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL UNIQUE
);

The UNIQUE constraint on url_slug is important โ€” it enforces uniqueness at the database level as a safety net, even if our PHP logic handles it first. Never rely on application code alone for uniqueness constraints.

Step 2 โ€” Create the Model

Add this to application/models/Post_model.php. It handles inserting data and checking for existing slugs:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Post_model extends CI_Model {

    public function __construct() {
        parent::__construct();
        $this->load->database();
    }

    // Insert a new post into the database
    public function insert_post($data) {
        return $this->db->insert('posts', $data);
    }

    // Check if a slug already exists in the database
    public function slug_exists($slug, $exclude_id = NULL) {
        $this->db->where('url_slug', $slug);
        if ($exclude_id) {
            $this->db->where('id !=', $exclude_id);
        }
        return $this->db->get('posts')->num_rows() > 0;
    }

    // Get all posts (for display)
    public function get_all_posts() {
        return $this->db->get('posts')->result();
    }

    // Get post by slug
    public function get_post_by_slug($slug) {
        return $this->db->where('url_slug', $slug)
                        ->get('posts')
                        ->row();
    }
}

Step 3 โ€” The Slug Generator Function

This is the core function. Add it to your controller or a helper file. It converts a title to a slug and keeps appending a number until it finds a slug that does not already exist in the database:

/**
 * Generate a unique SEO-friendly URL slug
 *
 * @param  string      $string  The title or text to slugify
 * @param  string      $table   The database table to check uniqueness against
 * @param  string      $field   The column name that stores slugs
 * @param  string|null $key     Optional: column name to exclude a record (for updates)
 * @param  mixed|null  $value   Optional: value of the exclusion column (for updates)
 * @return string      The unique slug
 */
function generate_url_slug($string, $table, $field, $key = NULL, $value = NULL) {
    $ci =& get_instance();
    $ci->load->helper('url'); // ensure url helper is loaded

    // Step 1 โ€” convert title to slug format
    $slug = url_title($string);   // converts spaces to hyphens
    $slug = strtolower($slug);    // make everything lowercase
    $slug = preg_replace('/[^a-z0-9\-]/', '', $slug); // remove special chars

    // Step 2 โ€” build the initial WHERE params
    $i      = 0;
    $params = [];
    $params[$field] = $slug;

    // Exclude current record if updating (prevents false conflicts with itself)
    if ($key) {
        $params["$key !="] = $value;
    }

    // Step 3 โ€” keep checking until slug is unique
    while ($ci->db->where($params)->get($table)->num_rows()) {
        if (!preg_match('/-{1}[0-9]+$/', $slug)) {
            // First conflict โ€” append -1
            $slug .= '-' . ++$i;
        } else {
            // Subsequent conflicts โ€” increment the existing number
            $slug = preg_replace('/[0-9]+$/', ++$i, $slug);
        }
        $params[$field] = $slug;
    }

    return $slug;
}

How It Works โ€” Step by Step

  • Title "How to Reverse a String" โ†’ slug: how-to-reverse-a-string
  • If how-to-reverse-a-string already exists in DB โ†’ becomes how-to-reverse-a-string-1
  • If how-to-reverse-a-string-1 also exists โ†’ becomes how-to-reverse-a-string-2
  • And so on until a unique slug is found

Step 4 โ€” The Controller

Add this to application/controllers/Welcome.php:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

    public function __construct() {
        parent::__construct();
        $this->load->helper(['form', 'url']);
        $this->load->model('Post_model');
    }

    // Show the form
    public function index() {
        $data['posts'] = $this->Post_model->get_all_posts();
        $this->load->view('index', $data);
    }

    // Handle form submission โ€” generate slug and save to DB
    public function add() {
        $title = $this->input->post('title');

        // Validate โ€” title is required
        if (empty(trim($title))) {
            redirect('index');
        }

        // Generate unique SEO slug from the title
        $slug = generate_url_slug(
            $title,
            'posts',
            'url_slug'
        );

        // Save to database
        $data = [
            'title'    => $title,
            'url_slug' => $slug,
        ];

        $this->Post_model->insert_post($data);

        redirect('index');
    }

    // Display a post by its slug
    public function view($slug) {
        $post = $this->Post_model->get_post_by_slug($slug);

        if (!$post) {
            show_404();
        }

        $data['post'] = $post;
        $this->load->view('post', $data);
    }

    // Slug generator function (can also be moved to a helper)
    function generate_url_slug($string, $table, $field, $key = NULL, $value = NULL) {
        $ci =& get_instance();
        $slug   = strtolower(url_title($string));
        $slug   = preg_replace('/[^a-z0-9\-]/', '', $slug);
        $i      = 0;
        $params = [$field => $slug];

        if ($key) $params["$key !="] = $value;

        while ($ci->db->where($params)->get($table)->num_rows()) {
            if (!preg_match('/-{1}[0-9]+$/', $slug))
                $slug .= '-' . ++$i;
            else
                $slug = preg_replace('/[0-9]+$/', ++$i, $slug);

            $params[$field] = $slug;
        }

        return $slug;
    }
}

Step 5 โ€” The View (index.php)

Add this to application/views/index.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Generate URL Slug โ€” CodeIgniter</title>
</head>
<body>

<h2>Create a New Post</h2>

<form method="post" action="<?= base_url('index.php/Welcome/add') ?>">
    <label for="title">Post Title:</label><br>
    <input type="text" id="title" name="title"
           placeholder="Enter your post title..." style="width:400px; padding:8px;">
    <button type="submit" style="padding:8px 16px;">Generate Slug & Save</button>
</form>

<hr>

<h3>All Posts</h3>
<ul>
    <?php foreach ($posts as $post): ?>
    <li>
        <strong><?= htmlspecialchars($post->title) ?></strong>
        โ€” Slug: <code><?= $post->url_slug ?></code>
        โ€” <a href="<?= base_url('index.php/Welcome/view/' . $post->url_slug) ?>">View Post</a>
    </li>
    <?php endforeach; ?>
</ul>

</body>
</html>

Step 6 โ€” Configure Routes (Optional but Recommended)

To get clean URLs without index.php in the path, add these routes to application/config/routes.php:

// Map /post/your-slug-here to the view method
$route['post/(:any)'] = 'welcome/view/$1';

// Home page
$route['default_controller'] = 'welcome';
$route['404_override']       = '';

Also make sure your .htaccess file in the project root removes index.php from URLs:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]

Real World Example โ€” Slug Generation Output

// Input titles and generated slugs
"Hello World"                    โ†’ hello-world
"How to Reverse a String"        โ†’ how-to-reverse-a-string
"How to Reverse a String"        โ†’ how-to-reverse-a-string-1  (duplicate)
"How to Reverse a String"        โ†’ how-to-reverse-a-string-2  (third entry)
"CSS Tips & Tricks!"             โ†’ css-tips-tricks
"Laravel 12 โ€” What's New?"       โ†’ laravel-12-whats-new
"100% Free PHP Tutorial"         โ†’ 100-free-php-tutorial

Tip โ€” Move the Function to a Helper for Reuse

If you need this function across multiple controllers, move it to a custom helper file so you only write it once:

// application/helpers/slug_helper.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

if (!function_exists('generate_url_slug')) {
    function generate_url_slug($string, $table, $field, $key = NULL, $value = NULL) {
        $ci =& get_instance();
        $slug   = strtolower(url_title($string));
        $slug   = preg_replace('/[^a-z0-9\-]/', '', $slug);
        $i      = 0;
        $params = [$field => $slug];
        if ($key) $params["$key !="] = $value;
        while ($ci->db->where($params)->get($table)->num_rows()) {
            if (!preg_match('/-{1}[0-9]+$/', $slug))
                $slug .= '-' . ++$i;
            else
                $slug = preg_replace('/[0-9]+$/', ++$i, $slug);
            $params[$field] = $slug;
        }
        return $slug;
    }
}

Load it in your controller or autoload it in config/autoload.php:

// In your controller
$this->load->helper('slug');

// Or in application/config/autoload.php โ€” loads automatically everywhere
$autoload['helper'] = ['url', 'form', 'slug'];

๐Ÿ› ๏ธ Try Our Free AI Slug Generator Tool

Not sure what slug to use for your post? Our free AI-powered Slug Generator tool gives you 5 SEO-optimized slug suggestions from any title โ€” instantly. No signup, no cost, just paste your title and get ready-to-use slugs:

๐Ÿค– AI Slug Generator โ€” Free Tool

Enter any post title and get 5 AI-generated SEO-friendly slug suggestions in one click. Perfect for blogs, portfolios, and e-commerce products.

Try the Slug Generator โ†’

Free ยท No signup ยท 5 AI suggestions per title

Common Mistakes to Avoid

1. Not sanitizing the input string

// โŒ Wrong โ€” special characters can break URLs
$slug = str_replace(' ', '-', $title); // misses ?, &, #, % etc.

// โœ… Correct โ€” use url_title() + regex to strip all non-slug characters
$slug = strtolower(url_title($string));
$slug = preg_replace('/[^a-z0-9\-]/', '', $slug);

2. No uniqueness check

// โŒ Wrong โ€” if two posts have the same title, second one overwrites the first
$slug = strtolower(url_title($title));
$this->db->insert('posts', ['title' => $title, 'url_slug' => $slug]);

// โœ… Correct โ€” always run the uniqueness check function before inserting
$slug = generate_url_slug($title, 'posts', 'url_slug');
$this->db->insert('posts', ['title' => $title, 'url_slug' => $slug]);

3. Not handling updates (editing existing posts)

// โŒ Wrong โ€” generates a new conflicting slug when updating the same post
$slug = generate_url_slug($title, 'posts', 'url_slug');

// โœ… Correct โ€” exclude the current post ID to avoid false conflicts
$slug = generate_url_slug($title, 'posts', 'url_slug', 'id', $post_id);

Final Thought

SEO-friendly URL slugs are a small implementation detail that makes a real difference โ€” in search engine rankings, in click-through rates, and in how professional your application looks and feels. The function we built here handles all the edge cases: special characters, duplicates, numbered suffixes, and update conflicts.

Once you add this to your CodeIgniter project you will never need to think about URL slugs again โ€” every post, product, or page gets a clean, unique, SEO-optimized URL automatically.

And if you want to experiment with slug ideas before writing any code, try our free AI Slug Generator tool โ€” it gives you 5 optimized slug suggestions from any title instantly.

Have questions or running into a specific issue with your CodeIgniter setup? Drop us a message at support@tipsandtricks.dev or use our contact page.

Subscribe to Our Newsletter

Join Our Developer Community!

Unsubscribe Anytime | No Spam