قواعد البيانات في Laravel: أكثر من مجرد اتصال واحد
في البداية، أغلب مشاريع Laravel تبدأ بقاعدة بيانات واحدة، اتصال واحد، وكل شيء واضح. لكن مع أول مشروع حقيقي فيه:
- توسّع
- تقسيم خدمات
- بيانات ضخمة
تبدأ تكتشف أن “قاعدة بيانات واحدة” ليست دائمًا الخيار الصحيح.
Laravel من الأساس مصمم ليتعامل مع أكثر من اتصال قاعدة بيانات، لكن كثير من المطورين لا يستخدمون هذه الإمكانية بشكل صحيح.
كيف Laravel يتعامل مع قواعد البيانات
كل شيء يبدأ من:
config/database.php
Laravel لا يربط التطبيق بقاعدة بيانات واحدة، بل يعرف مجموعة connections.
'default' => env('DB_CONNECTION', 'mysql'),
وهذا فقط يحدد الاتصال الافتراضي، وليس الوحيد.
تعريف أكثر من قاعدة بيانات
يمكنك تعريف أكثر من connection بسهولة:
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
...
],
'mysql_logs' => [
'driver' => 'mysql',
'host' => env('DB_LOG_HOST'),
'database' => env('DB_LOG_DATABASE'),
...
],
]
وفي .env:
DB_CONNECTION=mysql
DB_DATABASE=main_db
DB_LOG_DATABASE=logs_db
متى تحتاج أكثر من قاعدة بيانات؟
هذا ليس “ترف” أو “over engineering”.
تحتاجه فعليًا عندما:
- تفصل بيانات النظام عن logs
- تفصل بيانات المستخدمين عن analytics
- تتعامل مع microservices
- تستخدم قاعدة خارجية (external system)
استخدام connection مختلف داخل الكود
يمكنك تحديد connection مباشرة:
DB::connection('mysql_logs')->table('events')->insert([...]);
أو عبر Model:
class Log extends Model
{
protected $connection = 'mysql_logs';
}
وهذا يجعل كل استعلامات هذا الموديل تذهب لقاعدة البيانات الثانية.
العلاقات بين قواعد بيانات مختلفة (المشكلة الحقيقية)
Laravel لا يدعم العلاقات بين قواعد بيانات مختلفة بشكل مباشر عبر Eloquent.
يعني:
- لا يمكنك استخدام foreign keys بين قواعد مختلفة
- ولا joins مباشرة بين connections
وهنا يبدأ الفرق بين “مشروع بسيط” و “تصميم فعلي”.
كيف تتعامل مع العلاقات بين قواعد مختلفة
الحل ليس تقني فقط، بل معماري.
بدل الاعتماد على database constraints:
- تعتمد على application logic
- تخزن identifiers فقط (مثل user_id)
- تجلب البيانات من قاعدة أخرى عند الحاجة
مثال:
$user = User::find($log->user_id);
هذا أبطأ من join، لكنه يعطيك مرونة أكبر.
فصل القراءة عن الكتابة (Read / Write Split)
Laravel يدعم فصل عمليات القراءة عن الكتابة.
مثال:
'mysql' => [
'read' => [
'host' => ['192.168.1.1'],
],
'write' => [
'host' => ['192.168.1.2'],
],
]
وهذا يسمح:
- تخفيف الضغط عن قاعدة البيانات
- توزيع الحمل
Laravel يختار connection المناسب تلقائيًا.
هيكلة الجداول: الخطأ الذي لا يظهر مباشرة
أكبر مشكلة في قواعد البيانات ليست الأداء… بل التصميم.
القرارات التي تأخذها في البداية:
- كيف تربط الجداول
- كيف تخزن البيانات
- ما الذي يكون nullable
تؤثر على المشروع بعد أشهر، وليس فورًا.
أمثلة على تصميم جيد
1) عدم تكرار البيانات
بدل:
orders:
- user_name
- user_email
الأفضل:
orders:
- user_id
ثم الربط عبر العلاقة.
2) استخدام foreign keys بشكل صحيح
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
هذا يمنع:
- بيانات orphan
- علاقات مكسورة
3) الفهرسة (Indexing)
أغلب مشاكل الأداء ليست في السيرفر، بل في غياب index.
$table->index('user_id');
$table->index(['status', 'created_at']);
لكن:
index ليس حلًا سحريًا.
كل index:
- يزيد سرعة القراءة
- يقلل سرعة الكتابة
المشكلة التي لا يتكلم عنها كثير من الناس
Laravel يجعل التعامل مع قاعدة البيانات “سهل”.
لكن هذا يخفي:
- تكلفة الاستعلامات
- عدد queries
- مشاكل N+1
مثال:
$users = User::all();
foreach ($users as $user) {
echo $user->posts;
}
هذا يولد N+1 problem.
الحل:
$users = User::with('posts')->get();
متى تستخدم أكثر من قاعدة… ومتى لا
استخدام أكثر من قاعدة ليس دائمًا أفضل.
لا تستخدمه إذا:
- المشروع صغير
- لا يوجد ضغط كبير
- لا تحتاج فصل فعلي
استخدمه إذا:
- هناك اختلاف في طبيعة البيانات
- تحتاج scalability
- تتعامل مع external systems
أفضل ممارسة: لا تجعل قاعدة البيانات مجرد storage
قاعدة البيانات ليست فقط مكان حفظ.
هي:
- مصدر الحقيقة (source of truth)
- نظام علاقات
- طبقة منطق
كل قرار فيها:
- يؤثر على الأداء
- يؤثر على التوسع
- يؤثر على استقرار النظام