# Schema JSON Contract — base

> **الملفات:** `database/schema/<feature>.json` (ملف لكل فيتشر) + `database/schema/features.json` (الفهرس).  
> ملف موحّد واحد لم يعد مستخدماً — الـ schema **مقسّمة لكل فيتشر** ويقرؤها `SchemaDesignerController`.  
> **المصدر:** مصمّم قاعدة البيانات البصري (`/schema-designer`)  
> **الجمهور:** Agents تولّد migrations / models / factories / seeders  
> **جداول REUSE** (موجودة في base): تُدرَج بعلم `"external": true` كبطاقة مرجعية فقط (لا تُولَّد).  
> **التحقق من التطابق:** `php artisan schema:check` يقارن هذه الملفات بقاعدة البيانات الفعلية ويكشف أي انحراف (drift).  
> **إحداثيات `x`/`y` اختيارية:** لو الجداول المولّدة طلعت بنفس الإحداثيات أو متراكمة، `/schema-designer` **يرتّبها تلقائياً في grid** عند التحميل (وفيه زر «ترتيب الجداول» للترتيب اليدوي). فالمولّد مش لازم يحسب مواضع دقيقة.

---

## 1) الشكل الكامل

```json
{
  "version": 1,
  "updated_at": "2026-06-25T12:00:00+00:00",
  "meta": { "name": "base schema" },
  "tables": [
    {
      "id": "tbl_users",
      "name": "users",
      "color": "#6C4AB6",
      "x": 120,
      "y": 80,
      "timestamps": true,
      "soft_deletes": false,
      "columns": [
        {
          "id": "col_users_id",
          "name": "id",
          "type": "id",
          "nullable": false,
          "unique": false,
          "primary": true,
          "index": false,
          "unsigned": false,
          "default": null,
          "length": null,
          "precision": null,
          "scale": null,
          "values": null,
          "comment": null,
          "foreign": null
        },
        {
          "id": "col_users_email",
          "name": "email",
          "type": "string",
          "length": 255,
          "nullable": false,
          "unique": true,
          "primary": false,
          "index": false,
          "unsigned": false,
          "default": null,
          "precision": null,
          "scale": null,
          "values": null,
          "comment": "بريد ولي الأمر",
          "foreign": null
        }
      ]
    },
    {
      "id": "tbl_posts",
      "name": "posts",
      "color": "#185FA5",
      "x": 560,
      "y": 80,
      "timestamps": true,
      "soft_deletes": true,
      "columns": [
        {
          "id": "col_posts_id",
          "name": "id",
          "type": "id",
          "nullable": false,
          "unique": false,
          "primary": true,
          "index": false,
          "unsigned": false,
          "default": null,
          "length": null,
          "precision": null,
          "scale": null,
          "values": null,
          "comment": null,
          "foreign": null
        },
        {
          "id": "col_posts_user_id",
          "name": "user_id",
          "type": "foreignId",
          "nullable": false,
          "unique": false,
          "primary": false,
          "index": false,
          "unsigned": true,
          "default": null,
          "length": null,
          "precision": null,
          "scale": null,
          "values": null,
          "comment": null,
          "foreign": {
            "references_table": "users",
            "references_column": "id",
            "on_delete": "cascade",
            "on_update": "cascade"
          }
        }
      ]
    }
  ],
  "relations": [
    {
      "id": "rel_1",
      "from_table": "posts",
      "from_column": "user_id",
      "to_table": "users",
      "to_column": "id",
      "type": "many_to_one",
      "on_delete": "cascade",
      "on_update": "cascade"
    }
  ]
}
```

---

## 2) قواعد العقد

- كل عمود **لازم** يكون له `id` (فريد/ثابت) و `name` و `type`.
- باقي مفاتيح العمود لها defaults: `nullable:false`, `unique:false`, `primary:false`, `index:false`, `unsigned:false`, `default:null`, `length:null`, `precision:null`, `scale:null`, `values:null`, `comment:null`, `foreign:null`.
- عمود FK: `type` = `"foreignId"` أو `"foreignUuid"` + كائن `foreign` كامل.
- **كل FK لازم entry مطابق في `relations`** (تزامن إجباري).
- `relations[].type` ∈ `"one_to_one" | "one_to_many" | "many_to_one" | "many_to_many"`.
- `x`, `y` للعرض فقط — agents تتجاهلها.
- `updated_at` يُكتب من **السيرفر** عند الحفظ، ليس من JavaScript.

---

## 3) أنواع الأعمدة (`type`) → Laravel Blueprint

| `type` | Migration | Faker (factory) |
|--------|-----------|-----------------|
| `id` | `$table->id()` | — |
| `uuid` | `$table->uuid('{c}')` | `fake()->uuid()` |
| `string` | `$table->string('{c}', {length})` | `fake()->word()` |
| `text` | `$table->text('{c}')` | `fake()->paragraph()` |
| `longText` | `$table->longText('{c}')` | `fake()->text()` |
| `integer` | `$table->integer('{c}')` | `fake()->numberBetween(1,1000)` |
| `bigInteger` | `$table->bigInteger('{c}')` | `fake()->numberBetween(1,100000)` |
| `tinyInteger` | `$table->tinyInteger('{c}')` | `fake()->numberBetween(0,127)` |
| `boolean` | `$table->boolean('{c}')` | `fake()->boolean()` |
| `decimal` | `$table->decimal('{c}', {precision}, {scale})` | `fake()->randomFloat({scale},0,1000)` |
| `float` | `$table->float('{c}')` | `fake()->randomFloat(2,0,1000)` |
| `date` | `$table->date('{c}')` | `fake()->date()` |
| `dateTime` | `$table->dateTime('{c}')` | `fake()->dateTime()` |
| `timestamp` | `$table->timestamp('{c}')` | `fake()->dateTime()` |
| `time` | `$table->time('{c}')` | `fake()->time()` |
| `json` | `$table->json('{c}')` | `[]` |
| `enum` | `$table->enum('{c}', {values})` | `fake()->randomElement({values})` |
| `foreignId` | `$table->foreignId('{c}')->constrained('{ref_table}')->{onDelete}` | `RelatedModel::factory()` |

`{c}` = اسم العمود.

**ترتيب flags:** `->unsigned()` ثم `->nullable()` ثم `->unique()` ثم `->default(x)` ثم `->index()` ثم `->comment('...')`.

---

## 4) عقد توليد كود Laravel (للـ agents)

لكل `table` في JSON:

1. **Migration** `create_{name}_table`: `id()` أولاً إن وُجد، باقي الأعمدة بالترتيب، FKs بـ `foreignId()->constrained()`, ثم `timestamps()` / `softDeletes()` حسب flags الجدول.
2. **Model** `App\Models\{StudlySingular}`: `$fillable` (كل الأعمدة عدا id/timestamps), `$casts`, علاقات من `relations`.
3. **Factory**: `definition()` بـ Faker حسب الجدول + heuristics (`email→safeEmail`, `name→name`, `phone→phoneNumber`, `price/amount→randomFloat`, `*_id→related factory`).
4. **Seeder** `{Studly}Seeder`: `factory()->count(N)->create()`, مسجّل في `DatabaseSeeder`.

**ترتيب التنفيذ:** الجداول المرجعية (بدون FK outgoing) أولاً.

---

## 5) TODO

- [ ] حماية `/schema-designer` بـ auth/gate (أداة داخلية).
