# تعدد المستأجرين في Laravel: كيف يجد Laravilt مستأجر كل طلب ويجهّز قاعدته ويعزل بياناته

> تعدد المستأجرين في Laravel سؤالان: لأي مستأجر هذا الطلب، وأين تعيش بياناته؟ كيف يجيب Laravilt عن السؤالين خطوة بخطوة.

*2026-10-25T10:31:27Z*

تعدد المستأجرين (Tenancy) في Laravel يختصر في سؤالين مع كل طلب: لأي مستأجر هذا الطلب، وأين تعيش بيانات هذا المستأجر؟ يجيب Laravilt عن السؤالين داخل الـ Panel Provider، ويتحدد الـ middleware الذي يعمل بحسب النمط الذي تختاره.

كتبت من قبل عن الاختيار بين [قاعدة بيانات واحدة أو قاعدة لكل مستأجر](/ar/blog/laravel-multi-tenant-admin-panel-laravilt). هذا المقال يبقى عند ما يحدث وقت التشغيل: كيف يجد الطلب مستأجره، وماذا يحدث عند إنشاء مستأجر، وأين يضيع سياق المستأجر.

**الإجابة المختصرة:** في نمط القاعدة الواحدة يأتي المستأجر من المسار (`/admin/{tenant}/...`)، ويتحقق `IdentifyTenant` أن المستخدم المسجّل يستطيع فتحه. في نمط القواعد المتعددة يأتي المستأجر من النطاق الفرعي، ويحوّل `InitializeTenancyBySubdomain` الاتصال إلى قاعدة هذا المستأجر، وإنشاء المستأجر يجهّز قاعدته تلقائيًا. وفي الكود يأتي المستأجر الحالي من الواجهة `Laravilt`.

## نمطان، وطريقتان لإيجاد المستأجر

| | قاعدة واحدة | قواعد متعددة |
|---|---|---|
| مصدر المستأجر | معامل في المسار | النطاق الفرعي |
| مثال على الرابط | `/admin/{tenant}/...` | `{tenant}.myapp.com/admin/...` |
| الـ middleware | `IdentifyTenant` | `InitializeTenancyBySubdomain` |
| تحويل قاعدة البيانات | لا | نعم، إلى قاعدة المستأجر |

كل الـ middleware موجود في `Laravilt\Panel\Middleware`. وهناك ثالث هو `PreventAccessFromCentralDomains`، يمنع المسارات الخاصة بالمستأجرين عند طلبها على نطاق مركزي.

## قاعدة واحدة: المستأجر يأتي من المسار

