تصميم موقع الويب الخاص بك مع PHP و MySQL


الدرس: TP : مدونة مع التعليقات


الصفحة السابقة
المدونة هي على الأرجح التطبيق الأكثر شيوعًا في PHP باستخدام MySQL . على الرغم من أنه من المستحسن استخدام نظام جاهز (عن طريق تنزيل Wordpress أو Dotclear ، على سبيل المثال) ، إلا أن إنشاء نظام من البداية يعد تمرينًا ممتازًا.
ليس الغرض من هذا التمرين هو جعلك تنشئ مدونة من A إلى Z ، لأنها ستكون طويلة بعض الشيء ، ولكن لتطبيق أحدث مفاهيم SQL التي تعلمتها للتو على الوظائف والتواريخ.
سيكون لكل منشور مدونة تعليقات خاصة به. في هذا التمرين ، سنركز فقط على نشر المشاركات والتعليقات ؛ سيتوقف الأمر عندئذٍ عن إكمال المدونة لإدراج نماذج لإضافة المحتوى وتعديله.

تعليمات للعمل العملي


بالنسبة إلى هذا التمرين كما في السابق ، سوف نستعد معًا عن طريق مراجعة النقاط التالية:
  • المتطلبات الأساسية.
  • الأهداف؛
  • هيكل الجدول MySQL .
  • هيكل صفحات PHP .
المتطلبات الأساسية
في هذا التمرين ، سنركز على قاعدة البيانات. سنحتاج إلى المفاهيم التالية:
  • قراءة في جدول.
  • استخدام PDO والطلبات المعدة ؛
  • استخدام وظائف SQL ؛
  • المعالجات مع التواريخ في SQL .
أهداف
لنبدأ بتحديد ما نريد القيام به. نظام Blog مع تعليقات ، نعم ، ولكن لا يزال؟ عليك أن تعرف إلى أي مدى تريد أن تذهب ، وما تنوي تحقيقه وما الذي ستغفل عنه.
إذا كنت طموحًا جدًا ، فقد تندم على ذلك: قد تقضي أيامًا بالفعل وسيصبح هذا المختبر طويلًا ومعقدًا ومملًا. لذلك أقترح أن تقوم بالنشر الأساسي للمدونة والتعليقات المرتبطة بهذه المنشورات ، وسأدعوك بعد ذلك إلى تحسينه لإنشاء واجهة لإدارة المنشورات وإضافة تعليقات.
لذلك ، لا تكون إضافة المنشورات والتعليقات مدرجة في برنامج هذا التمرين ، مما سيتيح لنا التركيز على عرضها.
الصفحات التي سنطورها
ستكون هناك صفحتان لإكمالهما:
  • index.php  : قائمة آخر خمس منشورات ؛
  • commentaires.php  : عرض وظيفة وتعليقاتها.
هنا ، في الشكل التالي ، يجب أن تبدو قائمة المنشورات الحديثة ( index.php) .
web dynamique php
قائمة المنشورات
وفي الشكل التالي ، يجب أن يبدو شكل المنشور وتعليقاته ( commentaires.php) .
web dynamique php
قائمة التعليقات
كما ترون ، العرض بسيط. الهدف ليس تنفيذ تصميم هذه المدونة ولكن في الواقع الحصول على شيء وظيفي.
CSS
فيما يلي ملف CSS (بسيط جدًا) الذي سأستخدمه لهذا المشروع:

h1, h3
{
    text-align:center;
}
h3
{
    background-color:black;
    color:white;
    font-size:0.9em;
    margin-bottom:0px;
}
.news p
{
    background-color:#CCCCCC;
    margin-top:0px;
}
.news
{
    width:70%;
    margin:auto;
}

a
{
    text-decoration: none;
    color: blue;
}
أنت حر في استخدامه أم لا ، أو ان أردت تعديله ؛ باختصار ، افعل ما تريد به.
هيكل جداول MySQL
نعم ، هذه المرة لن نعمل مع جدول واحد وانما جدولين:
  • billets  : قائمة منشورات المدونة ؛
  • commentaires  : قائمة التعليقات Blog لكل منشور.
