Great question, Kahnu! You're saying:

You have a single PHP file (like category.php) that displays all categories and their nested children, and you want to manage clean URLs like:

example.com/category/parent-category/child-category/grandchild-category/

So now, you need to handle this with .htaccess routing, and have PHP parse the category slugs dynamically from the URL.


✅ Step-by-Step Setup

🔁 1. Folder Structure Assumption

Your app is in:

/var/www/html/
├── .htaccess
├── category.php
└── other files...

🧾 2. Sample URLs to Support

  • example.com/category/parent/

  • example.com/category/parent/child/

  • example.com/category/parent/child/grandchild/

All handled by category.php.


⚙️ 3. .htaccess Rules

RewriteEngine On
RewriteBase /

# Redirect category slug path to category.php
RewriteRule ^category/(.*)$ category.php?path=$1 [L,QSA]
  • This will pass the entire slug path as $_GET['path']

  • Example:

    • URL: /category/parent/child/grandchild

    • PHP gets: $_GET['path'] = "parent/child/grandchild"


🧑‍💻 4. In category.php, Parse the Path

<?php
// Get the slug path
$path = isset($_GET['path']) ? $_GET['path'] : '';

// Split the slugs into an array
$slugs = array_filter(explode('/', $path));

// Example: ['parent', 'child', 'grandchild']

// You can now loop through these slugs to fetch each category level from your DB
$parent_id = 0;
$category = null;

foreach ($slugs as $slug) {
    $stmt = $db->prepare("SELECT * FROM categories WHERE slug = ? AND parent_id = ?");
    $stmt->execute([$slug, $parent_id]);
    $category = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$category) {
        echo "Category not found!";
        exit;
    }

    $parent_id = $category['id']; // Update for next level
}

// Now $category holds the last matched category
echo "<h1>{$category['name']}</h1>";

// You can also list its child categories or posts now
?>

📌 Example Use Case

URL:

example.com/category/electronics/mobile/smartphones
  • Parsed as:
    ['electronics', 'mobile', 'smartphones']

  • Final $category will be smartphones, and its parent chain can be fetched for breadcrumbs.


✅ Summary

Feature Description
.htaccess Pass nested slugs to one PHP file
category.php Split path, recursively find categories
URL-friendly Clean, SEO-friendly structure
Dynamic Works for any depth of nesting

Would you like a full example database structure with slugs + a working breadcrumb loop for this too?



Absolutely, Kahnu! Here's a complete working version of the same breadcrumb system using MySQLi instead of PDO.


✅ Full Working Example using mysqli


🧩 Database Table: categories

CREATE TABLE categories (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  slug VARCHAR(255) NOT NULL,
  parent_id INT DEFAULT 0
);

🧪 Sample Data:

INSERT INTO categories (name, slug, parent_id) VALUES
('Electronics', 'electronics', 0),
('Mobile', 'mobile', 1),
('Smartphones', 'smartphones', 2),
('Feature Phones', 'feature-phones', 2),
('Accessories', 'accessories', 1);

⚙️ .htaccess Rewrite Rules

RewriteEngine On
RewriteBase /

# Route all /category/ paths to category.php
RewriteRule ^category/(.*)$ category.php?path=$1 [L,QSA]

🧑‍💻 category.php using mysqli

<?php
// Database connection
$mysqli = new mysqli("localhost", "your_user", "your_pass", "your_db");
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

// Get slug path from URL
$path = isset($_GET['path']) ? $_GET['path'] : '';
$slugs = array_filter(explode('/', $path));

$breadcrumbs = [];
$parent_id = 0;
$current_category = null;

foreach ($slugs as $slug) {
    $stmt = $mysqli->prepare("SELECT * FROM categories WHERE slug = ? AND parent_id = ?");
    $stmt->bind_param("si", $slug, $parent_id);
    $stmt->execute();
    $result = $stmt->get_result();
    
    if ($result->num_rows == 0) {
        http_response_code(404);
        echo "Category not found.";
        exit;
    }
    
    $category = $result->fetch_assoc();
    $breadcrumbs[] = $category;
    $parent_id = $category['id'];
    $current_category = $category;
    $stmt->close();
}

// Output breadcrumbs
echo '<nav>';
echo '<a href="/">Home</a> &gt; ';

