إطار Laravel

موفرو الخدمات

15 دقيقة الدرس 24 من 45

موفرو الخدمات

موفرو الخدمات هم المكان المركزي لجميع عمليات بدء تشغيل تطبيق Laravel. هم مسؤولون عن ربط الخدمات في الحاوية، وتسجيل مستمعي الأحداث، والبرامج الوسيطة، والمسارات، ومهام الإعداد الأخرى. فهم موفري الخدمات هو مفتاح فهم كيفية تكوين وبدء تشغيل تطبيقات Laravel.

ما هي موفرو الخدمات؟

موفرو الخدمات هم فئات تسجل وتبدأ مكونات تطبيق Laravel الخاص بك. كل تطبيق Laravel لديه موفر خدمة واحد على الأقل (AppServiceProvider)، ويتضمن الإطار نفسه العديد من الموفرين للخدمات الأساسية.

بنية موفر الخدمات الأساسية:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // ربط الخدمات في الحاوية
        // يعمل قبل أي طريقة boot()
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // تنفيذ بدء التشغيل بعد التسجيل
        // يعمل بعد تسجيل جميع الموفرين
    }
}
الفرق الأساسي: يجب أن تربط طريقة register() الأشياء فقط في حاوية الخدمات. يتم استدعاء طريقة boot() بعد تسجيل جميع الموفرين، لذا يمكنك استخدام الخدمات الأخرى بأمان في boot().

طريقة Register

طريقة register هي المكان الذي تربط فيه الخدمات والواجهات والتبعيات الأخرى في حاوية الخدمات:

استخدام طريقة Register:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\PaymentGateway;
use App\Services\StripePaymentGateway;
use App\Contracts\ReportGeneratorInterface;
use App\Services\PdfReportGenerator;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // ربط بسيط
        $this->app->bind(PaymentGateway::class, function ($app) {
            return new StripePaymentGateway(
                config('services.stripe.key')
            );
        });

        // ربط Singleton
        $this->app->singleton(
            ReportGeneratorInterface::class,
            PdfReportGenerator::class
        );

        // تسجيل ملف التكوين
        $this->mergeConfigFrom(
            __DIR__.'/../config/mypackage.php',
            'mypackage'
        );

        // التسجيل الشرطي بناءً على البيئة
        if ($this->app->environment('local')) {
            $this->app->register(DebugServiceProvider::class);
        }
    }
}
قاعدة مهمة: لا تحاول أبداً استخدام أي خدمات أو مسارات أو وظائف أخرى في طريقة register. قم فقط بربط الأشياء في الحاوية. إذا كنت بحاجة إلى استخدام الخدمات، افعل ذلك في طريقة boot.

طريقة Boot

تعمل طريقة boot بعد تسجيل جميع موفري الخدمات، مما يجعل من الآمن استخدام الخدمات الأخرى:

استخدام طريقة Boot:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Blade;
use App\Models\User;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // مشاركة البيانات مع جميع العروض
        View::share('appName', config('app.name'));

        // تعريف بوابات التفويض
        Gate::define('update-post', function (User $user, $post) {
            return $user->id === $post->user_id;
        });

        // تسجيل توجيهات Blade
        Blade::directive('datetime', function ($expression) {
            return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
        });

        // تسجيل مراقبي النموذج
        User::observe(UserObserver::class);

        // نشر أصول الحزمة
        $this->publishes([
            __DIR__.'/../resources/views' => resource_path('views/vendor/mypackage'),
        ], 'mypackage-views');

        // تسجيل الماكرو
        \Illuminate\Support\Str::macro('titleSnake', function ($value) {
            return \Illuminate\Support\Str::title(
                str_replace('_', ' ', $value)
            );
        });
    }
}

حقن التبعيات في طريقة Boot

يمكنك التلميح إلى أنواع التبعيات في طريقة boot، وستقوم الحاوية بحقنها تلقائياً:

حقن التبعيات في طريقة Boot:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\View\Factory as ViewFactory;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        //
    }

    /**
     * التبعيات محقونة تلقائياً
     */
    public function boot(
        ResponseFactory $response,
        ViewFactory $view
    ): void {
        // استخدام التبعيات المحقونة
        $view->composer('*', function ($view) {
            $view->with('currentYear', date('Y'));
        });

        // إضافة ماكرو استجابة مخصص
        $response->macro('caps', function ($value) use ($response) {
            return $response->make(strtoupper($value));
        });
    }
}

إنشاء موفري خدمات مخصصين

عند بناء تطبيقات أكبر، من الممارسات الجيدة إنشاء موفري خدمات مخصصين لمكونات مختلفة:

إنشاء موفر مخصص:
// إنشاء موفر خدمات جديد
php artisan make:provider PaymentServiceProvider

// app/Providers/PaymentServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\Payment\StripeGateway;
use App\Services\Payment\PayPalGateway;
use App\Contracts\PaymentGatewayInterface;

class PaymentServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // ربط بوابة الدفع بناءً على التكوين
        $this->app->singleton(
            PaymentGatewayInterface::class,
            function ($app) {
                $driver = config('payment.default');

                return match($driver) {
                    'stripe' => new StripeGateway(
                        config('payment.stripe.key')
                    ),
                    'paypal' => new PayPalGateway(
                        config('payment.paypal.client_id')
                    ),
                    default => throw new \Exception("Unsupported payment driver: {$driver}")
                };
            }
        );
    }

    public function boot(): void
    {
        // نشر التكوين
        $this->publishes([
            __DIR__.'/../config/payment.php' => config_path('payment.php'),
        ], 'payment-config');
    }
}