هل سنقوم حقًا بتخزين جميع التعليقات في جدول واحد ، حتى لو كانت متعلقة بمشاركات مختلفة؟
نعم. إنها الطريقة الصحيحة للقيام بذلك. سيتم تخزين جميع التعليقات ، بغض النظر عن النشر الذي تتعلق به ، في نفس الجدول. سيكون من الممكن الفرز بسهولة باستخدام حقل id_billet الذي يشير إلى رقم المنشور المرتبط بكل تعليق.
هنا هو الهيكل الذي اقترحه للجدول billets  :
  • id (الباحث): معرف المنشور، والمفتاح الأساسي auto_increment  .
  • Titre (varchar 255) : عنوان المنشور ؛
  • Contenu (النص): محتوى المنشور ؛
  • date_creation (التاريخ): تاريخ ووقت إنشاء المنشور.
وبالمثل ، إليك البنية التي سنستخدمها للجدول commentaires :
  • id (الباحث): معرف التعليق، والمفتاح الأساسي auto_increment  .
  • id_billet (int) : معرف المنشور التي يتوافق معها هذا التعليق ؛
  • Auteur (varchar 255) : مؤلف التعليق ؛
  • Commentaire (نص): محتوى التعليق ؛
  • date_commentaire (وقت): التاريخ والوقت عندما تم نشر التعليق.
إنها حقا الأساس. يمكنك إضافة حقول أخرى إذا كنت ترغب في ذلك. على سبيل المثال ، لم نحدد مجالًا لكاتب للمنشورات.
لاحظ أنه من الممكن إضافة حقول في أي وقت ، كما رأينا منذ فترة. توفر واجهة phpMyAdmin خيارات لهذا.
نظرًا لأننا لن ننشئ النماذج لإضافة المنشورات والتعليقات في البداية ، أنصحك بملء الجداول بنفسك باستخدام phpMyAdmin بعد إنشائها.
بالنسبة للجداول اوصيك بأن تتدرب على إنشاءها بنفسك.
هيكل صفحات PHP
نظرًا لأننا نركز على العرض ، فإن بنية الصفحات بسيطة للغاية ، كما هو موضح في الشكل التالي.
web dynamique php
هيكل صفحة المدونة
يصل الزائر أولاً إلى الفهرس حيث يتم عرض أحدث المنشورات. إذا اختار عرض تعليقات أحدهم ، فسيحمّل الصفحة commentaires.php التي ستعرض المنشور المختار بالإضافة إلى جميع تعليقاته. بالطبع ، سيتعين عليك إرسال مُدخل إلى الصفحة commentaires.php  حتى تعرف ما الذي يجب عرضه ... اسمح لك بتخمين أي مُدخل  .
سيكون من الممكن العودة إلى قائمة المنشورات من التعليقات باستخدام رابط الإرجاع.
دوركم الآن !
لقد أخبرتك بما فيه الكفاية: يجب أن يكون تحقيق هذا البرنامج بسيطًا نسبيًا إذا كنت قد اتبعته جيدًا حتى الآن.
لا تنس العناصر الأساسية للأمن ، ولا سيما حماية جميع النصوص بها htmlspecialchars() . ولا تثق بالمستخدم أبدًا!

تصحيح


إذا قرأت هذه السطور ، فهذا يعني أنه يجب أن تكون قد وصلت إلى نهاية TP . هذا لم يقدم أي صعوبات معينة ولكنه شكل مناسبة لممارسة أكثر قليلا مع MySQL ، مع الدعوة إلى وظائف والتواريخ في SQL .
index.php : قائمة بآخر المشاركات
يتكون TP من صفحتين. إليك التصحيح الذي أقترحه لك للصفحة index.php التي تسرد أحدث منشورات المدونة:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Mon blog</title>
    <link href="style.css" rel="stylesheet" /> 
    </head>
        
    <body>
        <h1>Mon super blog !</h1>
        <p>Derniers billets du blog :</p>
 