foreach ($breadcrumbs as $i => $cat) {
    $url = '/category/' . implode('/', array_slice($slugs, 0, $i + 1));
    if ($i < count($breadcrumbs) - 1) {
        echo "<a href=\"$url\">" . htmlspecialchars($cat['name']) . "</a> &gt; ";
    } else {
        echo '<span>' . htmlspecialchars($cat['name']) . '</span>';
    }
}
echo '</nav>';

// Output current category title
echo "<h1>" . htmlspecialchars($current_category['name']) . "</h1>";

// Optional: Show child categories
$stmt = $mysqli->prepare("SELECT * FROM categories WHERE parent_id = ?");
$stmt->bind_param("i", $current_category['id']);
$stmt->execute();
$children = $stmt->get_result();

if ($children->num_rows > 0) {
    echo "<ul>";
    while ($child = $children->fetch_assoc()) {
        $child_path = $path . '/' . $child['slug'];
        echo "<li><a href=\"/category/$child_path\">" . htmlspecialchars($child['name']) . "</a></li>";
    }
    echo "</ul>";
}

$stmt->close();
$mysqli->close();
?>

✅ Example URLs

URL Path Output Breadcrumb
/category/electronics Home > Electronics
/category/electronics/mobile Home > Electronics > Mobile
/category/electronics/mobile/smartphones Home > Electronics > Mobile > Smartphones

Would you like me to also generate a zip or GitHub-ready structure with this setup?


Yes, Kahnu — it's definitely possible and a great idea ✅

You can write a reusable function that takes either:

  • A category ID, or

  • A category slug

…and returns the full breadcrumb trail, climbing up the parent_id chain recursively or in a loop.


✅ PHP Function using MySQLi (Pass category id or slug)

function get_category_breadcrumbs($mysqli, $category_input, $type = 'id') {
    $breadcrumbs = [];

    // 1. Fetch the starting category
    if ($type === 'id') {
        $stmt = $mysqli->prepare("SELECT * FROM categories WHERE id = ?");
        $stmt->bind_param("i", $category_input);
    } else {
        $stmt = $mysqli->prepare("SELECT * FROM categories WHERE slug = ?");
        $stmt->bind_param("s", $category_input);
    }

    $stmt->execute();
    $result = $stmt->get_result();
    $category = $result->fetch_assoc();
    $stmt->close();

    if (!$category) {
        return false; // not found
    }

    // 2. Climb up the parent tree
    while ($category) {
        array_unshift($breadcrumbs, $category); // add to beginning
        if ($category['parent_id'] == 0) break;

        $stmt = $mysqli->prepare("SELECT * FROM categories WHERE id = ?");
        $stmt->bind_param("i", $category['parent_id']);
        $stmt->execute();
        $result = $stmt->get_result();
        $category = $result->fetch_assoc();
        $stmt->close();
    }

    return $breadcrumbs;
}

✅ Example Usage:

$mysqli = new mysqli("localhost", "your_user", "your_pass", "your_db");

// By ID
$breadcrumbs = get_category_breadcrumbs($mysqli, 3); // ID of "Smartphones"

// Or by Slug
// $breadcrumbs = get_category_breadcrumbs($mysqli, 'smartphones', 'slug');

if ($breadcrumbs) {
    echo '<nav>';
    echo '<a href="/">Home</a> &gt; ';
    foreach ($breadcrumbs as $i => $cat) {
        $slug_parts = array_column(array_slice($breadcrumbs, 0, $i + 1), 'slug');
        $path = '/category/' . implode('/', $slug_parts);

        if ($i < count($breadcrumbs) - 1) {
            echo "<a href=\"$path\">" . htmlspecialchars($cat['name']) . "</a> &gt; ";
        } else {
            echo '<span>' . htmlspecialchars($cat['name']) . '</span>';
        }
    }
    echo '</nav>';
} else {
    echo "Category not found.";
}

✅ Output Example (for ID 3 → smartphones)

Home > Electronics > Mobile > Smartphones

✅ Summary

Feature Description
Flexible input Accepts category id or slug
Clean output Returns full hierarchy as array
SEO-friendly Works with /category/slug/... format
Reusable Can be used anywhere (post page, archive…)

Would you like a similar function for posts, where it fetches the category → full breadcrumb trail from that?