مخطط قاعدة البيانات
يولّد FORGE مخطط قاعدة بيانات PostgreSQL كامل مع مفاتيح UUID أساسية، وفهرسة مناسبة، وحذف ناعم، وترجمات قائمة على JSONB. هذه الصفحة هي المرجع الكامل لكل جدول وعمود وعلاقة في التطبيق المولّد.
نظرة عامة على المخطط
كل تطبيق FORGE يأتي مع 19 جدولاً منظمة في خمس فئات:
┌─────────────────────────────────────────────────────────────────────┐
│ بنية قاعدة البيانات │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ الجداول الأساسية (المصادقة والصلاحيات) │
│ users, roles, permissions, role_user, permission_role, │
│ password_resets, otp_codes │
│ │
│ جداول النظام (الإعدادات والتدقيق) │
│ settings, languages, translations, api_keys, audit_logs, sessions │
│ │
│ جداول المحتوى (CMS والتنقل) │
│ contents, menus │
│ │
│ التخزين والمراجع │
│ media (polymorphic), lookups (hierarchical) │
│ │
│ المدفوعات (عند تفعيل مزود الدفع) │
│ invoices, invoice_items │
│ │
└─────────────────────────────────────────────────────────────────────┘جميع الجداول تتبع هذه الاتفاقيات
- مفاتيح UUID أساسية عبر
gen_random_uuid() - الطوابع الزمنية
created_atوupdated_atفي كل جدول - الحذف الناعم عبر عمود
deleted_atالقابل للقيمة الفارغة حيث ينطبق - أعمدة JSONB للترجمات والبيانات الوصفية المرنة
مخطط العلاقات
+-------------+
| users |
+------+------+
|
+----------------------+---------------------+
| | |
v v v
+---------------+ +-------------+ +-----------------+
| role_user | | audit_logs | | password_resets |
+-------+-------+ +-------------+ +-----------------+
|
v
+---------------+ +--------------------+
| roles |<-----| permission_role |
+---------------+ +----------+---------+
|
v
+-----------------+
| permissions |
+-----------------+
+----------+ +----------+ +--------------+ +----------+
| settings | | languages| | translations | | api_keys |
+----------+ +----------+ +--------------+ +----------+
+----------+ +----------+ +----------+ +----------+
| contents | | menus | | media | | lookups |
+----------+ +----------+ +----------+ +----------+الجداول الأساسية
users
جدول حسابات المستخدمين الرئيسي. يدعم المصادقة عبر البريد الإلكتروني والهاتف المحمول، مع حذف ناعم لإلغاء تفعيل الحسابات بأمان.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK, default gen_random_uuid() | المعرف الفريد |
email | VARCHAR(255) | UNIQUE, nullable | البريد الإلكتروني |
mobile | VARCHAR(20) | UNIQUE, nullable | رقم الهاتف |
password | VARCHAR(255) | nullable | تجزئة Argon2id |
remember_token | VARCHAR(100) | nullable | رمز استمرار الجلسة |
name | VARCHAR(255) | NOT NULL | اسم العرض |
avatar | VARCHAR(500) | nullable | رابط أو مسار الصورة الرمزية |
is_active | BOOLEAN | default true | علامة تفعيل الحساب |
email_verified_at | TIMESTAMP | nullable | تاريخ تأكيد البريد |
mobile_verified_at | TIMESTAMP | nullable | تاريخ تأكيد الهاتف |
last_login_at | TIMESTAMP | nullable | آخر تسجيل دخول |
last_login_ip | VARCHAR(45) | nullable | IP متوافق مع IPv6 |
deleted_at | TIMESTAMP | nullable | علامة الحذف الناعم |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
-- At least one identifier required
CONSTRAINT users_email_or_mobile CHECK (
email IS NOT NULL OR mobile IS NOT NULL
);
-- Partial indexes (exclude deleted rows)
CREATE INDEX idx_users_email ON users(email) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_mobile ON users(mobile) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_is_active ON users(is_active) WHERE deleted_at IS NULL;WARNING
عمودا email و mobile كلاهما قابلان للقيمة الفارغة بشكل فردي، لكن قيد CHECK يضمن وجود واحد منهما على الأقل دائماً. أي حقل مطلوب يعتمد على auth_method المختارة أثناء forge new.
roles
يحدد أدوار المستخدمين لنظام RBAC. أدوار النظام (admin، user) لا يمكن حذفها.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
name | VARCHAR(100) | NOT NULL, UNIQUE | الاسم الآلي (admin) |
display_name | VARCHAR(255) | NOT NULL | التسمية البشرية (Administrator) |
description | TEXT | nullable | وصف الدور |
is_system | BOOLEAN | default false | يمنع الحذف |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
permissions
سجلات الصلاحيات الدقيقة مجمعة حسب المورد.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
name | VARCHAR(100) | NOT NULL, UNIQUE | مفتاح التنقيط (users.create) |
display_name | VARCHAR(255) | NOT NULL | التسمية البشرية (Create Users) |
description | TEXT | nullable | وصف الصلاحية |
group_name | VARCHAR(100) | NOT NULL | مفتاح التجميع (users, roles) |
is_system | BOOLEAN | default true | يمنع الحذف |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
role_user (جدول الربط)
علاقة متعدد-لمتعدد بين المستخدمين والأدوار.
CREATE TABLE role_user (
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
assigned_at TIMESTAMP DEFAULT NOW(),
assigned_by UUID REFERENCES users(id),
PRIMARY KEY (user_id, role_id)
);permission_role (جدول الربط)
علاقة متعدد-لمتعدد بين الصلاحيات والأدوار.
CREATE TABLE permission_role (
permission_id UUID NOT NULL REFERENCES permissions(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
PRIMARY KEY (permission_id, role_id)
);جداول المصادقة
password_resets
يخزن رموز إعادة تعيين كلمة المرور المجزأة مع تاريخ انتهاء الصلاحية.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
email | VARCHAR(255) | NOT NULL | البريد المستهدف |
token | VARCHAR(255) | NOT NULL | رمز SHA-256 مجزأ |
expires_at | TIMESTAMP | NOT NULL | وقت انتهاء الصلاحية |
used_at | TIMESTAMP | nullable | متى تم استخدام الرمز |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
otp_codes
رموز كلمة المرور لمرة واحدة للمصادقة عبر الهاتف وتدفقات التحقق.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
identifier | VARCHAR(255) | NOT NULL | البريد أو رقم الهاتف |
code | VARCHAR(10) | NOT NULL | رمز OTP من 6 أرقام |
type | VARCHAR(50) | NOT NULL | login, verify, أو reset |
attempts | INT | default 0 | عدد محاولات التحقق الفاشلة |
max_attempts | INT | default 3 | الحد الأقصى للمحاولات |
expires_at | TIMESTAMP | NOT NULL | وقت انتهاء الصلاحية |
verified_at | TIMESTAMP | nullable | وقت التحقق الناجح |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
DANGER
رموز OTP لها حد صارم لـ max_attempts. بعد 3 محاولات فاشلة يتم إبطال الرمز. أنشئ دائماً OTP جديد بعد استنفاد المحاولات -- لا تقم بإعادة تعيين العداد.
جداول النظام
settings
إعدادات التطبيق بنمط مفتاح-قيمة مع معرفة النوع والتشفير الاختياري.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
group_name | VARCHAR(100) | NOT NULL | المجموعة المنطقية (general, email, sms) |
key | VARCHAR(255) | NOT NULL | مفتاح الإعداد (app_name) |
value | TEXT | nullable | نص عادي أو قيمة مشفرة |
type | VARCHAR(50) | default string | string, boolean, number, json, encrypted |
is_public | BOOLEAN | default false | معروض لواجهة API |
is_sensitive | BOOLEAN | default false | مخفي في واجهة الإدارة |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
الإعدادات المشفرة
عندما يكون type = 'encrypted'، تُخزن القيم كـ enc:v1:nonce:ciphertext:tag باستخدام AES-256-GCM. يُقرأ مفتاح التشفير من متغير البيئة FORGE_ENCRYPTION_KEY ولا يُخزن أبداً في قاعدة البيانات.
languages
اللغات المدعومة مع دعم اتجاه RTL.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
code | VARCHAR(10) | NOT NULL, UNIQUE | كود ISO (en, ar) |
name | VARCHAR(100) | NOT NULL | الاسم الإنجليزي (Arabic) |
native_name | VARCHAR(100) | nullable | الاسم الأصلي (العربية) |
direction | VARCHAR(3) | default ltr | ltr أو rtl |
is_default | BOOLEAN | default false | علامة اللغة الافتراضية |
is_active | BOOLEAN | default true | علامة التوفر |
sort_order | INT | default 0 | ترتيب العرض |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
translations
سلاسل ترجمة واجهة المستخدم المخزنة كـ JSONB مع جميع قيم اللغات في صف واحد.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
group_name | VARCHAR(100) | NOT NULL | المجموعة المنطقية (common, auth, admin) |
key | VARCHAR(255) | NOT NULL | مفتاح الترجمة (login_title) |
translations | JSONB | NOT NULL | {"en": "Sign In", "ar": "تسجيل الدخول"} |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
مجموعات الترجمة الافتراضية:
| المجموعة | الغرض | أمثلة المفاتيح |
|---|---|---|
common | سلاسل UI المشتركة | Save, Cancel, Delete, Confirm |
auth | صفحات المصادقة | login_title, register_title |
admin | لوحة التحكم | dashboard_title, تسميات الشريط الجانبي |
validation | رسائل النماذج | required, min_length |
web.home | الصفحة الرئيسية | Welcome_to, App_Description |
web.header | رأس الموقع | Sign_In, Get_Started |
web.footer | تذييل الموقع | All_rights_reserved, Privacy |
api_keys
إدارة مفاتيح API للأطراف الثالثة مع تحديد المعدل والإلغاء.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
name | VARCHAR(255) | NOT NULL | اسم وصفي |
key_hash | VARCHAR(255) | NOT NULL | تجزئة SHA-256 للمفتاح |
key_prefix | VARCHAR(12) | NOT NULL | أول 8 أحرف للتعريف |
permissions | JSONB | default [] | أسماء الصلاحيات المسموحة |
rate_limit | INT | default 1000 | الحد الأقصى للطلبات في الساعة |
last_used_at | TIMESTAMP | nullable | تاريخ آخر استخدام |
last_used_ip | VARCHAR(45) | nullable | IP آخر استخدام |
request_count | BIGINT | default 0 | إجمالي عدد الطلبات |
expires_at | TIMESTAMP | nullable | انتهاء الصلاحية (NULL = أبداً) |
revoked_at | TIMESTAMP | nullable | تاريخ الإلغاء |
revoked_by | UUID | FK to users | من ألغى المفتاح |
revoked_reason | TEXT | nullable | سبب الإلغاء |
created_by | UUID | NOT NULL, FK to users | منشئ المفتاح |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
audit_logs
سجل تدقيق النشاط غير القابل للتغيير. سجلات التدقيق لا تحتوي على عمود updated_at -- فهي للإضافة فقط.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
user_id | UUID | FK to users, nullable | المستخدم المنفذ (NULL للنظام) |
user_email | VARCHAR(255) | nullable | البريد للتاريخ |
api_key_id | UUID | FK to api_keys, nullable | إذا كان الإجراء عبر مفتاح API |
action | VARCHAR(50) | NOT NULL | created, updated, deleted, login |
resource_type | VARCHAR(100) | NOT NULL | user, role, setting |
resource_id | UUID | nullable | معرف المورد المتأثر |
old_values | JSONB | nullable | الحالة السابقة |
new_values | JSONB | nullable | الحالة الجديدة |
ip_address | VARCHAR(45) | nullable | IP العميل |
user_agent | TEXT | nullable | وكيل مستخدم العميل |
url | VARCHAR(500) | nullable | رابط الطلب |
created_at | TIMESTAMP | default NOW() | تاريخ الحدث |
جداول المحتوى
contents
صفحات محتوى CMS مع دعم كامل للترجمة وبيانات SEO والنشر المجدول.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
title | VARCHAR(255) | NOT NULL | عنوان اللغة الافتراضية |
slug | VARCHAR(255) | NOT NULL, UNIQUE | معرف صديق للرابط |
summary | TEXT | nullable | وصف قصير |
details | TEXT | nullable | محتوى HTML غني |
button_text | VARCHAR(100) | nullable | زر CTA اختياري |
seo | JSONB | default {} | {title, description, keywords, robots, canonical} |
translations | JSONB | default {} | تجاوزات لكل لغة لجميع الحقول النصية |
is_active | BOOLEAN | default true | علامة النشر |
publish_at | TIMESTAMPTZ | nullable | تاريخ النشر المجدول |
expire_at | TIMESTAMPTZ | nullable | تاريخ إلغاء النشر التلقائي |
sort_order | INT | default 0 | ترتيب العرض |
created_by | UUID | FK to users | المؤلف |
updated_by | UUID | FK to users | آخر محرر |
created_at | TIMESTAMPTZ | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMPTZ | default NOW() | تاريخ التحديث |
deleted_at | TIMESTAMPTZ | nullable | علامة الحذف الناعم |
هيكل JSONB للترجمات:
{
"ar": {
"title": "من نحن",
"summary": "نبذة عن الشركة",
"details": "<p>محتوى الصفحة...</p>",
"button_text": "اقرأ المزيد",
"seo": {
"title": "من نحن | الموقع",
"description": "وصف الصفحة"
}
}
}الصور المميزة
صور المحتوى لا تُخزن كعمود في جدول contents. بدلاً من ذلك، تستخدم جدول media متعدد الأشكال مع model_type='Content' و collection='featured_image'. استخدم MediaService.attach() و MediaService.get_first_for_model() لإدارتها.
menus
عناصر قائمة التنقل مع هيكل هرمي (أب/ابن)، وتحكم بالرؤية، والترجمات.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
title | VARCHAR(255) | NOT NULL | عنوان اللغة الافتراضية |
url | VARCHAR(500) | nullable | رابط URL |
icon | VARCHAR(100) | nullable | معرف الأيقونة |
target | VARCHAR(20) | default _self | _self أو _blank |
visibility | VARCHAR(20) | default public | public, auth, أو guest |
translations | JSONB | default {} | {"ar": {"title": "الرئيسية"}} |
parent_id | UUID | FK to menus(id), nullable | عنصر القائمة الأب |
is_active | BOOLEAN | default true | علامة التفعيل |
sort_order | INT | default 0 | ترتيب العرض |
created_at | TIMESTAMPTZ | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMPTZ | default NOW() | تاريخ التحديث |
deleted_at | TIMESTAMPTZ | nullable | علامة الحذف الناعم |
خيارات الرؤية:
| القيمة | من يراها |
|---|---|
public | الجميع (افتراضي) |
auth | المستخدمون المسجلون فقط |
guest | المستخدمون غير المسجلين فقط |
جدول التخزين
media
رفع الملفات متعدد الأشكال يدعم أي نوع نموذج. يتبع نمط Spatie حيث model_type + model_id + collection يحددون المالك.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
disk | VARCHAR(50) | default local | local أو s3 |
path | VARCHAR(500) | NOT NULL | مسار الملف النسبي |
filename | VARCHAR(255) | NOT NULL | اسم الملف الأصلي |
mime_type | VARCHAR(100) | nullable | نوع MIME |
size | BIGINT | nullable | حجم الملف بالبايت |
width | INT | nullable | عرض الصورة (بكسل) |
height | INT | nullable | ارتفاع الصورة (بكسل) |
variants | JSONB | default {} | {"thumb": "path/thumb.jpg"} |
alt_text | VARCHAR(500) | nullable | نص إمكانية الوصول |
caption | TEXT | nullable | تعليق العرض |
metadata | JSONB | default {} | بيانات EXIF، بيانات وصفية مخصصة |
model_type | VARCHAR(100) | nullable | نموذج المالك (Content, User) |
model_id | UUID | nullable | معرف سجل المالك |
collection | VARCHAR(100) | default default | featured_image, avatars |
uploaded_by | UUID | FK to users | الرافع |
created_at | TIMESTAMP | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMP | default NOW() | تاريخ التحديث |
جدول البحث
lookups
بيانات مرجعية قابلة للتكوين للقوائم المنسدلة والفئات والتصنيفات الهرمية.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
type | VARCHAR(100) | NOT NULL | فئة البحث (country, status) |
parent_id | UUID | FK to lookups(id), nullable | الأب للتسلسل الهرمي |
name | VARCHAR(255) | NOT NULL | اسم اللغة الافتراضية |
value | VARCHAR(255) | nullable | قيمة قابلة للقراءة آلياً |
translations | JSONB | default {} | {"ar": {"name": "الكويت"}} |
is_active | BOOLEAN | default true | علامة التفعيل |
sort_order | INT | default 0 | ترتيب العرض |
metadata | JSONB | default {} | بيانات إضافية (مثل أكواد الدول) |
created_at | TIMESTAMPTZ | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMPTZ | default NOW() | تاريخ التحديث |
استخدم lookups للبيانات المرجعية القابلة لإعادة الاستخدام
بدلاً من إنشاء جدول جديد لكل قائمة منسدلة (الدول، الحالات، الفئات)، خزنها كلها في lookups بقيم type مختلفة. عمود parent_id يتيح البيانات الهرمية مثل منطقة > دولة > مدينة.
جداول المدفوعات
يتم توليد هذه الجداول عند تفعيل مزود الدفع عبر forge provider:add payment stripe.
invoices
سجلات الفواتير لتتبع المدفوعات مع أرقام فواتير مولدة تلقائياً.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
invoice_number | VARCHAR(50) | NOT NULL, UNIQUE | مولد تلقائياً: INV-YYYYMM-XXXX |
customer_id | UUID | FK to users, nullable | حساب العميل |
customer_name | VARCHAR(255) | nullable | اسم العميل (للسجل) |
customer_email | VARCHAR(255) | nullable | بريد العميل (للسجل) |
subtotal | DECIMAL(12,2) | default 0 | مجموع البنود |
tax_amount | DECIMAL(12,2) | default 0 | مبلغ الضريبة |
discount_amount | DECIMAL(12,2) | default 0 | الخصم المطبق |
total_amount | DECIMAL(12,2) | NOT NULL | المبلغ النهائي |
currency | VARCHAR(3) | default USD | كود العملة ISO |
status | VARCHAR(20) | default pending | حالة الدفع |
payment_provider | VARCHAR(50) | nullable | stripe, hyperpay, إلخ. |
payment_intent_id | VARCHAR(255) | nullable | معرف الدفع من المزود |
payment_method | VARCHAR(50) | nullable | card, apple_pay, إلخ. |
card_last4 | VARCHAR(4) | nullable | آخر 4 أرقام |
card_brand | VARCHAR(20) | nullable | visa, mastercard, إلخ. |
description | TEXT | nullable | وصف الفاتورة |
notes | TEXT | nullable | ملاحظات داخلية |
metadata | JSONB | default {} | بيانات وصفية مخصصة |
due_date | TIMESTAMPTZ | nullable | تاريخ استحقاق الدفع |
paid_at | TIMESTAMPTZ | nullable | تاريخ الدفع |
refunded_at | TIMESTAMPTZ | nullable | تاريخ الاسترداد |
created_at | TIMESTAMPTZ | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMPTZ | default NOW() | تاريخ التحديث |
تدفق حالة الفاتورة:
pending → processing → paid → refunded
↓ ↓ ↓
cancelled failed partially_refunded| الحالة | الوصف |
|---|---|
pending | تم إنشاء الفاتورة، بانتظار الدفع |
processing | الدفع قيد التنفيذ |
paid | تم الدفع بنجاح |
failed | فشل الدفع |
cancelled | تم إلغاء الفاتورة قبل الدفع |
refunded | تمت معالجة استرداد كامل |
partially_refunded | تمت معالجة استرداد جزئي |
invoice_items
بنود الفواتير مع حساب المجموع التلقائي.
| العمود | النوع | القيود | الوصف |
|---|---|---|---|
id | UUID | PK | المعرف الفريد |
invoice_id | UUID | NOT NULL, FK to invoices | الفاتورة الأم |
description | VARCHAR(500) | NOT NULL | وصف البند |
quantity | INTEGER | default 1 | كمية البند |
unit_price | DECIMAL(12,2) | NOT NULL | سعر الوحدة |
amount | DECIMAL(12,2) | NOT NULL | quantity * unit_price |
product_id | UUID | nullable | مرجع منتج اختياري |
product_type | VARCHAR(100) | nullable | نوع نموذج المنتج |
created_at | TIMESTAMPTZ | default NOW() | تاريخ الإنشاء |
updated_at | TIMESTAMPTZ | default NOW() | تاريخ التحديث |
المجاميع المتتالية
عند إضافة أو تحديث أو حذف بنود الفاتورة، تقوم المشغلات تلقائياً بإعادة حساب subtotal و total_amount للفاتورة. لا تحتاج لتحديث هذه القيم يدوياً.
استراتيجية الفهرسة
يطبق FORGE عدة أنماط فهرسة لتحسين أداء الاستعلام:
| النمط | مثال | الغرض |
|---|---|---|
| فهارس جزئية | WHERE deleted_at IS NULL | تخطي الصفوف المحذوفة |
| فهارس مركبة | (resource_type, action, created_at) | مرشحات متعددة الأعمدة |
| فهارس GIN | USING GIN (translations) | استعلامات احتواء JSONB |
| فريد جزئي | WHERE is_default = true | فرض افتراضي واحد |
| تنازلي | created_at DESC | ترتيب الأحدث أولاً |
أنماط JSONB
يستخدم FORGE أعمدة JSONB في ثلاثة أنماط مميزة:
1. الترجمات المضمنة
تستخدمها contents و menus و lookups لتخزين النص لكل لغة مضمناً:
-- Get Arabic title
SELECT translations->'ar'->>'title' FROM contents WHERE slug = 'about';2. حزم الترجمة
يستخدمها جدول translations لتخزين سلاسل UI:
-- Get all Arabic auth translations
SELECT key, translations->>'ar' AS value
FROM translations
WHERE group_name = 'auth';3. البيانات الوصفية المهيكلة
تستخدمها contents.seo و media.metadata و api_keys.permissions:
-- Find content with specific SEO robots setting
SELECT * FROM contents
WHERE seo->>'robots' = 'noindex';ترتيب الترحيلات
تعمل الترحيلات بترتيب التبعية. يولد FORGE ملفات ترحيل مرقمة:
00001 create_languages_table
00002 create_users_table
00003 create_roles_table
00004 create_permissions_table
00005 create_role_user_table
00006 create_permission_role_table
00007 create_password_resets_table
00008 create_otp_codes_table
00009 create_settings_table
00010 create_translations_table
00011 create_api_keys_table
00012 create_audit_logs_table
00013 create_media_table
00014 create_contents_table
00015 create_menus_table
00016 migrate_media_polymorphic
00017 create_lookups_table
00018 create_invoices_table (عند تفعيل المدفوعات)
00019 create_invoice_items_table (عند تفعيل المدفوعات)بعد الترحيلات، تملأ البيانات الأولية البيانات الافتراضية بهذا الترتيب:
01 seed_permissions (جميع مجموعات الصلاحيات)
02 seed_roles (admin, user)
03 seed_users (admin@{app}.test, user@{app}.test)
04 seed_languages (en, ar)
05 seed_settings (general, auth, email, storage)
06 seed_translations (common, auth, admin, web.*)استعلامات مفيدة
الحصول على مستخدم مع الأدوار والصلاحيات
SELECT
u.id, u.email, u.name,
array_agg(DISTINCT r.name) AS roles,
array_agg(DISTINCT p.name) AS permissions
FROM users u
LEFT JOIN role_user ru ON u.id = ru.user_id
LEFT JOIN roles r ON ru.role_id = r.id
LEFT JOIN permission_role pr ON r.id = pr.role_id
LEFT JOIN permissions p ON pr.permission_id = p.id
WHERE u.id = $1
GROUP BY u.id, u.email, u.name;التحقق من امتلاك المستخدم لصلاحية محددة
SELECT EXISTS (
SELECT 1
FROM users u
JOIN role_user ru ON u.id = ru.user_id
JOIN permission_role pr ON ru.role_id = pr.role_id
JOIN permissions p ON pr.permission_id = p.id
WHERE u.id = $1 AND p.name = $2
) AS has_permission;الحصول على جميع الترجمات للغة
SELECT group_name, key, translations->>$1 AS value
FROM translations
WHERE translations ? $1;
-- $1 = language code, e.g. 'ar'ملخص سجل التدقيق (آخر 7 أيام)
SELECT
u.email, al.action, al.resource_type,
COUNT(*) AS event_count
FROM audit_logs al
JOIN users u ON al.user_id = u.id
WHERE al.created_at > NOW() - INTERVAL '7 days'
GROUP BY u.email, al.action, al.resource_type
ORDER BY event_count DESC;انظر أيضاً
- قاعدة البيانات -- طبقة قاعدة بيانات الخلفية وإدارة الاتصال
- النماذج -- تعريفات نماذج Rust المربوطة بهذه الجداول
- إدارة المحتوى -- كيف يستخدم CMS جدول contents
- تعدد اللغات -- أنماط ترجمة JSONB بالتفصيل