logo

সার্ভিস কন্টেইনার কি?

Q:
সার্ভিস কন্টেইনারটা আসলে কি?

সার্ভিস কন্টেইনার হল লারাভেলের প্রাণ, এটি লারাভেলের সমস্ত কম্পোনেন্টের এর গ্লু (আঠা) হিসেবে কাজ করে। সার্ভিস কন্টেইনার একটি ক্লাস যা লারাভেলে ক্লাস ডিপেন্ডেন্সি ম্যানেজ, ডিপেন্ডেন্সি ইনজেকশন, ইনভারশন অফ কন্ট্রোল ইত্যাদি কাজে ব্যবহত হয়। চলুন দেখি সার্ভিস কন্টেইনারের কিছু ব্যবহার -

ডিপেন্ডেন্সি ইনজেকশন হল কোন ক্লাসের ডিপেন্ডেন্সি বা প্রোপার্টি গুলো অটো ইঞ্জেক্ট করা অর্থাৎ তৈরি করা। যেমন নীচে UserRepository ক্লাসে $user নামে একটি প্রোপার্টি আছে যা User ক্লাসের একটি অবজেক্ট। অর্থাৎ User ক্লাসটি হল UserRepository ক্লাসের ডিপেন্ডেন্সি। আমরা কন্সট্রাক্টরে $user ভেরিএবলের আগে User ক্লাসের নাম লিখে টাইপহিন্ট করে দিয়েছি। এই টাইপহিন্টিং এর ফলে এখন Container ক্লাস php এর রিফ্লেকশন সুবিধা ব্যবহার করে যখনি আমরা UserRepository ক্লাসটি তৈরি করব তখন অটোমেটিক User ক্লাস টি তৈরি করে $user আর্গুমেন্ট হিসেবে সাপ্লাই করবে।

use App\User;

class UserRepository
{
    private $user;

    function __construct(User $user)
    {
        $this->user = $user;
    }
}

// সার্ভিস কন্টেইনার ছাড়া
$repository = new UserRepository(new User());

// সার্ভিস কন্টেইনার ব্যবহার করে
$repository = new UserRepository();

এখন মনে করুন User ক্লাসের নিজস্ব ডিপেন্ডেন্সি আছে। এক্ষেত্রে সার্ভিস কন্টেইনার প্রথমে UserRepository ক্লাস তৈরি করতে গিয়ে দেখবে এটি User ক্লাসের উপর নির্ভর করে সুতরাং কন্টেইনার User ক্লাসটি তৈরি করতে চাইবে এবং দেখবে User ক্লাসটি আবার আরেকটি ক্লাসের উপর নির্ভর করে, তাই প্রথমে সেই ক্লাসটি তৈরি করবে এরপর সেই object কে User ক্লাস তৈরির সময় constructor এর আর্গুমেন্ট হিসেবে ব্যবহার করবে এবং সর্বশেষে User ক্লাসের অবজেক্ট কে UserRepository ক্লাস তৈরির সময় আর্গুমেন্ট হিসেবে ব্যবহার করবে। এভাবে সার্ভিস কন্টেইনার যত গভীরে নেস্টেড ডিপেন্ডেন্সি থাকবে তা অটো রিসল্ভ করবে।

কিন্তু যদি এমন হয় কোন ক্লাসের ডিপেন্ডেন্সি অটো রিসল্ভেবল না হয়। যেমন নীচের উদাহরণে User ক্লাস $language নামে একটি স্ট্রিং প্রোপার্টির উপর নির্ভর করে। কিন্তু আমাদের কন্টেইনার জানে না এই প্রোপার্টির জন্য কোন ভ্যালু দিতে হবে। এক্ষেত্রে আমরা সার্ভিস কন্টেইনারকে বলে দিতে পারি কিভাবে এই ভ্যালু দিতে হবে, এজন্য আমরা কন্টেইনারের bind মেথড ব্যবহার করতে পারি।

class User
{
    private $language;

    function __construct(string $language)
    {
        $this->language = $language;
    }
}

আমরা AppServiceProvider.php ক্লাসের register মেথডের ভিতর User ক্লাসকে কন্টেইনারে বাইন্ড করে দিব যাতে এরপর যখনি আমাদের User ক্লাসটি দরকার হবে তখন কন্টেইনার আমাদেরকে User ক্লাসের একটি ইন্সট্যান্স দিতে পারে।

    public function register()
    {
        $this->app->bind('App\User', function ($app) {
            return new App\User(config('locale.language'));
        });
    }

