# مراجعة منطق الكود — رفع الطلاب وقائمة الطلاب

## 1. تدفق رفع ملف Excel/CSV (students.php)

### الترتيب العام
1. **التحقق من الطلب والملف:** POST + action=upload_excel، وجود الملف، حجم ≤ 10MB، امتداد csv/xlsx/xls.
2. **قراءة الملف:** `ExcelReader::readFile()` → إما `readCSV()` أو `readExcel()`.
3. **فحص سريع للملف:** عد الصفوف التي تحتوي بيانات، وعد الأرقام الجامعية **المختلفة** (`$unique_in_file`, `$rows_with_data`) لاستخدامها في الرسائل لاحقاً.
4. **التحقق من الكلية:** للأدمن من POST، لغير الأدمن من الجلسة؛ إذا كانت 0 → رسالة خطأ ولا معاملة.
5. **بدء المعاملة (Transaction)** ثم معالجة كل صف.

### معالجة كل صف
- **تطبيع المفاتيح:** `array_combine(array_map('trim', array_keys($row)), array_values($row))` حتى لا تؤثر مسافات زائدة من Excel.
- **استخراج الحقول:** البحث عن الرقم الجامعي والاسم العربي بعدة أسماء محتملة (إنجليزي/عربي)، واستخدام دالة `cellStr` لتحويل الأرقام إلى نص والحفاظ على الرقم الجامعي (مثل 11079388-23).
- **تخطي الصفوف الفارغة:** إذا كان الرقم والاسم فارغين معاً → `continue` بدون احتساب خطأ.
- **الحقول الاختيارية:** national_id, full_name_en, email, phone, major_code, enrollment_date, status، إلخ — مع قيم افتراضية مناسبة.
- **تحديد التخصص (major_id):**
  1. من الملف إن وُجد (`major_id_from_file` أو من `major_code` + college_id).
  2. من النموذج (`$_POST['major_id']`).
  3. للكليات بدون تشعيب: إن لم يُحدد تخصص، جلب أو إنشاء تخصص افتراضي "الكلية".
- **التحقق من الصلاحيات:** `belongsToCollege` أو دور admin.
- **التحقق من التكرار:**
  1. **بالرقم الجامعي:** `SELECT ... WHERE student_number = ?` — إذا وُجد → زيادة `duplicate_count` وإضافة للقائمة ثم `continue`.
  2. **بالرقم الوطني:** فقط إذا كان `national_id` غير فارغ؛ إذا وُجد → نفس السلوك.
- **المستخدم (user):** إن وُجد مستخدم بنفس `username = 'STU' + student_number` نستخدمه، وإلا نُنشئ مستخدماً جديداً بدور `student`.
- **إصلاح AUTO_INCREMENT إن لزم:** حذف سجل id=0، التحقق من وجود AUTO_INCREMENT على `id`، وتصحيح القيمة التالية إن كانت ≤ MAX(id).
- **INSERT الطالب:** بدون حقل `id`؛ القيم: college_id, major_id, student_number, national_id, full_name_ar, full_name_en, email, phone, address, enrollment_date, graduation_date, user_id, status.
- **إذا lastInsertId() أعاد 0:** إصلاح AUTO_INCREMENT ثم إعادة INSERT مرة واحدة؛ إن فشل مرة ثانية → استثناء.
- **في catch PDOException:** إن كان الخطأ Duplicate entry (أو 1062) → احتساب تكرار وإضافة للقائمة مع اسم الكلية (إن وُجد)، وإلا تسجيل الخطأ في `$errors`.

### ملاحظات منطقية
- **التكرار يُحسب على مستوى النظام كله** (حقل `student_number` UNIQUE في الجدول)، وليس لكلية فقط.
- فحص التكرار يحدث **قبل** إنشاء المستخدم والإدراج، فلا داعي لإلغاء إنشاء المستخدم عند التكرار.
- عند فشل الإدراج بسبب تكرار (مثلاً national_id)، استعلام جلب اسم الكلية يعتمد على `student_number`؛ إن لم يُوجَد سجل (لأن الإدراج فشل) يُستخدم '—' بعد إصلاح الوصول لـ `$dup_college` عند كونها null.

---

## 2. قارئ CSV (ExcelReader::readCSV)

