Databáze
Toto je druhý díl ze série Jak vytvořit blog s PHP a MySQL. První díl můžete získat zde
Budeme pokračovat tam, kde jsme skončili v minulém tutoriálu. V této sekci budeme pracovat na návrhu naší databáze a autentizaci uživatelů (registrace a přihlášení). Vytvořte databázi s názvem complete-blog-php. V této databázi vytvořte 2 tabulky: příspěvky a uživatele s následujícími poli.
příspěvky:
+----+-----------+--------------+------------+
| 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 | |
+----------------+--------------+------------+
uživatelé:
+----+-----------+------------------------+------------+
| 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 | |
+----------------+--------------+---------+------------+
Tyto tabulky můžete vytvořit pomocí těchto příkazů.
uživatelé:
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;
příspěvky:
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
Tyto skripty můžete spustit pomocí příkazového řádku SQL nebo PHPMyAdmin. Na PHPMyAdmin klikněte/vyberte databázi, pod kterou chcete tyto tabulky vytvořit (v tomto případě complete-blog-php), poté klikněte na záložku SQL na navigační liště někde v horní části stránky. Pokud v prostoru níže vidíte nějaký skript SQL, odeberte jej a vložte skript výše do určeného prostoru a kliknutím na tlačítko „Přejít“ vytvořte tabulky.
Pokud jste se místo toho rozhodli vytvořit tyto tabulky ručně, nezapomeňte, že pole slug v tabulce příspěvků bude JEDINEČNÉ a nezapomeňte nastavit pole user_id tabulky příspěvků jako cizí klíč, který odkazuje na id v tabulce uživatelů. Nastavte NO ACTION jako hodnotu pro možnosti ON DELETE a ON UPDATE, takže když je uživatel smazán nebo aktualizován, jeho příspěvky zůstanou v tabulce příspěvků a nebudou smazány.
Nyní vložte několik uživatelů do tabulky uživatelů a několik příspěvků do tabulky příspěvků. Můžete to udělat spuštěním těchto SQL dotazů pro vložení:
uživatelé:
INSERT INTO `users` (`id`, `username`, `email`, `role`, `password`, `created_at`, `updated_at`) VALUES
(1, 'Awa', '[email protected]', 'Admin', 'mypassword', '2018-01-08 12:52:58', '2018-01-08 12:52:58')
příspěvky:
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')
Pojďme se připojit k databázi, dotazovat se na tyto příspěvky a zobrazovat je na webové stránce.
V config.php přidáme kód pro připojení naší aplikace k databázi. Po přidání kódu bude náš soubor config.php vypadat takto:
<?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/');
?>
Vrátí to objekt připojení k databázi $conn, který můžeme použít v celé naší aplikaci pro dotazování na databázi.
Tato aplikace byla strukturována tak, aby kód PHP byl co nejvíce oddělen od HTML. Operace, jako je dotazování na databázi a provádění určité logiky na datech, se provádějí ve funkcích PHP a výsledky se odesílají do HTML k zobrazení. Proto, abychom získali všechny příspěvky z databáze, uděláme to ve funkci a vrátíme výsledky jako asociativní pole, které se má procházet a zobrazovat na stránce.
Proto vytvořte soubor s názvem public_functions.php ve složce include. Tento soubor bude obsahovat všechny naše funkce PHP pro veřejnou oblast. Všechny stránky využívající některou z funkcí v tomto souboru musí mít tento soubor zahrnut v horní části stránky.
Pojďme vytvořit naši první funkci v našem nově vytvořeném public_functions.php. Funkci pojmenujeme getPublishedPosts() a načte všechny příspěvky z tabulky příspěvků v databázi a vrátí je jako asociativní pole:
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 ...
?>
V horní části souboru index.php, těsně pod řádkem, který obsahuje config. php , přidejte tento kód pro dotaz na databázi:
<!-- 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(); ?>
Přidali jsme dva řádky kódu. První zahrnuje soubor public_functions.php (který obsahuje funkce) do našeho souboru index.php. Druhý řádek kódu volá funkci getPublishedPosts(), která se dotazuje na databázi a vrací příspěvky načtené z databáze v proměnné $posts. Nyní projdeme a zobrazíme tyto příspěvky na stránce index.php.
Znovu otevřete náš slavný soubor index.php. V části s obsahem někde uprostřed najdete značku
a komentář, který uvádí, kde bude další obsah. Do prostoru, přímo pod značku
, přidejte tento kód:
<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 ?>
Dobře, prosím, zatím stránku znovu nenačítavejte. Přidáme styl do tohoto seznamu příspěvků. Otevřete public_styling.css a přidejte do něj tento kód:
/* 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;
}
Nyní můžete stránku znovu načíst.
Pokud vše proběhlo v pořádku, pod názvem „Poslední články“ se zobrazí jeden příspěvek ve stylu miniatury. Pamatujte, že jsme do databáze vložili dva záznamy, ale zobrazuje se pouze jeden. Je tomu tak proto, že jeden ze záznamů měl pole publikované nastaveno na hodnotu false (tj. 0), a protože se zobrazují pouze publikované články, vidíme pouze jeden, publikovaný.
Ale naše příspěvky zatím nejsou zařazeny do žádného tématu. Vytvořme tabulku témat a vytvořte vztah Many-to-Many mezi příspěvky a tabulkou témat. Za tímto účelem vytvoříme dvě nové tabulky:topics pro ukládání témat a post_topic tabulku pro zpracování vztahu mezi příspěvky a tématy.
témata:
+----+-----------+------------------------+------------+
| 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) | |
+----------------+--------------+---------+------------+
Co nás opravdu zajímá, je tabulka post_topic. Toto je tabulka, která řeší vztah mezi příspěvky a tématy. Když je příspěvek vytvořen pod určitým tématem, vloží se do tabulky post_topic id tohoto příspěvku (post_id) a také id tématu (topic_id), pod kterým je příspěvek vytvořen.
Vytvořme tento vztah tak, že při smazání příspěvku bude automaticky smazán i jeho záznam v tabulce post_topic; nechcete uchovávat informace o vztahu příspěvku, když příspěvek neexistuje, že?
Klikněte/vyberte tabulku post_topic a poté klikněte na záložku struktury na navigační liště PHPMyAdmin. Dále klikněte na Relation View pod kartou Struktura (může být nalezena někde jinde v závislosti na vaší verzi PHPMyAdmin). Poté vyplňte níže uvedený formulář takto:
Tip:Odkaz +Přidat omezení se používá k přidání nového omezení.
ON DELETE a ON UPDATE jsou všechny nastaveny na CASCADE a NO ACTION, takže když je příspěvek nebo téma smazáno, jsou automaticky smazány také informace o jejich vztahu v tabulce post_topic. (Na obrázku jsem udělal chybu, když jsem nastavil ON UPDATE na CASCADE místo NO ACTION, omlouvám se za to).
Klikněte na uložit a je to. Tabulky spolu nyní souvisí. Abychom však vytvořili vztah mezi příspěvky a tématy, musíme naplnit tabulku témat tématy a případně tabulku post_topic, což jsou skutečné informace o vztahu.
Nyní vložíme několik položek do dvou tabulek:
témata:
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)
Vztah definovaný v tabulce post_topic říká, že téma s ID 1 v tabulce témat patří k příspěvku s ID 1 v tabulce příspěvků. Totéž platí pro téma s ID 2 a příspěvek s ID 2.
Na každém výpisu příspěvku na stránce index.php zobrazíme téma, pod kterým je příspěvek vytvořen.
Abychom to udělali, musíme upravit naše getPublishedPosts(), které jsme vytvořili v public_functions.php, aby se dotazovalo na téma každého příspěvku z databáze a vrátilo příspěvek vedle jeho tématu.
Upravte soubor public_functions.php tak, aby vypadal takto:
<?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;
}
?>
Nyní přejděte do souboru index.php. Uvnitř smyčky foreach, přímo pod značkou obrázku , přidejte příkaz if k zobrazení tématu. Vaše smyčka foreach by měla po úpravě vypadat takto:
<?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 ?>
Nyní znovu načtěte stránku a v příspěvku se zobrazí téma.
Uvnitř této smyčky foreach si všimnete, že existují dva odkazy, na které se po kliknutí dostanete na dvě stránky:filter_posts.php a single_post.php.
filter_posts.php je stránka, která obsahuje seznam všech příspěvků pod konkrétním tématem, když uživatel na toto téma klikne.
single_post.php je stránka, která zobrazuje celý příspěvek podrobně spolu s komentáři, když uživatel klikne na miniaturu příspěvku.
Tyto dva soubory potřebují několik funkcí z našeho souboru public_functions.php. filter_posts.php potřebuje dvě funkce nazvané getPublishedPostsByTopic() a getTopicNameById() , zatímco single_posts.php potřebuje getPost() a getAllTopics().
Začněme souborem filter_posts.php. Otevřete public_functions.php a přidejte tyto dvě funkce do seznamu funkcí:
/* * * * * * * * * * * * * * * *
* 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'];
}
Nejprve vytvořte soubor filter_posts.php v kořenové složce naší aplikace (tj. complete-blog-php/filtered_posts.php). Pokračuji a vložím celý kód této stránky do souboru:
filter_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 -->
Nyní obnovte stránku, klikněte na téma a pokud vás přesměruje na stránku zobrazující příspěvky pod tímto tématem, pak děláte správnou věc.
Udělejme to samé s single_post.php. Otevřete public_functions.php a přidejte do něj tyto 2 funkce:
/* * * * * * * * * * * * * * *
* 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;
}
Nyní vytvořte soubor complete-blog-php/single_post.php a vložte do něj tento kód:
<?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'); ?>
Aplikujme na to nyní styling. Otevřete public_styling.css a přidejte do něj tento stylingový kód:
/* * * * * * * * *
* 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;
}
Vypadá to teď dobře, že?
Zbývá udělat poslední věc a s veřejnou oblastí budeme téměř hotovi:Zavedeme registraci a přihlášení uživatelů.
Registrace a přihlášení uživatele
Vzhledem k tomu, že jsem již vytvořil výukový program o registraci a přihlášení uživatele, budu v této části téměř k věci a nebudu moc vysvětlovat.
Vytvořte dva soubory v kořenové složce s názvem register.php a login.php. Otevřete každý z nich a vložte do nich tento kód:
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 -->
Do horních sekcí obou souborů jsme zahrnuli soubor s názvem registration_login.php, který zajišťuje logiku registrace a přihlašování. Toto je soubor, do kterého budou odeslány informace z přihlašovacího a registračního formuláře a bude probíhat komunikace s databází. Pojďme jej vytvořit v naší složce include a plivneme do něj tento kód:
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 (count($errors) == 0) {
$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
$_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
header('location: ' . BASE_URL . 'admin/dashboard.php');
exit(0);
} else {
$_SESSION['message'] = "You are now logged in";
// redirect to public area
header('location: index.php');
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
$reg_user_id = mysqli_fetch_assoc($result)['id'];
// 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
header('location: ' . BASE_URL . '/admin/dashboard.php');
exit(0);
} else {
$_SESSION['message'] = "You are now logged in";
// redirect to public area
header('location: index.php');
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 $conn;
$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'=>'[email protected]', 'password'=> 'mypass']
return $user;
}
?>
Přejděte na http://localhost/complete-blog-php/register.php a zobrazí se chyba, že soubor errors.php nebyl nalezen.
errors.php soubor je soubor s kódem pro zobrazení chyb ověření formuláře. Vytvořte error.php uvnitř complete-blog-php/includes a vložte do něj tento kód:
<?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 ?>
Znovu otevřete public_styling.css, přidejte tento poslední kousek stylingového kódu pro tento soubor errors.php a několik dalších prvků:
/* 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;
}
A nyní je chybová zpráva pryč. Klikněte na tlačítko registrace bez vyplňování formuláře a uvidíte krásné chybové zprávy.
Vytvořme nového uživatele vyplněním formuláře na stránce register.php a kliknutím na tlačítko registrace. Můžete poskytnout jakékoli platné informace pro uživatelské jméno, e-mail a heslo; jen se ujistěte, že si je zapamatujete, protože je velmi brzy použijeme k přihlášení na přihlašovací stránce.
Když se uživatel přihlásí, určitě se bude muset umět odhlásit. V kořenové složce aplikace vytvořte soubor s názvem logout.php.
complete-blog-php/logout.php:
<?php
session_start();
session_unset($_SESSION['user']);
session_destroy();
header('location: index.php');
?>
Také když se uživatel přihlásí, chceme mu zobrazit jeho jméno a odkaz nebo tlačítko, aby se kliknutím odhlásil. Pro veřejnou oblast to uděláme v souboru banner.php, který jsme připojili. Otevřete soubor banner.php a upravte kód tak, aby vypadal takto:
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.