নির্ভরশীলতা স্বয়ংক্রিয় ইনজেকশন

webman-এ নির্ভরশীলতা স্বয়ংক্রিয় ইনজেকশন একটি বিকল্প বৈশিষ্ট্য, এই বৈশিষ্ট্যটি ডিফল্টভাবে বন্ধ থাকে। যদি আপনাকে নির্ভরশীলতা স্বয়ংক্রিয় ইনজেকশন প্রয়োজন হয়, তাহলে php-di-এর ব্যবহার করার সুপারিশ করা হচ্ছে, নীচে webman এর সাথে php-di যোগ করার পদ্ধতি দেওয়া হলো।

ইনস্টলেশন

composer require psr/container ^1.1.1 php-di/php-di ^6.3 doctrine/annotations ^1.14

config/container.php কনফিগারেশন পরিবর্তন করুন, এর চূড়ান্ত বিষয়বস্তু নিচে দেওয়া হলো:

$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return $builder->build();

config/container.php তে চূড়ান্তভাবে একটি PSR-11 বিধান অনুযায়ী কন্টেইনার ইনস্ট্যান্স ফেরত দেওয়া হয়। যদি আপনি php-di ব্যবহার করতে না চান, তাহলে এখানে অন্য একটি PSR-11 বিধান অনুযায়ী কন্টেইনার ইনস্ট্যান্স তৈরি এবং ফেরত দিতে পারেন।

কনস্ট্রাক্টর ইনজেকশন

app/service/Mailer.php নতুন করে তৈরি করুন (যদি ডিরেক্টরি বিদ্যমান না থাকে তাহলে নিজে তৈরি করুন) বিষয়বস্তু নিচে দেওয়া হলো:

<?php
namespace app\service;

class Mailer
{
    public function mail($email, $content)
    {
        // মেইল প্রেরণের কোড omit করা হয়েছে
    }
}

app/controller/UserController.php এর বিষয়বস্তু নিচে দেওয়া হলো:

<?php
namespace app\controller;

use support\Request;
use app\service\Mailer;

class UserController
{
    private $mailer;

    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    public function register(Request $request)
    {
        $this->mailer->mail('hello@webman.com', 'Hello and welcome!');
        return response('ok');
    }
}

সাধারণভাবে, app\controller\UserController এর ইনস্ট্যান্স তৈরি করতে নীচের কোডটি প্রয়োজন:

$mailer = new Mailer;
$user = new UserController($mailer);

যখন php-di ব্যবহার করা হয়, তখন ডেভেলপারদের ম্যানুয়ালি কন্ট্রোলারের Mailer ইনস্ট্যান্স তৈরি করার প্রয়োজন হয় না, webman স্বয়ংক্রিয়ভাবে এটি সম্পন্ন করে। যদি Mailer ইনস্ট্যান্স করার সময় অন্য কোন ক্লাসের নির্ভরতা থাকে, তাহলে webman স্বয়ংক্রিয়ভাবে সেগুলি ইনস্ট্যান্স করে এবং ইনজেকশন করে। ডেভেলপারদের কোনো প্রাথমিক কাজ করার প্রয়োজন নেই।

নোট
নির্ভরশীলতা স্বয়ংক্রিয় ইনজেকশন সম্পন্ন করতে হবে ফ্রেমওয়ার্ক বা php-di দ্বারা তৈরি ইনস্ট্যান্স হতে, ম্যানুয়ালি new দ্বারা তৈরি ইনস্ট্যান্স নির্ভরশীলতা স্বয়ংক্রিয় ইনজেকশন সম্পন্ন করতে পারবে না, যদি ইনজেক্ট করতে হয় তাহলে support\Container ইন্টারফেস ব্যবহার করার জন্য new বাক্যটি প্রতিস্থাপন করতে হবে, যেমন:

use app\service\UserService;
use app\service\LogService;
use support\Container;

// new কীওয়ার্ড দ্বারা তৈরি ইনস্ট্যান্স নির্ভরশীলতা ইনজেকশন করতে পারবে না
$user_service = new UserService;
// new কীওয়ার্ড দ্বারা তৈরি ইনস্ট্যান্স নির্ভরশীলতা ইনজেকশন করতে পারবে না
$log_service = new LogService($path, $name);

// Container দ্বারা তৈরি ইনস্ট্যান্স নির্ভরশীলতা ইনজেকশন করতে পারবে
$user_service = Container::get(UserService::class);
// Container দ্বারা তৈরি ইনস্ট্যান্স নির্ভরশীলতা ইনজেকশন করতে পারবে
$log_service = Container::make(LogService::class, [$path, $name]);

