Welcome to a quick tutorial on how to create a simple content management system (CMS) with PHP and MYSQL. Looking to challenge yourself or just want a really simple CMS for your current project?
A barebones CMS will only require the following components:
- A database to store the page title and contents.
- PHP library to manage (save/load) the contents.
- A page to manage and update the contents.
- A page to load and display the contents.
Well, an actual example will better explain things. Let us walk through one in this guide – Read on!
ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
QUICK SLIDES
TABLE OF CONTENTS
DOWNLOAD & NOTES
First, here is the download link to the example source code as promised.
QUICK NOTES
- Create a dummy database and import
1-database.sql
. - Change the database settings in
2-lib-content.php
to your own. - Launch
3-save.php
in your browser, enter any title and text. - Launch
4-load.php
in your browser, see how the page is rendered from the database.
EXAMPLE CODE DOWNLOAD
Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
BAREBONES CMS
All right, let us now get started with the barebones CMS in PHP and MYSQL.
STEP 1) CONTENT DATABASE TABLE
CREATE TABLE `contents` (
`content_id` int(11) NOT NULL,
`content_title` varchar(255) NOT NULL,
`content_text` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `contents`
ADD PRIMARY KEY (`content_id`),
ADD KEY `content_title` (`content_title`);
ALTER TABLE `contents` ADD FULLTEXT KEY `content_text` (`content_text`);
ALTER TABLE `contents`
MODIFY `content_id` int(11) NOT NULL AUTO_INCREMENT;
Field | Description |
content_id | The content ID, primary key. |
content_title | Title of the page. |
content_text | The contents of the page itself. |
Yep, this one should be very straightforward, we need a table to store the title and content of the pages. Just a small note here though, the title and text are indexed – ADD KEY `content_title`
and ADD FULLTEXT KEY `content_text`
. This indexing will take up a little more disk space but greatly improves the search performance.
STEP 2) CMS CONTENT LIBRARY
<?php
class Content {
// (A) CONSTRUCTOR - CONNECT TO DATABASE
private $pdo;
private $stmt;
public $error;
function __construct () {
try {
$this->pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET,
DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (Exception $ex) { exit($ex->getMessage()); }
}
// (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
function __destruct () {
$this->pdo = null;
$this->stmt = null;
}
// (C) SAVE CONTENT
function save ($title, $text, $id=null) {
if (is_numeric($id)) {
$sql = "REPLACE INTO `contents` (`content_id`, `content_title`, `content_text`) VALUES (?,?,?)";
$data = [$id, $title, $text];
} else {
$sql = "INSERT INTO `contents` (`content_title`, `content_text`) VALUES (?,?)";
$data = [$title, $text];
}
try {
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute($data);
return true;
} catch (Exception $ex) {
$this->error = $ex->getMessage();
return false;
}
}
// (D) LOAD CONTENT
function load ($id) {
$this->stmt = $this->pdo->prepare("SELECT * FROM `contents` WHERE `content_id`=?");
$this->stmt->execute([$id]);
return $this->stmt->fetch();
}
}
// (E) DATABASE SETTINGS - CHANGE THESE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8");
define("DB_USER", "root");
define("DB_PASSWORD", "");
// (F) CREATE NEW CONTENT OBJECT
$_CMS = new Content();
The core library may look a little intimidating at first, but keep calm and look carefully.
- (A & B) On creating
$_CMS = new Content()
object, the constructor will automatically connect to the database; The destructor will automatically close the database connection when the object is destroyed. - (C & D) There are only 2 functions here!
function save()
to save page contents.function load()
to get the page contents.
- (E & F) Self-explanatory…
That’s all, but a small reminder here to change the database settings to your own again.
STEP 3) UPDATE CONTENT PAGE
<?php
// (A) LOAD LIBRARY
require "2-lib-content.php";
$id = 1; // FIXED TO 1 FOR SIMPLICITY
// (B) SAVE CONTENT WHEN FORM IS SUBMITTED
if (isset($_POST["title"])) {
echo $_CMS->save($_POST["title"], $_POST["text"], $id)
? "<div>CONTENT UPDATED</div>"
: "<div>".$_CMS->error."</div>" ;
}
// (C) EDIT PAGE
$content = $_CMS->load($id); ?>
<form method="post" id="pageForm">
<label for="title">Title</label>
<input type="text" name="title" required
value="<?=$content==false?"":$content['content_title']?>"/>
<label for="text">Content</label>
<textarea name="text" required><?=$content==false?"":$content['content_text']?></textarea>
<input type="submit" value="Save"/>
</form>
Right, this page is seemingly complicated again, but let’s walk through it:
- Self-explanatory. Loads the PHP library, but we fix the content ID to
$id = 1
in this demo to keep things simple. - Save or update the content when the form is submitted.
- The HTML form to update the content itself.
That’s all for the essentials behind this “one-page admin”.
STEP 4) DISPLAY CONTENT PAGE
<?php
// (A) GET PAGE CONTENTS FROM DATABASE
require "2-lib-content.php";
$id = 1; // FIXED TO 1 FOR SIMPLICITY
$content = $_CMS->load($id);
// (B) OUTPUT HTML ?>
<!DOCTYPE html>
<html>
<head>
<title><?=$content["content_title"]?></title>
</head>
<body>
<h1><?=$content["content_title"]?></h1>
<main><?=$content["content_text"]?></main>
</body>
</html>
Now that we have some random contents in the database, the final step is to output it… This should not be a mystery anymore. We simply use
$content = $_CORE->load($id)
to fetch and output the contents.
USEFUL BITS & LINKS
That’s all for this guide, and here is a small section on some extras and links that may be useful to you.
HOW ABOUT MULTIPLE PAGES?
The above barebones example works, but it only works with a single page. As for “how to manage multiple pages”, everyone probably has a different requirement. So I will not go into the exact details, here are a few general steps:
- Add a new
getAll()
function in2-lib-content.php
,SELECT `content_id`, `content_title` FROM `contents`
. - Use the
getAll()
function to build another admin page, list all the available pages/content. - When the user clicks on a page, redirect to
3-save.php?id=N
. - The rest should be straightforward. Modify
3-save.php
and4-load.php
to follow$_GET["id"]
.
HOW TO SEARCH?
Want to add a new search feature?
- Add a new search function to
2-lib-content.php
. - Captain Obvious hint, do a
SELECT * FROM `contents` WHERE `content_text` LIKE ?
, and$this->stmt->execute(["%SEARCH%"])
.
MORE FUTURE DEVELOPMENT
I know, multiple pages are not the only concern. There are many more areas you can work and improve on.
- The
<textarea>
is too “raw” for a CMS. Check out TinyMCE, or do a search for your own preferred “What You See Is What You Get (WYSIWYG) Editor”. - Protect
3-save.php
. Create a user login system, or at least put a basic HTTP authentication on it. - Of course, change
4-load.php
to also load different pages, use your own HTML layout. - If you want a “URL slug” like WordPress or Drupal, feel free to add a “URL” field in the database. You will also need to work with
htaccess
, check out the pretty URL tutorial link below.
LINKS & REFERENCES
- How to Save TinyMCE Content With PHP MySQL – Code Boxx
- Simple User Login System – Code Boxx
- PHP htaccess Pretty URL – Code Boxx
- MySQL LIKE -MySQLTutorial
TUTORIAL VIDEO
THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!