Want to code your own forum?

The other day when I was forced to look after two dogs and with them not letting me get a single wink of sleep it occurred to me that I have never coded a forum before. With American Dad playing in the background I decided to create a very basic forum. Below I will discuss the logic of what I did and this will be a couple of parts but you can find the first version on GitHub – I decided to update it a little bit for this tutorial.

The first thing is we need to decide what a forum is. I came up with the following:

  • Index page that lists each topic.
  • Being able to post a reply to a topic.
  • Create a new topic.
  • Sign up for a user account.
  • Log into the user account.

Sure there are many other things we could do such as allowing the user to change their password, or password resets, moderation etc but for a very basic forum, I decided to ignore all of them after you carry on from this tutorial do make sure you add them but knowing myself I will end up adding these features anyway. (I have included a field in the database for an email in case you wish to use it later).

There will be no formatting when it comes to the HTML or CSS but rather I am showing you the logic of a basic forum so you can work on it further.

Each section will have an introduction of the code with my code comments embedded on this website and at the end of the tutorial I will provide the code so you can play around with it. Below you can find a list of each part!

Table of Contents

Database Layout

For the database, I decided to go with something simple and that is we just need three tables. The three tables are for the replies, topics and then the user. I decided this I wouldn’t record the user’s email address so we only need a username and password but if they forget their details that is on them – besides we ain’t even adding any moderation stuff.

So for the database, this is what I went with.

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    email VARCHAR(255),
    password VARCHAR(255)
);

