Drupal कस्टम मॉड्यूल्स में PHP Constructor Property Promotion का उपयोग
PHP 8 ने constructor property promotion पेश किया, जो एक ऐसी सुविधा है जो क्लास प्रॉपर्टी की परिभाषा और असाइनमेंट को सरल बनाती है। यह आपको constructor सिग्नेचर में ही प्रॉपर्टीज़ को घोषित करने और इनिशियलाइज़ करने की अनुमति देती है। यह ट्यूटोरियल Drupal कस्टम मॉड्यूल्स (जो PHP 8.0+ की आवश्यकता रखते हैं) में constructor property promotion का उपयोग कैसे करें, यह दिखाता है, विशेष रूप से आपके सर्विसेज़ और कंट्रोलर्स में dependency injection को सरल बनाने के लिए। हम पारंपरिक Drupal पैटर्न (जो PHP 7 और प्रारंभिक Drupal 9 में उपयोग होता था) की तुलना आधुनिक PHP 8+ तरीके से करेंगे, दोनों के लिए पूर्ण कोड उदाहरणों के साथ। अंत तक, आप देखेंगे कि यह आधुनिक सिंटैक्स कैसे boilerplate को घटाता है, कोड को स्पष्ट बनाता है, और वर्तमान सर्वोत्तम प्रथाओं के साथ मेल खाता है।
Drupal 10 (जो PHP 8.1+ की आवश्यकता रखता है) ने कोर में इन आधुनिक PHP सुविधाओं को अपनाना शुरू कर दिया है, इसलिए कस्टम मॉड्यूल डेवलपर्स को भी ऐसा करने के लिए प्रोत्साहित किया जाता है। आइए पहले Drupal में पारंपरिक dependency injection पैटर्न की समीक्षा करें, फिर इसे constructor property promotion का उपयोग करके refactor करें।
Drupal में पारंपरिक Dependency Injection (PHP 8 से पहले)
Drupal की सर्विसेज़ और कंट्रोलर्स में पारंपरिक पैटर्न के अंतर्गत dependency injection तीन चरणों में होता है:
- प्रत्येक dependency को एक क्लास प्रॉपर्टी के रूप में घोषित करें (आमतौर पर protected) उचित docblock के साथ।
- Constructor पैरामीटर्स में प्रत्येक dependency को type-hint करें, और उन्हें constructor के अंदर क्लास प्रॉपर्टीज़ को असाइन करें।
- Controllers (और कुछ plugin क्लासेस) के लिए, create(ContainerInterface $container)नामक एक static method को लागू करें, ताकि Drupal की service container से सेवाएं प्राप्त की जा सकें और क्लास को instantiate किया जा सके।
यह प्रक्रिया काफी boilerplate कोड उत्पन्न करती है। उदाहरण के लिए, एक साधारण कस्टम सर्विस को लें जिसे configuration factory और logger factory की आवश्यकता है। पारंपरिक रूप से, आप इस प्रकार लिखेंगे:
पारंपरिक Service क्लास उदाहरण
<?php
namespace Drupal\example;
/**
 * साइट नाम लॉग करने वाली उदाहरण सेवा।
 */
class ExampleService {
  /**
   * Configuration factory सेवा।
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;
  /**
   * लॉगर चैनल फैक्ट्री सेवा।
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $loggerFactory;
  /**
   * ExampleService ऑब्जेक्ट का constructor।
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Configuration factory।
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   लॉगर चैनल फैक्ट्री।
   */
  public function __construct(ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory) {
    // Injected services को स्टोर करें।
    $this->configFactory = $config_factory;
    $this->loggerFactory = $logger_factory;
  }
  /**
   * साइट का नाम लॉग करता है।
   */
  public function logSiteName(): void {
    $site_name = $this->configFactory->get('system.site')->get('name');
    $this->loggerFactory->get('example')->info('Site name: ' . $site_name);
  }
}
ऊपर दिए उदाहरण में हम दो प्रॉपर्टीज़ $configFactory और $loggerFactory घोषित करते हैं और उन्हें constructor में असाइन करते हैं। इस सेवा को मॉड्यूल की services YAML फाइल में भी परिभाषित करना होगा:
# example.services.yml
services:
  example.example_service:
    class: Drupal\example\ExampleService
    arguments:
      - '@config.factory'
      - '@logger.factory'
जब Drupal इस सेवा को instantiate करता है, तो वह सूचीबद्ध क्रम में constructor को आर्ग्युमेंट्स पास करता है।
पारंपरिक Controller क्लास उदाहरण
Drupal कंट्रोलर भी dependency injection का उपयोग कर सकते हैं। आमतौर पर एक कंट्रोलर ControllerBase को एक्सटेंड करता है और create() मेथड को परिभाषित करके कंटेनर इनजेक्शन लागू करता है। उदाहरण के लिए:
<?php
namespace Drupal\example\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
 * Example रूट्स के लिए कंट्रोलर।
 */
class ExampleController extends ControllerBase {
  /**
   * Entity type manager सेवा।
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;
  /**
   * String translation सेवा।
   *
   * @var \Drupal\Core\StringTranslation\TranslationInterface
   */
  protected $stringTranslation;
  /**
   * Constructor।
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager।
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   String translation सेवा।
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
    $this->entityTypeManager = $entity_type_manager;
    $this->stringTranslation = $string_translation;
  }
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new self(
      $container->get('entity_type.manager'),
      $container->get('string_translation')
    );
  }
  /**
   * एक साधारण पेज प्रतिक्रिया बनाता है।
   */
  public function build(): array {
    $node_count = $this->entityTypeManager->getStorage('node')->getQuery()->count()->execute();
    return [
      '#markup' => $this->t('There are @count nodes on the site.', ['@count' => $node_count]),
    ];
  }
}