بناء مدونة بسيطة باستخدام Node.js و MySQL

Amine
23/09/2024

في هذا الدليل الشامل، سنقوم ببناء مدونة بسيطة باستخدام Node.js و MySQL. ستتعلم كيفية إنشاء تطبيق ويب يقوم بعمليات الإنشاء و القراءة و التحديث و الحذف (CRUD) للمقالات. هذا المشروع يعد طريقة ممتازة لفهم كيفية استخدام Node.js مع قواعد البيانات العلائقية مثل MySQL.

المتطلبات المسبقة

  • معرفة أساسية بـ JavaScript.
  • معرفة بأساسيات Node.js و npm.
  • معرفة بأساسيات MySQL.
  • تثبيت Node.js و npm على جهازك.
  • تثبيت MySQL وإنشاء قاعدة بيانات.

إعداد بيئة التطوير

تثبيت Node.js و npm

قم بتنزيل وتثبيت Node.js من الموقع الرسمي.

تثبيت MySQL

قم بتنزيل وتثبيت MySQL من الموقع الرسمي. اتبع التعليمات لإنشاء مستخدم وكلمة مرور.

إنشاء قاعدة بيانات

افتح سطر الأوامر الخاص بـ MySQL أو استخدم أداة إدارة مثل phpMyAdmin أو MySQL Workbench، ونفذ الأمر التالي لإنشاء قاعدة بيانات جديدة:

CREATE DATABASE simple_blog;

إنشاء مشروع جديد

افتح سطر الأوامر ونفذ الأوامر التالية:

mkdir simple-blog
cd simple-blog
npm init -y

تثبيت التبعيات

سنقوم بتثبيت الحزم التالية:

  • express: إطار عمل ويب لـ Node.js.
  • mysql2: مكتبة للتعامل مع MySQL.
  • sequelize: ORM للتعامل مع قواعد البيانات.
  • ejs: محرك قوالب لعرض الصفحات.
  • body-parser: لتحليل بيانات النموذج.
  • method-override: لدعم عمليات HTTP PUT و DELETE.

نفذ الأمر التالي:

npm install express mysql2 sequelize ejs body-parser method-override

هيكلة المشروع

قم بإنشاء الملفات والمجلدات التالية:

  • app.js: الملف الرئيسي للتطبيق.
  • models/: مجلد لنماذج قاعدة البيانات.
    • index.js
    • blog.js
  • views/: مجلد لملفات القوالب.
    • index.ejs
    • new.ejs
    • show.ejs
    • edit.ejs
  • public/: مجلد لملفات CSS.

إعداد التطبيق

إعداد Sequelize والاتصال بقاعدة البيانات

افتح models/index.js وأضف الكود التالي:

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('simple_blog', 'root', 'كلمة_المرور_الخاصة_بك', {
  host: 'localhost',
  dialect: 'mysql',
});

module.exports = sequelize;

ملاحظة: استبدل 'كلمة_المرور_الخاصة_بك' بكلمة المرور الفعلية لمستخدم MySQL الخاص بك.

إنشاء نموذج المقالة

في models/blog.js:

const { DataTypes } = require('sequelize');
const sequelize = require('./index');

const Blog = sequelize.define('Blog', {
  title: {
    type: DataTypes.STRING,
    allowNull: false,
  },
  image: {
    type: DataTypes.STRING,
    allowNull: false,
  },
  body: {
    type: DataTypes.TEXT,
    allowNull: false,
  },
});

module.exports = Blog;

إعداد app.js

افتح app.js وأضف الكود التالي:

const express = require('express');
const sequelize = require('./models');
const Blog = require('./models/blog');
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
const app = express();

// إعدادات التطبيق
app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(methodOverride('_method'));

// اتصال بقاعدة البيانات وتزامن النماذج
sequelize
  .authenticate()
  .then(() => {
    console.log('تم الاتصال بقاعدة البيانات بنجاح.');
    return sequelize.sync(); // تزامن النماذج مع قاعدة البيانات
  })
  .then(() => {
    console.log('تم تزامن النماذج.');
  })
  .catch((err) => {
    console.error('غير قادر على الاتصال بقاعدة البيانات:', err);
  });

// تشغيل الخادم
app.listen(3000, () => {
  console.log('الخادم يعمل على المنفذ 3000');
});

// إضافة المسارات هنا (سنقوم بإضافتها في القسم التالي)

إعداد المسارات (Routes)

عرض جميع المقالات

app.get('/', (req, res) => {
  res.redirect('/blogs');
});

app.get('/blogs', async (req, res) => {
  try {
    const blogs = await Blog.findAll();
    res.render('index', { blogs: blogs });
  } catch (err) {
    console.log('خطأ في جلب البيانات:', err);
    res.render('index', { blogs: [] });
  }
});

إنشاء مقال جديد

عرض نموذج الإنشاء

app.get('/blogs/new', (req, res) => {
  res.render('new');
});

إضافة المقال إلى قاعدة البيانات

app.post('/blogs', async (req, res) => {
  try {
    await Blog.create(req.body.blog);
    res.redirect('/blogs');
  } catch (err) {
    console.log('خطأ في إنشاء المقال:', err);
    res.render('new');
  }
});

عرض مقال محدد

