Typed Entity
Drupalエンティティ用の型付きラッパークラスを提供し、オブジェクト指向によるビジネスロジックのカプセル化とコードの保守性向上を実現します。
typed_entity
インストール
composer require 'drupal/typed_entity:^4.1'
composer require 'drupal/typed_entity:^4.0'
概要
Typed Entityは、Drupalエンティティを型付きPHPクラスでラップすることで、エンティティの扱い方を根本から変える開発者向けモジュールです。エンティティ関連のビジネスロジックをhook実装や手続き型コードに散在させる代わりに、特定のエンティティタイプやバンドル用のビジネスロジックをすべてカプセル化した専用のラッパークラスにコードを整理できます。
このモジュールはRepositoryパターンとWrapperパターンを組み合わせて実装しており、開発者はエンティティのラッピングとレンダリングを管理するTypedRepositoryプラグインを作成できます。各リポジトリは、コンテキスト(ビューモード、フィールド値、カスタム条件など)に基づいて選択される複数のラッパークラスのバリアントとレンダラーのバリアントを定義できます。
主要なアーキテクチャ概念には以下が含まれます:Wrapped Entities - Drupalエンティティをラップし、すべてのビジネスロジックメソッドを含むPHPクラス、Typed Repositories - エンティティのラッピング、クエリ、レンダラー選択を管理するプラグインクラス、Renderers - レンダリング関連のロジックをカプセル化し、hookを通じてDrupalのレンダーパイプラインと統合するクラス、Variant Negotiation - コンテキスト条件に基づいて適切なラッパーまたはレンダラークラスを選択するシステム。
このアプローチにより、テストしやすく(モック化された依存関係でユニットテスト可能)、保守しやすく(ビジネスロジックが集約される)、オブジェクト指向のベストプラクティスに従ったコードが実現します。このモジュールはhook_entity_view_alter、hook_preprocess、hook_entity_display_build_alter、hook_entity_build_defaults_alterを実装することで、Drupalのレンダーパイプラインとシームレスに統合します。
Features
- PHP 8 AttributesまたはAnnotationsでエンティティ固有のリポジトリを定義するTypedRepositoryプラグインシステム
- ビジネスロジックメソッドを持つDrupalエンティティ周りの型付きラッパークラスを作成するWrappedEntity基底クラス
- エンティティのレンダリングロジックを専用クラスにカプセル化するTypedEntityRendererシステム
- コンテキスト条件に基づいてラッパーおよびレンダラークラスを選択するバリアントネゴシエーションシステム
- 組み込みバリアント条件:FieldValueVariantCondition(フィールド値をチェック)とEmptyFieldVariantCondition(空のフィールドをチェック)
- エンティティのラッピングと適切なリポジトリの検索を行うRepositoryManagerサービス
- コアhook(entity_view_alter、preprocess、entity_display_build_alter、entity_build_defaults_alter)を通じたDrupalのレンダーパイプラインとの自動統合
- ラップされたエンティティからのキャッシュメタデータの伝播を容易にするCacheableDependencyWrappedEntityTrait
- プラグイン検出のためのPHP 8 Attributesとレガシーのannotationsの両方をサポート
- 参照されたエンティティをラップするためのwrapReference()およびwrapReferences()ヘルパーメソッド
- リポジトリ上でエンティティの作成とラッピングを一度に行うcreateEntity()メソッド
- バンドルの自動フィルタリング付きgetQuery()メソッドによるクエリビルドのサポート
Use Cases
エンティティビジネスロジックのカプセル化
WrappedEntityクラスを作成して、エンティティタイプのすべてのビジネスロジックを集約します。例えば、Userラッパーはメールからユーザー名を抽出するnickname()メソッドを持つことができ、Articleラッパーはコンテンツ制限をチェックするメソッドを持つことができます。これにより、ビジネスロジックがhookに散在する代わりに、テスト可能で保守しやすい状態に保たれます。
コンテキスト対応のエンティティレンダリング
異なるビューモード用のTypedEntityRendererクラスを作成します。各レンダラーはプリプロセス、属性の追加、render arrayの変更をカスタマイズできます。システムはビューモードまたはapplies()メソッドで定義されたカスタム条件に基づいて、適切なレンダラーを自動的に選択します。
バリアントベースのエンティティ処理
ClassWithVariantsを使用して、同じエンティティタイプ/バンドルに対して複数のラッパークラスを定義します。例えば、「Baking」タグが付いた記事は専用のメソッドを持つBakingArticleラッパーを使用し、通常の記事は標準のArticleラッパーを使用できます。バリアント選択はapplies()静的メソッドに基づいて自動的に行われます。
リポジトリレベルのアクセス制御
TypedRepositoryまたはWrappedEntityクラスにAccessibleInterfaceを実装して、カスタムアクセスロジックを追加します。例えば、一定数以上の記事が公開されている場合にアクセスを拒否したり、著者のプロフィールに不適切なコンテンツがないかチェックしたりできます。
エンティティリファレンスのラッピング
WrappedEntityクラスでwrapReference()およびwrapReferences()メソッドを使用して、関連エンティティを簡単にラップできます。これにより、エンティティリファレンスを扱う際にコードベース全体でtyped entityパターンを維持できます。
テスト可能なエンティティロジック
ラッパーパターンにより、エンティティロジックはユニットテストで高度にテスト可能になります。エンティティと注入されたサービスをモック化し、Drupalの完全なブートストラップやデータベースなしで、ビジネスロジックメソッドを分離してテストできます。
Tips
- よりクリーンな構文とより良いIDEサポートのために、AnnotationsよりもPHP 8 Attributes(#[TypedRepository])を使用してください
- エンティティタイプ用の基底ラッパークラス(例:NodeBase)を作成し、バンドル固有のラッパーで拡張してください
- ラッパーまたはリポジトリにAccessibleInterfaceを実装して、カスタムアクセスロジックをDrupalのアクセスシステムと統合してください
- ラップされたエンティティからのキャッシュメタデータの伝播を容易にするために、CacheableDependencyWrappedEntityTraitを使用してください
- 開発中はTyped Entity UIサブモジュールを有効にして、リポジトリとクラス階層を視覚化してください
- リポジトリIDは「entity_type_id.bundle」パターン(例:「node.article」)に従います - バンドルのないリポジトリはそのエンティティタイプのすべてのバンドルをカバーします
- 新しいTypedRepositoryプラグインを作成した後、検出されるようにキャッシュをクリアしてください
- バリアントネゴシエーション中に頻繁に呼び出される可能性があるため、ラッパーとレンダラーのapplies()メソッドは高速である必要があります
- .moduleファイルでRepositoryManagerサービスに簡単にアクセスするために、typed_entity_repository_manager()ヘルパー関数を使用してください
- レンダラーはVIEW_MODE定数を通じてビューモードと照合します - より複雑なマッチングロジックにはapplies()をオーバーライドしてください
Technical Details
Admin Pages 2
/admin/config/development/typed-entity
typed entity repository定義を確認・理解するための管理ページ。登録されているすべてのTypedRepositoryプラグインの検索可能なテーブルを表示し、エンティティタイプ、バンドル、説明、クラス情報が含まれます。プラグインIDでのフィルタリングが可能で、各リポジトリの詳細確認ページへのリンクを提供します。
/admin/config/development/typed-entity/{typed_entity_id}
リポジトリクラス、親クラス、実装されたインターフェース、エンティティラッパー(フォールバックとバリアント付き)、エンティティレンダラー(フォールバックとバリアント付き)を含む、特定のTypedRepositoryプラグインの完全な情報を表示する詳細確認ページ。ファイルの場所と属性定義を含むPHPクラスサマリーを表示します。
権限 1
Hooks 5
hook_typed_repository_info_alter
モジュールがtyped repositoryプラグイン定義を使用前に変更できるようにします。
hook_entity_view_alter (used by module)
Typed Entityはこのhookを実装して、ラップされたエンティティに対してTypedEntityRenderer::viewAlter()を呼び出します。
hook_preprocess (used by module)
Typed Entityはこのhookを実装して、ラップされたエンティティに対してTypedEntityRenderer::preprocess()を呼び出します。
hook_entity_display_build_alter (used by module)
Typed Entityはこのhookを実装して、ラップされたエンティティに対してTypedEntityRenderer::displayBuildAlter()を呼び出します。
hook_entity_build_defaults_alter (used by module)
Typed Entityはこのhookを実装して、ラップされたエンティティに対してTypedEntityRenderer::buildDefaultsAlter()を呼び出します。
Troubleshooting 4
drush crを使用してすべてのキャッシュをクリアしてください。TypedRepositoryプラグインはプラグインマネージャーによって検出され、キャッシュされます。新しいプラグインクラスを作成した後、認識されるようにキャッシュをクリアする必要があります。
バリアントクラスがapplies()静的メソッドを正しく実装し、適切なコンテキストでTRUEを返すことを確認してください。バリアントがTypedRepository属性/annotationのClassWithVariantsの「variants」配列にリストされていることを確認してください。
$repository->wrap($entity)を呼び出すか、RepositoryManagerサービスを使用してエンティティをラップしていることを確認してください。直接のエンティティアクセスはラッパーシステムをバイパスします。ラップされたインスタンスを取得するにはtyped_entity_repository_manager()->wrap($entity)を使用してください。
TypedRepositoryプラグインのannotation/attributeに正しいentity_type_idとbundleの値があることを確認してください。wrappers ClassWithVariantsに、存在してWrappedEntityBaseを拡張する有効なフォールバッククラスがあることを確認してください。
Security Notes 3
- このモジュールはDrupalの標準的なパーミッションシステム以外の追加のアクセス制御を提供しません。「explore typed entity classes」パーミッションは内部クラス構造情報を公開するため、信頼できる管理者に制限する必要があります。
- ラッパーまたはリポジトリにAccessibleInterfaceを実装する場合、アクセス判定が適切にキャッシュされ、機密情報が漏洩しないことを確認してください。
- サンプルモジュール(typed_entity_example)はデモンストレーション目的のみであり、本番サイトでは有効にしないでください。