Часть 2: Сервис Контейнер

Это вторая часть статьи "Изучаем Drupal 8". В первой части мы посмотрели на общую структуру Drupal 8 и как он коррелируется с Symfony. Symfony и Drupal 8 состоят из некоторых компонентов. В этой части мы рассмотрим Сервис Контейнер и как он используется в Drupal 8. Это вжно знать, прежде чем начинать изучение маршрутизации(роутинга).

Symfony использует Сервис Контейнер, который применяется для эффективного управления сервисами в приложении. Эта концепция также известна под названием Иньекция Зависимости (Dependency Injection).

Этот Server Container представляет собой глобальный объект, который создается и содержится в ядре перед обработчиком запроса. Он может быть позже использован в коде для выбора Сервисов, так называемая ленивая загрузка на лету. Сервисы это глобальные объекты, которые могут использоваться для выполнения конкретных задач, таких как Сервис почтовой рассылки Mailer, или Сервис соединения с БД. Сервис соответствует ровно одному классу. Концепция Сервис Контейнеров очень важна, поскольку она содержит доступные Сервисы, "знает" об их отношениях(зависимостях) и конфигурациях, и даже создает их!

Зависимости и аргументы

Сервис может зависеть от других Сервисов. В документации Symfony приводится пример Сервиса NewsletterManager, который зависит от Сервиса Mailer для фактической отправки электронной почты. Эти зависимости также добавлны в Сервис Контейнер. Впоследствии, когда создается Сервис, его зависимости доставляются через аргументы в конструктор класса. Интерфейсы используются для определения того, какие методы зависимых Сервисов должны предоставлять, таким образом, чтобы реализация Сервиса могла быть заменена другим в случае необходимости.

Конфигурация

Сервисы в Сервис Контейнере может быть сконфигурирован несколькими путями: через код (известный как Расширения(Extensions)), с помощью XML, через YAML и т.д. Symfony использует комбинацию этих способов, но большая часть конфигурации Сервисов Ядра приходится на долю кода Бандлов Фреймворка. Сервисы Drupal отличаются от Симфони и базируются в основном на YAML-файлах. Используется конфигурация core.services.yml для всего, что связано с Ядром. Эта конфигурация расширяется с помощью дополнительных модулей и  ServiceProvider классов. Последние похожи на Расширения Symfony. Загрузка этих конфигураций обрабатывается Ядром Kernel Drupal.

YAML обеспечивает читаемый и гибкий синтаксис. Возьмем к примеру часть кода из core.services.yml:

services:
  ...
  router_listener:
    class: Symfony\Component\HttpKernel\EventListener\RouterListener
    tags:
      - { name: event_subscriber }
    arguments: ['@router', '@request_stack', '@router.request_context', NULL]
    ...
  router:
    class: Drupal\Core\Routing\AccessAwareRouter
    arguments: ['@router.no_access_checks', '@access_manager', '@current_user']
  router.dynamic:
    class: Symfony\Cmf\Component\Routing\DynamicRouter
    arguments: ['@router.request_context', '@router.matcher', '@url_generator']
    tags:
      - { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
  router.no_access_checks:
    class: Symfony\Cmf\Component\Routing\ChainRouter
    calls:
      - [setContext, ['@router.request_context']]
      - [add, ['@router.dynamic']]

Здесь определен Сервис router_listener. Соответствующий класс RouterListener необходим, чтобы иметь возможность загружать слушателя. Свойство Аргументы определяет то, что первый аргумент конструктора RouterListener (определяет совпадение адреса url или запроса) должен быть '@router', этот аргумент означает что Сервис имеет Service Id 'router'. Этот роутер тоже определен в конфигурации и имеет иной "класс", в отличии от используемого Symfony, как и было указано ранее.

Сервисы определенные в тегах

Метки или теги используются для загрузки Сервисов, определенных в тегах. На практике такой подход известен и используется как "система хуков"(hooky way) в Drupal. В приведенном ниже примере (node.services.yml) node модуль добавляет проверку прав доступа для 'add node' страницы добавления ноды, которая основана на паттерне Сервиса проверки доступа access_check, который в свою очередь используется после роутинга для поиска имеет ли пользователь права на доступ.

services:
  ...
  access_check.node.add:
    class: Drupal\node\Access\NodeAddAccessCheck
    arguments: ['@entity.manager']
    tags:
      - { name: access_check, applies_to: _node_add_access }
    ...

Compiler passes

Как Drupal понимает эти теги и что он с ними делает? Ну хорошо, Symfony имеет еще один из методов динамической конфигурации Сервис Контейнера: Compiler passes. Сервис Контейнер фактически "скомпилирован" непосредственно после создания его из статической конфигурации. В различных точках во время этой фазы, это позволяет реализовать объектам CompilerPassInterface для модификации конфигурации. В Drupal Сервис CoreServiceProvider регистрирует некоторые важные фазы компиляции, так RegisterAccessChecksPass, который пытается найти все Сервисы с тегами access_check (см. предыдущий пример) и добавляет его к AccessManager (с использованием директивы Сервис Контейнера addMethodCall). Затем, в фазе роутинга, Сервис AccessManager спрашивает о проверке прав доступа, и среди прочего, должен провести проверку NodeAddAccessCheck. Есть несколько других проходов компилятора в ядре Drupal, таких как для преобразования параметров роутинга к объектам и переводы строк. На практике у вас будет необходимость для использования этих маркированных тегами Сервисов в своих модулях для того, чтобы добавлять пользовательские проверки прав доступа и преобразований параметров пути запроса!

Замена Сервисов (Swapping services)

Сервис Контейнер дает возможность для гибкой конфигурации. Drupal 8 использует аргументы Сервисов альтернативным способом, вместо тех, которые использует Symfony для своих целей. Например, Drupal необходим другой механизм url роутинга, чем использует Symfony. Он выполняет это путем предоставления альтернативного сервиса 'router' (Symfony\Cmf\Component\Routing\ChainRouter) сравните с тем, что использует Symfony (Symfony\Bundle\FrameworkBundle\Routing\Router). Замена может быть произведена без необходимости менять строки кода в сервисе router_listener (Symfony\Component\HttpKernel\EventListener\RouterListener) делает это самостоятельно! Это стало возможным, потому что оба маршрутизатора реализуют RequestMatcherInterface, который является одним из требований к первому аргументу RouterListener.

Вывод

В этой части мы поговорили о компоненте Сервис Контейнер в Drupal 8. Теперь у вас должно быть лучшее представление о том, как Drupal 8 включает свои собственные сервисы вместо тех, что использует Symfony2 без необходимости менять строки кода. Это позволяет легко обновлять Symfony2 разработчиками ядра Drupal. При изучении Drupal 8 прийдется часто обращаться к конфигурации в файле core.services.yml чтобы выяснить какие сервисы используются.

В следующей части мы детальнее рассмотрим общий процесс управления в Drupal 8 и маршрутизацию.

Share this post

Leave a comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • To post pieces of code, surround them with <code>...</code> tags. For PHP code, you can use <?php ... ?>, which will also colour it based on syntax.
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.