php MySQL إنشاء مُدوّنة

تصحيح : إضافة المواضيع للمدونة

آخر تحيين: 13-12-2015

المدونة : إضافة التعاليق تمرين : إضافة المواضيع للمدونة


أتمنّى أن تكونوا حاولتم على الأقل ، و البعض منكم قد توصّل للحل بدون شك . سنقوم بتصحيح المرحلة الأولى أوّلا ، أي إضافة و عرض المواضيع ، ثم نتبعها بتصحيح المرحلة الثانية أي تعديل و حذف الموضوع

add-blog.php

تخزين بيانات الإستمارة في القاعدة
أهم شيء في هذه الصفحة هو معالجة البيانات ، و التأكد من أن المستخدم ملأ الإستمارة بالشكل المطلوب قبل تخزين البيانات في القاعدة . سنقتصر هنا على شيئين هامين :

  • التأكد من أن المستخدم ملأ جميع حقول الإستمارة
  • التأكد من أن عنوان الموضوع ، فريد من نوعه ، لأننا لا نريد الحصول على مواضيع مختلفة تحمل نفس العنوان
  • كنت قد طلبت منكم أ يضا ، عرض الأخطاء المحتملة مباشرة فوق الحقل المعني .

لنبدء إذاً . لمعالجة الأخطاء سنقوم بتحيين جدول الجمع (يضم المفاتيح و القيم) حتى نتمكن لاحقا عرض كل خطأ محتمل مباشرة فوق الحقل المعني بناء على مفتاحه في الجدول .
سنحصل على شيفرة شبيهة بهذه :

// تحيين جدول الجمع
$error = array(
    'err_author' => null,
    'err_title' => null,
    'err_content' => null,
    'err_title_exist' => null
   );

لتعيين خطأ ما ، نعتمد على مفتاح الخطأ ، ثم نحين قيمته لأي شيء نريد ، مثال :

if(empty($_POST['author']))
{
    $error['err_author'] = 'حقل الكاتب إلزامي !';
}

بهذه الطريقة سيسهل علينا التحكم في الأخطاء و التمكن من عرض كل خطأ فوق الحقل المعني .
الخطوة الثانية هي تخزين بيانات الموضوع في القاعدة ، شرط عدم وجود أي خطأ . لتفعيل هذا ، سنجري اختبار الشرط (if) على الجدول . بحيث إذا كان الجدول فارغا (لم نسجل أي خطأ) ، نقوم بتخزين البيانات في القاعدة .
في الجداول العادية ، يمكننا التأكد من فراغ الجدول أو عدمه باستعمال empty() مثلا .

$error = array();
// للتأكد من أن الجدول فارغا
if (empty($error)) { /* تخزين البيانات في القاعدة */ }

في جداول الجمع كجدولنا ، يختلف الأمر فالتعليمة "empty" و حدها غير عملية . لأنه مادام الجدول يضم مفاتيح و لو كانت قيمها فارغة ، فالجدول ليس فارغا .
للتأكد من فراغ جدول الجمع ، توجد عدة طرق ، سنستعمل المختصرة منها و هي دالة من دوال php : array_filter()
تقوم الدالة "array_filter" بحذف جميع القيم الفارغة ، و التي يمكن أن تكون : null أو 0 أو '' أو false
بهذه الطريقة ، إذا لم نسجل أي خطأ أثناء إرسال الإستمارة ، ستكون جميع قيم المفاتيح في الجدول فارغة . سنقوم بحذفها باستعمال الدالة السابقة . في هذه اللحظة فقط سيصبح جدولنا عاديا و يمكننا استعمال التعليمة empty():

$error = array_filter($error);

if (empty($error)) { /* تخزين البيانات في القاعدة */ }

يمكننا اختزال هذه الشيفرة إلى ما يلي :

if (!array_filter($error)) { /* تخزين البيانات في القاعدة */ }

صفحة add-blog.php كاملة

<?php 
require 'includes/header.php';
require 'includes/db-connection.php';

$error = array(
    'err_author' => null,
    'err_title' => null,
    'err_content' => null,
    'err_title_exist' => null
   );