// التسجيل في مصفوفة موفري config/app.php
'providers' => [
    // ... موفرون آخرون
    App\Providers\PaymentServiceProvider::class,
],
نصيحة التنظيم: أنشئ موفري خدمات منفصلين لمناطق مختلفة من تطبيقك (PaymentServiceProvider، NotificationServiceProvider، ReportServiceProvider، إلخ) للحفاظ على الكود منظماً وقابلاً للصيانة.

الموفرون المؤجلون

يتم تحميل الموفرين المؤجلين فقط عندما تكون خدماتهم مطلوبة فعلاً، مما يحسن أداء التطبيق:

إنشاء موفر مؤجل:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
use App\Services\ReportGenerator;

class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make('db'),
                $app->make('config')
            );
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array<int, string>
     */
    public function provides(): array
    {
        // قائمة بجميع الخدمات التي يقدمها هذا الموفر
        return [
            ReportGenerator::class,
        ];
    }
}

// الفوائد:
// - يتم تحميل الموفر فقط عندما تكون ReportGenerator مطلوبة
// - يقلل وقت بدء التشغيل
// - يحسن الأداء للطلبات التي لا تحتاج إلى تقارير
متى تؤجل: استخدم الموفرين المؤجلين للخدمات التي ليست مطلوبة في كل طلب (مولدات PDF، عملاء API الخارجيين، الآلات الحاسبة المعقدة). لا تؤجل الموفرين الذين يسجلون المسارات أو البرامج الوسيطة أو ضرورات وقت بدء التشغيل الأخرى.

نشر أصول الحزمة

تسمح موفرو الخدمات للحزم بنشر ملفات التكوين والعروض والهجرات والأصول الأخرى:

نشر الأصول:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class MyPackageServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // نشر التكوين
        $this->publishes([
            __DIR__.'/../config/mypackage.php' => config_path('mypackage.php'),
        ], 'mypackage-config');

        // نشر العروض
        $this->publishes([
            __DIR__.'/../resources/views' => resource_path('views/vendor/mypackage'),
        ], 'mypackage-views');

        // نشر الهجرات
        $this->publishes([
            __DIR__.'/../database/migrations' => database_path('migrations'),
        ], 'mypackage-migrations');

        // نشر الأصول (CSS، JS، الصور)
        $this->publishes([
            __DIR__.'/../public' => public_path('vendor/mypackage'),
        ], 'mypackage-assets');

        // نشر مجموعات أصول متعددة في وقت واحد
        $this->publishes([
            __DIR__.'/../config/mypackage.php' => config_path('mypackage.php'),
            __DIR__.'/../resources/views' => resource_path('views/vendor/mypackage'),
        ], 'mypackage-all');
    }
}

// المستخدمون ينشرون مع أوامر artisan:
php artisan vendor:publish --tag=mypackage-config
php artisan vendor:publish --tag=mypackage-views
php artisan vendor:publish --tag=mypackage-all
php artisan vendor:publish --provider="App\Providers\MyPackageServiceProvider"

تحميل موارد الحزمة

يمكن لموفري الخدمات تحميل العروض والترجمات والهجرات والمسارات من الحزم:

تحميل موارد الحزمة:
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class MyPackageServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // تحميل عروض الحزمة
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'mypackage');

        // تحميل ترجمات الحزمة
        $this->loadTranslationsFrom(__DIR__.'/../lang', 'mypackage');

        // تحميل هجرات الحزمة
        $this->loadMigrationsFrom(__DIR__.'/../database/migrations');

        // تحميل مسارات الحزمة
        $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
    }
}

// استخدام الموارد المحملة:

// العروض: return view('mypackage::dashboard');
// الترجمات: __('mypackage::messages.welcome')
// الهجرات: تعمل تلقائياً مع php artisan migrate
// المسارات: مسجلة تلقائياً
تمرين عملي 1: أنشئ NotificationServiceProvider يسجل قنوات إشعار مختلفة (Email، SMS، Slack) في الحاوية. استخدم config('notification.default') لتحديد القناة التي سيتم ربطها كافتراضية. أضف طريقة boot تنشر ملف تكوين الإشعارات.
تمرين عملي 2: ابنِ LoggingServiceProvider يقوم بإعداد قنوات تسجيل مختلفة بناءً على البيئة (local يستخدم الملفات اليومية، production يستخدم Slack + قاعدة البيانات). اجعله موفراً مؤجلاً يتم تحميله فقط عندما يتم استخدام المسجل فعلاً. قم بتضمين حقن التبعيات في طريقة boot لتكوين تنسيق السجل.
تمرين عملي 3: أنشئ ApiServiceProvider يسجل مسارات API من حزمة، ويحمل برامج وسيطة خاصة بـ API، وينشر عروض توثيق API، ويشارك بيانات إصدار API مع جميع العروض. اختبر نشر العروض والوصول إليها في قالب blade.

الخلاصة

موفرو الخدمات هم حجر الزاوية في تكوين تطبيق Laravel:

  • طريقة Register: ربط الخدمات في الحاوية فقط
  • طريقة Boot: بدء تشغيل الخدمات والعروض والبوابات وما إلى ذلك بعد التسجيل
  • حقن التبعيات: يمكن لطريقة boot التلميح إلى التبعيات
  • موفرون مخصصون: تنظيم التطبيق من خلال إنشاء موفرين مخصصين
  • موفرون مؤجلون: التحميل فقط عند الحاجة لأداء أفضل
  • النشر: السماح للمستخدمين بتخصيص أصول الحزمة
  • تحميل الموارد: جعل عروض الحزمة والترجمات والمسارات متاحة

في الدرس التالي، سنستكشف أوامر Artisan Console وكيفية إنشاء أدوات CLI مخصصة لتطبيق Laravel الخاص بك.