app.get('/blogs/:id', async (req, res) => {
  try {
    const blog = await Blog.findByPk(req.params.id);
    if (blog) {
      res.render('show', { blog: blog });
    } else {
      res.redirect('/blogs');
    }
  } catch (err) {
    console.log('خطأ في جلب المقال:', err);
    res.redirect('/blogs');
  }
});

تعديل مقال

عرض نموذج التعديل

app.get('/blogs/:id/edit', async (req, res) => {
  try {
    const blog = await Blog.findByPk(req.params.id);
    if (blog) {
      res.render('edit', { blog: blog });
    } else {
      res.redirect('/blogs');
    }
  } catch (err) {
    console.log('خطأ في جلب المقال للتعديل:', err);
    res.redirect('/blogs');
  }
});

تحديث المقال

app.put('/blogs/:id', async (req, res) => {
  try {
    await Blog.update(req.body.blog, {
      where: { id: req.params.id },
    });
    res.redirect('/blogs/' + req.params.id);
  } catch (err) {
    console.log('خطأ في تحديث المقال:', err);
    res.redirect('/blogs');
  }
});

حذف مقال

app.delete('/blogs/:id', async (req, res) => {
  try {
    await Blog.destroy({
      where: { id: req.params.id },
    });
    res.redirect('/blogs');
  } catch (err) {
    console.log('خطأ في حذف المقال:', err);
    res.redirect('/blogs');
  }
});

إنشاء القوالب

index.ejs

<!DOCTYPE html>
<html lang="ar">
<head>
  <meta charset="UTF-8">
  <title>المدونة البسيطة</title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <h1>المدونة البسيطة</h1>
  <a href="/blogs/new">إنشاء مقال جديد</a>
  <% blogs.forEach(blog => { %>
    <div class="blog">
      <h2><a href="/blogs/<%= blog.id %>"><%= blog.title %></a></h2>
      <img src="<%= blog.image %>" alt="<%= blog.title %>">
      <p><%= blog.body.substring(0, 100) %>...</p>
    </div>
  <% }) %>
</body>
</html>

new.ejs

<!DOCTYPE html>
<html lang="ar">
<head>
  <meta charset="UTF-8">
  <title>إنشاء مقال جديد</title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <h1>إنشاء مقال جديد</h1>
  <form action="/blogs" method="POST">
    <label>العنوان</label>
    <input type="text" name="blog[title]" required>
    <label>رابط الصورة</label>
    <input type="text" name="blog[image]" required>
    <label>المحتوى</label>
    <textarea name="blog[body]" required></textarea>
    <button type="submit">نشر</button>
  </form>
</body>
</html>

show.ejs

<!DOCTYPE html>
<html lang="ar">
<head>
  <meta charset="UTF-8">
  <title><%= blog.title %></title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <h1><%= blog.title %></h1>
  <img src="<%= blog.image %>" alt="<%= blog.title %>">
  <p><%= blog.body %></p>
  <a href="/blogs/<%= blog.id %>/edit">تعديل</a>
  <form action="/blogs/<%= blog.id %>?_method=DELETE" method="POST">
    <button type="submit">حذف</button>
  </form>
  <a href="/blogs">العودة</a>
</body>
</html>

edit.ejs

<!DOCTYPE html>
<html lang="ar">
<head>
  <meta charset="UTF-8">
  <title>تعديل المقال</title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <h1>تعديل المقال</h1>
  <form action="/blogs/<%= blog.id %>?_method=PUT" method="POST">
    <label>العنوان</label>
    <input type="text" name="blog[title]" value="<%= blog.title %>" required>
    <label>رابط الصورة</label>
    <input type="text" name="blog[image]" value="<%= blog.image %>" required>
    <label>المحتوى</label>
    <textarea name="blog[body]" required><%= blog.body %></textarea>
    <button type="submit">حفظ التغييرات</button>
  </form>
</body>
</html>

إنشاء ملف CSS

في مجلد public/، أنشئ ملف styles.css وأضف الأنماط التالية:

body {
  font-family: Tahoma, Arial, sans-serif;
  direction: rtl;
  margin: 20px;
}

h1, h2 {
  color: #333;
}

a {
  text-decoration: none;
  color: #0066cc;
}

a:hover {
  text-decoration: underline;
}

.blog {
  border-bottom: 1px solid #ccc;
  margin-bottom: 20px;
}

.blog img {
  max-width: 100%;
}

label {
  display: block;
  margin-top: 10px;
}

input[type="text"],
textarea {
  width: 100%;
  padding: 8px;
  box-sizing: border-box;
}

button {
  margin-top: 10px;
  padding: 10px 20px;
}

تشغيل التطبيق

لتشغيل التطبيق، نفذ الأمر التالي:

node app.js

أو إذا كنت تستخدم nodemon:

nodemon app.js

افتح المتصفح وانتقل إلى http://localhost:3000/ لتجربة المدونة.

الخاتمة

تهانينا! لقد قمت ببناء مدونة بسيطة باستخدام Node.js و MySQL. يمكنك تطوير هذا المشروع بإضافة ميزات مثل التحقق من صحة البيانات، والمصادقة، وتحسين التصميم، وإضافة إمكانيات أكثر تعقيدًا.

المصادر

التعليقات

اترك تعليقاً