if(isset($_POST['author']) && isset($_POST['title']) && isset($_POST['content']))
{
    $author = trim($_POST['author']);
    $title = trim($_POST['title']);
    $content = trim($_POST['content']);

    // التأكد من وجود البيانات
    if(empty($author))  { $error['err_author'] = 'حقل الكاتب إلزامي !';}
    if(empty($title))   { $error['err_title'] = 'حقل العنوان إلزامي !';}
    if(empty($content)) { $error['err_content'] = 'حقل المحتوى إلزامي !';    }

    // التأكد من عدم وجود موضوع يحمل نفس العنوان
    $response = $db->prepare('SELECT title FROM blog');
    $response->execute();
    $blogs = $response->fetchAll();
    $response->CloseCursor();
		
    foreach($blogs as $blog) {
        if ($blog['title'] == $_POST['title']) {
            $error['err_title_exist'] = 'يوجد موضوع يحمل نفس العنوان ، المرجو تغييره';			
        }
    }

    // إذا لم نسجل أي خطأ ، نُخزن بياناتنا في القاعدة
    if (!array_filter($error))
    {
        $stmt= $db->prepare('INSERT INTO blog ( title, content, author, creation_date) 
                             VALUES (:tit, :cont, :auth, NOW())
                           ');
        $stmt->bindValue(':tit',$title,PDO::PARAM_STR);
        $stmt->bindValue(':cont',$content,PDO::PARAM_STR);
        $stmt->bindValue(':auth',$author,PDO::PARAM_STR);
        $stmt->execute();        
        $stmt->CloseCursor();

		// التحويل التلقائي
        header('location:index.php'); exit; 
    }
}
else
{
    $_POST['title'] = $_POST['content'] = $_POST['author'] = '';
}

// الإستمارة
echo '
<form action="" method="post" id="form">
  <fieldset class="form-item">
    <legend>إضافة موضوع</legend>
    <div class="errors center">'.$error['err_author'].'</div>
    <label for="author"> الكاتب</label>
        <input type="text" name="author" id="author" value="'.htmlspecialchars($_POST['author']).'"><br>

    <div class="errors center">'.$error['err_title'].' '.$error['err_title_exist'].'</div>
    <label for="title"> العنوان</label>
        <input type="text" name="title" id="title" value="'.htmlspecialchars($_POST['title']).'"><br>

    <div class="errors center">'.$error['err_content'].'</div>
    <label for="content">المحتوى</label>
    <textarea name="content" id="content">'.htmlspecialchars($_POST['content']).'</textarea>
  </fieldset>

  <fieldset class="form-submit">
    <input type="submit" name="submit" value="إرسال" class="button green">
  </fieldset>
</form>';

require 'includes/footer.php';

index.php

كما رأينا ، هذه الصفحة ستضم :

  • عرض عناوين المواضيع . مع ملخّص قصير

صفحة index.php كاملة

<?php 
require 'includes/header.php';
require 'includes/db-connection.php';

//  أخذ جميع البيانات من القاعدة 
$response = $db->prepare('SELECT id, title, content, author, creation_date
            FROM blog 
            ORDER BY id DESC
           ');
$response->execute();
$blogs = $response->fetchAll();
$response->CloseCursor();

// عرض البيانات  
echo '<h2 class="center">صفحة الإستقبال</h2>';
if($response->rowCount() > 0 )
{
    foreach($blogs as $blog) 
    {
        $id = (int)$blog['id'];
        $title = htmlspecialchars($blog['title']);
        $author = htmlspecialchars($blog['author']);
        $content = nl2br(htmlspecialchars($blog['content']));
        $tari5 = htmlspecialchars($blog['creation_date']);

        echo'
        <article class="resume">
          <div class="odd">
            <h2>
                <a href="show.php?id='.$id.'" class="orange"> '.$title.'</a> .
            </h2>
            <div class="details">الكاتب:"<span class="black">'.$author.'</span>" ، في <time datetime="'.$tari5.'">'.date("d-m-Y",strtotime($tari5)).'</time></div>
          </div>
          <div class="even">
                '.mb_strcut($content, 0, 320, 'UTF-8').' ...
                <span class="more"><a href="show.php?id='.$id.'" class="button orange">المزيد</a></span>
          </div>
        </article>';
    }
}
else
{
    echo '<h2 class="errors"> لا يوجد أي موضوع حاليا ! </h2><hr>';
}

require 'includes/footer.php';

قمنا بعرض مُلخّص لجميع المواضيع . مع إضافة رابط أو وصلة إلى صفحة show.php . لمشاهدة الموضوع كاملاً .

show.php

عرض بيانات موضوع واحد اعتمادا على معرف "ID" الموضوع .

صفحة show.php كاملة

<?php
require 'includes/header.php';
require 'includes/db-connection.php';

// استقبال مفتاح الموضوع 
$blog_id = isset($_GET['id']) ? (int)$_GET['id']: 1;

// أخذ بيانات الموضوع من القاعدة ، إعتمادا على المفتاح
$response = $db->prepare('SELECT id, title, content, author, creation_date
            FROM blog 
            WHERE id = :bid
           ');
$response->bindValue(':bid', $blog_id, PDO::PARAM_INT);
$response->execute();
$blog = $response->fetch();
$response->CloseCursor();

// عرض البيانات 
if($blog)
{
    $blog_title = htmlspecialchars($blog['title']);
    $blog_author = htmlspecialchars($blog['author']);
    $blog_content = nl2br(htmlspecialchars($blog['content']));
    $blog_tari5 = htmlspecialchars($blog['creation_date']);

  echo '
    <article class="blog">
    <h2>
        '.$blog_title.'
    </h2>
    <div class="details">
        <a href="edit-blog.php?id='.$blog_id.'" class="button orange"> تعديل </a> - 
        <a href="delete-blog.php?id='.$blog_id.'" class="button red"> حذف </a>
    </div>
	<hr>
    <div>
        <div class="content">'.$blog_content.'</div>
        <div class="sidebar center"><span>الكاتب : '.$blog_author.'</span>
	        <time class="orange">'.date("Y-m-d س i:H", strtotime($blog_tari5)).'</time>
            <h3>مواضيع لنفس الكاتب</h3>
            <ul>
                <li><a href="#">كتمرين . يمكنك لاحقا عرض هنا ، روابط للمواضيع التي نشرها هذا الكاتب </a></li>
            </ul>
        </div>
    </div>
    </article>
<hr>';
}else {
  die('لا يوجد أي موضوع !!! ');
}

require 'includes/footer.php';
?>

صفحة بسيطة ، قمنا باستعلام عادي معتمدين على المفتاح id الذي أرسلناه عبر عنوان url . لهذا استعملنا $_GET لاستقباله .


أود أن أثير انتباهكم لملاحظة هامة تتعلق بطريقة عرض حقل التاريخ . و البعض منكم قد تساءل بدون شك ، لماذا لم أستعمل دالة sql DATE_FORMAT في الإستعلام كما رأينا في درس سابق : "التعامل مع حقول التاريخ و الوقت" .
الجواب : يمكنكم استعمال DATE_FORMAT بكل حرية في الإستعلام حسب رغبتكم ، بالمقابل لن يكون لديكم خيار آخر لعرض التاريخ و الوقت إلا الخيار الذي عينتموه في الإستعلام . لهذا فضّلت إجراء استعلام عادي و ترك التحكم في التاريخ لدوال php . بهذه الطريقة يمكنني عرض التاريخ و الوقت حسب الحاجة :
إذا أردت مثلا عرض حقل التاريخ و الوقت دون اللجوء لأي دالة من دوال php او sql . يمكنني فعل ذلك بكل سهولة :

echo $blog['creation_date'];

في هذه الحالة سيتم عرض التاريخ و الوقت كاملا كما هو في قاعدة البيانات ، ستحصلون على شيء شبيه بالتالي : "2015-10-24 13:30:39".
أما إذا أردت التحكم في طريقة العرض باستعمال php . فالدالتين date و strtotime تفيان بالغرض .

echo date("Y-m-d", strtotime($blog['creation_date']));

يمكنكم الآن إضافة المواضيع التي تريدونها . إذا كان لديكم أي اقتراح أو تساؤل لا تتردّدوا لطرحه في منتدى لغة php


بالنسبة للمرحلة الثانية المتعلقة بتعديل و حذف الموضوع ، ليس هناك جديد لا تعرفونه . لقد أنجزتم سابقا تمرين الدردشة ، الأمر لا يختلف كثيرا هنا ، إذا استثنينا تعديل عنوان الموضوع . للإشارة ، كما فعلنا أثناء إضافة الموضوع هنا أيضا يجب التأكد من أن عنوان الموضوع فريد من نوعه . لكن الإستعلام سيختلف هنا . لمقارنة عنوان الموضوع الحالي مع عناوين المواضيع ، يجب أن نستثني في المقارنة الموضوع الحالي . سيكون الإستعلام شبيها بالتالي :

/* :bid = الموضوع الحالي ID معرف */

SELECT title 
FROM blog 
WHERE id <> :bid

الإستعلام يعني : اخيار جميع العناوين من جدول blog باستثناء عنوان الموضوع الحالي .

صفحة edit-blog.php كاملة

<?php 
require 'includes/header.php';
require 'includes/db-connection.php';

// استقبال مفتاح الموضوع
$blog_id = isset($_GET['id']) ? (int)$_GET['id']:1;
// تحيين جدول الأخطاء
$error = array();

// أخذ بيانات الموضوع من القاعدة ، إعتمادا على المفتاح
// سنحتاج هذه البيانات لملأ الإستمارة
$response = $db->prepare('SELECT id, title, content, author, creation_date
            FROM blog 
            WHERE id = :bid
           ');
$response->bindValue(':bid',$blog_id,PDO::PARAM_INT);
$response->execute();
$blog = $response->fetch();
$response->CloseCursor();

if($blog)
{
    $id = (int)$blog['id'];
    $title = $blog['title'];
    $author = $blog['author'];
    $content = $blog['content'];
    // تثبيت العنوان الأول للموضوع لعرضه في حالة وجود خطأ
    $old_title = $title;
}
else
{
    die('لا يوجد أي موضوع بهذا المعرف !!');
}

// معالجة بيانات الإستمارة
if(isset($_POST['submit']))
{
    $title = trim($_POST['edit_title']);
    $content = trim($_POST['edit_content']);
    $author = trim($_POST['edit_author']);

    if(empty($_POST['edit_author']) OR empty($_POST['edit_title']) OR empty($_POST['edit_content']))
    {
        $error[] = 'يجب ملأ جميع الحقول !!';
    }

    // التأكد من عدم وجود موضوع يحمل نفس العنوان
    $response = $db->prepare('SELECT title FROM blog WHERE id <> :bid');
    $response->bindValue(':bid', $blog_id, PDO::PARAM_STR);
    $response->execute();
    $blogs = $response->fetchAll();
    $response->CloseCursor();
		
    foreach($blogs as $blog) {
        if ($blog['title'] == $_POST['edit_title']) {
            $error[] = 'يوجد موضوع يحمل نفس العنوان ، المرجو تغييره';			
        }
    }

    if(empty($error))
    {
        // تحيين البيانات في القاعدة .
        $stmt = $db->prepare('UPDATE blog 
                SET title = :tit, content = :cont, author = :auth
                WHERE id = :bid
                ');
        $stmt->bindValue(':tit',  $title,   PDO::PARAM_STR);
        $stmt->bindValue(':cont', $content, PDO::PARAM_STR);
        $stmt->bindValue(':auth', $author,  PDO::PARAM_STR);
        $stmt->bindValue(':bid',  $blog_id, PDO::PARAM_INT);
        $stmt->execute();        
        $stmt->CloseCursor();

        header('location:show.php?id='.$id); exit;
    }
}

// عرض عنوان الموضوع الذي سيتم تعديله 
echo '<h3>تعديل موضوع : <a href="show.php?id='.$id.'" class="button orange">'.htmlspecialchars($old_title).'</a></h3>';

// ....... الإستمارة ........
// عرض الأخطاء في حالة وجودها
if(!empty($error)) {
    echo '<div class="errors center">';

    foreach($error as $value) {
        echo $value.'<br>'; 
    }

    echo '</div>';
}

// . سيتم ملأ الإستمارة ببيانات الموضوع من قاعدة البيانات
// . في حالة وجود خطأ ، سيتم ملأ الإستمارة بالبيانات التي تم تعديلها 
echo '
<form action="" method="post" id="form" class="box">
    <label for="author">الكاتب</label>
        <input type="text" name="edit_author" id="author" value="'.htmlspecialchars($author).'"><br>
    <label for="title"> العنوان</label>
        <input type="text" name="edit_title" id="title" value="'.htmlspecialchars($title).'"><br>
    <label for="content">المحتوى</label>
        <textarea name="edit_content" id="content">'.htmlspecialchars($content).'</textarea>

    <p><input type="submit" name="submit" value="إرسال" class="button green"></p>
</form> ';

require 'includes/footer.php';

صفحة delete-blog.php كاملة

<?php
require 'includes/header.php';
require 'includes/db-connection.php';

$blog_id = isset($_GET['id']) ? (int)$_GET['id']: 0;

if(isset($_POST['delete']))
{
    $stmt = $db->prepare('DELETE FROM blog WHERE id = :bid');
    $stmt->bindValue(':bid',$blog_id,PDO::PARAM_INT);
    $stmt->execute();
    $stmt->CloseCursor(); 

    header('location:index.php');
}

echo '
<h3 class="errors">هل أنت متأكد من حذف الموضوع ؟</h3>
<form action="" method="post" class="inlineBlock">
  <input type="hidden" name="delete">
  <input type="submit" class="button" value="نعم">
</form>
<a href="show.php?id='.$blog_id.'" class="inlineBlock button">لا</a> ';

require 'includes/footer.php'; 

في الدرس الموالي ، سنتيح فرصة للزوار لكي يبدوا آراءهم حول مواضيع مُدوّنتكم