LoginSignup
1
1

More than 3 years have passed since last update.

Drupalで既存のサービスを上書きする

Last updated at Posted at 2021-02-24

Drupalのサービスとは

Drupalではカスタマイズ性を確保するためにD8から「サービス」という仕組み導入しています。
サービスはざっくりいうと、任意のクラスにサービス名という「名札」を付け、クラス名で直接呼び出すのを防ぐ仕組みです。

例えばDrupalのコアにはDrupal\Core\Language\LanguageManagerというクラスがあります。

このクラスを呼び出すには通常のPHPのやり方だと

use Drupal\Core\Language\LanguageManager;
$language_manager = new LanguageManager();

と書く必要がありますが、このクラスは/core/core.services.yml


language_manager:
    class: Drupal\Core\Language\LanguageManager
    arguments: ['@language.default']

のようにサービスとして登録されているので、

\Drupal::service('language_manager')

という風に呼び出すこともできます。コアやコントリビュートモジュールではほとんどの場合後者が使用されています。

サービスのメリット

これにはどのようなメリットがあるのでしょうか。

例えば、Drupalサイト開発中にLanguageManager内の処理を一部だけ変更する必要が出てきたとします。
もし、コアやコントリビュートモジュールでLanguageManagerを直接呼び出していた場合、処理を変更するのは困難になります。
単純な方法としては

  • LanguageManagerクラスそのものを上書きする
  • LanguageManagerに修正を加えたMyLanguageManagerを作り、呼び出し側でそちらを呼び出すように変更する

等があると思いますが、どちらにしてもコアやコントリビュートの一部に手を加えないといけません。画像は後者の方法で書き換える場合のイメージです。(白がカスタムモジュール、青はコアかコントリビュート。)コアやコントリビュートをがっつり書き換えてしまってますね。
スクリーンショット 2021-02-24 23.28.05.png

逆にコアやコントリビュートモジュールのすべてがlanguage_managerサービスを介してLanguageManagerを呼び出していた場合、ServiceProviderというクラスを書くことでlanguage_managerの紐付け対象をMyLanguageManagerに差し替えることができます。こちらはすべてカスタムコードで完結していますね。

スクリーンショット 2021-02-25 0.59.55.png

つまりサービス = クラスそのものやクラスの呼び出し元を修正することなくオーバーライドできるようにする仕組みということですね。

開発中に修正したいコアやコントリビュートのコードが出てきた場合は、まずはその箇所がサービスとして登録されているかどうかを確認するのがおすすめです。(登録されてたらラッキー!)

サービスを上書きする方法

先程説明したように、サービスを上書きするには

  • MyLanguageManager
  • MyModuleServiceProvider

という2つのクラスを書く必要があります。

MyLanguageManager

こちらは既存クラスを上書きするためのクラスです。

  • クラス名は適当でOK
  • ディレクトリも多分適当でいい(既存クラスのディレクトリを真似られる場合はその方がベター)

namespace Drupal\my_module;

use Drupal\Core\Language\LanguageManager;

class MyLanguageManager extends LanguageManager {

  // 上書きしたいメソッドだけ上書き.
  function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE) {
    // 処理
  }

}

MyModuleServiceProvider

こちらはサービスクラスの切り替えを指示するためのクラスです。

  • クラス名は[モジュール名]ServiceProviderの形式にする
  • ディレクトリはmy_module/src/MyModuleServiceProvider.php
  • ServiceProviderBaseを継承する
namespace Drupal\my_module;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;

/**
 * Language Managerクラスのサービスを上書きする.
 */
class MyModuleServiceProvider extends ServiceProviderBase {

  /**
   * {@inheritdoc}
   */
  public function alter(ContainerBuilder $container) {
    if ($container->hasDefinition('language_manager')) {
      $definition = $container->getDefinition('language_manager');
      $definition->setClass('Drupal\my_module\MyLanguageManager')
    }
  }
}

元のサービスは

arguments: ['@language.default']

の部分でlanguage.defaultという他のサービスを引数にしてますが、そちらは何も明示しなくても勝手に引き継がれるようです。

というわけで、Drupalの既存のサービスを上書きする方法でした。

参考: https://www.drupal.org/docs/drupal-apis/services-and-dependency-injection/altering-existing-services-providing-dynamic

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1