logo

Լրացուցիչ Բլոկների Տեսակներ (EBT) - Դասավորության Կառուցողի նոր փորձառություն❗

Լրացուցիչ Բլոկների Տեսակներ (EBT) - ձևավորված, կարգավորելի բլոկների տեսակներ՝ սլայդշոուներ, ներդիրներ, քարտեր, բացվող ցանկեր և շատ ուրիշներ։ Ներառված կարգավորումներ՝ ֆոնի, DOM տուփի, JavaScript փլագինների համար։ Փորձեք դասավորությունների կառուցման ապագան արդեն այսօր։

EBT մոդուլների ցուցադրական տարբերակներ Ներբեռնել EBT մոդուլները

❗Լրացուցիչ Պարբերությունների Տեսակներ (EPT) - Պարբերությունների նոր փորձառություն

Լրացուցիչ պարբերության տեսակներ (EPT) - անալոգիական պարբերության վրա հիմնված մոդուլների հավաքակազմ։

EPT մոդուլների ցուցադրական տարբերակներ Ներբեռնել EPT մոդուլները

Scroll

Drupal-ի պատվերով մոդուլներում PHP-ի կոնստրուկտորի հատկությունների առաջխաղացման օգտագործումը

13/06/2025, by Ivan

Menu

PHP 8-ը ներկայացրեց կոնստրուկտորի հատկությունների առաջխաղացումը (constructor property promotion)՝ մի գործառույթ, որը պարզեցնում է դասի հատկությունների հայտարարումն ու վերագրման գործընթացը՝ թույլ տալով հատկությունները հայտարարել և ինիցիալիզացնել հենց կոնստրուկտորի հայտարարության մեջ։ Այս ձեռնարկում կցուցադրենք, թե ինչպես օգտագործել կոնստրուկտորի հատկությունների առաջխաղացումը Drupal-ի պատվերով մոդուլներում (որոնք պահանջում են PHP 8.0 և բարձր), մասնավորապես՝ կախվածությունների ներարկումը (dependency injection) պարզեցնելու համար ծառայություններում և վերահսկիչներում (controllers)։ Մենք կհամեմատենք Drupal-ի ավանդական մոտեցումը (որը օգտագործվում էր PHP 7-ում և Drupal 9-ի նախնական տարբերակներում) և ժամանակակից, PHP 8+ տարբերակը՝ երկուսի համար լրիվ կոդի օրինակներով։ Վերջում դուք կտեսնեք, թե ինչպես է այս ժամանակակից շարահյուսությունը նվազեցնում boilerplate-ը, դարձնում կոդը ավելի հստակ և համապատասխանում ներկայիս լավագույն փորձերին։

Drupal 10-ը (որը պահանջում է PHP 8.1+) արդեն սկսել է կիրառել այս ժամանակակից PHP հնարավորությունները հիմնականում, ուստի խորհուրդ է տրվում, որ պատվերով մոդուլների մշակողները նույնպես կիրառեն դրանք։ Եկեք նախ դիտարկենք Drupal-ում կախվածությունների ավանդական ներարկման ձևաչափը, ապա վերափոխենք այն՝ օգտագործելով կոնստրուկտորի հատկությունների առաջխաղացումը։

Կախվածությունների ավանդական ներարկում Drupal-ում (Pre-PHP 8)

Drupal-ի ծառայություններում և վերահսկիչներում ավանդական ձևաչափը կախվածությունների ներարկման համար ունի երեք քայլ․

  1. Յուրաքանչյուր կախվածությունը հայտարարեք որպես դասի հատկություն (հիմնականում protected), հավելելով համապատասխան docblock:

  2. Կախվածության տիպը ավելացրեք կոնստրուկտորի պարամետրերում և այն վերագրեք դասի հատկությանը կոնստրուկտորի մարմնում։

  3. Վերահսկիչների (և որոշ plugin-ների) համար անհրաժեշտ է իրականացնել ստատիկ create(ContainerInterface $container) մեթոդը՝ ծառայությունները ծառայությունների կոնտեյներով ստանալու և օբյեկտը ինիցիալիզացնելու համար։

Այս ամենը բերում է բավականին շատ boilerplate կոդի։ Օրինակ, եթե ունեք պարզ ծառայություն, որին անհրաժեշտ է configuration factory և logger factory, ավանդական տարբերակով գրեք այսպես․

Ծառայության դասի ավանդական օրինակ

<?php

namespace Drupal\example;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

/**
 * Օրինակ ծառայություն, որը լոգավորում է կայքի անունը։
 */
class ExampleService {
  /**
   * Կոնֆիգուրացիայի ֆաբրիկայի ծառայությունը։
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Լոգերի ֆաբրիկայի ծառայությունը։
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $loggerFactory;

  /**
   * ExampleService օբյեկտի կոնստրուկտոր։
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Կոնֆիգուրացիայի ֆաբրիկա։
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   Լոգերի ֆաբրիկա։
   */
  public function __construct(ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory) {
    // Պահպանել ներարկված ծառայությունները։
    $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);
  }
}