অ্যানোটেশন ইনজেকশন

কনস্ট্রাক্টর নির্ভরশীলতা স্বয়ংক্রিয় ইনজেকশনের পাশাপাশি আমরা অ্যানোটেশন ইনজেকশনও ব্যবহার করতে পারি। উপরের উদাহরণটি চালিয়ে, app\controller\UserController কে নিচের মতে পরিবর্তন করুন:

<?php
namespace app\controller;

use support\Request;
use app\service\Mailer;
use DI\Annotation\Inject;

class UserController
{
    /**
     * @Inject
     * @var Mailer
     */
    private $mailer;

    public function register(Request $request)
    {
        $this->mailer->mail('hello@webman.com', 'Hello and welcome!');
        return response('ok');
    }
}

এই উদাহরণ @Inject অ্যানোটেশন দ্বারা ইনজেকশন করা হয়েছে এবং @var অ্যানোটেশন দ্বারা অবজেক্টের টাইপ ঘোষণা করা হয়েছে। এই উদাহরণটি কনস্ট্রাক্টর ইনজেকশনের মতোই কাজ করে, তবে কোডটি আরও সংক্ষিপ্ত।

নোট
webman 1.4.6 সংস্করণের পূর্বে কন্ট্রোলার প্যারামিটার ইনজেকশন সমর্থন করেনি, যেমন নিম্নলিখিত কোডটি webman<=1.4.6 সময়ে সমর্থিত নয়

<?php
namespace app\controller;

use support\Request;
use app\service\Mailer;

class UserController
{
    // 1.4.6 সংস্করণের পূর্বে কন্ট্রোলার প্যারামিটার ইনজেকশন সমর্থিত নয়
    public function register(Request $request, Mailer $mailer)
    {
        $mailer->mail('hello@webman.com', 'Hello and welcome!');
        return response('ok');
    }
}

কাস্টম কনস্ট্রাক্টর ইনজেকশন

বিক্ষিপ্তভাবে কনস্ট্রাক্টরে পাস করার প্যারামিটারগুলি ক্লাসের ইনস্ট্যান্স নাও হতে পারে, বরং স্ট্রিং, সংখ্যা, অ্যারে বা অন্যান্য ডেটা হতে পারে। উদাহরণস্বরূপ, Mailer কনস্ট্রাক্টরে SMTP সার্ভারের IP এবং পোর্ট পাস করতে হবে:

<?php
namespace app\service;

class Mailer
{
    private $smtpHost;

    private $smtpPort;

    public function __construct($smtp_host, $smtp_port)
    {
        $this->smtpHost = $smtp_host;
        $this->smtpPort = $smtp_port;
    }

    public function mail($email, $content)
    {
        // মেইল প্রেরণের কোড omit করা হয়েছে
    }
}

এই পরিস্থিতিতে পূর্বে উল্লেখিত কনস্ট্রাক্টর স্বয়ংক্রিয় ইনজেকশন সরাসরি ব্যবহার করা যায় না, কারণ php-di জানে না $smtp_host এবং $smtp_port এর মান কী হবে। সেক্ষেত্রে কাস্টম ইনজেকশন ব্যবহার করার চেষ্টা করতে পারেন।

config/dependence.php (ফাইলটি বিদ্যমান না থাকলে নিজে তৈরি করুন) এ নিম্নলিখিত কোড যুক্ত করুন:

return [
    // ... এখানে অন্যান্য কনফিগারেশন উপেক্ষা করা হয়েছে

    app\service\Mailer::class =>  new app\service\Mailer('192.168.1.11', 25);
];

এইভাবে, যখন নির্ভরশীলতা ইনজেকশন app\service\Mailer ইনস্ট্যান্স প্রাপ্ত করতে হবে, তখন এটি স্বয়ংক্রিয়ভাবে এই কনফিগারেশনে তৈরি app\service\Mailer ইনস্ট্যান্স ব্যবহার করবে।

আমরা লক্ষ্য করি, config/dependence.phpnew ব্যবহার করে Mailer ক্লাস ইনস্ট্যান্স হচ্ছে, এই উদাহরণে কোনও সমস্যা নেই, তবে কল্পনা করুন যদি Mailer ক্লাস অন্য ক্লাসের উপর নির্ভরশীল হয় অথবা Mailer ক্লাসের ভিতরে অ্যানোটেশন ইনজেকশন ব্যবহার করা হয়, তাহলে new দ্বারা ইনিশিয়ালাইজ করা হলে এটি স্বয়ংক্রিয় ইনজেকশন করবে না। এর সমাধান হলো কাস্টম ইন্টারফেস ইনজেকশন ব্যবহার করা, Container::get(ক্লাস নাম) অথবা Container::make(ক্লাস নাম, [কনস্ট্রাক্টর প্যারামিটার]) পদ্ধতি ব্যবহার করে ক্লাস ইনিশিয়ালাইজ করা।

