يا هلا بيك يا باشمهندس في مقال جديد من سلسلة الاحتراف في تطوير الـ Web Development. خلينا نتفق في الأول على حاجة.. كم مرة العميل كلمك في التليفون وقال لك بصوت كله رعب: "لحقني يا بشمهندس.. الداتا كلها اتمسحت بالغلط!" أو كام مرة كتبت كود حذف وديت بيه السجلات في داهية من غير ما تقصد ولا تقصد تديها Final Delete؟ الوجع ده كلنا دوقناه، وعشان كده إطار العمل العظيم لارفيل (Laravel) جه عشان ينقذنا بمفهوم الحذف الناعم أو الـ Soft Deletes.
لكن استنى.. الموضوع مش بس شوية كود بنكتبهم وخلاص، الحكاية فيها أسرار وتفاصيل متقدمة، خصوصاً لما ندخل في الجد ونبクス مع العلاقات (Eloquent Relationships) واسترجاع البيانات المحذوفة (Restoring Soft Deleted Records). في المقال ده، هناخد رحلة عميقة عشان نفهم إزاي نطبق الـ Soft Deletes بشكل احترافي ومتعارف عليه في أكبر شركات السوفت وير.
Table of contents [Show]
- 1 إيه هو الـ Soft Deletes أصلاً وليه بنحتاجه؟
- 2 تفعيل الـ Soft Deletes في الـ Eloquent Models
- 3 التعامل مع العلاقات (Eloquent Relationships) والمشاكل المستخبية
- 4 استرجاع البيانات المحذوفة (Restoring Records) بكل سهولة
- 5 تطبيق الـ Soft Deletes على العلاقات تلقائياً (Cascading Soft Deletes)
- 6 خاتمة ونصيحة من أخ
إيه هو الـ Soft Deletes أصلاً وليه بنحتاجه؟
بشكل بسيط، الـ Soft Deletes مش بيعمل مسح حقيقي (DELETE) للبيانات من جدول قاعدة البيانات (Database Table). كل ما هنالك إنه بيسجل تاريخ ووقت الحذف في عمود اسمه deleted_at. لما السجل بياخد تايم ستامب (Timestamp) في العمود ده، لارفيل بيعتبره كأنه مش موجود أصلاً ومش بيجيبه في أي استعلام (Query) عادي.
الاحتياج هنا بيبقى أمني وربحي جداً؛ الشركات بتخاف على داتا المستخدمين، أحياناً بيبقى فيه مراجع مالية أو قانونية بتفرض عليك تحتفظ بالداتا لفترة معينة، وأحياناً بيبقى مجرد حماية لغباء المبرمج أو المستخدم اللي بيدوس Delete وهو مش فاهم.
تفعيل الـ Soft Deletes في الـ Eloquent Models
عشان نبدأ الشغل الصح، محتاجين نضيف التريت (Trait) الشهير SoftDeletes جوه الموديل (Model) بتاعنا. تعالوا نشوف مثال عملي على موديل اسمه Post:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $fillable = ['title', 'body', 'user_id'];
}
وطبعاً ما تنساش تروح تعمل Migration وتزود العمود ده في الداتابيز بتاعتك باستخدام الميثود البسيطة $table->softDeletes();. من اللحظة دي، أي استعلام زي Post::all() مش هيجيب غير السجلات اللي عمود deleted_at فيها بيساوي NULL.
التعامل مع العلاقات (Eloquent Relationships) والمشاكل المستخبية
هنا بقى بيظهر "الأسد" اللي بيطلع لكل المبرمجين. تخيل إن عندك علاقة واحد لكثير (One-to-Many) بين الـ User والـ Post، واليوزر ده اتقاله Soft Delete، أو العكس، بوست اتمسح بس اليوزر لسه موجود.. إيه اللي بيحصل؟
لو عندك بوستات محذوفة بـ Soft Delete، وحاولت تجيب الـ Comments أو الـ User المرتبط بيها بالطريقة العادية، ممكن تلاقي الدنيا بتلخبط أو الداتا مش بترجع بالشكل السليم. عشان تحل المشكلة دي وتخلي لارفيل يفهم إنك عايز تجيب الداتا حتى لو كانت محذوفة، لازم تستخدم الميثود withTrashed():
// استرجاع البوستات ومعاها المستخدمين حتى لو المستخدم محذوف
$posts = Post::withTrashed()->with('user')->get();
ولو انت عايز تجيب الداتا المحذوفة فقط (وهو ده المطلوب غالباً في شاشات الـ Recycle Bin أو Trash)، بتستخدم الميثود onlyTrashed() بالشكل ده:
// جلب البوستات المحذوفة فقط
$trashedPosts = Post::onlyTrashed()->get();
استرجاع البيانات المحذوفة (Restoring Records) بكل سهولة
العميل كلمك وقال لك رجع البوست الفلاني؟ الموضوع أبسط ما يمكن. لارفيل بيوفر لك ميثود اسمها restore() بتخلي عمود الdeleted_at يرجع تاني NULL، كأن شيئاً لم يكن:
// استرجاع بوست محذوف بناءً على الـ ID
$post = Post::withTrashed()->find($id);
if ($post && $post->trashed()) {
$post->restore();
}
طيب لو عايز تمسح الداتا دي نهائياً من قاعدة البيانات عشان توفر مساحة (Force Delete)؟ بتستخدم ميثود forceDelete():
// حذف نهائي من الداتابيز
$post->forceDelete();
تطبيق الـ Soft Deletes على العلاقات تلقائياً (Cascading Soft Deletes)
نقطة متقدمة جداً ومهمة: لما تمسح عميل (User) عنده 50 مقال (Posts)، هل المقالات دي بتتمسح معاها Soft Delete تلقائي؟ الإجابة الافتراضية للارففيل هي "لا"، لازم تعمل ده يدوياً أو تظبطه في قاعدة البيانات (Foreign Key Cascade) أو تكود الـ Event جوه الـ Model.
أفضل طريقة برمجية في لارفيل هي استخدام الـ Eloquent Events (زي الـ deleting event) جوه الـ Model عشان تضمن إن أي علاقة تابعة تتقفل وراها الباب:
protected static function booted()
{
static::deleting(function ($user) {
if (! $user->isForceDeleting()) {
$user->posts()->each(function ($post) {
$post->delete();
});
}
});
static::restoring(function ($user) {
$user->posts()->withTrashed()->each(function ($post) {
$post->restore();
});
});
}
الكود ده بيضمن إن اليوزر لما يتعمل له Soft Delete، كل بوستاته بتنزل معاه للقبر، ولما نعمل له Restore، كل بوستاته بترجع تنور تاني معاه! حركة احترافية جداً وبتمنع وجود "داتا يتيمة" (Orphaned Data) في النظام بتاعك.
خاتمة ونصيحة من أخ
يا باشمهندس، تقنيات زي الـ Soft Deletes بتفرق جداً بين مبرمج بيعرف يكتب كود بيشتغل، ومبرمج محترف بيفني حياته في بناء تطبيقات قوية (Robust Applications) آمنة على داتا العملاء. نصيحتي ليك: متسترخصش في استخدام الـ Soft Deletes في الجداول الجوهرية زي المستخدمين، المدفوعات، والطلبات (Orders). لكن في نفس الوقت، ما تحطهاش في كل جدول أعمى، عشان ما تملأش قاعدة البيانات داتا ميتة وتبطئ الاستعلامات (Performance Issues) بدون داعي.
اتمرن على الأمثلة دي، طبقها في مشروعك الجاي، وادعي لي. بالتوفيق ومستنيكم في مقالات تقنية أعمق وأقوى قريباً!