100% found this document useful (1 vote)
1K views85 pages

How To Create A Blog in PHP and MySQL Database

The document provides instructions for creating a blog application using PHP and MySQL. It will include features like user registration and login, an admin area for creating/editing posts and topics, and a public area for viewing published posts. The implementation section begins coding the project, creating folders for admin, includes, and static files. It then creates an index.php file with basic HTML structure and includes reusable code like the header, navbar, and footer in separate PHP files to avoid repetition.

Uploaded by

Jesus Rodriguez
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
Download as docx, pdf, or txt
100% found this document useful (1 vote)
1K views85 pages

How To Create A Blog in PHP and MySQL Database

The document provides instructions for creating a blog application using PHP and MySQL. It will include features like user registration and login, an admin area for creating/editing posts and topics, and a public area for viewing published posts. The implementation section begins coding the project, creating folders for admin, includes, and static files. It then creates an index.php file with basic HTML structure and includes reusable code like the header, navbar, and footer in separate PHP files to avoid repetition.

Uploaded by

Jesus Rodriguez
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1/ 85

How to create a blog in PHP and MySQL

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.

Recommended course: PHP Beginner To Master - CMS Project

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.

The 3 folders will hold the following contents:

 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.

In the root folder of the application, create a file named index.php:

Open it up and paste this code in it:

<!DOCTYPE html><html><head>
<!-- Google Fonts -->

<link
href="https://fonts.googleapis.com/css?family=Averia+Serif+Libre|Noto
+Serif|Tangerine" rel="stylesheet">

<!-- Styling for public area -->

<link rel="stylesheet" href="static/css/public_styling.css">

<meta charset="UTF-8">

<title>LifeBlog | Home </title></head><body>

<!-- container - wraps whole page -->

<div class="container">

<!-- navbar -->

<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>

<!-- // navbar -->

<!-- Page content -->

<div class="content">

<h2 class="content-title">Recent Articles</h2>


<hr>

<!-- more content still to come here ... -->

</div>

<!-- // Page content -->

<!-- footer -->

<div class="footer">

<p>MyViewers &copy; <?php echo date('Y'); ?></p>

</div>

<!-- // footer -->

</div>

<!-- // container --></body></html>

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.

To view this in your browser, go to http://localhost/complete-blog-php/index.php.

Doesn't look as cool as you would've loved right?

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.

Open public_styling.css and add this styling code to it:


/****************

*** DEFAULTS

*****************/* { margin: 0px; padding: 0px; }

html { height: 100%; box-sizing: border-box; }body {

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; }

