مفاهیم معماری در لاراول – Service Container

مفاهیم معماری در لاراول - Service Container

 

Dependency Injection Container (DIC) به عنوان یک وسیله برای مدیریت وابستگی‌های بین کلاس‌ها در برنامه‌نویسی شی‌گرا استفاده می‌شود. برای مثال، وقتی یک شیء از یک کلاس خاص به یک کلاس دیگر وابسته است، می‌توان از DIC استفاده کرد تا این وابستگی‌ها به صورت خودکار مدیریت شوند. در لاراول، Container به عنوان یک DIC قدرتمند در نظر گرفته شده است.

با استفاده از Container در لاراول ، می‌توانید اشیاء مختلف را به صورت خودکار در برنامه خود تزریق کنید، از جمله اشیاء مربوط به اجزای فریم‌ورک، کلاس‌های سفارشی شما و حتی اشیاء مربوط به پکیج‌هایی که در برنامه خود استفاده می‌کنید. همچنین، Container ابزاری قدرتمند برای مدیریت وابستگی‌های بین کلاس‌های شماست و به شما اجازه می‌دهد که برنامه خود را به صورت بهتر و قابل توسعه‌تری طراحی کنید.

این صفحه مستندات مربوط به “کانتینر” در فریمورک لاراول است. کانتینر در لاراول 10 از اهمیت بالایی برخوردار است زیرا این ابزار به شما این امکان را می‌دهد که اشیاء و وابستگی‌هایی که برنامه شما نیاز دارد را به صورت خودکار و به درستی مدیریت کنید. این صفحه شامل توضیحاتی درباره تعریف کانتینر، کاربردها و نحوه استفاده از کانتینر در لاراول است. همچنین، این صفحه شامل توضیحاتی درباره مفاهیم مربوط به کانتینر مانند برنامه‌های کاربردی، توابع توسعه‌یافته و تزریق وابستگی‌ها در کانتینر است.

# مقدمه (Introduction)

کانتینر سرویس لاراول ابزاری قدرتمند برای مدیریت وابستگی‌های کلاس و انجام تزریق وابستگی است. تزریق وابستگی (Dependency Injection) به روشی گفته می‌شود که در آن وابستگی‌های یک کلاس، از طریق سازنده یا روش‌های دیگر، به کلاس دیگری “تزریق” می‌شوند. در واقع در این روش، کلاسی که وابستگی‌هایی دارد، از این وابستگی‌ها آگاه نیست و فقط به صورت پارامتر‌هایی در سازنده یا روش‌های دیگر، به آن تزریق می‌شوند. با استفاده از این روش، می‌توان کد را منظم‌تر و قابل تست‌تر کرد و همچنین از تکرار کد و اشتباهات احتمالی در ارتباط با وابستگی‌ها جلوگیری کرد.

بیایید به یک مثال ساده نگاه کنیم:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\Models\User;
use Illuminate\View\View;
 
class UserController extends Controller
{
    /**
     * Create a new controller instance.
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
 
    /**
     * Show the profile for the given user.
     */
    public function show(string $id): View
    {
        $user = $this->users->find($id);
 
        return view('user.profile', ['user' => $user]);
    }
}

در این مثال، کنترلر کاربر (UserController) نیاز دارد که کاربران را از یک منبع داده بازیابی کند. بنابراین، ما یک سرویس که قادر به بازیابی کاربران است را تزریق خواهیم کرد. در این زمینه، احتمالاً مخزن کاربران (UserRepository) ما از Eloquent برای بازیابی اطلاعات کاربر از پایگاه داده استفاده می‌کند. با این حال، با تزریق مخزن، ما می‌توانیم به راحتی آن را با یک پیاده‌سازی دیگر جایگزین کنیم. همچنین، ما می‌توانیم به راحتی “mock”، یا یک پیاده‌سازی مجازی از UserRepository را در هنگام تست برنامه‌ی خود ایجاد کنیم.

درک عمیقی از کانتینر سرویس لاراول برای ساختن یک برنامه بزرگ و قدرتمند ضروری است.

