How To Create A Blog in PHP and MySQL Database
How To Create A Blog in PHP and MySQL Database
database
I'm excited to be taking you through this long awaited tutorial, finally. I'll show you
how to build a complete blog application from scratch using PHP and MySQL
database. A blog as you know it is an application where some users (Admin users) can
create, edit, update and publish articles to make them available in the public to read
and maybe comment on. Users and the public can browse through a catalog of these
articles and click to anyone to read more about the article and comment on them.
Features:
A user registration system that manages two types of users: Admin and Normal Users
The blog will have an admin area and a public area separate from each other
The admin area will be accessible only to logged in admin users and the public area to
the normal users and the general public
In the admin section, two types of admins exist:
o Admin:
Can create, view, update, publish/unpublish and delete ANY post.
Can also create, view, update and delete topics.
An Admin user (and only an Admin user) can create another admin user
or Author
Can view, update and delete other admin users
o Author:
Can create, view, update and delete only posts created by themselves
They cannot publish a post. All publishing of posts is done by the Admin
user.
Only published posts are displayed in the public area for viewing
Each post is created under a particular topic
A many-to-many relationship exists between posts and topics.
The public page lists posts; each post displayed with a featured image, author, and date
of creation.
The user can browse through all posts listings under a particular topic by clicking on the
topic
When a user clicks on a post, they can view the full post and comment at the bottom of
the posts.
A Disqus commenting system is implemented which allows users to comment using
social media accounts with platforms like Facebook, GooglePlus, Twitter.
Implementation
Okay straight away let's start coding.
We'll call the project complete-blog-php. On your server directory (htdocs or www),
create a folder named complete-blog-php. Open this folder in a text editor of your
choice, for example, Sublime Text. Create the following subfolders inside it: admin,
includes, and static.
admin: Will hold files for the admin backend area. Files concerned with creating, viewing,
updating and deleting posts, topics, users.
includes: Will hold files containing pieces of code that will be included into one or more
other pages. E.g. Files for error display
static: Hold static files such as images, CSS stylesheets, Javascript.
<!DOCTYPE html><html><head>
<!-- Google Fonts -->
<link
href="https://fonts.googleapis.com/css?family=Averia+Serif+Libre|Noto
+Serif|Tangerine" rel="stylesheet">
<meta charset="UTF-8">
<div class="container">
<div class="navbar">
<div class="logo_div">
<a
href="index.php"><h1>LifeBlog</h1></a>
</div>
<ul>
<li><a class="active"
href="index.php">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>
</div>
<div class="content">
</div>
<div class="footer">
</div>
</div>
Between the <head></head> tags, we've included links to some Google Fonts. There's
also a link to our styling file public_styling.css, which we'll create in a minute.
Notice also the <div> element with a class set to container that wrap our entire
application including the navbar, page content and footer sections of the page.
The static folder as stated earlier will hold, among other things, styling for the site.
Create 3 subfolders inside static folder: css, images, js. In the css subfolder you just
created, create a file named public_styling.css.
*** DEFAULTS
position: relative;
margin: 0;
padding-bottom: 6rem;
min-height: 100%;}/* HEADINGS DEFAULT */h1, h2, h3, h4, h5, h6 { color:
#444; font-family: 'Averia Serif Libre', cursive; }a { text-decoration:
none; }ul, ol { margin-left: 40px; }hr { margin: 10px 0px; opacity: .25; }
text-align: center;
width: 100%;
display: block;
font-size: 1em;
border-radius: 3px;
box-sizing : border-box;
background: transparent;
color: white;
background: #4E6166;
text-align: center;
border: none;
border-radius: 5px;
display: block;
letter-spacing: .1em;
text-decoration: none;}.container {
width: 80%;
margin: 0 auto;
overflow: hidden;
background-color: #3E606F;
list-style-type: none;
float: right;}.navbar ul li {
float: left;
display: block;
color: white;
text-align: center;
color: #B9E6F2;
background-color: #334F5C;}
float: left;
padding-top: 5px;
color: #B9E6F2;
font-size: 3em;
letter-spacing: 5px;
font-weight: 100;
/* FOOTER */.footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
color: white;
background-color: #73707D;
text-align: center;
width: 80%;
This code starts with default styling for the site followed by styling for navbar and
that for the footer.
Reload the page in the browser. Our simple page now has a nice looking navbar with
a logo, some beautiful fonts, and, if you scroll down, our footer is hiding somewhere
at the bottom. Great!
Our page, however, has a few code segments that will be repeated across many other
pages of the website. For instance, most pages will need a navbar and the footer as
well as the links to the styling and fonts that are in the head section. In PHP, we can
write a piece of code in one file and include it at a particular position in several other
files. This is the same as writing that same code in those locations but with the
advantage that it prevents repetition of code. We do this using the include or require
keywords.
As you may have already guessed, it is time to make use of our includes folder we
created at the beginning. The sections that are repeating are the head section, the
navbar, and the footer. So in your includes folder, create 3 files
namely head_section.php, navbar.php and footer.php.
Go to the index.php file, select the part of the code from the first line down to and
including the <meta charset="UTF-8"> tag directly above your <title> tags and cut it.
Now head over to the newly created file
complete-blog-php/includes/head_section.php and paste the code in it.
<!DOCTYPE html><html><head>
<link
href="https://fonts.googleapis.com/css?family=Averia+Serif+Libre|Noto
+Serif|Tangerine" rel="stylesheet">
<meta charset="UTF-8">
Back in your index.php file, replace the code you just cut with the following line:
Note that the line that immediately follows this include line is the <title> tag. We
didn't include the <title> tag in the head_section.php file because the title of each page
might have to be different from the others for Search Engine Optimization purposes.
Now let's do the same with the navbar section and the footer.
In your index.php file, select and cut the navbar code where it is indicated with a
comment and paste it inside navbar.php in the includes folder. Here is navbar.php
after pasting:
<div class="navbar">
<div class="logo_div">
<a href="index.php"><h1>LifeBlog</h1></a>
</div>
<ul>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul></div>
As for the footer, select and cut the code from the opening footer <div> tag right
down to the last line in the page, and paste it in the newly created footer.php file. Here
is footer.php file after pasting:
<div class="footer">
</div>
</div>
Then replace this section in the index.php page with the include for footer.php
After all this rearrangement, our index.php file looks like this:
<div class="container">
<hr>
</div>
Now let's add a banner on the homepage immediately below the navbar. Create a new
file named banner.php in your complete-blog-php/includes folder and paste this code
in it:
<div class="banner">
<div class="welcome_msg">
<h1>Today's Inspiration</h1>
<p>
</p>
</div>
<div class="login_div">
<h2>Login</h2>
<input type="text" name="username"
placeholder="Username">
</form>
</div></div>
Once you've done that, add this CSS code in your public_styling.css file; it is styling
for the banner:
min-height: 400px;
color: white;
border-radius: 5px;
background-image: url('../images/banner.jpg');
width: 45%;
float: left;
color: #B9E6F2;
font-size: 2.4em;
font-family: 'Averia Serif Libre', cursive;}.banner .welcome_msg
p {
color: white;
font-size: 1.5em;
line-height: 1.8em;
font-size: .81em;
width: 30%;
font-size: 1.2em;
background: #374447;}
width: 50%;
color: white;
margin-bottom: 20px;
width: 60%;
color: white;
letter-spacing: 1.3px;
display: block;
background: #006384;
margin-left: 20%;}
If you did everything correctly, then you'll have a beautiful banner with the image
banner.jpg on its background, a login form to the right and an inspiring quote together
with a 'Join Us' button on the left. Cool!
Let's create a file to initialize our application's global variables. In the root folder
(complete-blog-php) of your application, create a file named config.php and paste this
code into it:
<?php
session_start();
define('BASE_URL', 'http://localhost/complete-blog-php/');?>
ROOT_PATH is set to the physical address with respect to the operating system, to
the current directory on which this file (config.php) resides. On my machine for
example, ROOT_PATH has the value /opt/lampp/htdocs/complete-blog-php/. It is
used to include physical files like PHP source code files (like the ones we just
included), physical downloadable files like images, video files, audio files, etc. But in
this tutorial, we will use it only to include PHP source files.
BASE_URL is merely a web address that sets a URL pointing to the root of our
website. In our case, its value is http://localhost/complete-blog-php. It is used to form
links to CSS, images, javascript.
I hope I explained those two variables well enough. If you didn't understand, just stick
around, you might figure it out as we use the variables.
Now let's include the newly created config.php file as our very first line in index.php.
After including it, we are going to modify the four places in our code where we
included other code segments so that they now use the variable ROOT_PATH in
pointing to those included files. After all these changes, our index.php will look like
this:
<!-- The first include should be config.php --><?php
require_once('config.php') ?><?php require_once( ROOT_PATH .
'/includes/head_section.php') ?>
<div class="container">
<div class="content">
<hr>
</div>
This way of including files has the advantage that, if later on some files are moved to
other directories, we may not need to update the includes. Also if we decide to change
the path to the root directory, we will only do that in one place, that is, in the
config.php file.
We are done with the basic setup of the public area. So far the application isn't
dynamic. We haven't created a database yet and if you click on the join us button,
you'll get an error that says "file not found". User login has not yet been implemented
too. These will all be dealt with in the next parts of this tutorial.
I hope you enjoyed it. If you liked this post and want more, please don't forget to
share with your friends using any of the social media links below. Thanks
The Database
This is the second part in a series on How To Create A Blog with PHP and MySQL.
You can get the first part here
We will continue where we left off in the last tutorial. In this section we will be
working on our database design and user authentication (registration and login).
Create a database named complete-blog-php. In this database, create 2
tables: posts and users with the following fields.
posts:
+----+-----------+--------------+------------+| field |
type | specs |+----+-----------+--------------+------------+|
id | INT(11) | || user_id | INT(11) |
|| title | VARCHAR(255) | || slug |
VARCHAR(255) | UNIQUE || views | INT(11) | ||
image | VARCHAR(255) | || body | TEXT |
|| published | boolean | || created_at | TIMESTAMP
| || updated_at | TIMESTAMP |
|+----------------+--------------+------------+
users:
+----+-----------+------------------------+------------+| field
| type | specs
|+----+-----------+------------------------+------------+| id
| INT(11) | || username | VARCHAR(255)
| UNIQUE || email | VARCHAR(255) | UNIQUE ||
role | ENUM("Admin","Author") | || password |
VARCHAR(255) | || created_at | TIMESTAMP
| || updated_at | TIMESTAMP |
|+----------------+--------------+---------+------------+
users:
posts:
You can run these scripts by using the SQL command prompt or PHPMyAdmin. On
PHPMyAdmin, click/select the database you want these tables created under (in this
case complete-blog-php), then click on the SQL tab on the navbar somewhere at the
top of the page. If you see any SQL script in the space below, remove it and paste the
script above into the space provided and click 'Go' to create the tables.
If you chose to create these tables manually instead, do remember to make the slug
field on the post table UNIQUE, and remember to set the user_id field of posts table
as the foreign key that references id on users table. Set NO ACTION as the value for
the ON DELETE and ON UPDATE options so that when a user is deleted or updated,
their posts remains on the posts table and is not deleted.
Now insert a few users into the users table and a few posts into the posts table. You
can do that by running these SQL queries to insert:
users:
posts:
Let's connect to the database, query these posts and display on the webpage.
In config.php, let's add code to connect our application to the database. After adding
the code, our config.php file will looks like this:
<?php
if (!$conn) {
define('BASE_URL', 'http://localhost/complete-blog-php/');?>
This returns a database connectivity object $conn which we can use in our entire
application for querying the database.
This application has been structured in a way that PHP code is as separate from
HTML as possible. Operations such as querying the database and performing some
logic on data are done in PHP functions and the results sent to HTML to be displayed.
Therefore to get all posts from the database, we will do that in a function and return
the results as an associative array to be looped through and displayed on the page.
Therefore, create a file named public_functions.php in the includes folder. This file is
going to hold all our PHP functions for the public area. All pages using any of the
functions in this file must have this file included in the top section of the page.
Let's create our first function in our newly created public_functions.php. We'll name
the function getPublishedPosts() and it will retrieve all posts from the posts table in
the database and return them as an associative array:
public_functions.php:
<?php /* * * * * * * * * * * * * * *
At the top section of the index.php file, just below the line that includes config.php,
add this code to query the database:
We added two lines of code. The first includes the public_functions.php (which holds
the functions) file to our index.php file. The second line of code calls the
getPublishedPosts() function which queries the database and returns posts retrieved
from the database in a variable called $posts. Now let's loop through and display these
posts on the index.php page.
Open our famous index.php file again. In the content section somewhere in the middle,
you will find a <hr> tag and a comment indicating where more content is to come. In
the space, right below the <hr> tag, add this code:
<div class="post_info">
<div class="info">
<span class="read_more">Read
more...</span>
</div>
</div>
</a>
Okay pleeease don't reload the page just yet. Let's add styling to this post listing.
Open public_styling.css and add this code to it:
/* CONTENT */.content {
border-radius: 5px;
min-height: 400px;}.content:after {
content: "";
display: block;
color: #374447;
width: 335px;
margin: 9px;
min-height: 320px;
float: left;
border-radius: 2px;
margin-top: 0px;
color: #374447;
background: white;
display: inline-block;
border-radius: 2px;
position: absolute;
color: white;
background: #374447;
transition: .4s;
width: 100%;
width: 100%;
height: 100%;
font-weight: 200;
color: #A6A6A6;
position: absolute;
If all went well, you'll see a single post styled as a thumbnail below the "Recent
articles" title. Remember we had inserted two records in the database but only one is
being displayed. This is so because one of the records had its published field set to
false (that is, 0), and since only published articles get to be displayed, we see only one,
the published one.
But our posts as of now are not classified under any topic. Let's create a topics table
and form a Many-to-Many relationship between the posts and the topics table. To do
this, we'll create two new tables: topics to store topics, and post_topic table to handle
the relationship between posts and topics.
topics:
+----+-----------+------------------------+------------+| field
| type | specs
|+----+-----------+------------------------+------------+| id
| INT(11) | || name | VARCHAR(255)
| || slug | VARCHAR(255) | UNIQUE
|+----------------+--------------+---------+------------+
post_topic:
+----+-----------+------------------------+------------+| field
| type | specs
|+----+-----------+------------------------+------------+| id
| INT(11) | || post_id | INT(11)
| UNIQUE || topic_id | INT(11) |
|+----------------+--------------+---------+------------+
What we are really interested in is the post_topic table. This is the table that handles
the relationship between posts and topics. When a post is created under a particular
topic, the id of that post (post_id), as well as the id of the topic (topic_id) under which
that post is created, are inserted in the post_topic table.
Let's establish this relationship so that when a post is deleted, their entry in the
post_topic table will automatically be deleted too; you don't want to keep info about a
post's relationship when the post doesn't exist right?
Click/select the post_topic table, then click on the structure tab of the PHPMyAdmin
navbar. Next, click on Relation View just underneath the structure tab (it could be
found somewhere else depending on your version of PHPMyAdmin). Then fill the
form below as follows:
Tip: The +Add constraint link is used to add a new constraint.
Click save and that's it. The tables are now related. But to establish a relationship
between posts and topics, we need to populate the topics table with topics and
eventually the post_topic table which is the actual relationship info.
post_topic:
The relationship defined on the post_topic table says that the topic with id 1 on the
topics table belongs to the post with id 1 on the posts table. The same holds true for
the topic with id 2 and post with id 2.
On each post listing on the index.php page, we are going to display the topic under
which the post is created.
<?php /* * * * * * * * * * * * * * *
$final_posts = array();
$post['topic'] = getPostTopic($post['id']);
array_push($final_posts, $post);
}
return $final_posts;}/* * * * * * * * * * * * * * *
* * * * * * * * * * * * * * */function getPostTopic($post_id){
global $conn;
$topic = mysqli_fetch_assoc($result);
return $topic;}?>
Now go to index.php file. Inside the foreach loop, directly below the image tag <img
/>, add the if statement to display topic. Your foreach loop should look like this after
modifying:
<a
class="btn category">
</a>
<div class="post_info">
<div class="info">
<span class="read_more">Read
more...</span>
</div>
</div>
</a>
Now reload the page and you'll see the topic displayed in the post.
Inside this foreach loop, you notice that there are two links which when clicked, will
take you to two pages: filtered_posts.php and single_post.php.
filtered_posts.php is a page that lists all the posts under a particular topic when the
user clicks on that topic.
single_post.php is a page that displays the full post in detail together with comments
when the user clicks on the post thumbnail.
These two files need a few functions from our public_functions.php file.
filtered_posts.php needs two function
called getPublishedPostsByTopic() and getTopicNameById() while single_posts.php
needs getPost() and getAllTopics().
Let's start with filtered_posts.php file. Open public_functions.php and add these two
functions to the list of functions:
/* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * */function getPublishedPostsByTopic($topic_id)
{
global $conn;
WHERE ps.id IN
$final_posts = array();
$post['topic'] = getPostTopic($post['id']);
array_push($final_posts, $post);
return $final_posts;}/* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * */function getTopicNameById($id){
global $conn;
$topic = mysqli_fetch_assoc($result);
return $topic['name'];}
Let's first Create the filtered_posts.php file in the root folder of our application (that is,
complete-blog-php/filtered_posts.php). I'll just go ahead and paste the entire code of
this page inside the file:
filtered_posts.php:
<?php include('config.php'); ?><?php
include('includes/public_functions.php'); ?><?php
include('includes/head_section.php'); ?><?php // Get posts
under a particular topic if (isset($_GET['topic'])) {
$topic_id = $_GET['topic'];
$posts = getPublishedPostsByTopic($topic_id);
}?>
<h2 class="content-title">
</h2>
<hr>
<div class="post_info">
<h3><?php echo
$post['title'] ?></h3>
<div class="info">
<span
class="read_more">Read more...</span>
</div>
</div>
</a>
</div>
Now refresh the page, click on the topic and if it takes you to a page displaying posts
under that topic, then you're doing the right thing.
Let's do the same thing with single_post.php. Open public_functions.php and add
these 2 functions to it:
/* * * * * * * * * * * * * * *
* * * * * * * * * * * * * * */function getPost($slug){
if ($post) {
return $post;}/* * * * * * * * * * * *
* * * * * * * * * * * * */function getAllTopics(){
global $conn;
$sql = "SELECT * FROM topics";
return $topics;}
Now create the file complete-blog-php/single_post.php and paste this code into it:
if (isset($_GET['post-slug'])) {
$post = getPost($_GET['post-slug']);
$topics = getAllTopics();?><?php
include('includes/head_section.php'); ?><title> <?php echo
$post['title'] ?> | LifeBlog</title></head><body><div
class="container">
<div class="post-wrapper">
<div class="full-post-div">
<?php echo
html_entity_decode($post['body']); ?>
</div>
</div>
</div>
<div class="post-sidebar">
<div class="card">
<div class="card-header">
<h2>Topics</h2>
</div>
<div class="card-content">
<a
href="<?php echo
BASE_URL . 'filtered_posts.php?topic=' . $topic['id'] ?>">
<?php echo
$topic['name']; ?>
</a>
</div>
</div>
Let's now apply styling to this. Open public_styling.css and add this styling code to it:
/* * * * * * * * *
* SINGLE PAGE
* * * * * * * * */.content .post-wrapper {
width: 70%;
float: left;
min-height: 250px;}.full-post-div {
min-height: 300px;
padding: 20px;
text-align: center;}.post-body-div {
font-size: 1.2em;}.post-body-div p {
margin:20px 0px;}.post-sidebar {
width: 24%;
float: left;
margin-left: 5px;
margin-top: 25px;
border-radius: 2px;
width: 95%;
padding: 10px;
text-align: center;
display: block;
box-sizing: border-box;
padding-left: 20px;
background: #F9F9F9;
transition: 0.1s;}
One last thing to do and we'll be pretty much done with the public area: We'll be
implementing user registration and login.
register.php:
<h2>Register on LifeBlog</h2>
<?php include(ROOT_PATH .
'/includes/errors.php') ?>
<p>
</p>
</form>
login.php:
<h2>Login</h2>
<?php include(ROOT_PATH .
'/includes/errors.php') ?>
<p>
</p>
</form>
</div></div><!-- // container -->
complete-blog-php/includes/registration_login.php:
$email = "";
$errors = array();
$email = esc($_POST['email']);
$password_1 = esc($_POST['password_1']);
$password_2 = esc($_POST['password_2']);
OR
email='$email' LIMIT 1";
$user = mysqli_fetch_assoc($result);
VALUES('$username', '$email',
'$password', now(), now())";
mysqli_query($conn, $query);
exit(0);
} else {
exit(0);
$username = esc($_POST['username']);
$password = esc($_POST['password']);
if (empty($errors)) {
if (mysqli_num_rows($result) > 0) {
exit(0);
} else {
exit(0);
} else {
array_push($errors, 'Wrong
credentials');
return $val;
} // Get user info from user id function
getUserById($id)
global $conn;
$user = mysqli_fetch_assoc($result);
}?>
errors.php file is the file with code to display form validation errors. Create errors.php
inside complete-blog-php/includes and paste this code in it:
Once again open public_styling.css let's add this last piece of styling code for this
errors.php file and a few other elements:
width: 100%;
background: #dff0d8;
border-radius: 5px;
text-align: center;}.error {
color: #a94442;
background: #f2dede;
margin-bottom: 20px;}.validation_errors p {
text-align: left;
margin-left: 10px;}.logged_in_info {
text-align: right;
padding: 10px;}
And now the error message is gone. Click on the register button without filling the
form and you see beautiful error messages rendered.
Let's create a new user by filling the form on the register.php page and clicking on the
register button. You can provide whatever valid info for the username, email, and
password; just make sure you remember them because we will use them to log in very
soon on the login page.
When a user logs in, they'll definitely need to be able to log out. In the root folder of
the application, create a file named logout.php.
complete-blog-php/logout.php:
<?php
session_start();
session_unset($_SESSION['user']);
session_destroy();
header('location: index.php');?>
Also when a user logs in, we want to display their name and a link or button for them
to click to log out. For the public area, we'll do that in the banner.php file we included.
Open banner.php file and modify the code to look like this:
complete-blog-php/includes/banner.php:
<div class="logged_in_info">
<span><a href="logout.php">logout</a></span>
<div class="banner">
<div class="welcome_msg">
<h1>Today's Inspiration</h1>
<p>
</p>
</div>
<div class="login_div">
<h2>Login</h2>
</div>
</form>
</div>
</div><?php } ?>
It checks the session to see if a user is available (logged in). If logged in, the
username is displayed with the logout link. When there is a logged in user, the banner
does not get displayed since it is some sort of a welcome screen to guest users.
You notice that the banner has a login form and this banner is included inside
index.php file. Therefore we need to include the code that handles registration and
login inside our index.php file also. Open index.php and add this line directly under
the include for public_functions.php:
And that's it with user registration and login. In the next section, we begin work on
the admin area.
Thank you so much for sticking around up to this point. I hope you found it helpful. If
you have any worries, please leave it in the comments below. Your feedback is
always very helpful and if you have any bugs in your code, I will try my best to help
you out.
I will be very much encouraged to create more of these tutorials with improved
quality if you share, subscribe to my site and recommend my site to your friends.
Awa Melvine
Next part: Backend (Admin area)
This is part 3 of a 4-part series on How To Build a Blog with PHP and MySQL. You
can view the previous two parts here: part 1, part 2.
In the last two parts of this tutorial, we finished creating the public area. We even set
up our database, inserted some data into the database tables and were able to query
this and display on the page. But we don't always want to create users, posts, topics
using a database client like PHPMyAdmin, do we? We want an interface on the
website and a logged in user with admin privileges to do that.
When a user with admin privileges logs in, they are automatically redirected to the
admin dashboard. But we haven't created an admin user in our system yet. We will do
that soon.
dashboard.php:
<?php include(ROOT_PATH .
'/admin/includes/admin_functions.php'); ?>
<?php include(ROOT_PATH .
'/admin/includes/head_section.php'); ?>
<title>Admin | Dashboard</title></head><body>
<div class="header">
<div class="logo">
<h1>LifeBlog - Admin</h1>
</a>
</div>
<div class="user-info">
<span><?php echo
$_SESSION['user']['username'] ?></span>
</div>
</div>
<h1>Welcome</h1>
<div class="stats">
<span>43</span> <br>
</a>
<a href="posts.php">
<span>43</span> <br>
<span>Published posts</span>
</a>
<a>
<span>43</span> <br>
<span>Published comments</span>
</a>
</div>
<br><br><br>
<div class="buttons">
</div>
</div></body></html>
The reason we have created a separate includes folder for the admin area is to ensure
that all admin files are in one folder (admin). Later on, we can strengthen the security
of the admin folder using htaccess authentication. We won't be doing that in this
tutorial though.
In this newly created includes folder, create a file named head_section.php. Open
head_section.php and add this code to it:
Once in the backend, the user can create, read, update and delete users, posts, and
topics. Let's start with users. In your admin folder, create a file named users.php
users.php:
<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>
<div class="action">
<?php include(ROOT_PATH .
'/includes/errors.php') ?>
<input type="hidden"
name="admin_id" value="<?php echo $admin_id; ?>">
<input type="password"
name="passwordConfirmation" placeholder="Password confirmation">
<select name="role">
<option value="<?php
echo $role; ?>"><?php echo $role; ?></option>
</select>
<!-- if editing user, display the update
button instead of create button -->
<button type="submit"
class="btn" name="update_admin">UPDATE</button>
<button type="submit"
class="btn" name="create_admin">Save User</button>
</form>
</div>
<div class="table-div">
<?php include(ROOT_PATH .
'/includes/messages.php') ?>
<table class="table">
<thead>
<th>N</th>
<th>Admin</th>
<th>Role</th>
<th
colspan="2">Action</th>
</thead>
<tbody>
<tr>
<td><?php echo
$key + 1; ?></td>
<td>
<?php
echo $admin['username']; ?>,
<?php
echo $admin['email']; ?>
</td>
<td><?php echo
$admin['role']; ?></td>
<td>
<a
class="fa fa-pencil btn edit"
</a>
</td>
<td>
<a
class="fa fa-trash btn delete"
</a>
</td>
</tr>
</table>
</div>
</div></body></html>
This is all we will need to do in the users.php file. Creating, editing, and deletion of a
user will be done in this one file.
At the top of the users.php file, we are calling a function getAdminUsers() which
returns an array of all admin users from the database. This function is to be defined
inside the admin_functions.php file which we haven't yet created but you can see it
included in our users.php file just before the function call.
In your admin folder, create admin_functions.php and add this code to it:
admin_functions.php:
/* - - - - - - - - - -
$isEditingUser = true;
$admin_id = $_GET['edit-admin'];
$admin_id = $_GET['delete-admin'];
deleteAdmin($admin_id);}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */function
getAdminUsers(){
return $users;}/* * * * * * * * * * * * * * * * * * * * *
$string = strtolower($string);
return $slug;}?>
Reload the dashboard.php page on your browser and the error message is gone.
Next is to include the navbar.php page and the menu.php page. These are repeating
segments of admin pages just as was the case with the public area.
So, create these 2 files inside admin/includes folder: navbar.php and menu.php.
navbar.php:
<div class="header">
<div class="logo">
<h1>LifeBlog - Admin</h1>
</a>
</div>
<div class="user-info">
</div></div>
menu.php:
<div class="menu">
<div class="card">
<div class="card-header">
<h2>Actions</h2>
</div>
<div class="card-content">
</div>
</div></div>
Now let's create styling for the admin section. Inside complete-blog-php/static/css/
folder, create a file named admin_styling.css and add this code to it:
/* * * * * * * * * *
* STYLING DEFAULTS
width: 100%;
display: block;
font-size: 1em;
border-radius: 3px;
box-sizing : border-box;
background: transparent;
color: white;
background: #4E6166;
text-align: center;
border: none;
border-radius: 5px;
display: block;
letter-spacing: .1em;
text-decoration: none;}/* * * * * * * * * *
* HEADER
* * * * * * * * * */.header {
padding: 15px 45px;
color: white;
* DASHBOARD
* * * * * * * * * */.container {
width: 95%;
display: inline-block;
padding: 30px;
margin: 5px;
width: 25%;
text-align: center;
border-radius: 3px;
display: inline-block;
margin: 10px;
text-decoration: none;
color: #444;
background-color: #0E7D92;
color: white;}/* * * * * * * * * *
* PAGE CONTENT
padding: 10px;
text-align: center;
background:
#3E606F;}.container.content .menu .card .card-header h2 { color:
white; }.container.content .menu .card .card-content a {
display: block;
box-sizing: border-box;
* VALIDATION ERRORS
* * * * * * * * * */.message {
width: 100%;
margin: 0px auto;
color: #3c763d;
background: #dff0d8;
border-radius: 5px;
<p>
<?php
echo $_SESSION['message'];
unset($_SESSION['message']);
?>
</p>
This displays notification messages to give the user feedback on their actions.
admin_functions.php:
/* - - - - - - - - - - - -
- - - - - - - - - - - - -*//* * * * * * * * * * * * * * * * * * * * * *
*
* * * * * * * * * * * * * * * * * * * * * * */function
createAdmin($request_values){
$username = esc($request_values['username']);
$email = esc($request_values['email']);
$password = esc($request_values['password']);
$passwordConfirmation =
esc($request_values['passwordConfirmation']);
if(isset($request_values['role'])){
$role = esc($request_values['role']);
$user = mysqli_fetch_assoc($result);
mysqli_query($conn, $query);
header('location: users.php');
exit(0);
}}/* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * */function editAdmin($admin_id){
global $conn, $username, $role, $isEditingUser, $admin_id,
$email;
$admin = mysqli_fetch_assoc($result);
$email = $admin['email'];}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */function
updateAdmin($request_values){
$username = esc($request_values['username']);
$email = esc($request_values['email']);
$password = esc($request_values['password']);
$passwordConfirmation =
esc($request_values['passwordConfirmation']);
if(isset($request_values['role'])){
$role = $request_values['role'];
mysqli_query($conn, $query);
header('location: users.php');
exit(0);
global $conn;
if (mysqli_query($conn, $sql)) {
header("location: users.php");
exit(0);
}}
The code we just added has 3 main parts: initialization of admin user variables,
Admin user actions, and Admin user functions, in that order. This is the same format
in which we will add code for topics which is coming next. For now, you can already
create, read, update and delete a user.
complete-blog-php/admin/topics.php:
<?php include('../config.php'); ?><?php include(ROOT_PATH .
'/admin/includes/admin_functions.php'); ?><?php include(ROOT_PATH .
'/admin/includes/head_section.php'); ?><!-- Get all topics from DB
--><?php $topics = getAllTopics(); ?>
<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>
<div class="action">
<?php include(ROOT_PATH .
'/includes/errors.php') ?>
<input type="hidden"
name="topic_id" value="<?php echo $topic_id; ?>">
<button type="submit"
class="btn" name="create_topic">Save Topic</button>
</form>
</div>
<div class="table-div">
<?php include(ROOT_PATH .
'/includes/messages.php') ?>
<table class="table">
<thead>
<th>N</th>
<th>Topic Name</th>
<th
colspan="2">Action</th>
</thead>
<tbody>
<tr>
<td><?php echo
$key + 1; ?></td>
<td><?php echo
$topic['name']; ?></td>
<td>
<a
class="fa fa-pencil btn edit"
</a>
</td>
<td>
<a
class="fa fa-trash btn delete"
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div></body></html>
The following code has three sections. Each section has been labelled on top using a
comment and the three are variables, actions and functions. So in your
admin_functions.php file, add the following code to it but make sure you split it
accordingly as indicated below using the comments.
admin_functions.php:
/* - - - - - - - - - -
- - - - - - - - - - -*/// ... /* - - - - - - - - - -
- Topic actions
$isEditingTopic = true;
$topic_id = $_GET['edit-topic'];
$topic_id = $_GET['delete-topic'];
deleteTopic($topic_id);}
/* - - - - - - - - - - - -
- - - - - - - - - - - - -*/// .../* - - - - - - - - - -
- Topics functions
global $conn;
$topic_name = esc($request_values['topic_name']); //
create slug: if topic is "Life Advice", return "life-advice" as slug
$topic_slug = makeSlug($topic_name); // validate form
if (empty($topic_name)) {
VALUES('$topic_name',
'$topic_slug')";
mysqli_query($conn, $query);
header('location: topics.php');
exit(0);
}}/* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * */function editTopic($topic_id)
{
$topic_name = esc($request_values['topic_name']);
mysqli_query($conn, $query);
header('location: topics.php');
exit(0);
global $conn;
if (mysqli_query($conn, $sql)) {
header("location: topics.php");
exit(0);
}}
Awa Melvine
This is the 4th and last part of a 4-part series on How To Build a Blog with PHP and
MySQL database. You can view the other parts here: part 1, part 2, and part 3.
So far we have created a public area that lists published posts from a MySQL
database posts table. We have also completed a user registration system that handles
login for both admin users and normal users. In the backend, the logged in admin user
can now create other admin users as well as topics.
In this section, we will be working on blog posts. We will create a page that provides
logged in admin users (or authors) with a form to create a new blog post.
We will create two files: posts.php file inside admin folder, and post_functions.php
inside admin/includes folder. The posts.php file lists all the posts gotten from the
database in a table format while the post_functions.php contains functions that
perform operations on posts such as querying them from the database and returning
them to the posts.php file.
<!-- Get all admin posts from DB --><?php $posts = getAllPosts(); ?>
<title>Admin | Manage Posts</title></head><body>
<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>
<?php include(ROOT_PATH .
'/includes/messages.php') ?>
<table class="table">
<thead>
<th>N</th>
<th>Title</th>
<th>Author</th>
<th>Views</th>
<?php if
($_SESSION['user']['role'] == "Admin"): ?>
<th><small>Publish</small></th>
<th><small>Edit</small></th>
<th><small>Delete</small></th>
</thead>
<tbody>
<tr>
<td><?php echo
$key + 1; ?></td>
<td><?php echo
$post['author']; ?></td>
<td>
<a
target="_blank"
</a>
</td>
<td><?php echo
$post['views']; ?></td>
<td>
<?php if
($post['published'] == true): ?>
href="po
</a>
<?php
else: ?>
href="po
</a>
<?php
endif ?>
</td>
<td>
<a
class="fa fa-pencil btn edit"
</a>
</td>
<td>
<a
class="fa fa-trash btn delete"
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div></body></html>
In the top section, we include the newly created post_functions.php file. Let's open it
now and add the code that retrieves posts from the database.
post_functions.php:
/* - - - - - - - - - -
- Post functions
global $conn;
$user_id = $_SESSION['user']['id'];
$sql = "SELECT * FROM posts WHERE user_id=$user_id";
$final_posts = array();
$post['author'] = getPostAuthorById($post['user_id']);
array_push($final_posts, $post);
global $conn;
if ($result) {
} else {
return null;
}}?>
If all went well, you'll see that the page has 2 posts (the ones we created earlier)
displayed in a table.
At this point, we are able to query the database for posts and list them in a tabular
format. Let's provide a form for actually creating posts. Create a file
create_post.php inside your admin folder and paste this code into it:
complete-blog-php/admin/create_post.php:
<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>
<form method="post"
enctype="multipart/form-data" action="<?php echo BASE_URL .
'admin/create_post.php'; ?>" >
<?php include(ROOT_PATH .
'/includes/errors.php') ?>
<input type="file"
name="featured_image" >
<select name="topic_id">
<option value="<?php
echo $topic['id']; ?>">
<?php echo
$topic['name']; ?>
</option>
</select>
<?php if ($_SESSION['user']['role'] ==
"Admin"): ?>
Publish
<input
type="checkbox" value="1" name="publish" checked="checked">
</label>
<label for="publish">
Publish
<input
type="checkbox" value="1" name="publish">
</label>
<button type="submit"
class="btn" name="update_post">UPDATE</button>
<button type="submit"
class="btn" name="create_post">Save Post</button>
</form>
</div>
</div></body></html>
<script>
CKEDITOR.replace('body');</script>
Often for a blog post, you will need to write some text in Bold, Italics, Underline,
Headings, ordered and unordered lists as well as upload images. To do this you will
need a ckeditor to provide you with a textarea rich in these features. This requires us
to include the ckeditor plugin script which we already did in the head_section.php
file.
1. We must include the CKEditor source script (as we already did in the head_section.php
file)
2. We must have a textarea and give it an id (say id="body", as we did in this case)
3. Lastly, we must initialize the textarea with this script (as we did in create_post.php):
<script>
CKEDITOR.replace('body');</script>
We are going to use the same form to create and edit posts. What is left now is for us
to write the functions responsible for creating, editing, updating and deleting posts.
We do that in post_functions.php. Open post_functions.php and add these functions
and if statements to it.
complete-blog-php/admin/includes/post_functions.php:
/* - - - - - - - - - -
- Post actions
$isEditingPost = true;
$post_id = $_GET['edit-post'];
$post_id = $_GET['delete-post'];
deletePost($post_id);}
/* - - - - - - - - - -
- Post functions
- - - - - - - - - - -*/function createPost($request_values)
$title = esc($request_values['title']);
$body = htmlentities(esc($request_values['body']));
if (isset($request_values['topic_id'])) {
$topic_id = esc($request_values['topic_id']);
if (isset($request_values['publish'])) {
$published = esc($request_values['publish']);
if (empty($featured_image)) { array_push($errors,
"Featured image is required"); }
mysqli_query($conn, $sql);
header('location: posts.php');
exit(0);
}
/* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * */
function editPost($role_id)
$post = mysqli_fetch_assoc($result);
$body = $post['body'];
$published = $post['published'];
function updatePost($request_values)
$title = esc($request_values['title']);
$body = esc($request_values['body']);
$post_id = esc($request_values['post_id']);
if (isset($request_values['topic_id'])) {
$topic_id = esc($request_values['topic_id']);
}
if
(!move_uploaded_file($_FILES['featured_image']['tmp_name'], $target))
{
$inserted_post_id =
mysqli_insert_id($conn);
$_SESSION['message'] = "Post
created successfully";
header('location: posts.php');
exit(0);
header('location: posts.php');
exit(0);
global $conn;
if (mysqli_query($conn, $sql)) {
header("location: posts.php");
exit(0);
One last thing, let's add code to publish/unpublish post. In the same
post_functions.php file, add this code:
$message = "";
if (isset($_GET['publish'])) {
$post_id = $_GET['publish'];
} else if (isset($_GET['unpublish'])) {
$post_id = $_GET['unpublish'];
global $conn;
if (mysqli_query($conn, $sql)) {
$_SESSION['message'] = $message;
header("location: posts.php");
exit(0);
}}
This permits the user (Admin user) to publish/unpublish a post. And that brings us to
the end of this tutorial.
Conclusion
Thank you very much for your patience. It gives me immense satisfaction to know
that someone actually followed my tutorial to the very end. I hope you learned
something that was worth your time. If you have any comments, worries, suggestions,
please leave them in the comments section below. You can even just say hi in the
comments if you don't have anything to say. I just need to know that you made it this
far because that encourages me to do more tutorials like this.
Remember you can always help greatly by sharing with your friends.