निर्भरता स्वचालित इंजेक्शन

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)
    {
        // ईमेल भेजने का कोड छोटा किया गया
    }
}

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 सर्वर आईपी और पोर्ट पास करने की आवश्यकता होती है:

<?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)
    {
        // ईमेल भेजने का कोड छोटा किया गया
    }
}

इस स्थिति में, पिछले वर्णित कंस्ट्रक्टर स्वचालित इंजेक्शन का सीधे उपयोग नहीं किया जा सकता है, क्योंकि 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.php में Mailer कक्षा को इंस्टेंसिएट करने के लिए new का उपयोग किया गया है, यह इस उदाहरण में कोई समस्या नहीं है, लेकिन कल्पना करें यदि Mailer कक्षा अन्य कक्षाओं पर निर्भर करती है या Mailer कक्षा के अंदर एनोटेशन इंजेक्शन का उपयोग किया गया है, तो new प्रारंभिककरण निर्भरता स्वचालित इंजेक्शन नहीं करेगा। समाधान यह है कि कस्टम इंटरफ़ेस इंजेक्शन का उपयोग करें, कक्षा को प्रारंभ करने के लिए Container::get(क्लास नाम) या Container::make(क्लास नाम, [कंस्ट्रक्टर पैरामीटर]) का उपयोग करें।

कस्टम इंटरफ़ेस इंजेक्शन

वास्तविक परियोजनाओं में, हम आमतौर पर इंटरफ़ेस प्रोग्रामिंग की ओर अधिक इच्छुक होते हैं, न कि विशिष्ट कक्षाओं की। उदाहरण के लिए, app\controller\UserController में app\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)
    {
        // ईमेल भेजने का कोड छोटा किया गया
    }
}

विशिष्ट कार्यान्वयन के बजाय 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.php में MailerInterface इंटरफेस को इस प्रकार परिभाषित करें:

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)
    {
        // ईमेल भेजने का कोड छोटा किया गया
        echo "{$this->smtpHost}:{$this->smtpPort}\n"; // यह 192.168.1.11:25 आउटपुट करेगा
    }
}

नोट: @Inject("key") के अंदर डबल कोट हैं।

अधिक सामग्री

कृपया php-di मैनुअल का संदर्भ लें।