- **ترميز:** تحويل المحتوى إلى UTF-8 عبر `ensureUtf8()` ثم كتابته في ملف مؤقت وقراءته بـ `fgetcsv`.
- **العناوين:** السطر الأول يُقرأ كعناوين، وتُنظَّف وتُخزَّن في `$original_headers`.
- **الصفوف:** كل صف يُقرأ بـ `fgetcsv()`؛ الصفوف الفارغة (بعد array_filter) تُتخطى.
- **ربط القيم بالعناوين:** لكل صف يُنشأ `$row_data` بحيث المفتاح = اسم العمود من العناوين، والقيمة من `$row[$index]` مع تنظيف UTF-8. إذا كان عدد الأعمدة في الصف **أقل** من عدد العناوين، الأعمدة الناقصة تأخذ قيمة فارغة (`isset($row[$index])`). إذا كان العدد **أكبر**، الأعمدة الزائدة تُتجاهل. **لا يوجد فحص بعدد أعمدة ثابت** — لذلك لن يظهر خطأ "Invalid column count" من هذا الكود (ذلك الخطأ من phpMyAdmin عند استيراد CSV هناك).
- **مفاتيح إضافية:** يُخزَّن نفس القيمة تحت العنوان بالأحرف الصغيرة وبأسماء قياسية (مثل student_number, full_name_ar) حسب تطابق اسم العمود، لضمان توافق القالب مع الحقول المتوقعة في students.php.

---

## 3. قائمة الطلاب (استعلامات العرض)

- **للأدمن:**  
  - إذا لم يُختر فلتر (college_id و major_id = 0): استعلام بسيط بدون WHERE، مع LEFT JOIN على majors و colleges و COALESCE لاسم الكلية/التخصص، لضمان ظهور كل الطلاب حتى مع college_id/major_id غير مطابق.  
  - إذا وُجد فلتر: إضافة شروط WHERE (college_id و/أو major_id) مع نفس الـ JOIN.
- **لمشرف الكلية/المسجل:** دائماً شرط `s.college_id = ?` من الجلسة، وإضافة شرط major_id إن وُجد.
- **الترقيم التلقائي:** لا يُستخدم في العرض؛ الاعتماد على `id` من الجدول وAUTO_INCREMENT عند الإدراج فقط.

---

## 4. إصلاحات تمت أثناء المراجعة

- **معالجة `$dup_college` عند كونها null:** في catch تكرار الإدراج، استبدال `$dup_college['name_ar'] ?? '—'` بـ `($dup_college && isset($dup_college['name_ar'])) ? $dup_college['name_ar'] : '—'` لتجنب محاولة الوصول لمفتاح مصفوفة على قيمة null (مثلاً عند تكرار national_id فقط وعدم وجود سجل بنفس student_number).

---

## 5. إصلاحات نهائية (تم تنفيذها)

- **إيقاف عرض الأخطاء للمستخدم:** `display_errors = 0` و `log_errors = 1` لأسباب أمان.
- **التحقق من تاريخ التسجيل وتاريخ التخرج:** في الرفع، `enrollment_date` و `graduation_date` يُحوَّلان إلى Y-m-d عبر `strtotime`/`date`؛ إن كان التاريخ غير صالح يُستخدم اليوم أو null.
- **national_id فارغ = NULL في قاعدة البيانات:** في الرفع والإضافة، عند فراغ الرقم الوطني يُمرَّر `null` بدلاً من `''` لتجنب خرق UNIQUE (عدة طلاب برقم وطني فارغ كسلسلة).
- **نموذج إضافة طالب واحد:**
  - التحقق من تكرار الرقم الجامعي والرقم الوطني قبل الإدراج، مع رسالة خطأ واضحة.
  - إعادة استخدام مستخدم موجود إن وُجد (username = STU + student_number) بدلاً من إنشاء مستخدم جديد.
  - دالة مساعدة `addOneStudent()` تضم فحص AUTO_INCREMENT والإدراج وإصلاح lastInsertId عند الحاجة.
- **توحيد INSERT students:** نموذج الإضافة يدرج الحقول: address (NULL)، graduation_date (NULL)، status ('active') ليتوافق مع بنية الجدول.

## 6. توصيات اختيارية (لم تُنفَّذ)

- **تسجيل السطر في الأخطاء:** إضافة رقم السطر في الملف لرسائل الأخطاء (`$row_index + 2`) لتسهيل تصحيح الملف.
- **تحديد مصدر التكرار:** عند التقاط Duplicate entry، التحقق من اسم العمود في رسالة الخطأ (student_number vs national_id) وعرض رسالة أوضح للمستخدم.