Վերևում մենք հայտարարում ենք երկու հատկություն՝ $configFactory և $loggerFactory, և դրանք վերագրում կոնստրուկտորի մեջ։ Համապատասխան ծառայությունը պետք է սահմանվի մոդուլի services YAML-ում (նույնպես նշելով պահանջվող ծառայությունները որպես արգումենտներ), օրինակ․

# example.services.yml
services:
  example.example_service:
    class: Drupal\example\ExampleService
    arguments:
      - '@config.factory'
      - '@logger.factory'

Երբ Drupal-ը ինիցիալիզացնում է այս ծառայությունը, նշված արգումենտները փոխանցում է կոնստրուկտորին նույն հերթականությամբ։

Վերահսկիչի դասի ավանդական օրինակ

Drupal-ի վերահսկիչները նույնպես կարող են օգտագործել կախվածությունների ներարկում։ Սովորաբար վերահսկիչի դասը ընդլայնում է ControllerBase-ը (հարմարության մեթոդների համար) և իրականացնում կոնտեյների ներարկում՝ create() մեթոդի միջոցով։ 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 {
  /**
   * Էակների տիպի մենեջերի ծառայությունը։
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Տողերի թարգմանության ծառայությունը։
   *
   * @var \Drupal\Core\StringTranslation\TranslationInterface
   */
  protected $stringTranslation;

  /**
   * ExampleController օբյեկտի կոնստրուկտոր։
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Էակների տիպի մենեջեր։
   * @param \Drupal\Core\StringTranslation\TranslationInterface $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('Կայքում կա @count հանգույց։', ['@count' => $node_count]),
    ];
  }
}

Կոնստրուկտորի հատկությունների առաջխաղացման օգտագործումը (PHP 8+) Drupal-ում

Կոնստրուկտորի հատկությունների առաջխաղացումը պարզեցնում է այս օրինակը՝ թույլ տալով հատկությունները հայտարարել և վերագրել անմիջապես կոնստրուկտորի հայտարարության մեջ։ PHP 8-ում կարող եք կոնստրուկտորի պարամետրերին ավելացնել հասանելիության մոդիֆիկատորներ (օրինակ՝ protected, private, կամ public), և PHP-ն ինքնաբերաբար կհայտարարի ու կվերագրի այդ հատկությունները։ Սա նշանակում է, որ այլևս հարկավոր չէ առանձին հայտարարել հատկությունը կամ կոնստրուկտորի մարմնում կատարեք վերագրում․ PHP-ն դա անում է ձեզ համար։

Կարևորը, սա շարահյուսական հարմարություն է (syntactic sugar)։ Դա չի փոխում Drupal-ի կախվածությունների ներարկման մեխանիզմը՝ պարզապես նվազեցնում է ձեր գրած կոդի քանակը։ Դուք դեռ շարունակում եք ծառայությունները հայտարարելու YAML-ում (կամ թողնում եք Drupal-ին autowire անել), իսկ վերահսկիչների համար՝ դեռ պետք է կիրառեք create() ֆաբրիկա-մեթոդը (եթե վերահսկիչը չի գրանցվել որպես ծառայություն)։ Տարբերությունն արտահայտվում է միայն դասի կոդի շարահյուսության մեջ։ Արդյունքում ստացվում է շատ ավելի քիչ boilerplate, ինչի օրինակ է նաև Drupal core-ում համապատասխան փոփոխությունը, երբ տասնյակ տողերի հայտարարումները և վերագրումները կրճատվեցին կոնստրուկտորի մի քանի տողով։

Եկեք վերափոխենք մեր օրինակները՝ կիրառելով կոնստրուկտորի հատկությունների առաջխաղացումը։

Ծառայության դաս՝ կոնստրուկտորի հատկությունների առաջխաղացմամբ

Ահա ExampleService դասի տարբերակը՝ օգտագործելով PHP 8-ի առաջխաղացված հատկությունների շարահյուսությունը․

<?php

namespace Drupal\example;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

/**
 * Օրինակ ծառայություն, որը լոգավորում է կայքի անունը (առաջխաղացված հատկություններով)։
 */
class ExampleService {
  /**
   * ExampleService օբյեկտի կոնստրուկտոր՝ ներարկված ծառայություններով։
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Կոնֆիգուրացիայի ֆաբրիկա։
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerFactory
   *   Լոգերի ֆաբրիկա։
   */
  public function __construct(
    protected ConfigFactoryInterface $configFactory,
    protected LoggerChannelFactoryInterface $loggerFactory
  ) {
    // Մարմին պետք չէ, հատկությունները ավտոմատ վերագրվում են։
  }

  /**
   * Օրինակ գործողություն՝ կայքի անունը լոգավորել։
   */
  public function logSiteName(): void {
    $site_name = $this->configFactory->get('system.site')->get('name');
    $this->loggerFactory->get('example')->info('Կայքի անունը՝ ' . $site_name);
  }
}

Վերահսկիչի դաս՝ կոնստրուկտորի հատկությունների առաջխաղացմամբ

Այժմ տեսնենք ExampleController-ի տարբերակը՝ առաջխաղացված հատկություններով․

<?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 վերահսկիչ՝ առաջխաղացված հատկություններով։
 */
final class ExampleController extends ControllerBase {
  /**
   * ExampleController-ի կոնստրուկտոր։
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Էակների տիպի մենեջեր։
   * @param \Drupal\Core\StringTranslation\TranslationInterface $stringTranslation
   *   Տողերի թարգմանության ծառայություն։
   */
  public function __construct(
    private EntityTypeManagerInterface $entityTypeManager,
    private TranslationInterface $stringTranslation
  ) {
    // Մարմին պետք չէ, հատկությունները ավտոմատ վերագրվում են։
  }

  /**
   * {@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('Կայքում կա @count հանգույց։', ['@count' => $node_count]),
    ];
  }
}

Կոնստրուկտորի հատկությունների առաջխաղացման առավելությունները

Կոնստրուկտորի հատկությունների առաջխաղացումը ձեր Drupal դասերում ունի մի քանի կարևոր առավելություններ․

  • Boilerplate-ի նվազեցում․ Գրավում եք էականորեն ավելի քիչ կոդ։ Արձանագրությունները և վերագրումները հեռացվում են, ինչը հատկապես օգտակար է բազմաթիվ կախվածություններ ունեցող դասերի դեպքում։ Սա մոդուլները դարձնում է ավելի մաքուր և հեշտ պահպանվող։

  • Ավելի հստակ և հակիրճ կոդ․ Դասի կախվածությունները ամբողջությամբ տեսանելի են մեկ վայրում՝ կոնստրուկտորի հայտարարության մեջ, այլ ոչ թե բաշխված՝ հատկությունների հայտարարությունների և կոնստրուկտորի մարմնի մեջ։ Սա բարելավում է ընթեռնելիությունը և ակնհայտ է դարձնում, թե ինչ ծառայություններ են անհրաժեշտ։

  • Պակաս docblock-ներ․ Քանի որ հատկությունները կոնստրուկտորում հայտարարված են տիպերով, շատ դեպքերում կարելի է բաց թողնել ավելորդ @var և @param դոկբլոկները (եթե նպատակը ակնհայտ է անուններից)։ Կոդն ինքնին փաստաթղթավորող է։ Կարող եք փաստաթղթավորել միայն ոչ ակնհայտ բաները։

  • Ժամանակակից PHP շարահյուսություն․ Կոնստրուկտորի առաջխաղացումն օգտագործելը ձեր կոդը դարձնում է արդի PHP-ի լավագույն փորձերին համապատասխան։ Drupal 10+ միջուկը նույնպես օգտագործում է այս շարահյուսությունը, ուստի պատվերով մոդուլները այսպես գրելը ապահովում է հետևողականություն հիմնական և համայնքային օրինակների հետ։ Բացի այդ, պատրաստում է ձեր կոդային բազան հետագա թարմացումների համար (օրինակ՝ PHP 8.1+—ի readonly հատկությունը կարող է ավելացնել կախվածությունների փոփոխություն բացառելու համար)։

Արտադրողականությունն ու գործառույթը մնում են նույնը ինչ ավանդական ներարկման դեպքում՝ առաջխաղացումը պարզապես լեզվական հարմարություն է։ Դուք շարունակում եք լիարժեք տիպային հատկությունները օգտագործել դասի ցանկացած հատվածում (օր.՝ $this->entityTypeManager վերահսկիչի օրինակով)։ Ի վերջո, արդյունքը նույնն է, ուղղակի ստացվում է շատ ավելի հակիրճ։

Եզրակացություն

Կոնստրուկտորի հատկությունների առաջխաղացումը՝ որպես PHP 8-ի պարզ, բայց հզոր հնարավորութուն, կարող եք կիրառել Drupal-ի պատվերով մոդուլներում՝ նվազեցնելու և պարզեցնելու կոդը։ Այն թույլ է տալիս կենտրոնանալ դասի իրական գործառույթի վրա՝ առանց խանգարվելու կախվածությունների wiring-ից։ Ցույց տվեցինք, թե ինչպես կարող եք վերափոխել ծառայության և վերահսկիչի դասերը առաջխաղացված հատկություններով և համեմատեցինք ավանդական տարբերակների հետ։ Արդյունքում ստանում եք առավել հակիրճ և պահպանելի կոդ՝ առանց հստակության կամ ֆունկցիոնալի զիջման։ Քանի որ Drupal-ը շարժվում է ժամանակակից PHP-ի ուղղությամբ, կոնստրուկտորի առաջխաղացման կիրառումը կօգնի պահպանել ձեր կոդը մաքուր, հստակ և լավագույն փորձերին համապատասխան։ Գործածեք ժամանակակից շարահյուսությունը, որպեսզի ձեր Drupal-ի զարգացումը դառնա թե՛ հեշտ, թե՛ էլեգանտ։