উপরে আমরা কন্টেইনারকে বলে দিচ্ছি যখন কেউ User ক্লাসটি চাইবে তখন locale.php config ফাইল থেকে ভ্যালু নিয়ে User ক্লাসের আর্গুমেন্ট হিসেবে ব্যবহার করার জন্য এবং User ক্লাসের একটি ইন্সট্যান্স রিটার্ন করার জন্য।

আমরা সার্ভিস কন্টেইনার ব্যবহার করে interface কে কন্টেইনারে বাইন্ড করতে পারি। এটি করলে লাভ হল আমরা প্রয়োজন অনুযায়ী একটি ইন্টারফেইসের আলাদা আলাদা ইমপ্লিমেন্টেশন ব্যবহার করতে পারব। মনে করুন নীচের WeatherController ক্লাসটি আবহাওয়া সংক্রান্ত ডাটা প্রভাইড করে এবং এজন্য আমরা একটি ক্লাস লিখলাম WorldWeatherApi নামে যা একটি থার্ড পার্টি সার্ভিস World Weather API কল করে এবং বর্তমান আবহাওয়ার ডাটা রিটার্ন করে। এই ক্লাসটিকে আমরা কন্সট্রাক্টরে অটো ইঞ্জেক্ট করছি। এখন মনে করেন আমরা সিদ্ধান্ত নিলাম World Weather API ব্যবহার না করে আমরা Open Weather API ব্যবহার করব, সেক্ষেত্রে আমাদেরে কন্সট্রাক্টরে চেঞ্জ করে OpenWeatherAPI ক্লাসটি ব্যবহার করতে হবে এবং যত জায়গায় আমরা এটি ব্যবহার করেছি সব জায়গায় আমাদের এই চেঞ্জটি করতে হবে। কিন্তু আমরা যদি একটি ইন্টারফেইস তৈরি করি যা উপরোক্ত ক্লাসগুলো ইমপ্লিমেন্ট করে তাহলে আমাদের এই চেঞ্জ করার প্রয়োজন পরে না।

class WeatherController
{
    private $weatherApi;

    function __construct(WorldWeatherApi $weatherApi)
    {
        $this->weatherApi = weatherApi;
    }
}

আমরা WeatherDataProvider নামে একটি ইন্টারফেইস তৈরি করলাম যা WorldWeatherApi ইমপ্লিমেন্ট করে এবং এখন এই ইন্তারফেইসটি আমরা কন্সট্রাক্টরে টাইপহিন্ট করে দিব ফলে পরবর্তীতে যদি আমরা WorldWeatherApi এর পরিবর্তে OpenWeatherAPI ক্লাসটি ব্যবহার করতে চাই তাহলে শুধু আমাদের বাইন্ডিং এ চেঞ্জ করে দিলেই হবে।

    public function register()
    {
        $this->app->bind(
            'App\Contracts\WeatherDataProvider',
            'App\ApiServices\WorldWeatherApi'
        );
    }

আমরা এখন কন্সট্রাক্টরে ক্লাসের বদলে ইন্টারফেইসটি টাইপহিন্ট করে দিলে কন্টেইনার WeatherDataProvider ইনটারফেইস এর জন্য যে ইমপ্লিমেন্টেশন ক্লাসটি বাইন্ড করা আছে তা আমাদের সরবরাহ করবে।

class WeatherController
{
    private $weatherApi;

    function __construct(WeatherDataProvider $weatherApi)
    {
        $this->weatherApi = weatherApi;
    }
}

কন্টেইনার ইউজ করে আমরা ক্লাস ইন্সট্যান্সও রিসল্ভ করতে পারি। যেমন মনে করি আমাদের Validator এই ক্লাসের একটি অবজেক্ট দরকার, কন্টেইনারের make মেথড ব্যবহার করে আমরা Validator ক্লাসের একটি অবজেক্ট পেতে পারি।

$validator = $this->app->make('Validator');

কন্টেইনার দিয়ে এমনিভাবে singleton, contextual binding, aliasing ইত্যাদি কাজও করা সম্ভব।

আপনার মন্তব্যঃ
পোস্ট সম্পর্কে আপনার মতামত বা কোন ত্রুটি পেয়ে থাকলে নিচে জানাতে ভুলবেন না।