/* FORM DEFAULTS */form h2 {

margin: 25px auto;

text-align: center;

font-family: 'Averia Serif Libre', cursive;}form input {

width: 100%;

display: block;

padding: 13px 13px;

font-size: 1em;

margin: 5px auto 10px;

border-radius: 3px;

box-sizing : border-box;

background: transparent;

border: 1px solid #3E606F;}form input:focus {

outline: none;}/* BUTTON DEFAULT */.btn {

color: white;

background: #4E6166;

text-align: center;

border: none;

border-radius: 5px;
display: block;

letter-spacing: .1em;

margin: 10px 0px;

padding: 13px 20px;

text-decoration: none;}.container {

width: 80%;

margin: 0px auto;}/* NAVBAR */.navbar {

margin: 0 auto;

overflow: hidden;

background-color: #3E606F;

border-radius: 0px 0px 6px 6px;}.navbar ul {

list-style-type: none;

float: right;}.navbar ul li {

float: left;

font-family: 'Noto Serif', serif;}.navbar ul li a {

display: block;

color: white;

text-align: center;

padding: 20px 28px;

text-decoration: none;}.navbar ul li a:hover {

color: #B9E6F2;

background-color: #334F5C;}

/* LOGO */.navbar .logo_div {

float: left;

padding-top: 5px;

padding-left: 40px;}.navbar .logo_div h1 {

color: #B9E6F2;

font-size: 3em;
letter-spacing: 5px;

font-weight: 100;

font-family: 'Tangerine', cursive;}

/* FOOTER */.footer {

position: absolute;

right: 0;

bottom: 0;

left: 0;

color: white;

background-color: #73707D;

text-align: center;

width: 80%;

margin: 20px auto 0px;

padding: 20px 0px;}

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.

So the head_section.php file has the following code:

<!DOCTYPE html><html><head>

<!-- Google Fonts -->

<link
href="https://fonts.googleapis.com/css?family=Averia+Serif+Libre|Noto
+Serif|Tangerine" rel="stylesheet">

<!-- Styling for public area -->

<link rel="stylesheet" href="static/css/public_styling.css">

<meta charset="UTF-8">

Back in your index.php file, replace the code you just cut with the following line:

<?php require_once('includes/head_section.php') ?>

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 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>

Then in place of the navbar in your index.php, add this line:

<!-- navbar --><?php include('includes/navbar.php') ?>

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">

<p>MyViewers &copy; <?php echo date('Y'); ?></p>

</div>

</div>

<!-- // container --></body></html>

Then replace this section in the index.php page with the include for footer.php

<!-- footer --><?php include('includes/footer.php') ?>

After all this rearrangement, our index.php file looks like this:

<?php require_once('includes/head_section.php') ?>

<title>LifeBlog | Home </title></head><body>

<!-- container - wraps whole page -->

<div class="container">

<!-- navbar -->

<?php include('includes/navbar.php') ?>

<!-- Page content -->


<div class="content">

<h2 class="content-title">Recent Articles</h2>

<hr>

<!-- more content still to come here ... -->

</div>

<!-- // Page content -->

<!-- footer -->

<?php include('includes/footer.php') ?>

If you reload the page there won't be any change observed.

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>

One day your life <br>

will flash before your eyes. <br>

Make sure it's worth watching. <br>

<span>~ Gerard Way</span>

</p>

<a href="register.php" class="btn">Join us!</a>

</div>

<div class="login_div">

<form action="index.php" method="post" >

<h2>Login</h2>
<input type="text" name="username"
placeholder="Username">

<input type="password" name="password"


placeholder="Password">

<button class="btn" type="submit"


name="login_btn">Sign in</button>

</form>

</div></div>

Include this in your index.php immediately below the navbar include:

<!-- navbar is up here... -->

<!-- banner --><?php include('includes/banner.php') ?>

PLEASE DOWNLOAD this image, rename it to banner.jpg and place it in your


complete-blog-php/static/images/ folder. It is the background image to the banner.

Once you've done that, add this CSS code in your public_styling.css file; it is styling
for the banner:

/* BANNER: Welcome message; */.banner {

margin: 5px auto;

min-height: 400px;

color: white;

border-radius: 5px;

background-image: url('../images/banner.jpg');

background-size: 100% 100%;}.banner .welcome_msg {

width: 45%;

float: left;

padding: 20px;}.banner .welcome_msg h1 {

color: #B9E6F2;

margin: 25px 0px;

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-family: 'Noto Serif', serif;}.banner .welcome_msg p span {

font-size: .81em;

color: #3E606F;}.banner .welcome_msg a {

width: 30%;

margin: 20px 0px;

padding: 12px 15px;

font-size: 1.2em;

text-decoration: none;}.banner .welcome_msg a:hover {

background: #374447;}

/* BANNER: Login Form; */.banner .login_div {

width: 50%;

float: left;}.banner .login_div form {

margin-top: 40px;}.banner .login_div form h2 {

color: white;

margin-bottom: 20px;

font-family: 'Noto Serif', serif;}.banner .login_div form input {

width: 60%;

color: white;

border: 1px solid white;

margin: 10px auto;

letter-spacing: 1.3px;

font-family: 'Noto Serif', serif;}.banner .login_div form button {

display: block;
background: #006384;

margin-left: 20%;}

Reload the page now.

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();

// connect to database // coming soon...

define ('ROOT_PATH', realpath(dirname(__FILE__)));

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') ?>

<title>LifeBlog | Home </title></head><body>

<!-- container - wraps whole page -->

<div class="container">

<!-- navbar -->

<?php include( ROOT_PATH . '/includes/navbar.php') ?>

<!-- // navbar -->

<!-- banner -->

<?php include( ROOT_PATH . '/includes/banner.php') ?>

<!-- // banner -->

<!-- Page content -->

<div class="content">

<h2 class="content-title">Recent Articles</h2>

<hr>

<!-- more content still to come here ... -->

</div>

<!-- // Page content -->

<!-- footer -->

<?php include( ROOT_PATH . '/includes/footer.php') ?>

<!-- // footer -->

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

Next part: The database design.

How to create a blog in PHP and MySQL


database - DB design

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 |
|+----------------+--------------+---------+------------+

You can create these tables using these commands.

users:

CREATE TABLE `users` (

`id` int(11) AUTO_INCREMENT PRIMARY KEY NOT NULL,

`username` varchar(255) NOT NULL,

`email` varchar(255) NOT NULL,

`role` enum('Author','Admin') DEFAULT NULL,

`password` varchar(255) NOT NULL,

`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,

`updated_at` timestamp NULL DEFAULT NULL) ENGINE=InnoDB DEFAULT


CHARSET=latin1;

posts:

CREATE TABLE `posts` (

`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,

`user_id` int(11) DEFAULT NULL,

`title` varchar(255) NOT NULL,

`slug` varchar(255) NOT NULL UNIQUE,

`views` int(11) NOT NULL DEFAULT '0',

`image` varchar(255) NOT NULL,

`body` text NOT NULL,

`published` tinyint(1) NOT NULL,


`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP,

`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',

FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION


ON UPDATE NO ACTION) ENGINE=InnoDB DEFAULT CHARSET=latin1

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:

INSERT INTO `users` (`id`, `username`, `email`, `role`, `password`,


`created_at`, `updated_at`) VALUES(1, 'Awa', 'info@codewithawa.com',
'Admin', 'mypassword', '2018-01-08 12:52:58', '2018-01-08 12:52:58')

posts:

INSERT INTO `posts` (`id`, `user_id`, `title`, `slug`, `views`, `image`,


`body`, `published`, `created_at`, `updated_at`) VALUES(1, 1, '5 Habits
that can improve your life', '5-habits-that-can-improve-your-life', 0,
'banner.jpg', 'Read every day', 1, '2018-02-03 07:58:02', '2018-02-01
19:14:31'),(2, 1, 'Second post on LifeBlog', 'second-post-on-lifeblog',
0, 'banner.jpg', 'This is the body of the second post on this site', 0,
'2018-02-02 11:40:14', '2018-02-01 13:04:36')

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

session_start(); // connect to database $conn =


mysqli_connect("localhost", "root", "", "complete-blog-php");

if (!$conn) {

die("Error connecting to database: " .


mysqli_connect_error());

// define global constants define ('ROOT_PATH',


realpath(dirname(__FILE__)));

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 /* * * * * * * * * * * * * * *

* Returns all published posts

* * * * * * * * * * * * * * */function getPublishedPosts() { // use


global $conn object in function global $conn;

$sql = "SELECT * FROM posts WHERE published=true";

$result = mysqli_query($conn, $sql);

// fetch all posts as an associative array called $posts


$posts = mysqli_fetch_all($result, MYSQLI_ASSOC);

return $posts;}// more functions to come here ...?>

At the top section of the index.php file, just below the line that includes config.php,
add this code to query the database:

<!-- config.php should be here as the first include -->

<?php require_once( ROOT_PATH . '/includes/public_functions.php') ?>

<!-- Retrieve all posts from database --><?php $posts =


getPublishedPosts(); ?>

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:

<hr><!-- more content still to come here ... -->

<!-- Add this ... --><?php foreach ($posts as $post): ?>

<div class="post" style="margin-left: 0px;">

<img src="<?php echo BASE_URL . '/static/images/' .


$post['image']; ?>" class="post_image" alt="">

<a href="single_post.php?post-slug=<?php echo


$post['slug']; ?>">

<div class="post_info">

<h3><?php echo $post['title'] ?></h3>

<div class="info">

<span><?php echo date("F j, Y ",


strtotime($post["created_at"])); ?></span>

<span class="read_more">Read
more...</span>

</div>

</div>

</a>

</div><?php endforeach ?>

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 {

margin: 5px auto;

border-radius: 5px;

min-height: 400px;}.content:after {

content: "";
display: block;

clear: both;}.content .content-title {

margin: 10px 0px;

color: #374447;

font-family: 'Averia Serif Libre', cursive;}.content .post {

width: 335px;

margin: 9px;

min-height: 320px;

float: left;

border-radius: 2px;

border: 1px solid #b3b3b3;

position: relative;}.content .post .category {

margin-top: 0px;

padding: 3px 8px;

color: #374447;

background: white;

display: inline-block;

border-radius: 2px;

border: 1px solid #374447;

box-shadow: 3px 2px 2px;

position: absolute;

left: 5px; top: 5px;

z-index: 3;}.content .post .category:hover {

box-shadow: 3px 2px 2px;

color: white;

background: #374447;

transition: .4s;

opacity: 1;}.content .post .post_image {


height: 260px;

width: 100%;

background-size: 100%;}.content .post .post_image {

width: 100%;

height: 260px;}.content .post .post_info {

height: 100%;

padding: 0px 5px;

font-weight: 200;

font-family: 'Noto Serif', serif;}.content .post .post_info {

color: #222;}.content .post .post_info span {

color: #A6A6A6;

font-style: italic;}.content .post .post_info span.read_more {

position: absolute;

right: 5px; bottom: 5px;}

Now you can reload the page.

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.

ON DELETE and ON UPDATE are all set to CASCADE and NO ACTION


respectively so that when a post or a topic is deleted, it's relationship info in the
post_topic table is automatically deleted too. (In the image I made a mistake of setting
ON UPDATE to CASCADE instead of NO ACTION, sorry for that).

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.

Now let's insert a few entries into the two tables:


topics:

INSERT INTO `topics` (`id`, `name`, `slug`) VALUES(1, 'Inspiration',


'inspiration'),(2, 'Motivation', 'motivation'),(3, 'Diary', 'diary')

post_topic:

INSERT INTO `post_topic` (`id`, `post_id`, `topic_id`) VALUES(1, 1, 1),(2,


2, 2)

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.

To do this, we have to modify our getPublishedPosts() we created inside


public_functions.php to query the topic of each post from the database and return the
post alongside its topic.

Modify the public_functions.php file to look like this:

<?php /* * * * * * * * * * * * * * *

* Returns all published posts

* * * * * * * * * * * * * * */function getPublishedPosts() { // use


global $conn object in function global $conn;

$sql = "SELECT * FROM posts WHERE published=true";

$result = mysqli_query($conn, $sql); // fetch all posts as an


associative array called $posts $posts =
mysqli_fetch_all($result, MYSQLI_ASSOC);

$final_posts = array();

foreach ($posts as $post) {

$post['topic'] = getPostTopic($post['id']);

array_push($final_posts, $post);

}
return $final_posts;}/* * * * * * * * * * * * * * *

* Receives a post id and

* Returns topic of the post

* * * * * * * * * * * * * * */function getPostTopic($post_id){

global $conn;

$sql = "SELECT * FROM topics WHERE id=

(SELECT topic_id FROM post_topic WHERE


post_id=$post_id) LIMIT 1";

$result = mysqli_query($conn, $sql);

$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:

<?php foreach ($posts as $post): ?>

<div class="post" style="margin-left: 0px;">

<img src="<?php echo BASE_URL . '/static/images/' .


$post['image']; ?>" class="post_image" alt="">

<!-- Added this if statement... -->

<?php if (isset($post['topic']['name'])): ?>

<a

href="<?php echo BASE_URL .


'filtered_posts.php?topic=' . $post['topic']['id'] ?>"

class="btn category">

<?php echo $post['topic']['name'] ?>

</a>

<?php endif ?>


<a href="single_post.php?post-slug=<?php echo
$post['slug']; ?>">

<div class="post_info">

<h3><?php echo $post['title'] ?></h3>

<div class="info">

<span><?php echo date("F j, Y ",


strtotime($post["created_at"])); ?></span>

<span class="read_more">Read
more...</span>

</div>

</div>

</a>

</div><?php endforeach ?>

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:

/* * * * * * * * * * * * * * * *

* Returns all posts under a topic

* * * * * * * * * * * * * * * * */function getPublishedPostsByTopic($topic_id)
{
global $conn;

$sql = "SELECT * FROM posts ps

WHERE ps.id IN

(SELECT pt.post_id FROM post_topic pt

WHERE pt.topic_id=$topic_id GROUP BY


pt.post_id

HAVING COUNT(1) = 1)";

$result = mysqli_query($conn, $sql); // fetch all posts as an


associative array called $posts $posts =
mysqli_fetch_all($result, MYSQLI_ASSOC);

$final_posts = array();

foreach ($posts as $post) {

$post['topic'] = getPostTopic($post['id']);

array_push($final_posts, $post);

return $final_posts;}/* * * * * * * * * * * * * * * *

* Returns topic name by topic id

* * * * * * * * * * * * * * * * */function getTopicNameById($id){

global $conn;

$sql = "SELECT name FROM topics WHERE id=$id";

$result = mysqli_query($conn, $sql);

$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);

}?>

<title>LifeBlog | Home </title></head><body><div


class="container"><!-- Navbar -->

<?php include( ROOT_PATH . '/includes/navbar.php'); ?><!-- //


Navbar --><!-- content --><div class="content">

<h2 class="content-title">

Articles on <u><?php echo


getTopicNameById($topic_id); ?></u>

</h2>

<hr>

<?php foreach ($posts as $post): ?>

<div class="post" style="margin-left: 0px;">

<img src="<?php echo BASE_URL .


'/static/images/' . $post['image']; ?>" class="post_image" alt="">

<a href="single_post.php?post-slug=<?php echo


$post['slug']; ?>">

<div class="post_info">

<h3><?php echo
$post['title'] ?></h3>

<div class="info">

<span><?php echo date("F


j, Y ", strtotime($post["created_at"])); ?></span>

<span
class="read_more">Read more...</span>

</div>
</div>

</a>

</div>

<?php endforeach ?></div><!-- // content --></div><!-- //


container -->

<!-- Footer -->

<?php include( ROOT_PATH . '/includes/footer.php'); ?><!-- //


Footer -->

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:

/* * * * * * * * * * * * * * *

* Returns a single post

* * * * * * * * * * * * * * */function getPost($slug){

global $conn; // Get single post slug $post_slug =


$_GET['post-slug'];

$sql = "SELECT * FROM posts WHERE slug='$post_slug' AND


published=true";

$result = mysqli_query($conn, $sql);

// fetch query results as associative array. $post =


mysqli_fetch_assoc($result);

if ($post) {

// get the topic to which this post belongs


$post['topic'] = getPostTopic($post['id']);

return $post;}/* * * * * * * * * * * *

* Returns all topics

* * * * * * * * * * * * */function getAllTopics(){

global $conn;
$sql = "SELECT * FROM topics";

$result = mysqli_query($conn, $sql);

$topics = mysqli_fetch_all($result, MYSQLI_ASSOC);

return $topics;}

Now create the file complete-blog-php/single_post.php and paste this code into it:

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


include('includes/public_functions.php'); ?><?php

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">

<!-- Navbar -->

<?php include( ROOT_PATH . '/includes/navbar.php'); ?>

<!-- // Navbar -->

<div class="content" >

<!-- Page wrapper -->

<div class="post-wrapper">

<!-- full post div -->

<div class="full-post-div">

<?php if ($post['published'] == false): ?>

<h2 class="post-title">Sorry... This


post has not been published</h2>

<?php else: ?>

<h2 class="post-title"><?php echo


$post['title']; ?></h2>
<div class="post-body-div">

<?php echo
html_entity_decode($post['body']); ?>

</div>

<?php endif ?>

</div>

<!-- // full post div -->

<!-- comments section -->

<!-- coming soon ... -->

</div>

<!-- // Page wrapper -->

<!-- post sidebar -->

<div class="post-sidebar">

<div class="card">

<div class="card-header">

<h2>Topics</h2>

</div>

<div class="card-content">

<?php foreach ($topics as


$topic): ?>

<a

href="<?php echo
BASE_URL . 'filtered_posts.php?topic=' . $topic['id'] ?>">

<?php echo
$topic['name']; ?>

</a>

<?php endforeach ?>


</div>

</div>

</div>

<!-- // post sidebar -->

</div></div><!-- // content -->

<?php include( ROOT_PATH . '/includes/footer.php'); ?>

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;

border: 1px solid #e4e1e1;

border-radius: 2px;}.full-post-div h2.post-title {

margin: 10px auto 20px;

text-align: center;}.post-body-div {

font-family: 'Noto Serif', serif;

font-size: 1.2em;}.post-body-div p {

margin:20px 0px;}.post-sidebar {

width: 24%;

float: left;

margin-left: 5px;

min-height: 400px;}.content .post-comments {

margin-top: 25px;
border-radius: 2px;

border-top: 1px solid #e4e1e1;

padding: 10px;}.post-sidebar .card {

width: 95%;

margin: 10px auto;

border: 1px solid #e4e1e1;

border-radius: 10px 10px 0px


0px;}.post-sidebar .card .card-header {

padding: 10px;

text-align: center;

border-radius: 3px 3px 0px 0px;

background: #3E606F;}.post-sidebar .card .card-header h2 {

color: white;}.post-sidebar .card .card-content a {

display: block;

box-sizing: border-box;

padding: 8px 10px;

border-bottom: 1px solid #e4e1e1;

color: #444;}.post-sidebar .card .card-content a:hover {

padding-left: 20px;

background: #F9F9F9;

transition: 0.1s;}

Looks good now right?

One last thing to do and we'll be pretty much done with the public area: We'll be
implementing user registration and login.

User registration and login


Because I have already made a tutorial on user registration and login, I will be pretty
much to the point with this part and won't do much explaining.
Create two files in your root folder named register.php and login.php. Open each of
them and place this code in them:

register.php:

<?php include('config.php'); ?><!-- Source code for handling


registration and login --><?php
include('includes/registration_login.php'); ?>

<?php include('includes/head_section.php'); ?>

<title>LifeBlog | Sign up </title></head><body><div class="container">

<!-- Navbar -->

<?php include( ROOT_PATH . '/includes/navbar.php'); ?>

<!-- // Navbar -->

<div style="width: 40%; margin: 20px auto;">

<form method="post" action="register.php" >

<h2>Register on LifeBlog</h2>

<?php include(ROOT_PATH .
'/includes/errors.php') ?>

<input type="text" name="username"


value="<?php echo $username; ?>" placeholder="Username">

<input type="email" name="email" value="<?php


echo $email ?>" placeholder="Email">

<input type="password" name="password_1"


placeholder="Password">

<input type="password" name="password_2"


placeholder="Password confirmation">

<button type="submit" class="btn"


name="reg_user">Register</button>

<p>

Already a member? <a


href="login.php">Sign in</a>

</p>
</form>

</div></div><!-- // container --><!-- Footer -->

<?php include( ROOT_PATH . '/includes/footer.php'); ?><!-- //


Footer -->

login.php:

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


include('includes/registration_login.php'); ?><?php
include('includes/head_section.php'); ?>

<title>LifeBlog | Sign in </title></head><body><div


class="container">

<!-- Navbar -->

<?php include( ROOT_PATH . '/includes/navbar.php'); ?>

<!-- // Navbar -->

<div style="width: 40%; margin: 20px auto;">

<form method="post" action="login.php" >

<h2>Login</h2>

<?php include(ROOT_PATH .
'/includes/errors.php') ?>

<input type="text" name="username" value="<?php


echo $username; ?>" value="" placeholder="Username">

<input type="password" name="password"


placeholder="Password">

<button type="submit" class="btn"


name="login_btn">Login</button>

<p>

Not yet a member? <a


href="register.php">Sign up</a>

</p>

</form>
</div></div><!-- // container -->

<!-- Footer -->

<?php include( ROOT_PATH . '/includes/footer.php'); ?><!-- //


Footer -->

In the top sections of both files, we included a file named registration_login.php to


handle the logic of registration and login. This is the file to which the login and
register form info will be submitted to and communication with the database will be
done. Let's create it in our includes folder and spit this code inside it:

complete-blog-php/includes/registration_login.php:

<?php // variable declaration $username = "";

$email = "";

$errors = array();

// REGISTER USER if (isset($_POST['reg_user'])) {

// receive all input values from the form


$username = esc($_POST['username']);

$email = esc($_POST['email']);

$password_1 = esc($_POST['password_1']);

$password_2 = esc($_POST['password_2']);

// form validation: ensure that the form is correctly


filled if (empty($username)) { array_push($errors, "Uhmm...We
gonna need your username"); }

if (empty($email)) { array_push($errors, "Oops.. Email


is missing"); }

if (empty($password_1)) { array_push($errors, "uh-oh you


forgot the password"); }

if ($password_1 != $password_2) { array_push($errors,


"The two passwords do not match");}

// Ensure that no user is registered twice.


// the email and usernames should be unique
$user_check_query = "SELECT * FROM users WHERE
username='$username'

OR
email='$email' LIMIT 1";

$result = mysqli_query($conn, $user_check_query);

$user = mysqli_fetch_assoc($result);

if ($user) { // if user exists if


($user['username'] === $username) {

array_push($errors, "Username already


exists");

if ($user['email'] === $email) {

array_push($errors, "Email already exists");

// register user if there are no errors in the form if (coun

$password = md5($password_1);//encrypt the


password before saving in the database $query = "INSERT
INTO users (username, email, password, created_at, updated_at)

VALUES('$username', '$email',
'$password', now(), now())";

mysqli_query($conn, $query);

// get id of created user


$reg_user_id = mysqli_insert_id($conn);

// put logged in user into session array $

// if user is admin, redirect to admin area i


$_SESSION['message'] = "You are now
logged in";

// redirect to admin area h

exit(0);

} else {

$_SESSION['message'] = "You are now


logged in";

// redirect to public area h

exit(0);

// LOG USER IN if (isset($_POST['login_btn'])) {

$username = esc($_POST['username']);

$password = esc($_POST['password']);

if (empty($username)) { array_push($errors, "Username


required"); }

if (empty($password)) { array_push($errors, "Password


required"); }

if (empty($errors)) {

$password = md5($password); // encrypt password


$sql = "SELECT * FROM users WHERE
username='$username' and password='$password' LIMIT 1";

$result = mysqli_query($conn, $sql);

if (mysqli_num_rows($result) > 0) {

// get id of created user $


// put logged in user into session array
$_SESSION['user'] =
getUserById($reg_user_id);

// if user is admin, redirect to admin


area if ( in_array($_SESSION['user']['role'],
["Admin", "Author"])) {

$_SESSION['message'] = "You are


now logged in";

// redirect to admin area

exit(0);

} else {

$_SESSION['message'] = "You are


now logged in";

// redirect to public area

exit(0);

} else {

array_push($errors, 'Wrong
credentials');

} // escape value from form function esc(String


$value)

// bring the global db connect object into function global $

$val = trim($value); // remove empty space sorrounding


string $val = mysqli_real_escape_string($conn, $value);

return $val;
} // Get user info from user id function
getUserById($id)

global $conn;

$sql = "SELECT * FROM users WHERE id=$id LIMIT 1";

$result = mysqli_query($conn, $sql);

$user = mysqli_fetch_assoc($result);

// returns user in an array format: //


['id'=>1 'username' => 'Awa', 'email'=>'a@a.com', 'password'=> 'mypass']
return $user;

}?>

Go to http://localhost/complete-blog-php/register.php and you'll see an error saying


that errors.php file not found.

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:

<?php if (count($errors) > 0) : ?>

<div class="message error validation_errors" >

<?php foreach ($errors as $error) : ?>

<p><?php echo $error ?></p>

<?php endforeach ?>

</div><?php endif ?>

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:

/* NOTIFICATION MESSAGES */.message {

width: 100%;

margin: 0px auto;

padding: 10px 0px;


color: #3c763d;

background: #dff0d8;

border: 1px solid #3c763d;

border-radius: 5px;

text-align: center;}.error {

color: #a94442;

background: #f2dede;

border: 1px solid #a94442;

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:

<?php if (isset($_SESSION['user']['username'])) { ?>

<div class="logged_in_info">

<span>welcome <?php echo


$_SESSION['user']['username'] ?></span>

<span><a href="logout.php">logout</a></span>

</div><?php }else{ ?>

<div class="banner">

<div class="welcome_msg">

<h1>Today's Inspiration</h1>

<p>

One day your life <br>

will flash before your eyes. <br>

Make sure it's worth watching. <br>

<span>~ Gerard Way</span>

</p>

<a href="register.php" class="btn">Join us!</a>

</div>

<div class="login_div">

<form action="<?php echo BASE_URL .


'index.php'; ?>" method="post" >

<h2>Login</h2>

<div style="width: 60%; margin: 0px


auto;">
<?php include(ROOT_PATH .
'/includes/errors.php') ?>

</div>

<input type="text" name="username"


value="<?php echo $username; ?>" placeholder="Username">

<input type="password" name="password"


placeholder="Password">

<button class="btn" type="submit"


name="login_btn">Sign in</button>

</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:

top section of complete-blog-php/index.php:

<?php require_once( ROOT_PATH . '/includes/registration_login.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)

How to create a blog in PHP and MySQL


database - Backend

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.

In your complete-blog-php/admin folder, create a file named dashboard.php.

dashboard.php:

<?php include('../config.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">

<a href="<?php echo


BASE_URL .'admin/dashboard.php' ?>">

<h1>LifeBlog - Admin</h1>

</a>

</div>

<?php if (isset($_SESSION['user'])): ?>

<div class="user-info">

<span><?php echo
$_SESSION['user']['username'] ?></span> &nbsp; &nbsp;

<a href="<?php echo BASE_URL .


'/logout.php'; ?>" class="logout-btn">logout</a>

</div>

<?php endif ?>

</div>

<div class="container dashboard">

<h1>Welcome</h1>

<div class="stats">

<a href="users.php" class="first">

<span>43</span> <br>

<span>Newly registered users</span>

</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">

<a href="users.php">Add Users</a>

<a href="posts.php">Add Posts</a>

</div>

</div></body></html>

On your browser, visit http://localhost/complete-blog-php/admin/dashboard.php and


you will see that there is no styling and there are error messages about failure to
include some two files. That's because we have not yet created these files. We'll do
that soon.

Create a folder inside your admin folder named includes.

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:

<!DOCTYPE html><html><head><meta charset="UTF-8"><!-- Google Fonts


--><link
href="https://fonts.googleapis.com/css?family=Averia+Serif+Libre|Noto
+Serif|Tangerine" rel="stylesheet"><!-- Font awesome --><link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/f
ont-awesome.min.css" /><!-- ckeditor --><script
src="https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.8.0/ckeditor.j
s"></script><!-- Styling for public area --><link rel="stylesheet"
href="../static/css/admin_styling.css">
Reload the dashboard.php page on the browser. Now the error message that remains is
for just one file (admin_functions.php). We'll come to that soon.

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

Now open users.php and add this code to it:

users.php:

<?php include('../config.php'); ?><?php include(ROOT_PATH .


'/admin/includes/admin_functions.php'); ?><?php // Get all admin
users from DB $admins = getAdminUsers();

$roles = ['Admin', 'Author']; ?><?php


include(ROOT_PATH . '/admin/includes/head_section.php'); ?>

<title>Admin | Manage users</title></head><body>

<!-- admin navbar -->

<?php include(ROOT_PATH . '/admin/includes/navbar.php') ?>

<div class="container content">

<!-- Left side menu -->

<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>

<!-- Middle form - to create and edit -->

<div class="action">

<h1 class="page-title">Create/Edit Admin


User</h1>
<form method="post" action="<?php echo BASE_URL .
'admin/users.php'; ?>" >

<!-- validation errors for the form -->

<?php include(ROOT_PATH .
'/includes/errors.php') ?>

<!-- if editing user, the id is required


to identify that user -->

<?php if ($isEditingUser === true): ?>

<input type="hidden"
name="admin_id" value="<?php echo $admin_id; ?>">

<?php endif ?>

<input type="text" name="username"


value="<?php echo $username; ?>" placeholder="Username">

<input type="email" name="email"


value="<?php echo $email ?>" placeholder="Email">

<input type="password" name="password"


placeholder="Password">

<input type="password"
name="passwordConfirmation" placeholder="Password confirmation">

<select name="role">

<option value="" selected


disabled>Assign role</option>

<?php foreach ($roles as $key =>


$role): ?>

<option value="<?php
echo $role; ?>"><?php echo $role; ?></option>

<?php endforeach ?>

</select>
<!-- if editing user, display the update
button instead of create button -->

<?php if ($isEditingUser === true): ?>

<button type="submit"
class="btn" name="update_admin">UPDATE</button>

<?php else: ?>

<button type="submit"
class="btn" name="create_admin">Save User</button>

<?php endif ?>

</form>

</div>

<!-- // Middle form - to create and edit -->

<!-- Display records from DB-->

<div class="table-div">

<!-- Display notification message -->

<?php include(ROOT_PATH .
'/includes/messages.php') ?>

<?php if (empty($admins)): ?>

<h1>No admins in the database.</h1>

<?php else: ?>

<table class="table">

<thead>

<th>N</th>

<th>Admin</th>

<th>Role</th>

<th
colspan="2">Action</th>
</thead>

<tbody>

<?php foreach ($admins as $key =>


$admin): ?>

<tr>

<td><?php echo
$key + 1; ?></td>

<td>

<?php
echo $admin['username']; ?>, &nbsp;

<?php
echo $admin['email']; ?>

</td>

<td><?php echo
$admin['role']; ?></td>

<td>

<a
class="fa fa-pencil btn edit"

href="users.php?edit-admin=<?php echo $admin['id'] ?>">

</a>

</td>

<td>

<a
class="fa fa-trash btn delete"

href="users.php?delete-admin=<?php echo $admin['id'] ?>">

</a>

</td>

</tr>

<?php endforeach ?>


</tbody>

</table>

<?php endif ?>

</div>

<!-- // Display records from DB -->

</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:

<?php // Admin user variables$admin_id = 0;$isEditingUser =


false;$username = "";$role = "";$email = "";// general variables$errors
= [];

/* - - - - - - - - - -

- Admin users actions

- - - - - - - - - - -*/// if user clicks the create admin buttonif


(isset($_POST['create_admin'])) {

createAdmin($_POST);}// if user clicks the Edit admin buttonif


(isset($_GET['edit-admin'])) {

$isEditingUser = true;

$admin_id = $_GET['edit-admin'];

editAdmin($admin_id);}// if user clicks the update admin buttonif


(isset($_POST['update_admin'])) {

updateAdmin($_POST);}// if user clicks the Delete admin buttonif


(isset($_GET['delete-admin'])) {

$admin_id = $_GET['delete-admin'];
deleteAdmin($admin_id);}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *

* - Returns all admin users and their corresponding roles

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */function
getAdminUsers(){

global $conn, $roles;

$sql = "SELECT * FROM users WHERE role IS NOT NULL";

$result = mysqli_query($conn, $sql);

$users = mysqli_fetch_all($result, MYSQLI_ASSOC);

return $users;}/* * * * * * * * * * * * * * * * * * * * *

* - Escapes form submitted value, hence, preventing SQL injection

* * * * * * * * * * * * * * * * * * * * * */function esc(String $value){


// bring the global db connect object into function global
$conn; // remove empty space sorrounding string $val =
trim($value);

$val = mysqli_real_escape_string($conn, $value);

return $val;}// Receives a string like 'Some Sample String'// and


returns 'some-sample-string'function makeSlug(String $string){

$string = strtolower($string);

$slug = preg_replace('/[^A-Za-z0-9-]+/', '-', $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">

<a href="<?php echo BASE_URL .'admin/dashboard.php' ?>">

<h1>LifeBlog - Admin</h1>

</a>

</div>

<div class="user-info">

<span>Awa</span> &nbsp; &nbsp; <a href="<?php echo


BASE_URL . '/logout.php'; ?>" class="logout-btn">logout</a>

</div></div>

menu.php:

<div class="menu">

<div class="card">

<div class="card-header">

<h2>Actions</h2>

</div>

<div class="card-content">

<a href="<?php echo BASE_URL .


'admin/create_post.php' ?>">Create Posts</a>

<a href="<?php echo BASE_URL .


'admin/posts.php' ?>">Manage Posts</a>

<a href="<?php echo BASE_URL .


'admin/users.php' ?>">Manage Users</a>

<a href="<?php echo BASE_URL .


'admin/topics.php' ?>">Manage Topics</a>

</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

* * * * * * * * * */* { margin: 0px; padding: 0px; }a { text-decoration:


none; }h1, h2, h3, h4, h5, h6 { font-family: 'Noto Serif', serif; }/* forms
*/form { width: 60%; margin: 5px auto; padding-bottom: 50px; }form
input[type=file], input[type=email], input[type=password],
input[type=text], form select, form textarea {

width: 100%;

display: block;

padding: 13px 13px;

font-size: 1em;

margin: 5px auto 10px;

border-radius: 3px;

box-sizing : border-box;

background: transparent;

border: 1px solid #3E606F;}input[type="checkbox"] { height: 20px;


float: left; }form button { float: right; margin-left: 24%; }form
input:focus { outline: none; }label { margin-top: 20px; float:
left; }/* tables */table { border-collapse: collapse; width: 70%; margin:
20px auto; }th, td { padding: 8px; text-align: left; border: 1px solid
#ddd; }th { text-align: center;}/* buttons */.btn {

color: white;

background: #4E6166;

text-align: center;

border: none;

border-radius: 5px;

display: block;

letter-spacing: .1em;

padding: 13px 20px;

text-decoration: none;}/* * * * * * * * * *

* HEADER

* * * * * * * * * */.header {
padding: 15px 45px;

font-family: 'Noto Serif', serif;

color: white;

background: black;}.header .logo { width: 50%; float:


left; }.header .logo h1 { color: white; }.header .user-info { width: 10%;
margin-top: 10px; float: right;}.header .logout-btn { color: red;
text-decoration: none; }.header:after{ content: ""; display: block; clear:
both; }/* * * * * * * * * *

* DASHBOARD

* * * * * * * * * */.container {

width: 95%;

margin: 5px auto 50px;

border: 1px solid #BFBCB3;

padding: 10px 0px 50px;}.container:after { content: ""; display:


block; clear: both; }.container.dashboard h1 { text-align: center; margin:
25px; }.container.dashboard .stats a {

display: inline-block;

padding: 30px;

margin: 5px;

width: 25%;

text-align: center;

border-radius: 3px;

border: 1px solid #BFBCB3;}.container.dashboard .stats a.first


{ margin-left: 25px; }.container.dashboard .stats a:hover { cursor:
pointer; background-color: #E1E1E1; }.container.dashboard .buttons
{ margin-left: 15px; }.container.dashboard .buttons a {

display: inline-block;

margin: 10px;

text-decoration: none;

color: #444;

padding: 10px 25px;


border: none;

background-color: #0E7D92;

color: white;}/* * * * * * * * * *

* PAGE CONTENT

* * * * * * * * * */.container.content .menu { width: 16%; float: left;


padding: 40px 10px; }/* Menu card
*/.container.content .menu .card .card-header {

padding: 10px;

text-align: center;

border-radius: 3px 3px 0px 0px;

background:
#3E606F;}.container.content .menu .card .card-header h2 { color:
white; }.container.content .menu .card .card-content a {

display: block;

box-sizing: border-box;

padding: 8px 10px;

border-bottom: 1px solid #e4e1e1;

color: #444;}.container.content .menu .card .card-content


a:hover {

padding-left: 20px; background: #F9F9F9; transition: 0.1s;}/*


Actions div (at the middle) */.container.content .action { width: 35%;
float: left; text-align: center; }.container.content .action form { width:
90%; }.container.content .action .page-title { margin:
25px; }.container.content .action.create-post-div { width: 80%; }/* Table
div (Displaying records from DB) */.table-div { float: left; width:
47%; }.table-div .message { width: 90%; margin-top: 20px; }.table-div
table { width: 90%; }.table-div a.fa { color: white; padding:
3px; }.table-div .edit { background: #004220; }.table-div .delete
{ background: #F70E1A; }.table-div .publish { background:
red; }.table-div .unpublish { background: green; }/* * * * * * * * * *

* VALIDATION ERRORS

* * * * * * * * * */.message {

width: 100%;
margin: 0px auto;

padding: 10px 0px;

color: #3c763d;

background: #dff0d8;

border: 1px solid #3c763d;

border-radius: 5px;

text-align: center;}.error {color: #a94442; background: #f2dede;


border: 1px solid #a94442; margin-bottom: 20px; }.validation_errors p
{text-align: left;margin-left: 10px;}

In your browser, go to http://localhost/complete-blog-php/admin/users.php you'll see


an error that says file messages.php is not found. Create a file named messages.php in
your complete-blog-ph/includes folder and add this code in it:

<?php if (isset($_SESSION['message'])) : ?>

<div class="message" >

<p>

<?php

echo $_SESSION['message'];

unset($_SESSION['message']);

?>

</p>

</div><?php endif ?>

This displays notification messages to give the user feedback on their actions.

Create, Read, Update and Deleting Admin users


The users.php page already presents a form for both creating and updating an admin
user, as well as a table for listing and deleting users. All that is left is to add the PHP
functions that perform these actions. So open admin_functions.php file and add this
code directly after the Admin user actions (the if statements) at the top of the page and
before the getAdminUsers() function:

admin_functions.php:
/* - - - - - - - - - - - -

- Admin users functions

- - - - - - - - - - - - -*//* * * * * * * * * * * * * * * * * * * * * *
*

* - Receives new admin data from form

* - Create new admin user

* - Returns all admin users with their roles

* * * * * * * * * * * * * * * * * * * * * * */function
createAdmin($request_values){

global $conn, $errors, $role, $username, $email;

$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']);

} // form validation: ensure that the form is correctly


filled if (empty($username)) { array_push($errors, "Uhmm...We gonna
need the username"); }

if (empty($email)) { array_push($errors, "Oops.. Email is


missing"); }

if (empty($role)) { array_push($errors, "Role is required for


admin users");}

if (empty($password)) { array_push($errors, "uh-oh you forgot the


password"); }

if ($password != $passwordConfirmation) { array_push($errors,


"The two passwords do not match"); } // Ensure that no user is
registered twice. // the email and usernames should be unique
$user_check_query = "SELECT * FROM users WHERE
username='$username'
OR
email='$email' LIMIT 1";

$result = mysqli_query($conn, $user_check_query);

$user = mysqli_fetch_assoc($result);

if ($user) { // if user exists if ($user['username']


=== $username) {

array_push($errors, "Username already exists");

if ($user['email'] === $email) {

array_push($errors, "Email already exists");

} // register user if there are no errors in the form


if (count($errors) == 0) {

$password = md5($password);//encrypt the password before


saving in the database $query = "INSERT INTO users (username,
email, role, password, created_at, updated_at)

VALUES('$username', '$email', '$role',


'$password', now(), now())";

mysqli_query($conn, $query);

$_SESSION['message'] = "Admin user created


successfully";

header('location: users.php');

exit(0);

}}/* * * * * * * * * * * * * * * * * * * * *

* - Takes admin id as parameter

* - Fetches the admin from database

* - sets admin fields on form for editing

* * * * * * * * * * * * * * * * * * * * * */function editAdmin($admin_id){
global $conn, $username, $role, $isEditingUser, $admin_id,
$email;

$sql = "SELECT * FROM users WHERE id=$admin_id LIMIT 1";

$result = mysqli_query($conn, $sql);

$admin = mysqli_fetch_assoc($result);

// set form values ($username and $email) on the form to be updated


$username = $admin['username'];

$email = $admin['email'];}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

* - Receives admin request from form and updates in database

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */function
updateAdmin($request_values){

global $conn, $errors, $role, $username, $isEditingUser,


$admin_id, $email; // get id of the admin to be updated
$admin_id = $request_values['admin_id']; // set edit state
to false $isEditingUser = false;

$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'];

} // register user if there are no errors in the form


if (count($errors) == 0) {

//encrypt the password (security purposes)


$password = md5($password);
$query = "UPDATE users SET username='$username',
email='$email', role='$role', password='$password' WHERE id=$admin_id";

mysqli_query($conn, $query);

$_SESSION['message'] = "Admin user updated


successfully";

header('location: users.php');

exit(0);

}}// delete admin user function deleteAdmin($admin_id) {

global $conn;

$sql = "DELETE FROM users WHERE id=$admin_id";

if (mysqli_query($conn, $sql)) {

$_SESSION['message'] = "User successfully deleted";

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.

Now go to http://localhost/complete/admin/users.php. Create a user and assign that


user Admin role. Make sure you remember the username and password of this admin
user because in the future we will log in with this user. For example:

username: John, email: john@example.com, password: johndoe.

Create, Read, Update and Delete Topics


For topics, we are going to create just one file, topics.php inside
complete-blog-php/admin/ folder.

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(); ?>

<title>Admin | Manage Topics</title></head><body>

<!-- admin navbar -->

<?php include(ROOT_PATH . '/admin/includes/navbar.php') ?>

<div class="container content">

<!-- Left side menu -->

<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>

<!-- Middle form - to create and edit -->

<div class="action">

<h1 class="page-title">Create/Edit Topics</h1>

<form method="post" action="<?php echo BASE_URL .


'admin/topics.php'; ?>" >

<!-- validation errors for the form -->

<?php include(ROOT_PATH .
'/includes/errors.php') ?>

<!-- if editing topic, the id is required


to identify that topic -->

<?php if ($isEditingTopic === true): ?>

<input type="hidden"
name="topic_id" value="<?php echo $topic_id; ?>">

<?php endif ?>

<input type="text" name="topic_name"


value="<?php echo $topic_name; ?>" placeholder="Topic">

<!-- if editing topic, display the update


button instead of create button -->

<?php if ($isEditingTopic === true): ?>


<button type="submit"
class="btn" name="update_topic">UPDATE</button>

<?php else: ?>

<button type="submit"
class="btn" name="create_topic">Save Topic</button>

<?php endif ?>

</form>

</div>

<!-- // Middle form - to create and edit -->

<!-- Display records from DB-->

<div class="table-div">

<!-- Display notification message -->

<?php include(ROOT_PATH .
'/includes/messages.php') ?>

<?php if (empty($topics)): ?>

<h1>No topics in the database.</h1>

<?php else: ?>

<table class="table">

<thead>

<th>N</th>

<th>Topic Name</th>

<th
colspan="2">Action</th>

</thead>

<tbody>

<?php foreach ($topics as $key =>


$topic): ?>

<tr>
<td><?php echo
$key + 1; ?></td>

<td><?php echo
$topic['name']; ?></td>

<td>

<a
class="fa fa-pencil btn edit"

href="topics.php?edit-topic=<?php echo $topic['id'] ?>">

</a>

</td>

<td>

<a
class="fa fa-trash btn delete"

href="topics.php?delete-topic=<?php echo $topic['id'] ?>">

</a>

</td>

</tr>

<?php endforeach ?>

</tbody>

</table>

<?php endif ?>

</div>

<!-- // Display records from DB -->

</div></body></html>

Next we write the PHP code to inside admin_functions.php to perform these


operations on topic.

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:

<?php // Admin user variables// ... varaibles here ...// Topics


variables$topic_id = 0;$isEditingTopic = false;$topic_name = "";

/* - - - - - - - - - -

- Admin users actions

- - - - - - - - - - -*/// ... /* - - - - - - - - - -

- Topic actions

- - - - - - - - - - -*/// if user clicks the create topic buttonif


(isset($_POST['create_topic'])) { createTopic($_POST); }// if user
clicks the Edit topic buttonif (isset($_GET['edit-topic'])) {

$isEditingTopic = true;

$topic_id = $_GET['edit-topic'];

editTopic($topic_id);}// if user clicks the update topic buttonif


(isset($_POST['update_topic'])) {

updateTopic($_POST);}// if user clicks the Delete topic buttonif


(isset($_GET['delete-topic'])) {

$topic_id = $_GET['delete-topic'];

deleteTopic($topic_id);}

/* - - - - - - - - - - - -

- Admin users functions

- - - - - - - - - - - - -*/// .../* - - - - - - - - - -

- Topics functions

- - - - - - - - - - -*/// get all topics from DBfunction getAllTopics()


{

global $conn;

$sql = "SELECT * FROM topics";

$result = mysqli_query($conn, $sql);


$topics = mysqli_fetch_all($result, MYSQLI_ASSOC);

return $topics;}function createTopic($request_values){

global $conn, $errors, $topic_name;

$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)) {

array_push($errors, "Topic name required");

} // Ensure that no topic is saved twice.


$topic_check_query = "SELECT * FROM topics WHERE
slug='$topic_slug' LIMIT 1";

$result = mysqli_query($conn, $topic_check_query);

if (mysqli_num_rows($result) > 0) { // if topic exists array_pu

} // register topic if there are no errors in the form


if (count($errors) == 0) {

$query = "INSERT INTO topics (name, slug)

VALUES('$topic_name',
'$topic_slug')";

mysqli_query($conn, $query);

$_SESSION['message'] = "Topic created successfully";

header('location: topics.php');

exit(0);

}}/* * * * * * * * * * * * * * * * * * * * *

* - Takes topic id as parameter

* - Fetches the topic from database

* - sets topic fields on form for editing

* * * * * * * * * * * * * * * * * * * * * */function editTopic($topic_id)
{

global $conn, $topic_name, $isEditingTopic, $topic_id;

$sql = "SELECT * FROM topics WHERE id=$topic_id LIMIT 1";


$result = mysqli_query($conn, $sql);

$topic = mysqli_fetch_assoc($result); // set form values


($topic_name) on the form to be updated $topic_name =
$topic['name'];}function updateTopic($request_values) {

global $conn, $errors, $topic_name, $topic_id;

$topic_name = esc($request_values['topic_name']);

$topic_id = esc($request_values['topic_id']); // create slug:


if topic is "Life Advice", return "life-advice" as slug
$topic_slug = makeSlug($topic_name); // validate form
if (empty($topic_name)) {

array_push($errors, "Topic name required");

} // register topic if there are no errors in the form


if (count($errors) == 0) {

$query = "UPDATE topics SET name='$topic_name',


slug='$topic_slug' WHERE id=$topic_id";

mysqli_query($conn, $query);

$_SESSION['message'] = "Topic updated successfully";

header('location: topics.php');

exit(0);

}}// delete topic function deleteTopic($topic_id) {

global $conn;

$sql = "DELETE FROM topics WHERE id=$topic_id";

if (mysqli_query($conn, $sql)) {

$_SESSION['message'] = "Topic successfully deleted";

header("location: topics.php");

exit(0);

}}

With this, we can create, read, update and delete topics.


Thanks for your patience. In the next part, we add a form for creating posts. We use
ckeditor to give a user the ability to add styling to texts, images, and lists.

See you there!

Awa Melvine

Next part: Admin posts creation

How to create a blog in PHP and MySQL


database - Admin Posts

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.

Place this code in your posts.php file:

<?php include('../config.php'); ?><?php include(ROOT_PATH .


'/admin/includes/admin_functions.php'); ?><?php include(ROOT_PATH .
'/admin/includes/post_functions.php'); ?><?php include(ROOT_PATH .
'/admin/includes/head_section.php'); ?>

<!-- Get all admin posts from DB --><?php $posts = getAllPosts(); ?>
<title>Admin | Manage Posts</title></head><body>

<!-- admin navbar -->

<?php include(ROOT_PATH . '/admin/includes/navbar.php') ?>

<div class="container content">

<!-- Left side menu -->

<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>

<!-- Display records from DB-->

<div class="table-div" style="width: 80%;">

<!-- Display notification message -->

<?php include(ROOT_PATH .
'/includes/messages.php') ?>

<?php if (empty($posts)): ?>

<h1 style="text-align: center;


margin-top: 20px;">No posts in the database.</h1>

<?php else: ?>

<table class="table">

<thead>

<th>N</th>

<th>Title</th>

<th>Author</th>

<th>Views</th>

<!-- Only Admin can


publish/unpublish post -->

<?php if
($_SESSION['user']['role'] == "Admin"): ?>
<th><small>Publish</small></th>

<?php endif ?>

<th><small>Edit</small></th>

<th><small>Delete</small></th>

</thead>

<tbody>

<?php foreach ($posts as $key =>


$post): ?>

<tr>

<td><?php echo
$key + 1; ?></td>

<td><?php echo
$post['author']; ?></td>

<td>

<a
target="_blank"

href="<?php echo BASE_URL . 'single_post.php?post-slug=' .


$post['slug'] ?>">

<?php echo $post['title']; ?>

</a>

</td>

<td><?php echo
$post['views']; ?></td>

<!-- Only Admin


can publish/unpublish post -->
<?php if
($_SESSION['user']['role'] == "Admin" ): ?>

<td>

<?php if
($post['published'] == true): ?>

<a class="fa fa-check btn unpublish"

href="po

</a>

<?php
else: ?>

<a class="fa fa-times btn publish"

href="po

</a>

<?php
endif ?>

</td>

<?php endif ?>

<td>

<a
class="fa fa-pencil btn edit"

href="create_post.php?edit-post=<?php echo $post['id'] ?>">

</a>

</td>

<td>
<a
class="fa fa-trash btn delete"

href="create_post.php?delete-post=<?php echo $post['id'] ?>">

</a>

</td>

</tr>

<?php endforeach ?>

</tbody>

</table>

<?php endif ?>

</div>

<!-- // Display records from DB -->

</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:

<?php // Post variables$post_id = 0;$isEditingPost = false;$published =


0;$title = "";$post_slug = "";$body = "";$featured_image = "";$post_topic
= "";

/* - - - - - - - - - -

- Post functions

- - - - - - - - - - -*/// get all posts from DBfunction getAllPosts(){

global $conn;

// Admin can view all posts // Author can only view


their posts if ($_SESSION['user']['role'] == "Admin") {

$sql = "SELECT * FROM posts";

} elseif ($_SESSION['user']['role'] == "Author") {

$user_id = $_SESSION['user']['id'];
$sql = "SELECT * FROM posts WHERE user_id=$user_id";

$result = mysqli_query($conn, $sql);

$posts = mysqli_fetch_all($result, MYSQLI_ASSOC);

$final_posts = array();

foreach ($posts as $post) {

$post['author'] = getPostAuthorById($post['user_id']);

array_push($final_posts, $post);

return $final_posts;}// get the author/username of a postfunction


getPostAuthorById($user_id){

global $conn;

$sql = "SELECT username FROM users WHERE id=$user_id";

$result = mysqli_query($conn, $sql);

if ($result) {

// return username return


mysqli_fetch_assoc($result)['username'];

} else {

return null;

}}?>

Now go to http://localhost/complete-blog-php/login.php and log in with the


username and password you created in the last tutorial. If you don't login you
are going to encounter errors.

After logging in, go to http://localhost/complete-blog-php/admin/posts.php.

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('../config.php'); ?><?php include(ROOT_PATH .


'/admin/includes/admin_functions.php'); ?><?php include(ROOT_PATH .
'/admin/includes/post_functions.php'); ?><?php include(ROOT_PATH .
'/admin/includes/head_section.php'); ?><!-- Get all topics --><?php
$topics = getAllTopics(); ?>

<title>Admin | Create Post</title></head><body>

<!-- admin navbar -->

<?php include(ROOT_PATH . '/admin/includes/navbar.php') ?>

<div class="container content">

<!-- Left side menu -->

<?php include(ROOT_PATH .
'/admin/includes/menu.php') ?>

<!-- Middle form - to create and edit -->

<div class="action create-post-div">

<h1 class="page-title">Create/Edit Post</h1>

<form method="post"
enctype="multipart/form-data" action="<?php echo BASE_URL .
'admin/create_post.php'; ?>" >

<!-- validation errors for the form -->

<?php include(ROOT_PATH .
'/includes/errors.php') ?>

<!-- if editing post, the id is required


to identify that post -->

<?php if ($isEditingPost === true): ?>


<input type="hidden"
name="post_id" value="<?php echo $post_id; ?>">

<?php endif ?>

<input type="text" name="title"


value="<?php echo $title; ?>" placeholder="Title">

<label style="float: left; margin: 5px


auto 5px;">Featured image</label>

<input type="file"
name="featured_image" >

<textarea name="body" id="body"


cols="30" rows="10"><?php echo $body; ?></textarea>

<select name="topic_id">

<option value="" selected


disabled>Choose topic</option>

<?php foreach ($topics as


$topic): ?>

<option value="<?php
echo $topic['id']; ?>">

<?php echo
$topic['name']; ?>

</option>

<?php endforeach ?>

</select>

<!-- Only admin users can view publish


input field -->

<?php if ($_SESSION['user']['role'] ==
"Admin"): ?>

<!-- display checkbox according


to whether post has been published or not -->

<?php if ($published == true): ?>


<label for="publish">

Publish

<input
type="checkbox" value="1" name="publish" checked="checked">&nbsp;

</label>

<?php else: ?>

<label for="publish">

Publish

<input
type="checkbox" value="1" name="publish">&nbsp;

</label>

<?php endif ?>

<?php endif ?>

<!-- if editing post, display the update


button instead of create button -->

<?php if ($isEditingPost === true): ?>

<button type="submit"
class="btn" name="update_post">UPDATE</button>

<?php else: ?>

<button type="submit"
class="btn" name="create_post">Save Post</button>

<?php endif ?>

</form>

</div>

<!-- // Middle form - to create and edit -->

</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.

To activate this ckeditor on a textarea, 3 things must be done:

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>

Open http://localhost/complete-blog-php/admin/create_post.php in the browser and


you'll see the transformed textarea.

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

- - - - - - - - - - -*/// if user clicks the create post buttonif


(isset($_POST['create_post'])) { createPost($_POST); }// if user clicks
the Edit post buttonif (isset($_GET['edit-post'])) {

$isEditingPost = true;

$post_id = $_GET['edit-post'];

editPost($post_id);}// if user clicks the update post buttonif


(isset($_POST['update_post'])) {

updatePost($_POST);}// if user clicks the Delete post buttonif


(isset($_GET['delete-post'])) {

$post_id = $_GET['delete-post'];
deletePost($post_id);}

/* - - - - - - - - - -

- Post functions

- - - - - - - - - - -*/function createPost($request_values)

global $conn, $errors, $title, $featured_image,


$topic_id, $body, $published;

$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']);

// create slug: if title is "The Storm Is Over", return


"the-storm-is-over" as slug $post_slug = makeSlug($title);

// validate form if (empty($title))


{ array_push($errors, "Post title is required"); }

if (empty($body)) { array_push($errors, "Post body is


required"); }

if (empty($topic_id)) { array_push($errors, "Post topic


is required"); }

// Get image name $featured_image =


$_FILES['featured_image']['name'];

if (empty($featured_image)) { array_push($errors,
"Featured image is required"); }

// image file directory $target =


"../static/images/" . basename($featured_image);
if
(!move_uploaded_file($_FILES['featured_image']['tmp_name'], $target))
{

array_push($errors, "Failed to upload image.


Please check file settings for your server");

// Ensure that no post is saved twice.


$post_check_query = "SELECT * FROM posts WHERE slug='$post_slug'
LIMIT 1";

$result = mysqli_query($conn, $post_check_query);

if (mysqli_num_rows($result) > 0) { // if post exists a

// create post if there are no errors in the form if (coun

$query = "INSERT INTO posts (user_id, title, slug,


image, body, published, created_at, updated_at) VALUES(1, '$title',
'$post_slug', '$featured_image', '$body', $published, now(), now())";

if(mysqli_query($conn, $query)){ // if post


created successfully $inserted_post_id =
mysqli_insert_id($conn);

// create relationship between post and


topic $sql = "INSERT INTO post_topic (post_id,
topic_id) VALUES($inserted_post_id, $topic_id)";

mysqli_query($conn, $sql);

$_SESSION['message'] = "Post created


successfully";

header('location: posts.php');

exit(0);

}
/* * * * * * * * * * * * * * * * * * * * *

* - Takes post id as parameter

* - Fetches the post from database

* - sets post fields on form for editing

* * * * * * * * * * * * * * * * * * * * * */

function editPost($role_id)

global $conn, $title, $post_slug, $body, $published,


$isEditingPost, $post_id;

$sql = "SELECT * FROM posts WHERE id=$role_id LIMIT 1";

$result = mysqli_query($conn, $sql);

$post = mysqli_fetch_assoc($result);

// set form values on the form to be updated


$title = $post['title'];

$body = $post['body'];

$published = $post['published'];

function updatePost($request_values)

global $conn, $errors, $post_id, $title, $featured_image,


$topic_id, $body, $published;

$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']);
}

// create slug: if title is "The Storm Is Over", return


"the-storm-is-over" as slug $post_slug = makeSlug($title);

if (empty($title)) { array_push($errors, "Post title is


required"); }

if (empty($body)) { array_push($errors, "Post body is


required"); }

// if new featured image has been provided


if (isset($_POST['featured_image'])) {

// Get image name


$featured_image = $_FILES['featured_image']['name'];

// image file directory


$target = "../static/images/" . basename($featured_image);

if
(!move_uploaded_file($_FILES['featured_image']['tmp_name'], $target))
{

array_push($errors, "Failed to upload


image. Please check file settings for your server");

// register topic if there are no errors in the form if (coun

$query = "UPDATE posts SET title='$title',


slug='$post_slug', views=0, image='$featured_image', body='$body',
published=$published, updated_at=now() WHERE id=$post_id";

// attach topic to post on post_topic table i

$inserted_post_id =
mysqli_insert_id($conn);

// create relationship between


post and topic $sql = "INSERT INTO
post_topic (post_id, topic_id) VALUES($inserted_post_id, $topic_id)";
mysqli_query($conn, $sql);

$_SESSION['message'] = "Post
created successfully";

header('location: posts.php');

exit(0);

$_SESSION['message'] = "Post updated


successfully";

header('location: posts.php');

exit(0);

} // delete blog post function deletePost($post_id)

global $conn;

$sql = "DELETE FROM posts WHERE id=$post_id";

if (mysqli_query($conn, $sql)) {

$_SESSION['message'] = "Post successfully


deleted";

header("location: posts.php");

exit(0);

Now we are able to create, read, update and delete posts.

One last thing, let's add code to publish/unpublish post. In the same
post_functions.php file, add this code:

// if user clicks the publish post buttonif (isset($_GET['publish']) ||


isset($_GET['unpublish'])) {

$message = "";
if (isset($_GET['publish'])) {

$message = "Post published successfully";

$post_id = $_GET['publish'];

} else if (isset($_GET['unpublish'])) {

$message = "Post successfully unpublished";

$post_id = $_GET['unpublish'];

togglePublishPost($post_id, $message);}// delete blog


postfunction togglePublishPost($post_id, $message){

global $conn;

$sql = "UPDATE posts SET published=!published WHERE id=$post_id";

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.

Enjoy the rest of your day.


Awa Melvine

You might also like