في هذا النمط يفعل [`IdentifyTenant`](/ar/docs/panel/tenancy/middleware#identifytenant) ثلاثة أشياء: يقرأ المستأجر من معامل المسار، ويتحقق أن المستخدم يستطيع الوصول إليه، ويضبط سياق المستأجر. لا يوجد تحويل لقاعدة البيانات.

تفعّله في الـ Panel Provider:

```php
use App\Models\Team;
use Laravilt\Panel\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        ->tenant(Team::class, 'team', 'slug')   // model, ownership relationship, slug attribute
        ->tenantRegistration()
        ->tenantProfile();
}
```

خطوة «يتحقق أن المستخدم يستطيع الوصول إليه» هي موضع نموذج `User` عندك. عليه أن ينفّذ واجهتين، وينفّذ نموذج المستأجر واجهتين أخريين:

| الواجهة | الدوال |
|-----------|---------|
| `HasTenants` (المستخدم) | `getTenants(Panel $panel)`, `canAccessTenant(Model $tenant)` |
| `HasDefaultTenant` (المستخدم) | `getDefaultTenant(Panel $panel)` |
| `HasTenantName` (المستأجر) | `getTenantName()` |
| `HasTenantAvatar` (المستأجر) | `getTenantAvatarUrl()` |

يوفّر الـ trait المنشور `HasTeams` الدوال الخاصة بالمستخدم، فلا تكتبها يدويًا في [تطبيق قائم على الفرق](/ar/docs/panel/tenancy/teams). وإن وصل مستخدم إلى «لا توجد فرق»، فالتوثيق يحدد أربعة أشياء تفحصها: أن `User` ينفّذ `HasTenants` و`HasDefaultTenant`، وأنه يستخدم الـ trait المسمى `HasTeams`، وأن `current_team_id` قابل للتعبئة (fillable)، وأن جدول الربط `team_user` موجود.

## قواعد متعددة: المستأجر يأتي من النطاق الفرعي

هنا يفعل [`InitializeTenancyBySubdomain`](/ar/docs/panel/tenancy/middleware#initializetenancybysubdomain) خمسة أشياء:

1. يستخرج النطاق الفرعي من اسم المضيف.
2. يرفض النطاقات الفرعية المحجوزة.
3. يجد المستأجر بالنطاق أو بالـ slug.
4. يحوّل الاتصال إلى قاعدة المستأجر.
5. يضبط سياق المستأجر.

والتفعيل استدعاء واحد على اللوحة:

```php
use Laravilt\Panel\Models\Tenant;
use Laravilt\Panel\Panel;

public function panel(Panel $panel): Panel
{
    return $panel->multiDatabaseTenancy(Tenant::class, 'myapp.com');
}
```

المعامل الثاني هو النطاق الأساسي. و[ملف الإعدادات](/ar/docs/panel/tenancy/configuration) `config/laravilt-tenancy.php` يحمل الباقي. هذان الجزءان الأهم لسلوك التشغيل، مقتطعان من الملف المنشور:

```php
'provisioning' => [
    'auto_create_database' => true,
    'auto_migrate' => true,
    'auto_seed' => false,
    'seeder' => null,
    'queue' => false,
    'queue_name' => 'default',
],

'subdomain' => [
    'domain' => env('APP_DOMAIN', 'localhost'),
    'reserved' => ['www', 'api', 'admin', 'app', /* ... */],
],
```

### ماذا يحدث عند إنشاء مستأجر

نموذج `Tenant` يعيش في القاعدة المركزية، ويستخدم مفاتيح نصية (غير تلقائية الزيادة) وحذفًا ناعمًا (soft deletes):

```php
use Laravilt\Panel\Models\Tenant;

$tenant = Tenant::create([
    'name' => 'Acme Corp',
    'slug' => 'acme',
    'email' => 'admin@acme.com',
    'owner_id' => $user->id,
]);
```

مع إعدادات التجهيز الافتراضية، إنشاء المستأجر ينشئ قاعدته ويشغّل الـ migrations عليها. والأمر `php artisan tenant:create` يفعل الشيء نفسه من سطر الأوامر. وللتسجيل الأسرع يمكنك وضعه في الطابور بضبط `'queue' => true` مع `queue_name`، فتُنشأ قاعدة المستأجر في الخلفية.

النطاقات الفرعية للمستأجرين سجلات من نموذج `Domain`:

```php
use Laravilt\Panel\Models\Domain;

$domain = Domain::createSubdomain(
    tenant: $tenant,
    subdomain: 'acme',
    baseDomain: 'myapp.com',
    isPrimary: true
);

$tenant = Domain::findTenantByDomain('acme.myapp.com');
```

### النطاقات الفرعية المحجوزة

يأتي الإعداد بقائمة محجوزة (`www` و`api` و`admin` و`app` وغيرها). أضف إلى `reserved` أي نطاق فرعي لا يجوز للمستأجرين حجزه، مثل `support` أو `docs` أو `status`.

## قراءة المستأجر الحالي في الكود

بعد أن يعمل الـ middleware، يكون المستأجر الحالي متاحًا من الواجهة `Laravilt`:

```php
use Laravilt\Panel\Facades\Laravilt;

$tenant = Laravilt::getTenant();

if (Laravilt::hasTenant()) {
    $name = Laravilt::getTenant()->name;
}
```

وتوفّر الواجهة أيضًا `setTenant()` و`getTenantId()` و`getTenants()` و`canAccessTenant()` و`isTenancyEnabled()`.

## أين يضيع سياق المستأجر: مهام الطوابير

مهام الطوابير (Queued Jobs) لا تحمل سياق المستأجر. مرّر معرّف المستأجر إلى المهمة، واستعد المستأجر عند تنفيذها:

```php
use Illuminate\Contracts\Queue\ShouldQueue;
use Laravilt\Panel\Facades\Laravilt;
use Laravilt\Panel\Models\Tenant;

class ProcessOrder implements ShouldQueue
{
    public function __construct(
        public string $tenantId,
        public int $orderId,
    ) {}

    public function handle(): void
    {
        Laravilt::setTenant(Tenant::find($this->tenantId));

        // Process in tenant context
    }
}
```

هذا خطأ سهل الوقوع في أي تطبيق Laravel متعدد المستأجرين: يعمل الكود أثناء الطلب، ثم تعمل مهمة بلا مستأجر. اجعل استعادة المستأجر أول سطر في كل مهمة تلمس بيانات المستأجر.

## أنماط مختلفة على لوحات مختلفة

كل لوحة تضبط التعدد بنفسها، فيمكن لتطبيق واحد أن يخلط الأنماط:

```php
use App\Models\Team;
use Laravilt\Panel\Models\Tenant;

// Admin panel: multi-database
$panel->multiDatabaseTenancy(Tenant::class, 'myapp.com');

// Portal panel: single-database
$panel->tenant(Team::class);

// Marketing panel: no tenancy call at all
```

ولفحص حالة اللوحة استخدم `$panel->hasTenancy()` و`$panel->isMultiDatabaseTenancy()` و`$panel->isSingleDatabaseTenancy()`.

## إن كنت قادمًا من Filament

قارنت الإطارين في مقال [Laravilt أم Filament](/ar/blog/laravilt-vs-filament). وفي موضوع تعدد المستأجرين الخلاصة هي: كلاهما يدعم تعدد المستأجرين، وواجهة PHP متقاربة بما يكفي: إن كتبت موردًا (Resource) في Filament فسيبدو مورد Laravilt مألوفًا. لكن Laravilt ليس بديلًا يُركّب مكان الآخر مباشرة. إضافات Filament لا تعمل داخل Laravilt، فلا تفترض أن إضافة تعدد مستأجرين مكتوبة لـ Filament ستنتقل معك.

## حدود بصراحة

- **هذا تعدد مستأجرين داخل لوحة Laravilt.** إن لم تكن تبني على Laravilt، فهذه ليست حزمة عامة لتعدد المستأجرين في Laravel.
- **القواعد المتعددة تعني تشغيلًا أكثر.** كل migration يعمل مرة لكل مستأجر عبر `tenants:migrate`، ويجب أن يملك مستخدم قاعدة البيانات صلاحية إنشاء قواعد.
- **الانتقال بين النمطين هو نقل بيانات.** إعدادات اللوحة تتغير في بضعة أسطر، لكن نقل السجلات الموجودة بين قاعدة مشتركة وقواعد لكل مستأجر عمل حقيقي، فاختر مبكرًا.
- **استعادة سياق المستأجر في مهام الطوابير مسؤوليتك.** لا شيء يفعلها عنك.
- **Laravilt حديث.** إن كنت تحتاج اليوم إلى مجموعة كبيرة من إضافات تعدد المستأجرين الجاهزة، فتحقق مما هو متاح أولًا.

## الأسئلة الشائعة

### كيف يعرف Laravilt لأي مستأجر ينتمي الطلب؟
في نمط القاعدة الواحدة يقرأ `IdentifyTenant` المستأجر من معامل المسار ويتحقق أن المستخدم يستطيع الوصول إليه. وفي نمط القواعد المتعددة يقرأ `InitializeTenancyBySubdomain` النطاق الفرعي، ويرفض المحجوز منها، ويجد المستأجر بالنطاق أو بالـ slug، ثم يحوّل الاتصال إلى قاعدته.

### هل ينشئ نمط القواعد المتعددة قاعدة المستأجر تلقائيًا؟
مع إعدادات التجهيز الافتراضية نعم: إنشاء المستأجر ينشئ قاعدته ويشغّل الـ migrations عليها. ويمكنك وضع هذا العمل في الطابور بـ `'queue' => true`.

### هل يستطيع تطبيق واحد استخدام النمطين معًا؟
نعم. كل لوحة تضبط التعدد بنفسها، فيمكن للوحة الإدارة أن تستخدم `multiDatabaseTenancy()` بينما تستخدم لوحة البوابة `tenant()`.

### كيف أحافظ على سياق المستأجر في مهام الطوابير؟
المهام لا تحمله. مرّر معرّف المستأجر إلى المهمة، واستدعِ `Laravilt::setTenant(Tenant::find($this->tenantId))` في بداية `handle()`.

## الخطوة التالية

المرجع الكامل للنمطين موجود في [توثيق تعدد المستأجرين في Laravilt](/ar/docs/panel/tenancy/overview).

تبني منتج SaaS متعدد المستأجرين بـ Laravel؟ أصمم هذه المنتجات وأبنيها، بما في ذلك العمل باسم الوكالات (White-Label). اطّلع على [هندسة المنتجات](https://fadymondy.com/ar/services/product-engineering?utm_source=laravilt&utm_medium=referral&utm_campaign=seo-content-oct26) أو [تواصل معي](https://fadymondy.com/ar/contact?utm_source=laravilt&utm_medium=referral&utm_campaign=seo-content-oct26).

---

Source: https://laravilt.com/ar/blog/tenancy-for-laravel-laravilt
