وحدة إضافة وتعديل المنتجات لصلاحية المسؤول (Admin)
في هذا الدرس، ستقوم ببناء وحدة كاملة لإضافة وتعديل المنتجات للمسؤولين باستخدام PHP (PDO) و AJAX، مع نظام بسيط لرفع الملفات. يغطي المقال تصميم قواعد البيانات، التحقق من البيانات في الخلفية، التعامل الآمن مع الملفات، إرسال النماذج عبر AJAX، سيناريوهات الأعمال الواقعية، نصائح تجربة المستخدم (UX)، واعتبارات تحسين محركات البحث (SEO).
لماذا تهم هذه الوحدة الشركات الحقيقية؟
يحتاج كل متجر إلكتروني إلى واجهة مسؤول موثوقة لإضافة وصيانة المنتجات. وحدة الإضافة والتعديل البسيطة والآمنة تسرع العمليات لمديري الكتالوجات، وتقلل الأخطاء، وتؤثر مباشرة على استمرارية المبيعات. أمثلة على أهميتها:
- المتاجر الإلكترونية الصغيرة: يقوم المالك برفع منتجات موسمية جديدة يومياً؛ نموذج الإضافة السريع يوفر الوقت.
- إدارة المخزون المحلي: يمكن للموظفين تحديث السعر والتوافر دون الحاجة لمساعدة المطورين.
- مسؤول منصة متعددة البائعين (Marketplace): يمكن للمشرفين إزالة أو تعديل القوائم الإشكالية بسرعة.
أهداف الدرس
- تصميم مخطط قاعدة بيانات آمن للمنتجات.
- تنفيذ التحقق من البيانات من جهة الخادم باستخدام PHP + PDO.
- إنشاء نموذج إضافة وتعديل يعمل بتقنية AJAX مع خاصية رفع الملفات.
- التعامل مع رفع الصور بأمان وتخزين مراجعها في قاعدة البيانات.
- إظهار رسائل النجاح والخطأ في واجهة المستخدم وتحديث قائمة المنتجات ديناميكياً.
- اتباع أفضل الممارسات في الحماية وتجربة المستخدم.
مخطط قاعدة البيانات (مثال)
جدول منتجات مبسط ومفيد للأمثلة:
CREATE TABLE products (
pr_id INT AUTO_INCREMENT PRIMARY KEY,
pr_name VARCHAR(255) NOT NULL,
pr_price DECIMAL(10,2) NOT NULL,
pr_image VARCHAR(255) DEFAULT NULL,
pr_status ENUM('draft','published') DEFAULT 'draft',
pr_description TEXT,
pr_length INT DEFAULT 0,
pr_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
الفهارس (Indexes): قم بإنشاء فهرس على pr_status واختيارياً على pr_price لتسريع عملية الفلترة.
هيكل الملفات والروابط (موصى به)
/assets/connect/backlistjoin.php— يعالج نماذج AJAX POST (إضافة منتج، حفظ منتج، إلخ)./indexes/products.php— عرض إدارة المنتجات (القائمة، نموذج الإضافة، نموذج التعديل)./assets/js/script.js— معالجة النماذج من جهة العميل و AJAX./assets/images/— تخزين الصور المرفوعة (تأكد من الأذونات الصحيحة)./inc/functions.php— فئات/وظائف مساعدة (مثال: Store::getData).
تدفق نموذج AJAX (مستوى عالٍ)
- يقوم المسؤول بملء النموذج (الاسم، السعر، الوصف، الحالة، ملف الصورة).
- التحقق من جهة العميل يمنع ترك الحقول المطلوبة فارغة.
- يتم بناء كائن FormData وإرساله عبر AJAX إلى
backlistjoin.phpمع تحديد نوع النموذج. - يقوم الخادم بالتحقق من المدخلات، فحص الصورة، وإدراج/تحديث قاعدة البيانات باستخدام PDO Prepared Statements.
- يعيد الخادم استجابة JSON تحتوي على مفاتيح
successأوerror. - يعرض العميل الرسائل ويقوم اختيارياً بإعادة التوجيه أو تحديث الواجهة.
مثال من جهة الخادم: إضافة منتج (PHP + PDO)
نقاط رئيسية: استخدم الاستعلامات المُعدة، قم بتنقية المدخلات، افحص نوع وحجم الملف، خزن اسم الملف فقط في القاعدة، وانقل الملف باستخدام move_uploaded_file.
// backlistjoin.php (مبسط)
<?php
require 'inc/bootstrap.php'; // اتصال القاعدة وبدء الجلسة
$response = [];
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['form'])) {
$form = $_POST['form'];
switch($form) {
case 'add_product':
$name = trim($_POST['product_name'] ?? '');
$price = trim($_POST['product_price'] ?? '');
$status = trim($_POST['product_status'] ?? '');
$desc = trim($_POST['product_description'] ?? '');
if($name === '' || $price === '' || $status === '') {
$response['error'] = 'الحقول المطلوبة مفقودة.';
echo json_encode($response); exit;
}
if(!is_numeric($price)) {
$response['error'] = 'صيغة السعر غير صحيحة.';
echo json_encode($response); exit;
}
$imageName = null;
if(isset($_FILES['product_image']) && $_FILES['product_image']['error'] === UPLOAD_ERR_OK) {
$allowed = ['image/jpeg','image/png','image/gif'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['product_image']['tmp_name']);
finfo_close($finfo);
if(!in_array($mime, $allowed)) {
$response['error'] = 'نوع الصورة غير مدعوم.';
echo json_encode($response); exit;
}
$ext = pathinfo($_FILES['product_image']['name'], PATHINFO_EXTENSION);
$imageName = uniqid('pr_') . '.' . $ext;
$target = __DIR__ . '/../assets/images/' . $imageName;
}
try {
$con = DB::getConnection();
$sql = "INSERT INTO products (pr_name, pr_price, pr_length, pr_image, pr_status, pr_time, pr_description)
VALUES (:name, :price, :length, :image, :status, :time, :description)";
$stmt = $con->prepare($sql);
$length = 100;
$time = date('Y-m-d H:i:s');
$stmt->bindParam(':name', $name);
$stmt->bindParam(':price', $price);
$stmt->bindParam(':length', $length, PDO::PARAM_INT);
$stmt->bindParam(':image', $imageName);
$stmt->bindParam(':status', $status);
$stmt->bindParam(':time', $time);
$stmt->bindParam(':description', $desc);
$stmt->execute();
if($stmt->rowCount()) {
$id = $con->lastInsertId();
if($imageName && isset($target)) {
move_uploaded_file($_FILES['product_image']['tmp_name'], $target);
}
$response['success'] = 'product';
$response['id'] = $id;
} else {
$response['error'] = 'فشل إدراج البيانات.';
}
} catch (Exception $e) {
$response['error'] = 'خطأ في الخادم: ' . $e->getMessage();
}
echo json_encode($response);
break;
}
}
?>
مثال من جهة العميل: إرسال AJAX (jQuery)
هام: أرسل البيانات كـ FormData، واضبط contentType: false و processData: false.
// assets/js/script.js
$(document).ready(function () {
$('form[name=add_product]').on('submit', function (e) {
e.preventDefault();
var form = $(this)[0];
var fd = new FormData(form);
fd.append('form', 'add_product');
if(!$('input[name=product_name]').val()) {
alert('اسم المنتج مطلوب');
return;
}
$.ajax({
url: localDir() + 'assets/connect/backlistjoin.php',
type: 'POST',
data: fd,
processData: false,
contentType: false,
dataType: 'json'
}).done(function (res) {
if(res.error) {
$('.form-error', form).text(res.error).show();
} else if(res.success) {
$('.notes', form).html('تمت إضافة المنتج بنجاح.. عرض هنا');
}
}).fail(function () {
$('.form-error', form).text('خطأ في الشبكة');
});
});
});
أفضل ممارسات الحماية
- المصادقة والتفويض: اسمح فقط للمسؤولين بالوصول لصفحات الإضافة/التعديل. تحقق من رتبة المستخدم في الخادم مع كل طلب.
- الاستعلامات المُعدة (Prepared Statements): استخدم دائماً PDO لتنفيذ الاستعلامات.
- سلامة رفع الملفات: تحقق من نوع MIME باستخدام
finfo_file، حدد حجماً أقصى للملفات، واحمِ المجلد من تنفيذ السكربتات عبر ملف .htaccess. - تنقية المخرجات: استخدم
htmlspecialchars()عند عرض البيانات لمنع هجمات XSS.
تحسين محركات البحث (SEO) وتحسين الأعمال
للمساعدة في اكتشاف المتجر وتحسين محركات البحث:
- تأكد من أن صفحات المنتجات تحتوي على عناوين وصفية، وأوصاف ميتا (Meta Descriptions)، وبيانات منظمة (JSON-LD).
- استخدم أسماء ملفات وصفية للصور.
- وفر أوصافاً فريدة للمنتجات — وتجنب تكرار النصوص التلقائية عبر العديد من المنتجات.
- اسمح للمسؤولين بتعيين حقول SEO (عنوان الميتا، وصف الميتا، الرابط الأساسي Canonical).
الأسئلة الشائعة
- س: أين يجب تخزين الصور؟
- ج: استخدم مجلداً محمياً داخل المشروع أو خدمة تخزين سحابية (S3). إذا كان داخل مسار الويب، امنع تنفيذ الملفات البرمجية فيه.
- س: كيف يتم التعامل مع الكتالوجات الكبيرة؟
- ج: قم بتنفيذ نظام تقسيم الصفحات (Pagination)، الفهرسة، ونقاط التحديث الجماعي (Bulk Update).
مثال على بيانات Product JSON-LD لـ SEO
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "اسم المنتج التجريبي",
"image": "https://example.com/assets/images/pr_12345.jpg",
"description": "وصف قصير للمنتج هنا.",
"offers": {
"@type": "Offer",
"priceCurrency": "USD",
"price": "19.99",
"availability": "https://schema.org/InStock"
}
}
</script>