CREATE TABLE topics (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    body TEXT,
    user_id INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE replies (
    id INT AUTO_INCREMENT PRIMARY KEY,
    body TEXT,
    user_id INT,
    topic_id INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (topic_id) REFERENCES topics(id)
);

Basic PHP templates

This tutorial isn’t going to use any templates but we do want to have some reusable code for the header and footer so we don’t have to keep a copy and paste everything. So far you should have nothing in your folder so what we are going to do is make a file and call it header.php and pop it into a folder called template.

I am going to paste the code below and explain what it does and while I have included a stylesheet there is nothing in mine so feel free to design it as you go!

/template/header.php

<!DOCTYPE html>
<html>
<head>
  <!-- This sets the title of the page, combining the values of two variables, $page_name and $site_name -->
  <title><?php echo $page_name . " - " . $site_name; ?></title>
  <!-- This includes a CSS stylesheet called "style.css" -->
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <!-- The header section of the page -->
  <header>
    <!-- The navigation menu -->
    <nav>
      <!-- An unordered list for the menu items -->
      <ul>
        <!-- A menu item for the homepage -->
        <li><a href="index.php">Home</a></li>
        <!-- A conditional statement to determine what menu items to display based on whether a user is logged in or not -->
        <?php if(isset($_SESSION['user'])) { ?>
          <!-- If a user is logged in, display a greeting message with their username -->
          <li>Hello, <?php echo $_SESSION['user']['username']; ?></li>
          <!-- A menu item for logging out -->
          <li><a href="logout.php">Logout</a></li>
        <?php } else { ?>
          <!-- If a user is not logged in, display menu items for logging in and signing up -->
          <li><a href="login.php">Login</a></li>
          <li><a href="signup.php">Sign Up</a></li>
        <?php } ?>
      </ul>
    </nav>
  </header>

I put HTML comments above to explain the code so make sure to remove them in your actual forum. The logic behind the code is this is a re-usable template that we can use for the header and then two variables that we will declare later called $page_name and $site_name so we can update the title element later.

I then did a navigation tag and an unordered list and then checked to see if the user is logged in or not and show two different menus so they can either log in or sign up. I also show the username to show they are logged in and a future update that you can work on is to list to a profile page so they can change their password and such.

/template/footer.php

I am not going to comment on the next code but all we are doing is making a small footer and ending it.

<hr />
Programmed by YOUR NAME.
</body>
</html>

Config file

Next up we have a config file where we want to store a few details we are going to use later. We are going to record the database login details and declare a variable called $site_name which we can use around the website such as the title.

/config.php

<?php

// Define the database connection parameters
$host = '';
$dbname = '';
$username = '';
$password = '';

// Set the site name
$site_name = "Your Simple Forum";

// Try to connect to the database using PDO
try {
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    
    // Set the error reporting mode to exception
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// If the connection fails, catch the exception and display an error message
} catch (PDOException $e) {
    die("Error connecting to database: " . $e->getMessage());
}
?>

Creating the index

Now we have the config file we are going to make a page that displays all of the topics. We are not going to use categories so we are only going to list each topic in the topic database. You might in future decide to update it so it shows you the username of the person that created the topic – right now it just shows the user_id.

/index.php

<?php

// Include the config.php file to access the database connection variables
require_once 'config.php';

// Start a session to maintain user information
session_start();

// Try to connect to the database
try {
  // Create a new PDO object using the database connection details
  $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
  // Set the error mode to throw exceptions
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  // Prepare a SELECT statement to retrieve all topics from the "topics" table
  $stmt = $pdo->prepare("SELECT * FROM topics ORDER BY created_at DESC");
  // Execute the SELECT statement
  $stmt->execute();
  // Fetch the result as an array
  $topics = $stmt->fetchAll();

} catch(PDOException $e) {
  // If an error occurs, display the error message
  echo "Error: " . $e->getMessage();
}

// Set the page name to "Forum"
$page_name = "Forum";
// Include the header template
include("template/header.php")
?>
<main>
    <h2>Topics</h2>
    <table>
      <thead>
        <tr>
          <th>Title</th>
          <th>Created By</th>
          <th>Date</th>
        </tr>
      </thead>
      <tbody>
        <?php 
        // Loop through the topics array and display the title, creator and creation date for each topic
        foreach($topics as $topic) { 
        ?>
          <tr>
            <!-- Create a link to the topic detail page using the topic's id -->
            <td><a href="view_topic.php?id=<?php echo $topic['id']; ?>"><?php echo $topic['title']; ?></a></td>
            <td>
            <?php 
            $user_id = $topic['user_id'];
            $user_stmt = $pdo->prepare("SELECT username FROM users WHERE id = :id");
            $user_stmt->execute([
                'id' => $user_id
            ]);
            $user = $user_stmt->fetch();
            echo $user['username']; 
            ?>
          </td>
            <td><?php echo $topic['created_at']; ?></td>
          </tr>
        <?php } ?>
      </tbody>
    </table>
    <?php 
    // If the user is logged in, display a link to create a new topic
    if(isset($_SESSION['user'])) { 
    ?>
      <a href="create_topic.php">Create a new topic</a>
    <?php } ?>
  </main>
<?php 
// Include the footer template
include("template/footer.php") 
?>

Creating the topic page

Phew, so far this isn’t going so bad. Right now we should have the following.

/template/header.php
/template/footer.php
/config.php
/index.php

Next, we have to make a page where we can see each topic and what it contains. Like the other files we are going to pass on a variable for the title but we also want to display the topic content and all the replies. We are going to do this by a loop in the replies table using an ID we got from $_GET[‘id’].

/view_topic.php

<?php
// Start a session
session_start();

// Include the config file to access database details
include 'config.php';

// Get the topic ID from the URL parameter
$topic_id = $_GET['id'];

try {
    // Prepare a query to select the topic details
    $stmt = $pdo->prepare("SELECT * FROM topics WHERE id = :topic_id");

    // Execute the query with the topic ID parameter
    $stmt->execute([
        'topic_id' => $topic_id
    ]);

    // Fetch the topic details
    $topic = $stmt->fetch();
} catch (PDOException $e) {
    // If an error occurs, display an error message and exit the script
    echo "Error loading topic: " . $e->getMessage();
    exit;
}

try {
    // Prepare a query to select the replies for the topic
    $stmt = $pdo->prepare("SELECT * FROM replies WHERE topic_id = :topic_id");

    // Execute the query with the topic ID parameter
    $stmt->execute([
        'topic_id' => $topic_id
    ]);

    // Fetch all the replies for the topic
    $replies = $stmt->fetchAll();
} catch (PDOException $e) {
    // If an error occurs, display an error message and exit the script
    echo "Error loading replies: " . $e->getMessage();
    exit;
}

// Set the page name to be the title of the topic
$page_name = $topic['title'];

// Include the header template
include("template/header.php");
?>

<!-- Display the topic title -->
<h1><?php echo $topic['title']; ?></h1>

<!-- Display the topic body -->
<p>
    <?php echo $topic['body']; ?>
</p>
<hr>

<!-- Display a header for the replies section -->
<h2>Replies</h2>

<!-- Loop through the replies -->
<?php foreach ($replies as $reply) { 
    try {
        // Prepare a query to select the user details for each reply
        $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :user_id");

        // Execute the query with the user ID parameter
        $stmt->execute([
            'user_id' => $reply['user_id']
        ]);

        // Fetch the user details for each reply
        $user = $stmt->fetch();
    } catch (PDOException $e) {
        // If an error occurs, display an error message and exit the script
        echo "Error loading user: " . $e->getMessage();
        exit;
    }
?>
    <!-- Display the username, creation date, and body of the reply -->
    <p>
        <strong><?php echo $user['username']; ?></strong> posted on <?php echo $reply['created_at']; ?><br>
        <?php echo $reply['body']; ?>
    </p>
    <hr>
<?php } ?>

<!-- Check if the user is logged in -->
<?php if (isset($_SESSION['user'])) { ?>

    <h2>Post a Reply as <?php echo $_SESSION['user']['username']; ?>.</h2>
<form action="post_reply.php" method="post">
    <input type="hidden" name="topic_id" value="<?php echo $topic_id; ?>">
    <p>
        <textarea name="body" rows="5" cols="80"></textarea>
    </p>
    <p>
        <input type="submit" name="submit" value="Submit">
    </p>
</form>

<?php } else { ?>

    <p>
    Please <a href="login.php">login</a> to post a reply.
</p>

<?php } 

include("template/footer.php")
?>

Creating the new topic

Finally, we are nearly there. We have created all of the files now that display information and I am assuming you have been putting dummy data into your database as you have been going along if so you should see the forum works but you can’t post anything.

So for the new topic, all we do is take the $_POST data from the page we are about to make, submit it to itself and then update the database.

/create_topic.php

<?php

require_once 'config.php';

// Start a session
session_start();

// Check if the user is logged in
if (!isset($_SESSION['user'])) {
    // If the user is not logged in, redirect them to the login page
    header('Location: login.php');
    exit;
}

try {
    // Connect to the database using the provided parameters
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Check if the form was submitted
    if (isset($_POST['submit'])) {
        // Get the values from the form
        $title = $_POST['title'];
        $body = $_POST['body'];
        $user_id = $_SESSION['user']['id'];

        // Prepare an SQL statement to insert the topic into the database
        $stmt = $pdo->prepare("INSERT INTO topics (title, body, created_at, user_id) VALUES (:title, :body, NOW(), :user_id)");

        // Bind the parameters to the statement
        $stmt->bindParam(':title', $title);
        $stmt->bindParam(':body', $body);
        $stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);

        // Execute the statement
        $stmt->execute();

        // Redirect the user to the index page
        header('Location: index.php');
        exit;
    }
} catch (PDOException $e) {
    // If an error occurs, display an error message
    echo "Error: " . $e->getMessage();
}