কাস্টম ইন্টারফেস ইনজেকশন

বাস্তব প্রকল্পে আমরা সাধারণত ইন্টারফেস প্রোগ্রামিংকে পছন্দ করি, নির্দিষ্ট ক্লাস নয়। যেমন app\controller\UserControllerapp\service\MailerInterface অন্তর্ভুক্ত করা উচিত, app\service\Mailer নয়।

MailerInterface ইন্টারফেস সংজ্ঞায়িত করুন।

<?php
namespace app\service;

interface MailerInterface
{
    public function mail($email, $content);
}

MailerInterface ইন্টারফেসের বাস্তবায়ন সংজ্ঞায়িত করুন।

<?php
namespace app\service;

class Mailer implements MailerInterface
{
    private $smtpHost;

    private $smtpPort;

    public function __construct($smtp_host, $smtp_port)
    {
        $this->smtpHost = $smtp_host;
        $this->smtpPort = $smtp_port;
    }

    public function mail($email, $content)
    {
        // মেইল প্রেরণের কোড omit করা হয়েছে
    }
}

নির্দিষ্ট বাস্তবায়ন নয়, বরং MailerInterface ইন্টারফেস অন্তর্ভুক্ত করুন।

<?php
namespace app\controller;

use support\Request;
use app\service\MailerInterface;
use DI\Annotation\Inject;

class UserController
{
    /**
     * @Inject
     * @var MailerInterface
     */
    private $mailer;

    public function register(Request $request)
    {
        $this->mailer->mail('hello@webman.com', 'Hello and welcome!');
        return response('ok');
    }
}

config/dependence.phpMailerInterface ইন্টারফেসের বাস্তবায়ন নীচেররূপে সংজ্ঞায়িত করুন।

use Psr\Container\ContainerInterface;
return [
    app\service\MailerInterface::class => function(ContainerInterface $container) {
        return $container->make(app\service\Mailer::class, ['smtp_host' => '192.168.1.11', 'smtp_port' => 25]);
    }
];

এইভাবে যখন ব্যবসায় MailerInterface ইন্টারফেস ব্যবহার করার প্রয়োজন হয়, তখন এটি স্বয়ংক্রিয়ভাবে Mailer বাস্তবায়ন ব্যবহার করবে।

ইন্টারফেস প্রোগ্রামিংয়ের সুবিধা হলো, যখন আমাদের কোন একটি উপাদান পরিবর্তন করতে হয়, তখন ব্যবসায়িক কোড পরিবর্তন করার প্রয়োজন নেই, শুধু config/dependence.php এ নির্দিষ্ট বাস্তবায়ন পরিবর্তন করতে হবে। এটি ইউনিট টেস্টিং করার জন্যও অত্যন্ত সহায়ক।

অন্যান্য কাস্টম ইনজেকশন

config/dependence.php কেবল ক্লাসের নির্ভরতাই নয়, অন্যান্য মূল্য যেমন স্ট্রিং, সংখ্যা, অ্যারে ইত্যাদি নির্ধারণ করতে পারে।

যেমন config/dependence.php নিম্নরূপ সংজ্ঞায়িত করুন:

return [
    'smtp_host' => '192.168.1.11',
    'smtp_port' => 25
];

এই পরিস্থিতিতে আমরা @Inject ব্যবহার করে smtp_host এবং smtp_port ক্লাসের বৈশিষ্ট্যগুলিতে ইনজেক্ট করতে পারি।

<?php
namespace app\service;

use DI\Annotation\Inject;

class Mailer
{
    /**
     * @Inject("smtp_host")
     */
    private $smtpHost;

    /**
     * @Inject("smtp_port")
     */
    private $smtpPort;

    public function mail($email, $content)
    {
        // মেইল প্রেরণের কোড omit করা হয়েছে
        echo "{$this->smtpHost}:{$this->smtpPort}\n"; // আউটপুট হবে 192.168.1.11:25
    }
}

নোট: @Inject("key") এর ভিতরে দ্বি-উদ্ধৃতি ব্যবহার করুন

আরো বিষয়বস্তু

দয়া করে php-di ম্যানুয়াল এ দেখুন