Mock به معنای ایجاد یک شبیه‌سازی از یک شیء واقعی برای تست یا بررسی عملکرد کد است. در برنامه‌نویسی، ما اغلب از “mock objects” استفاده می‌کنیم تا به ما کمک کنند تا کد خود را به دقت تست کنیم. با استفاده از این شبیه‌سازی ها، ما می‌توانیم عملکرد کد وابسته‌ی کلاس‌ها و توابع را بدون اینکه به داده‌های واقعی دسترسی داشته باشیم، بررسی کنیم. به عبارت دیگر، mock objects به ما اجازه می‌دهند که کد را در شرایط مختلف و با داده‌های مختلف تست کنیم و از عملکرد درست آن اطمینان حاصل کنیم. در متن فوق، به معنای ساخت یک پیاده‌سازی مجازی از UserRepository برای تست برنامه اشاره شده است.

# تفسیر بدون نیاز به تنظیمات اضافی (Zero Configuration Resolution)

صفر تنظیم‌کردن وابستگی‌ها، اگر یک کلاس وابستگی نداشته باشد یا فقط به کلاس‌های دیگر مشخص (نه رابط‌ها) وابستگی داشته باشد، کانتینر نیاز به دستورالعمل برای حل این کلاس ندارد. به عنوان مثال، شما می‌توانید کد زیر را در فایل routes/web.php قرار دهید:

<?php
 
class Service
{
    // ...
}
 
Route::get('/', function (Service $service) {
    die(get_class($service));
});

در این مثال، وقتی به مسیر / برنامه‌ی شما دسترسی پیدا کند، کلاس Service به صورت خودکار تزریق وابستگی‌های را انجام داده و در handler مسیر شما تزریق می‌شود. این تغییربخشی است. به این معناست که می‌توانید برنامه‌ی خود را توسعه داده و از تزریق وابستگی استفاده کنید، بدون این که نگران فایل‌های پیکربندی و حجیم باشید.

خوشبختانه، بسیاری از کلاس‌هایی که در حین ساخت یک برنامه‌ی Laravel می‌نویسید، به‌صورت خودکار وابستگی‌های خود را از طریق کانتینر دریافت می‌کنند، از جمله کنترلرها، گوش‌کنندگان رویداد، میان‌افزارها و غیره. علاوه بر این، شما می‌توانید در متد handle شغل‌های صف، وابستگی‌های نوع را تعیین کنید. یکباری که از قدرت تزریق وابستگی خودکار و بدون پیکربندی را تجربه کنید، به نظر می‌رسد که بدون آن توسعه‌ی برنامه‌ی خود امکان‌پذیر نیست.

# مواردی که استفاده از کانتینرها در آنها مفید است (When To Use The Container)

زمان استفاده از کانتینر سرویس، با توجه به این که کانتینر سرویس در لاراول به صورت خودکار وابستگی‌های آن را تزریق می‌کند، بدون اینکه نیازی به تعریف پیکربندی دستی باشد.، شما بسیاری از وابستگی‌ها را در مسیرها، کنترلرها، رویداد و در مکان‌های دیگر با type-hint تعریف خواهید کرد بدون اینکه به صورت دستی با کانتینر تعامل داشته باشید. به عنوان مثال، شما می‌توانید شی Illuminate\Http\Request را در تعریف مسیر خود با type-hint تعریف کنید تا به راحتی بتوانید به درخواست فعلی دسترسی پیدا کنید. هرچند که با نوشتن این کد، هیچ وقت نیازی به تعامل با کانتینر نداریم، اما کانتینر در پشت‌صحنه تزریق وابستگی‌های این کد را مدیریت می‌کند:

use Illuminate\Http\Request;
 
Route::get('/', function (Request $request) {
    // ...
});

در بسیاری از موارد، برای ساخت برنامه‌های لاراول، به دلیل تزریق وابستگی‌های خودکار و فاسادها، شما به هیچ وجه نیازی به binding یا resolving شدن دستی چیزی از کانتینر ندارید. پس در کدام حالت باید به صورت دستی با کانتینر تعامل داشت؟ دو حالت را بررسی می‌کنیم.

اول، اگر یک کلاسی را که یک رابط پیاده‌سازی می‌کند بنویسید و می‌خواهید رابط را در یک مسیر یا سازنده کلاس type-hint کنید، باید به کانتینر بگویید که چگونه رابط را حل کند. و دومین حالت، اگر شما یک بسته لاراول را می‌نویسید که قصد دارید با سایر توسعه‌دهندگان Laravel به اشتراک بگذارید، ممکن است نیاز به binding کردن سرویس‌های بسته خود در کانتینر داشته باشید.