// Set the name of the page
$page_name = "Create new topic";

// Include the header template
include("template/header.php");

?>
<main>
    <h2>Create a new topic as <?php echo $_SESSION['user']['username']; ?>.</h2>
    <form action="" method="post">
        <div>
            <label for="title">Title</label>
            <input type="text" name="title" id="title">
        </div>
        <div>
            <label for="body">Body</label>
            <textarea name="body" id="body"></textarea>
        </div>
        <div>
            <input type="submit" name="submit" value="Submit">
        </div>
    </form>
</main>
<?php 

// Include the footer template
include("template/footer.php"); 

?>

Creating the reply page

We are now about done and that is to wrap up being able to reply. You may notice earlier we referenced post_reply.php and now we are going to make it. All this file does is take the stuff in $_POST and update the database and then redirect the user to the topic so they can see what they posted.

/post_reply.php

<?php
session_start();
include 'config.php';

// Start a session
session_start();

// Include the database configuration file
include 'config.php';

// Try to connect to the database
try {
    // Create a new PDO instance with the database connection details
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);

    // Set the error mode of PDO to throw exceptions
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Check if the submit button was pressed
    if (isset($_POST['submit'])) {
        // Get the values from the form
        $body = $_POST['body'];
        $topic_id = $_POST['topic_id'];
        $user_id = $_SESSION['user']['id'];
        
        // Prepare an SQL statement to insert the reply into the database
        $stmt = $pdo->prepare("INSERT INTO replies (body, created_at, user_id, topic_id) VALUES (:body, NOW(), :user_id, :topic_id)");
        
        // Bind the values to the SQL statement
        $stmt->bindParam(':body', $body);
        $stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
        $stmt->bindParam(':topic_id', $topic_id, PDO::PARAM_INT);
        
        // Execute the statement
        $stmt->execute();
        
        // Redirect to the topic page after inserting the reply
        header('Location: view_topic.php?id=' . $topic_id);
        exit;
    }

} catch (PDOException $e) {
    // If an error occurs, display an error message
    echo "Error: " . $e->getMessage();
}
?>