<?php
// Connexion à la base de données
try
{
    $bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

// On récupère les 5 derniers billets
$req = $bdd->query('SELECT id, titre, contenu, DATE_FORMAT(date_creation, \'%d/%m/%Y à %Hh%imin%ss\') AS date_creation_fr FROM billets ORDER BY date_creation DESC LIMIT 0, 5');

while ($donnees = $req->fetch())
{
?>
<div class="news">
    <h3>
        <?php echo htmlspecialchars($donnees['titre']); ?>
        <em>le <?php echo $donnees['date_creation_fr']; ?></em>
    </h3>
    
    <p>
    <?php
    // نعرض محتوى المنشور
    echo nl2br(htmlspecialchars($donnees['contenu']));
    ?>
    <br />
    <em><a href="commentaires.php?billet=<?php echo $donnees['id']; ?>">Commentaires</a></em>
    </p>
</div>
<?php
} 
$req->closeCursor();
?>
</body>
</html>
ستجد أن جميع النصوص محمية  htmlspecialchars()، بما في ذلك العناوين. أنا أيضا استخدام وظيفة التي يجب أن تكون جديدة بالنسبة لك: nl2br() . تسمح لك بتحويل فواصل الأسطر إلى علامات HTML  <br /> . هذه هي الوظيفة التي ستحتاجها بالتأكيد للحفاظ على فواصل الأسطر التي تم إدخالها في النماذج بسهولة.
بجانب SQL ، تنفذ هذه الصفحة طلبًا واحدًا فقط: الطلب الذي يسترجع آخر خمس نشرات.

SELECT id, titre, contenu, DATE_FORMAT(date_creation, '%d/%m/%Y à %Hh%imin%ss') AS date_creation_fr FROM billets ORDER BY date_creation DESC LIMIT 0, 5
نحن نستعيد جميع البيانات التي تهمنا في هذا الجدول ، من خلال وضع التاريخ في شكل معين لهذا ، نستخدم الدالة العددية DATE_FORMATالتي تتيح لنا الحصول على تاريخ بالتنسيق الفرنسي.
يتم طلب المنشورات حسب تاريخ التناقص ، وآخرها في أعلى الصفحة.
أخيرًا ، يتبع كل منشور رابط للصفحة commentaires.php ينقل رقم المنشور في عنوان URL :

<a href="commentaires.php?billet=<?php echo $donnees['id']; ?>">Commentaires</a>
comments.php : نشر منشور وتعليقاته
تحتوي هذه الصفحة على أوجه تشابه مع الصفحة السابقة ولكنها أكثر تعقيدًا. في الواقع ، لعرض منشور وتعليقاته ، نحتاج إلى تقديم استعلامات SQL :
  • طلب لاسترداد محتوى المنشور ؛
  • طلب لاسترداد التعليقات المرتبطة بالمنشور.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Mon blog</title>
    <link href="style.css" rel="stylesheet" /> 
    </head>
        
    <body>
        <h1>Mon super blog !</h1>
        <p><a href="index.php">Retour à la liste des billets</a></p>
 
<?php
try
{
    $bdd = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

$req = $bdd->prepare('SELECT id, titre, contenu, DATE_FORMAT(date_creation, \'%d/%m/%Y à %Hh%imin%ss\') AS date_creation_fr FROM billets WHERE id = ?');
$req->execute(array($_GET['billet']));
$donnees = $req->fetch();
?>

<div class="news">
    <h3>
        <?php echo htmlspecialchars($donnees['titre']); ?>
        <em>le <?php echo $donnees['date_creation_fr']; ?></em>
    </h3>
    
    <p>
    <?php
    echo nl2br(htmlspecialchars($donnees['contenu']));
    ?>
    </p>
</div>

<h2>Commentaires</h2>

<?php
$req->closeCursor(); 

$req = $bdd->prepare('SELECT auteur, commentaire, DATE_FORMAT(date_commentaire, \'%d/%m/%Y à %Hh%imin%ss\') AS date_commentaire_fr FROM commentaires WHERE id_billet = ? ORDER BY date_commentaire');
$req->execute(array($_GET['billet']));

while ($donnees = $req->fetch())
{
?>
<p><strong><?php echo htmlspecialchars($donnees['auteur']); ?></strong> le <?php echo $donnees['date_commentaire_fr']; ?></p>
<p><?php echo nl2br(htmlspecialchars($donnees['commentaire'])); ?></p>
<?php
} 
$req->closeCursor();
?>
</body>
</html>
هذا الكود كبير بعض الشيء ولكن يمكننا تقطيعه إلى قسمين:
  • عرض المنشورات ؛
  • نشر التعليقات.
يشبه الطلب الذي يسترجع المنشور الطلب الموجود في الصفحة السابقة ، مع اختلاف أنه طلب معد لأنه يعتمد على مُدخل: معرف المنشور ( تم توفيره بواسطة $_GET['billet'] الذي تلقيناه في URL) .
نظرًا لأننا نحصل بالضرورة على منشور واحدة ، فلا فائدة من إنشاء حلقة. يكون العرض مطابقًا للعرض الذي تم إجراؤه لكل منشور في الصفحة السابقة ، باستثناء رابط صفحة التعليقات الذي لم يعد مفيدًا (بما أننا في صفحة التعليقات).
نفكر في تحرير المؤشر بعد نشر المنشور باستخدام:

<?php
$req->closeCursor();
?>
في الواقع ، يسمح هذا "بإنهاء" معالجة الطلب بحيث يمكن معالجة الطلب التالي دون مشكلة.
ثم يتم استرداد التعليقات باستخدام الطلب التالي:

SELECT auteur, commentaire, DATE_FORMAT(date_commentaire, '%d/%m/%Y à %Hh%imin%ss') AS date_commentaire_fr FROM commentaires WHERE id_billet = ? ORDER BY date_commentaire
ستلاحظ أننا نتصل فقط بقاعدة البيانات مرة واحدة في كل صفحة. لا حاجة للاتصال مرة أخرى لتقديم هذا الطلب الثاني.

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

اذهب أبعد من ذلك


غطى هذا المشروع فقط البنية الأساسية للمدونة مع التعليقات. نظرًا لأنه بسيط نسبيًا ، فإن إمكانيات التمديد فيه ممكنة جدا .
إذن كيف يمكننا تحسين مدونتنا؟ إليك بعض الاقتراحات التي أوصيك بدراستها والتي ستساعدك على التقدم.
نموذج لإضافة التعليقات
في الصفحة commentaires.php، أضف نموذجًا حتى يتمكن أي زائر من نشر تعليق.
سيتم إعادة توجيه هذا النموذج إلى صفحة تسجل التعليق ثم تعيد التوجيه إلى قائمة التعليقات ، كما فعلنا في الدردشة المصغرة. هذا هو ما تراه في الشكل التالي.
web dynamique php
بنية الصفحة مع إضافة التعليقات
إنه في متناول يديك ؛ لقد تمكنت بالفعل من القيام بذلك ، انتقل لذلك!
استخدام includes
بعض أجزاء الكود متكررة قليلاً. على سبيل المثال ، نجد أن الكتلة نفسها تعرض منشورًا على صفحة النشر وعلى صفحة التعليقات.
قد يكون من الأسهل وجود كود واحد في ملف تقوم بتضمينه من index.php و commentaires.php .
تحقق من وجود المنشور في صفحة التعليقات
تخيل أن الزائر لديه متعة في تعديل عنوان URL لصفحة التعليقات. على سبيل المثال ، إذا حاول الوصول commentaires.php?billet=819202 وكان المنشور رقم 819202 غير موجود ، فلن تظهر له رسالة خطأ (في الواقع ، سيكون محتوى الصفحة فارغًا). لجعل موقعك يبدو أكثر جدّية قليلاً ، يجب عليك عرض خطأ.
للقيام بذلك ، تحقق مما إذا كان الطلب الذي يسترجع محتوى البطاقة يُرجع البيانات. إن أبسط طريقة لذلك هي التحقق من fetch() إذا كان المتغير $donnees فارغًا أم لا ، وذلك بفضل الوظيفة empty() .
وبالتالي ، إذا كان المتغير فارغًا ، فستتمكن من عرض رسالة خطأ مثل "هذا المنشور غير موجود" . وإلا ، فسوف تعرض بقية الصفحة كالمعتاد.
ترقيم الصفحات والتعليقات
عندما تبدأ في الحصول على الكثير من المشاركات (والكثير من التعليقات) ، قد لا ترغب في رؤية كل شيء في صفحة واحدة. للقيام بذلك ، يجب إنشاء نظام الصفحات.
افترض أنك تريد عرض خمسة تعليقات فقط لكل صفحة. إذا كنت تريد نشر روابط إلى كل صفحة ، فأنت بحاجة إلى معرفة عدد مشاركات مدونتك.
على سبيل المثال ، إذا كان لديك 5 منشورات ، فستكون هناك صفحة واحدة فقط. إذا كان لديك 12 منشور ، ستكون هناك ثلاث صفحات. لمعرفة عدد المنشورات ، يعد استعلام SQL COUNT(*)ضروريًا:

SELECT COUNT(*) AS nb_billets FROM billets
بمجرد استرداد هذا العدد من المنشورات ، يمكنك العثور على عدد الصفحات وإنشاء روابط لكل منها:
Page : 1 2 3 4
سيؤدي كل رقم من هذه الأرقام إلى نفس الصفحة وإضافة رقم الصفحة إلى عنوان URL :

<a href="index.php?page=2">2</a>
باستخدام المُدخل ، $_GET['page'] ستتمكن من تحديد الصفحة التي يجب عرضها. الأمر متروك لك لتكييف استعلام SQL للبدء فقط من المنشور بلا $ x $. على سبيل المثال ، إذا طلبت عرض الصفحة 2 ، فستحتاج إلى عرض المنشورات من 4 إلى 8 فقط (تذكر أننا نبدأ في العد من 0!). مراجعة القسم على LIMIT إذا لزم الأمر.
وإذا لم يتم تحديد رقم الصفحة في عنوان URL ، عندما نصل إلى المدونة لأول مرة؟
في هذه الحالة ، إذا $_GET['page'] لم يتم تعريفها ، فسوف يتعين عليك التفكير في أن الزائر يريد عرض الصفحة 1 (الأحدث).
الأمر يتطلب بعض التفكير ولكن اللعبة تستحق الجهد! لا تتردد في طلب المساعدة في المنتديات إذا لزم الأمر.
إنشاء واجهة إدارة Blog
ربما هذا هو التحسين الأطول. ستحتاج إلى إنشاء صفحات تسمح لك بتعديل وحذف وإضافة منشورات جديدة.
مشكلة واحدة ستعترضك: كيفية حماية الوصول إلى هذه الصفحات؟ في الواقع ، يجب أن تكون الشخص الوحيد الذي يمكنه الوصول إلى واجهة الإدارة الخاصة بك ، وإلا يمكن لأي شخص إضافة منشورات إذا كان يعرف عنوان URL لصفحة الإدارة!
توجد العديد من التقنيات لحماية الوصول إلى الإدارة. أسهل طريقة في هذه الحالة هي إنشاء مجلد فرعي  admin الذي يحتوي على كافة ملفات إدارة Blog (  add.php ، modify.php، delete.php ... ) . هذا المجلد admin يجب ان يكون محميا بشكل كامل باستخدام ملفات  .htaccess و  .htpasswd ، بحيث لا يمكن لأحد تحميله يحتوي على صفحات إلا إذا كنت تعرف المستخدم وكلمة السر (الشكل أدناه).
web dynamique php
هيكل الصفحات مع Admin

الى العمل !!