Practical Projects in PHP + SQL
Mini CMS / Blog (Users, Posts, Comments)
A mini CMS or a simple blog is an excellent project to practice PHP & MySQL. Typically, such a project involves the following tables:
users- stores information about users (id, name, email, hashed password);posts- stores articles published by users (id, user_id, title, content, created_at);comments- stores comments on posts (id, post_id, user_id, content, created_at).
Database Structure
Example of creating tables in phpMyAdmin or with a PHP script:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(100) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE comments (
id INT AUTO_INCREMENT PRIMARY KEY,
post_id INT NOT NULL,
user_id INT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
Main Features
- Create, edit, and delete posts by users;
- Add and delete comments on posts;
- User authentication and registration with password hashing and sessions;
- Display list of posts, comments, and author information;
- Like / dislike for each post.
PHP Code Example - Display Posts and Comments
<?php
try {
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT posts.id AS post_id, posts.title, posts.content, posts.created_at,
users.name AS author_name, users.email AS author_email
FROM posts
INNER JOIN users ON posts.user_id = users.id
ORDER BY posts.created_at DESC";
$stmt = $pdo->query($sql);
while ($post = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "<h4>" . htmlspecialchars($post['title']) . "</h4>";
echo "<p>" . htmlspecialchars($post['content']) . "</p>";
echo "<small>Written by: " . htmlspecialchars($post['author_name']) . " (" . htmlspecialchars($post['author_email']) . ") on " . $post['created_at'] . "</small><br>";
// display comments
$sql_comments = "SELECT comments.content, comments.created_at, users.name AS commenter
FROM comments
INNER JOIN users ON comments.user_id = users.id
WHERE comments.post_id = :post_id
ORDER BY comments.created_at ASC";
$stmt_comments = $pdo->prepare($sql_comments);
$stmt_comments->execute(['post_id' => $post['post_id']]);
while ($comment = $stmt_comments->fetch(PDO::FETCH_ASSOC)) {
echo "<div style='margin-left:20px'>";
echo "<p>" . htmlspecialchars($comment['commenter']) . ": " . htmlspecialchars($comment['content']) . "</p>";
echo "<small>" . $comment['created_at'] . "</small></div>";
}
echo "<hr>";
}
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
<?php
try {
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "traffice_php", "!@y3Ge-Z!WQI");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT posts.id AS post_id, posts.title, posts.content, posts.created_at,
users.name AS author_name, users.email AS author_email
FROM posts
INNER JOIN users ON posts.user_id = users.id
ORDER BY posts.created_at DESC";
$stmt = $pdo->query($sql);
while ($post = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "<h4>" . htmlspecialchars($post['title']) . "</h4>";
echo "<p>" . htmlspecialchars($post['content']) . "</p>";
echo "<small>Scris de: " . htmlspecialchars($post['author_name']) . " (" . htmlspecialchars($post['author_email']) . ") la " . $post['created_at'] . "</small><br>";
$sql_comments = "SELECT comments.content, comments.created_at, users.name AS commenter
FROM comments
INNER JOIN users ON comments.user_id = users.id
WHERE comments.post_id = :post_id
ORDER BY comments.created_at ASC";
$stmt_comments = $pdo->prepare($sql_comments);
$stmt_comments->execute(['post_id' => $post['post_id']]);
while ($comment = $stmt_comments->fetch(PDO::FETCH_ASSOC)) {
echo "<div style='margin-left:20px'>";
echo "<p>" . htmlspecialchars($comment['commenter']) . ": " . htmlspecialchars($comment['content']) . "</p>";
echo "<small>" . $comment['created_at'] . "</small></div>";
}
echo "<hr>";
}
} catch (PDOException $e) {
echo "Eroare: " . $e->getMessage();
}
?>
To-Do List with DB
A To-Do List project is ideal for practicing CRUD operations and sessions in PHP. Each user can have their own task list, which is stored in a MySQL database.
Database Structure
Example table for tasks:
CREATE TABLE todos (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
task VARCHAR(255) NOT NULL,
is_done TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
PHP Example - CRUD for To-Do List
<?php
session_start();
try {
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$user_id = 1; // Example, current user
// Add task
if(isset($_POST['add_task'])) {
$task = $_POST['task'];
$stmt = $pdo->prepare("INSERT INTO todos (user_id, task) VALUES (:user_id, :task)");
$stmt->execute(['user_id' => $user_id, 'task' => $task]);
}
// Delete task
if(isset($_GET['delete_id'])) {
$stmt = $pdo->prepare("DELETE FROM todos WHERE id = :id AND user_id = :user_id");
$stmt->execute(['id' => $_GET['delete_id'], 'user_id' => $user_id]);
}
// Mark as done
if(isset($_GET['toggle_id'])) {
$stmt = $pdo->prepare("UPDATE todos SET is_done = 1 - is_done WHERE id = :id AND user_id = :user_id");
$stmt->execute(['id' => $_GET['toggle_id'], 'user_id' => $user_id]);
}
// Retrieve all tasks
$stmt = $pdo->prepare("SELECT * FROM todos WHERE user_id = :user_id ORDER BY created_at DESC");
$stmt->execute(['user_id' => $user_id]);
$todos = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
<h3>My Task List</h3>
<form method="post">
<input type="text" name="task" placeholder="Add a task" required>
<button type="submit" name="add_task">Add</button>
</form>
<ul>
<?php foreach($todos as $todo): ?>
<li>
<?= htmlspecialchars($todo['task']) ?>
<a href="?toggle_id=<?= $todo['id'] ?>">[Toggle]</a>
<a href="?delete_id=<?= $todo['id'] ?>">[Delete]</a>
<?php if($todo['is_done']): ?>โ<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php
session_start();
try {
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "traffice_php", "!@y3Ge-Z!WQI");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$user_id = 35; // Example: current user
// Add task
if(isset($_POST['add_task'])) {
$task = $_POST['task'];
$stmt = $pdo->prepare("INSERT INTO todos (user_id, task) VALUES (:user_id, :task)");
$stmt->execute(['user_id' => $user_id, 'task' => $task]);
}
// Delete task
if(isset($_GET['delete_id'])) {
$stmt = $pdo->prepare("DELETE FROM todos WHERE id = :id AND user_id = :user_id");
$stmt->execute(['id' => $_GET['delete_id'], 'user_id' => $user_id]);
}
// Toggle task completion
if(isset($_GET['toggle_id'])) {
$stmt = $pdo->prepare("UPDATE todos SET is_done = 1 - is_done WHERE id = :id AND user_id = :user_id");
$stmt->execute(['id' => $_GET['toggle_id'], 'user_id' => $user_id]);
}
// Fetch all tasks
$stmt = $pdo->prepare("SELECT * FROM todos WHERE user_id = :user_id ORDER BY created_at DESC");
$stmt->execute(['user_id' => $user_id]);
$todos = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
<h3>My To-Do List</h3>
<form method="post">
<input type="text" name="task" placeholder="Add a task" required>
<button type="submit" name="add_task">Add</button>
</form>
<ul>
<?php foreach($todos as $todo): ?>
<li>
<?= htmlspecialchars($todo['task']) ?>
<a href="?toggle_id=<?= $todo['id'] ?>">[Toggle]</a>
<a href="?delete_id=<?= $todo['id'] ?>">[Delete]</a>
<?php if($todo['is_done']): ?>โ<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
Contact Form with Email Sending
A contact form allows users to send messages directly through the website. The data is validated and then sent
using the PHP mail() function.
PHP Example - Simple Contact Form
<?php
$success = '';
$error = '';
if(isset($_POST['send'])) {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$message = trim($_POST['mesaj']);
// Simple validation
if(empty($name) || empty($email) || empty($message)) {
$error = "All fields are required.";
} elseif(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = "The email address is not valid.";
} else {
$to = "contact@example.com"; // recipient address
$subject = "Message from $name";
$body = "name: $name\nEmail: $email\n\nMessage:\n$message";
$headers = "From: $email";
if(mail($to, $subject, $body, $headers)) {
$success = "The message has been sent successfully!";
} else {
$error = "Error sending the message.";
}
}
}
?>
<h3>Contact Form</h3>
<?php if($success): ?>
<p style="color:green"><?= $success ?></p>
<?php endif; ?>
<?php if($error): ?>
<p style="color:red"><?= $error ?></p>
<?php endif; ?>
<form method="post">
<input type="text" name="name" placeholder="your name" required><br>
<input type="email" name="email" placeholder="your email" required><br>
<textarea name="mesaj" placeholder="your message" required></textarea><br>
<button type="submit" name="send">Send</button>
</form>
This code can be placed in a single file and run live. This way, message sending can be tested without
additional configuration, using just a local server that supports mail() or by configuring a local
SMTP server.
Login / Register with Hashing & Sessions
To create a secure authentication system, we use password_hash() at registration and
password_verify() at login. User data is stored in a database (e.g., the users
table), and sessions are used to maintain the authentication state.
Update users Table for Login
Add the password column to the users table so we can store the hashed password:
ALTER TABLE users
ADD COLUMN password VARCHAR(255) NOT NULL AFTER email;
PHP version to run from a script and check if the column exists:
<?php
try {
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "ALTER TABLE users
ADD COLUMN IF NOT EXISTS password VARCHAR(255) NOT NULL AFTER email";
$pdo->exec($sql);
echo "โ
The 'password' column has been added to the 'users' table.";
} catch (PDOException $e) {
echo "โ Error: " . $e->getMessage();
}
?>
After adding the column, the login and register example will work correctly.
PHP Example - Register
<?php
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test","root","");
$register_msg = '';
if(isset($_POST['register'])) {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
if(empty($username) || empty($email) || empty($password)) {
$register_msg = "All fields are required.";
} elseif(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$register_msg = "Invalid email.";
} else {
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
if($stmt->execute([$username, $email, $hash])) {
$register_msg = "โ
Account created successfully!";
} else {
$register_msg = "โ Error creating the account.";
}
}
}
?>
<h3>Register Form</h3>
<?php if($register_msg): ?>
<p><?= $register_msg ?></p>
<?php endif; ?>
<form method="post">
<input type="text" name="username" placeholder="name" required><br>
<input type="email" name="email" placeholder="Email" required><br>
<input type="password" name="password" placeholder="Password" required><br>
<button type="submit" name="register">Register</button>
</form>
PHP Example - Login
<?php
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test","root","");
$login_msg = '';
if(isset($_POST['login'])) {
$email = trim($_POST['email']);
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$login_msg = "โ
Login successful! Welcome, " . htmlspecialchars($user['name']);
} else {
$login_msg = "โ Incorrect email or password.";
}
}
?>
<h3>Login Form</h3>
<?php if($login_msg): ?>
<p><?= $login_msg ?></p>
<?php endif; ?>
<form method="post">
<input type="email" name="email" placeholder="Email" required><br>
<input type="password" name="password" placeholder="Password" required><br>
<button type="submit" name="login">Login</button>
</form>
This code can be placed in a single file to test registration and login live. Sessions allow authentication to persist between pages and secure access to protected areas.
<?php
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "traffice_php", "!@y3Ge-Z!WQI");
$register_msg = '';
if(isset($_POST['register'])) {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
if(empty($username) || empty($email) || empty($password)) {
$register_msg = "All fields are required.";
} elseif(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$register_msg = "Invalid email.";
} else {
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
if($stmt->execute([$username, $email, $hash])) {
$register_msg = "โ
Account created successfully!";
} else {
$register_msg = "โ Error creating account.";
}
}
}
?>
<h3>Register Form</h3>
<?php if($register_msg): ?>
<p><?= $register_msg ?></p>
<?php endif; ?>
<form method="post">
<input type="text" name="username" placeholder="Name" required><br>
<input type="email" name="email" placeholder="Email" required><br>
<input type="password" name="password" placeholder="Password" required><br>
<button type="submit" name="register">Register</button>
</form>
<?php
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "traffice_php", "!@y3Ge-Z!WQI");
$login_msg = '';
if(isset($_POST['login'])) {
$email = trim($_POST['email']);
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$login_msg = "โ
Login successful! Welcome, " . htmlspecialchars($user['name']);
} else {
$login_msg = "โ Incorrect email or password.";
}
}
?>
<h3>Login Form</h3>
<?php if($login_msg): ?>
<p><?= $login_msg ?></p>
<?php endif; ?>
<form method="post">
<input type="email" name="email" placeholder="Email" required><br>
<input type="password" name="password" placeholder="Password" required><br>
<button type="submit" name="login">Login</button>
</form>
Flash Messages in PHP
Flash messages are temporary messages that appear only once after an action (e.g., successful login, form error) and disappear after the page reloads. They are usually stored in the session.
Practical Example: Flash message after login
<?php
session_start();
// Set flash message after login
$_SESSION['flash_message'] = "โ
You have successfully logged in!";
// Redirect to the main page
header("Location: index.php");
exit;
?>
<?php
session_start();
// Display flash message and remove it after displaying
if(isset($_SESSION['flash_message'])) {
echo "<div class='flash'>" . $_SESSION['flash_message'] . "</div>";
unset($_SESSION['flash_message']);
}
?>
This way, the message will be visible only once, then it disappears automatically.
Like / Dislike Posts
To implement the like/dislike functionality, we need an additional table to store users' actions on posts.
Preparing the database for examples
1. Create a file named update-db.php in your PHP project folder.
2. Copy the code below into it (the one that creates the traffice-test database, users,
posts, post_likes tables, and adds test data).
3. Open your browser and run the file like this:
http://localhost/your-folder-name/update-db.php
4. If everything is fine, you will see the message:
โ
Database and tables have been created with functional test data.
5. Now you have all tables and data ready to run examples of: CRUD, Flash messages, and Like/Dislike posts.
<?php
try {
$pdo = new PDO("mysql:host=localhost", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Create database
$pdo->exec("CREATE DATABASE IF NOT EXISTS traffice-test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$pdo->exec("USE traffice-test");
// Create users table
$pdo->exec("CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
password VARCHAR(255) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
// Insert users
$pdo->exec("INSERT INTO users (id, name, email, password) VALUES
(1, 'John Smith', 'ion.popescu@example.com', '123'),
(2, 'Mary Ionella', 'maria.ionescu@example.com', '123')
ON DUPLICATE KEY UPDATE name=VALUES(name), email=VALUES(email)");
// Create posts table
$pdo->exec("CREATE TABLE IF NOT EXISTS posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(100) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)");
// Insert posts
$pdo->exec("INSERT INTO posts (id, user_id, title, content) VALUES
(1, 1, 'First post', 'This is the first post written by user 1.'),
(2, 1, 'Second post', 'User 1 has another post.'),
(3, 2, 'Hello!', 'Message written by user 2.')
ON DUPLICATE KEY UPDATE title=VALUES(title), content=VALUES(content)");
// Create post_likes table
$pdo->exec("CREATE TABLE IF NOT EXISTS post_likes (
id INT AUTO_INCREMENT PRIMARY KEY,
post_id INT NOT NULL,
user_id INT NOT NULL,
type ENUM('like','dislike') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_like (post_id,user_id),
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)");
echo "โ
Database and tables have been created with functional test data.";
} catch (PDOException $e) {
echo "โ Error: " . $e->getMessage();
}
?>
PHP Example for Like/Dislike
<?php
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$post_id = 1; // Post ID
$user_id = 1; // User ID
$type = 'like'; // or 'dislike'
// Insert like/dislike
$stmt = $pdo->prepare("INSERT INTO post_likes (post_id, user_id, type) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE type = VALUES(type)");
$stmt->execute([$post_id, $user_id, $type]);
// Total count of likes and dislikes for a post
$counts = $pdo->query("SELECT type, COUNT(*) as total FROM post_likes WHERE post_id = $post_id GROUP BY type")
->fetchAll(PDO::FETCH_ASSOC);
foreach($counts as $c) {
echo "<p>" . ucfirst($c['type']) . ": " . $c['total'] . "</p>";
}
?>
This way, users can like or dislike, and the total count updates automatically. The `ON DUPLICATE KEY UPDATE` function ensures that a user cannot like and dislike the same post simultaneously.
<?php
// Database connection
try {
$pdo = new PDO("mysql:host=localhost;dbname=traffice-test", "traffice_php", "!@y3Ge-Z!WQI");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("DB connection error: " . $e->getMessage());
}
// Process like/dislike
if (isset($_POST['like']) && isset($_POST['post_id']) && isset($_POST['user_id'])) {
$type = $_POST['like'] === 'like' ? 'like' : 'dislike';
$stmt = $pdo->prepare("INSERT INTO post_likes (post_id, user_id, type)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE type = VALUES(type)");
$stmt->execute([$_POST['post_id'], $_POST['user_id'], $type]);
}
// Fetch posts and like/dislike count
$sql = "SELECT posts.id, posts.title, posts.content, posts.created_at, users.name, users.email,
SUM(post_likes.type='like') AS likes,
SUM(post_likes.type='dislike') AS dislikes
FROM posts
INNER JOIN users ON posts.user_id = users.id
LEFT JOIN post_likes ON posts.id = post_likes.post_id
GROUP BY posts.id
ORDER BY posts.created_at DESC";
$stmt = $pdo->query($sql);
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
// List of users (for demo: user_id select)
$users = $pdo->query("SELECT * FROM users")->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Like/Dislike Demo</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: auto; }
.post { border: 1px solid #ccc; padding: 1rem; margin-bottom: 1rem; border-radius: 8px; }
.buttons { margin-top: 0.5rem; }
button { margin-right: 0.5rem; }
select { margin-bottom: 1rem; }
</style>
</head>
<body>
<h1>Demo: Like / Dislike Posts</h1>
<form method="post">
<label for="user_id">Select user:</label>
<select name="user_id" id="user_id">
<?php foreach($users as $user): ?>
<option value="<?php echo $user['id']; ?>"><?php echo htmlspecialchars($user['name']); ?></option>
<?php endforeach; ?>
</select>
</form>
<?php foreach($posts as $post): ?>
<div class="post">
<h2><?php echo htmlspecialchars($post['title']); ?></h2>
<p><?php echo htmlspecialchars($post['content']); ?></p>
<small>Written by <?php echo htmlspecialchars($post['name']); ?> (<?php echo htmlspecialchars($post['email']); ?>) on <?php echo $post['created_at']; ?></small>
<div class="buttons">
<form method="post" style="display:inline;">
<input type="hidden" name="post_id" value="<?php echo $post['id']; ?>">
<input type="hidden" name="user_id" value="<?php echo $users[0]['id']; ?>">
<button type="submit" name="like" value="like">๐ Like (<?php echo $post['likes'] ?? 0; ?>)</button>
</form>
<form method="post" style="display:inline;">
<input type="hidden" name="post_id" value="<?php echo $post['id']; ?>">
<input type="hidden" name="user_id" value="<?php echo $users[0]['id']; ?>">
<button type="submit" name="like" value="dislike">๐ Dislike (<?php echo $post['dislikes'] ?? 0; ?>)</button>
</form>
</div>
</div>
<?php endforeach; ?>
</body>
</html>