Creating the signup & login page

We have now finished the core of a forum. We can see every topic posted on the site, we can post a reply to each topic which to me is what a forum is about. What we need to do now is create a signup page and allow people to log in.

/signup.php

<?php
include 'config.php';

// Check if the form has been submitted
if (isset($_POST['username']) && isset($_POST['password'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // Try to run the query and handle any exceptions
    try {
        // Check if the username already exists
        $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
        $stmt->execute([
            'username' => $username
        ]);
        $existingUser = $stmt->fetch();

        // If the username already exists, set an error message
        if ($existingUser) {
            $error = "Username already exists.";
        } 
        // If the username is available, insert a new user into the database
        else {
            $stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (:username, :password)");
            $stmt->execute([
                'username' => $username,
                'password' => password_hash($password, PASSWORD_DEFAULT)
            ]);

            // Redirect the user to the login page
            header('Location: login.php');
            exit;
        }
    } catch (PDOException $e) {
        // If an error occurs, set an error message
        $error = "Error signing up: " . $e->getMessage();
    }
}

$page_name = "Sign Up";
include("template/header.php")

?>
<!-- Display the sign up form -->
<h1>Sign Up</h1>
<hr>
<!-- Display any error messages -->
<?php if (isset($error)) { ?>
    <p style="color: red;"><?php echo $error; ?></p>
    <?php } ?>
<!-- Sign up form -->
<form action="" method="post">
    <p>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username">
    </p>
    <p>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password">
    </p>
    <p>
        <input type="submit" value="Submit">
    </p>
</form>
<!-- Include the footer -->
<?php include("template/footer.php"); ?>

Now let us make our final two files – the login. All we are doing is taking $_POST and updating the database. For our logout, we just wipe $_SESSION. I don’t think you need it commented on anymore!

/login.php

<?php
session_start();
include 'config.php';

if (isset($_POST['username']) && isset($_POST['password'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    try {
        $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
        $stmt->execute([
            'username' => $username
        ]);
        $user = $stmt->fetch();

        if ($user && password_verify($password, $user['password'])) {
            $_SESSION['user'] = $user;
            $_SESSION['user_id'] = $user['id'];
            header('Location: index.php');
            exit;
        } else {
            $error = "Incorrect username or password.";
        }
    } catch (PDOException $e) {
        $error = "Error logging in: " . $e->getMessage();
    }
}

$page_name = "Login";
include("template/header.php")
?>

<h1>Login</h1>
    <hr>
    <?php if (isset($error)) { ?>
        <p style="color: red;"><?php echo $error; ?></p>
    <?php } ?>
    <form action="" method="post">
        <p>
            <label for="username">Username:</label>
            <input type="text" name="username" id="username">
        </p>
        <p>
            <label for="password">Password:</label>
            <input type="password" name="password" id="password">
        </p>
        <p>
            <input type="submit" value="Submit">
        </p>
    </form>
    <p>
        Don't have an account? <a href="signup.php">Sign up here</a>.
    </p>
<?php include("template/footer.php") ?>

/logout.php

<?php

session_start();

// Unset all of the session variables
$_SESSION = array();

// Destroy the session.
session_destroy();

// Redirect to the index page
header("location: index.php");
exit;

?>

End statement

There we go. You have just learned how to make a basic forum. There is so much you could update and work further on and if you do please leave a comment I would love to see how you go along with it. It is worth saying this isn’t the best way to do so but I just wanted to have some fun programming in the early hours!

Do not use this on a production website. There are a few security issues that you need to patch – this is a tutorial on the LOGIC of making a forum.

You can download it here: v0.02 forum tutorial.

Need to reference?

Ellis, M. (2023). Want to code your own forum?. [online] Snat's Narratives & Tales. Available at: https://snat.co.uk/stem/scripts/want-to-code-your-forum.html [Accessed 27 Apr 2024].

Thanks for reading! You may be interested in this …

Join the Discussion

Learn how to create a basic forum with an index page, post replies, create new topics, and user authentication in this tutorial.

2 thoughts on “Want to code your own forum?”

  1. I may have gone a little bit overboard now. On the GitHub link you can now find a newer version that has actual formatting, gravatar support and basic features such as changing passwords, forgot password etc.

    Going to soon make an admin panel to be able to delete topics, users and some other basic moderation stuff.

    Reply

Add to the discussion!

This site uses Akismet to reduce spam. Learn how your comment data is processed.