# Binding

Bindings در Container، نحوه تزریق وابستگی‌ها را تعیین می‌کنند. به عبارت دیگر، Bindings مشخص می‌کنند که هر بار که به یک نوع از کلاس‌ها نیاز دارید، باید چه شیئی ایجاد شود. برای ایجاد Binding، می‌توانید از متد bind() استفاده کنید.

# مفاهیم پایه‌ای Binding به همراه مثال‌ها (Binding Basics)

Binding های ساده
تقریباً تمامیBinding های کانتینر سرویس شما در داخل ارائه‌دهنده‌های سرویس ثبت می‌شوند، بنابراین بیشتر این مثال‌ها نحوه استفاده از کانتینر در این زمینه را نشان می‌دهند.

در داخل یک ارائه‌دهنده‌ی سرویس، شما همیشه به کانتینر از طریق خصوصیت $this->app دسترسی دارید. ما می‌توانیم با استفاده از روش bind، با گذراندن نام کلاس یا رابطی که می‌خواهیم ثبت کنیم به همراه یک closure که یک نمونه از کلاس را برمی‌گرداند، یک بستر را ثبت کنیم:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->bind(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

توجه کنید که ما کانتینر را به عنوان یک آرگومان به تابع resolver دریافت می‌کنیم. سپس می‌توانیم از کانتینر برای حل وابستگی‌های فرعی شیءی که در حال ساخت آن هستیم استفاده کنیم.

همانطور که گفته شد، شما به طور معمول با کانتینر در داخل ارائه‌دهنده‌های سرویس تعامل می‌کنید؛ با این حال، اگر می‌خواهید خارج از یک ارائه‌دهنده‌ی سرویس با کانتینر تعامل داشته باشید، می‌توانید از Facade App استفاده کنید.

use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;
 
App::bind(Transistor::class, function (Application $app) {
    // ...
});

شما می‌توانید از روش bindIf برای ثبت بستر کانتینر استفاده کنید، تنها در صورتی که برای نوع داده شده قبلاً بستری ثبت نشده باشد:

$this->app->bindIf(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

 

اگر کلاس‌ها به هیچ رابطی وابسته نباشند، نیازی به بستر کانتینر برای آن‌ها نیست. چون کانتینر می‌تواند این شیء‌ها را با استفاده از بازتابی که دارد به صورت خودکار حل کند، نیازی به آموزش کانتینر برای ایجاد این شیء‌ها وجود ندارد.

 

بستری برای Singleton
متد singleton یک کلاس یا رابط را به کانتینر متصل می‌کند که تنها یک بار باید حل شود. هنگامی که یک بستر singleton حل می‌شود، نمونه شیء مشابه در تماس‌های بعدی با کانتینر برگردانده می‌شود:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->singleton(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

شما می‌توانید از روش singletonIf برای ثبت بستر کانتینر singleton استفاده کنید، تنها در صورتی که برای نوع داده شده بستری singleton ثبت نشده باشد:

$this->app->singletonIf(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

بستری برای Scoped Singletons
متد scoped کلاس یا رابطی را به کانتینر متصل می‌کند که تنها یک بار در دوره Lifecycle لاراول حل می‌شود. این متد به متد singleton شبیه است، با این تفاوت که نمونه‌هایی که با استفاده از scoped ثبت شده‌اند، هر زمان که برنامه Laravel یک “دوره جدید” را شروع می‌کند، مانند زمانی که یک کارگر Laravel Octane یک درخواست جدید را پردازش می‌کند یا زمانی که یک کارگر صف لاراول یک کار جدید را پردازش می‌کند، پاک خواهند شد:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->scoped(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

بستری برای نمونه‌ها
شما همچنین می‌توانید از روش instance برای متصل کردن یک نمونه شیء موجود در کانتینر استفاده کنید. نمونه داده شده همیشه در تماس‌های بعدی با کانتینر برگردانده می‌شود

use App\Services\Transistor;
use App\Services\PodcastParser;
 
$service = new Transistor(new PodcastParser);
 
$this->app->instance(Transistor::class, $service);

# نحوه Binding یک Interface به یک Implementation خاص در لاراول (Binding Interfaces To Implementations)

متصل کردن رابط‌ها به پیاده‌سازی‌ها، یکی از ویژگی‌های بسیار قدرتمند کانتینر سرویس، قابلیت متصل کردن یک رابط به یک پیاده‌سازی خاص است. به عنوان مثال، بگذارید فرض کنیم یک رابط EventPusher و یک پیاده‌سازی RedisEventPusher داریم. پس از پیاده‌سازی RedisEventPusher، می‌توانیم آن را با کانتینر سرویس به شکل زیر ثبت کنیم:

use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;
 
$this->app->bind(EventPusher::class, RedisEventPusher::class);

این دستور به کانتینر می‌گوید که هنگامی که یک کلاس به یک پیاده‌سازی از EventPusher نیاز دارد، باید RedisEventPusher را درج کند. حال می‌توانیم رابط EventPusher را در constructor یک کلاس قرار دهیم که توسط کانتینر حل می‌شود و از آن استفاده کنیم. به یاد داشته باشید که کنترلرها، listener های رویداد، میان‌افزارها و انواع مختلفی از کلاس‌ها در برنامه‌های لاراول همیشه با استفاده از کانتینر حل می‌شوند.

use App\Contracts\EventPusher;
 
/**
 * Create a new class instance.
 */
public function __construct(
    protected EventPusher $pusher
) {}

# متصل کردن زمینه‌ای (Contextual Binding)

گاهی اوقات ممکن است دو کلاس وجود داشته باشد که از یک رابط استفاده کنند، اما شما می‌خواهید پیاده‌سازی‌های مختلفی را در هر کلاس درج کنید. به عنوان مثال، دو کنترلر ممکن است به پیاده‌سازی‌های مختلف قرارداد Illuminate\Contracts\Filesystem\Filesystem وابسته باشند. لاراول یک رابط ساده و قابل فهم برای تعریف این رفتار فراهم می کند:

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
 
$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });
 
$this->app->when([VideoController::class, UploadController::class])
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

# متصل کردن مقادیر ابتدایی (Binding Primitives)

گاهی اوقات ممکن است یک کلاس وجود داشته باشد که برخی از کلاس‌های درج شده را دریافت کرده‌است، اما همچنین نیاز به دریافت یک مقدار ابتدایی مانند یک عدد صحیح دارد. شما می‌توانید به سادگی از متصل کردن زمینه‌ای برای درج هر مقداری که کلاس شما نیاز دارد، استفاده کنید:

use App\Http\Controllers\UserController;
 
$this->app->when(UserController::class)
          ->needs('$variableName')
          ->give($value);

گاهی اوقات یک کلاس ممکن است به یک آرایه از نمونه‌های برچسب‌گذاری شده وابسته باشد. با استفاده از متد giveTagged، می‌توانید به سادگی تمامی بایندهای کانتینر را با این برچسب درج کنید:

$this->app->when(ReportAggregator::class)
    ->needs('$reports')
    ->giveTagged('reports');

اگر نیاز به درج یک مقدار از یکی از فایل‌های پیکربندی برنامه‌ی خود دارید، می‌توانید از متد giveConfig استفاده کنید.

$this->app->when(ReportAggregator::class)
    ->needs('$timezone')
    ->giveConfig('app.timezone');

# Binding Typed Variadics

گاهی اوقات ممکن است یک کلاس وجود داشته باشد که یک آرایه از شیء‌های تایپ شده را با استفاده از یک آرگومان سازنده با امکان ارسال تعداد نامحدود آرگومان به یک تابع یا متد با نوع خاص، دریافت می‌کند:

<?php
 
use App\Models\Filter;
use App\Services\Logger;
 
class Firewall
{
    /**
     * The filter instances.
     *
     * @var array
     */
    protected $filters;
 
    /**
     * Create a new class instance.
     */
    public function __construct(
        protected Logger $logger,
        Filter ...$filters,
    ) {
        $this->filters = $filters;
    }
}

با استفاده از متصل کردن زمینه‌ای، می‌توانید به راحتی وابستگی‌های خود را حل کنید، برای مثال برای حل وابستگی به چندین نمونه از یک رابط خاص. با استفاده از متد give و یک closure که یک آرایه از نمونه‌های Filter را برمی‌گرداند، می‌توانید این وابستگی را حل کنید. مثال زیر را در نظر بگیرید:

$this->app->when(Firewall::class)
          ->needs(Filter::class)
          ->give(function (Application $app) {
                return [
                    $app->make(NullFilter::class),
                    $app->make(ProfanityFilter::class),
                    $app->make(TooLongFilter::class),
                ];
          });

برای راحتی کار، می‌توانید به جای استفاده از یک closure، یک آرایه از نام کلاس‌ها را به container بدهید تا هر زمان که Firewall به نمونه‌های Filter نیاز دارد، این کلاس‌ها توسط container حل شوند.

$this->app->when(Firewall::class)
          ->needs(Filter::class)
          ->give([
              NullFilter::class,
              ProfanityFilter::class,
              TooLongFilter::class,
          ]);

# برچسب‌گذاری (Tagging)

گاهی اوقات ممکن است نیاز داشته باشید که تمامی بایندینگ‌هایی که به یک “دسته” خاص تعلق دارند را حل کنید. به عنوان مثال، شاید شما در حال ساخت یک تحلیلگر گزارش هستید که یک آرایه از پیاده‌سازی‌های مختلف رابط گزارش را دریافت می‌کند. پس از ثبت پیاده‌سازی‌های گزارش، می‌توانید با استفاده از متد tag به آن‌ها یک برچسب اختصاص دهید:

$this->app->bind(CpuReport::class, function () {
    // ...
});
 
$this->app->bind(MemoryReport::class, function () {
    // ...
});
 
$this->app->tag([CpuReport::class, MemoryReport::class], 'reports');

با تگ‌گذاری سرویس‌ها، شما می‌توانید به راحتی تمامی آن‌ها را با استفاده از متد tagged کانتینر حل کنید.

$this->app->bind(ReportAnalyzer::class, function (Application $app) {
    return new ReportAnalyzer($app->tagged('reports'));
});

# گسترش بایندینگ‌ها (Extending Bindings)

متد extend امکان تغییر سرویس‌های حل شده را فراهم می‌کند. به عنوان مثال، هنگامی که یک سرویس حل شده (Resolved) است، می‌توانید کدهای اضافه برای تزئین یا پیکربندی سرویس را اجرا کنید. متد extend دو آرگومان دریافت می‌کند: کلاس سرویس که می‌خواهید برای آن گسترش دهید و یک closure که باید سرویس تغییر یافته را برگرداند. این closure شامل سرویسی است که در حال حل شدن است و نمونه کانتینر است که به عنوان دومین پارامتر به آن پاس داده می‌شود.

$this->app->extend(Service::class, function (Service $service, Application $app) {
    return new DecoratedService($service);
});

# Resolving

# روش ساخت متد (The Make Method)

شما می‌توانید از متد make برای حل کردن (Resolved) یک نمونه از کلاس از container استفاده کنید. متد make نام کلاس یا رابط را که می‌خواهید حل شود را قبول می‌کند:

use App\Services\Transistor;
 
$transistor = $this->app->make(Transistor::class);

اگر برخی از وابستگی‌های کلاس شما با استفاده از container قابل حل نیستند، می‌توانید آن‌ها را با ارسال آن‌ها به صورت یک آرایه‌ی همراه با کلید و مقدار به متد makeWith تزریق کنید. به عنوان مثال، ما می‌توانیم آرگومان سازنده $id را که برای سرویس Transistor مورد نیاز است، به صورت دستی تزریق کنیم.

use App\Services\Transistor;
 
$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);

می‌توان از متد bound برای تشخیص دادن اینکه آیا یک کلاس یا رابط به طور صریح در container بایند شده است یا نه، استفاده کرد:

if ($this->app->bound(Transistor::class)) {
    // ...
}

اگر در خارج از یک سرویس پرووایدر و در جایی از کدتان هستید که دسترسی به متغیر $app را ندارید، شما می‌توانید از فاساد App یا helper app برای حل کردن یک نمونه از کلاس از container استفاده کنید:

use App\Services\Transistor;
use Illuminate\Support\Facades\App;
 
$transistor = App::make(Transistor::class);
 
$transistor = app(Transistor::class);

اگر می‌خواهید نمونه‌ی خود container لارراول به عنوان یک dependency در کلاسی که توسط container حل می‌شود، تزریق شود، می‌توانید کلاس Illuminate\Container\Container را در constructor کلاس خود type-hint کنید:

use Illuminate\Container\Container;
 
/**
 * Create a new class instance.
 */
public function __construct(
    protected Container $container
) {}

# تزریق خودکار (Automatic Injection)

تزریق اتوماتیک، به عنوان یک روش دیگر و مهم، شما می‌توانید وابستگی را در constructor یک کلاس که توسط container حل می‌شود (شامل کنترلرها، گوش‌گیرندگان رویداد، میان افزارها و غیره) type-hint کنید. همچنین، می‌توانید وابستگی‌ها را در متد `handle` جابجایی کارهای صف‌شده type-hint کنید. در عمل، این روش بیشتر اشیا شما را باید توسط container حل شود.

به عنوان مثال، شما می‌توانید یک ریپازیتوری تعریف شده توسط برنامه خود را در constructor یک کنترلر type-hint کنید. ریپازیتوری به طور خودکار حل و در کلاس تزریق می‌شود.

<?php
 
namespace App\Http\Controllers;
 
use App\Repositories\UserRepository;
use App\Models\User;
 
class UserController extends Controller
{
    /**
     * Create a new controller instance.
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
 
    /**
     * Show the user with the given ID.
     */
    public function show(string $id): User
    {
        $user = $this->users->findOrFail($id);
 
        return $user;
    }
}

# روش های فراخوانی و تزریق (Method Invocation & Injection)

گاهی اوقات شما ممکن است بخواهید یک متد را روی یک نمونه از شیء صدا بزنید، در حالی که به container اجازه می‌دهید وابستگی‌های آن متد به صورت اتوماتیک تزریق شوند. به عنوان مثال، با توجه به کلاس زیر:

<?php
 
namespace App;
 
use App\Repositories\UserRepository;
 
class UserReport
{
    /**
     * Generate a new user report.
     */
    public function generate(UserRepository $repository): array
    {
        return [
            // ...
        ];
    }
}

شما می‌توانید متد generate را از طریق container به شکل زیر فراخوانی کنید:

use App\UserReport;
use Illuminate\Support\Facades\App;
 
$report = App::call([new UserReport, 'generate']);

متد call هر callable در PHP را قبول می‌کند. حتی می‌توانید از متد call container برای فراخوانی یک closure با تزریق اتوماتیک وابستگی‌های آن استفاده کنید.

use App\Repositories\UserRepository;
use Illuminate\Support\Facades\App;
 
$result = App::call(function (UserRepository $repository) {
    // ...
});

# رویداد های کانتینری (Container Events)

سرویس container هر بار که یک شیء را حل می‌کند، یک رویداد را منتشر می‌کند. شما می‌توانید به این رویداد با استفاده از متد resolving گوش دهید.

use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->resolving(Transistor::class, function (Transistor $transistor, Application $app) {
    // Called when container resolves objects of type "Transistor"...
});
 
$this->app->resolving(function (mixed $object, Application $app) {
    // Called when container resolves object of any type...
});

همانطور که مشاهده می‌کنید، شیء در حال حل شدن به callback منتقل می‌شود که به شما اجازه می‌دهد هر ویژگی اضافی را در شیء قرار دهید قبل از آنکه به مصرف‌کننده خود داده شود.

# PSR-11

سرویس container لاراول رابط PSR-11 را پیاده‌سازی می‌کند. بنابراین، شما می‌توانید رابط container PSR-11 را type-hint کنید تا نمونه‌ای از container لاراول دریافت کنید.

use App\Services\Transistor;
use Psr\Container\ContainerInterface;
 
Route::get('/', function (ContainerInterface $container) {
    $service = $container->get(Transistor::class);
 
    // ...
});

PSR-11 چیست؟

PSR-11 یک استاندارد ارائه شده توسط گروه انجمن PHP-FIG است که مشخصاتی برای رابط container در PHP تعریف می‌کند. این استاندارد شما را قادر می‌سازد تا از container های مختلف در PHP استفاده کنید، بدون اینکه برنامه‌ی شما به طور مستقیم به یک container خاص محدود شود. این یک استاندارد شایع در جامعه PHP است و بسیاری از container های معروف در PHP این استاندارد را پیاده‌سازی کرده‌اند.

در صورتی که شناسه داده شده قابل حل نباشد، یک استثناء پرتاب می‌شود. اگر شناسه هرگز بایند نشده بود، استثناء یک instance از Psr\Container\NotFoundExceptionInterface خواهد بود. اگر شناسه بایند شده بود، اما قابل حل نبود، یک instance از Psr\Container\ContainerExceptionInterface پرتاب خواهد شد.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *