Typed Entity
Proporciona clases wrapper tipadas para entidades de Drupal, permitiendo la encapsulación de lógica de negocio orientada a objetos y una mejor mantenibilidad del código.
typed_entity
Install
composer require 'drupal/typed_entity:^4.1'
composer require 'drupal/typed_entity:^4.0'
Overview
Typed Entity es un módulo orientado a desarrolladores que transforma la manera de trabajar con entidades de Drupal al envolverlas en clases PHP tipadas. En lugar de dispersar la lógica de negocio relacionada con entidades a través de implementaciones de hooks y código procedural, este módulo permite organizar el código en clases wrapper dedicadas que encapsulan toda la lógica de negocio para tipos de entidad y bundles específicos.
El módulo implementa el Repository Pattern combinado con el Wrapper Pattern, permitiendo a los desarrolladores crear plugins TypedRepository que gestionan el envoltorio y renderizado de entidades. Cada repositorio puede definir múltiples variantes de clases wrapper y variantes de renderer que se seleccionan según el contexto (como el view mode, valores de campo o condiciones personalizadas).
Los conceptos arquitectónicos clave incluyen: Wrapped Entities - clases PHP que envuelven entidades de Drupal y contienen todos los métodos de lógica de negocio; Typed Repositories - clases de plugins que gestionan el envoltorio de entidades, consultas y selección de renderers; Renderers - clases que encapsulan la lógica relacionada con el renderizado e integran con el pipeline de renderizado de Drupal a través de hooks; y Variant Negotiation - un sistema que selecciona la clase wrapper o renderer apropiada basándose en condiciones de contexto.
Este enfoque resulta en código más fácil de probar (a través de pruebas unitarias con dependencias mockeadas), más mantenible (la lógica de negocio está centralizada) y sigue las mejores prácticas orientadas a objetos. El módulo se integra perfectamente con el pipeline de renderizado de Drupal implementando hook_entity_view_alter, hook_preprocess, hook_entity_display_build_alter y hook_entity_build_defaults_alter.
Features
- Sistema de plugins TypedRepository para definir repositorios específicos de entidades con Attributes de PHP 8 o Annotations
- Clase base WrappedEntity para crear clases wrapper tipadas alrededor de entidades de Drupal con métodos de lógica de negocio
- Sistema TypedEntityRenderer para encapsular la lógica de renderizado de entidades en clases dedicadas
- Sistema de negociación de variantes que selecciona clases wrapper y renderer basándose en condiciones de contexto
- Condiciones de variante integradas: FieldValueVariantCondition (verificar valores de campo) y EmptyFieldVariantCondition (verificar campos vacíos)
- Servicio RepositoryManager para envolver entidades y encontrar repositorios apropiados
- Integración automática con el pipeline de renderizado de Drupal a través de hooks del core (entity_view_alter, preprocess, entity_display_build_alter, entity_build_defaults_alter)
- CacheableDependencyWrappedEntityTrait para fácil propagación de metadatos de caché desde entidades envueltas
- Soporte tanto para Attributes de PHP 8 como para Annotations legacy para descubrimiento de plugins
- Métodos helper wrapReference() y wrapReferences() para envolver entidades referenciadas
- Método createEntity() en repositorios para crear y envolver nuevas entidades en un solo paso
- Soporte para construcción de consultas a través del método getQuery() con filtrado automático de bundle
Use Cases
Encapsulación de lógica de negocio de entidades
Crear una clase WrappedEntity para centralizar toda la lógica de negocio para un tipo de entidad. Por ejemplo, un wrapper de User puede tener un método nickname() que extrae el nombre de usuario del email, o un wrapper de Article puede tener métodos para verificar restricciones de contenido. Esto mantiene la lógica de negocio testeable y mantenible en lugar de dispersa a través de hooks.
Renderizado de entidades según contexto
Crear clases TypedEntityRenderer para diferentes view modes. Cada renderer puede personalizar el preprocesamiento, añadir atributos o modificar el render array. El sistema selecciona automáticamente el renderer apropiado basándose en el view mode o condiciones personalizadas definidas en el método applies().
Manejo de entidades basado en variantes
Usar ClassWithVariants para definir múltiples clases wrapper para el mismo tipo de entidad/bundle. Por ejemplo, los artículos etiquetados con 'Baking' pueden usar un wrapper BakingArticle con métodos especializados, mientras que los artículos regulares usan el wrapper Article estándar. La selección de variantes ocurre automáticamente basándose en el método estático applies().
Control de acceso a nivel de repositorio
Implementar AccessibleInterface en tus clases TypedRepository o WrappedEntity para añadir lógica de acceso personalizada. Por ejemplo, denegar acceso a artículos cuando hay más de cierta cantidad publicados, o verificar el perfil del autor en busca de contenido inapropiado.
Envoltorio de referencias de entidades
Usar los métodos wrapReference() y wrapReferences() en tus clases WrappedEntity para envolver fácilmente entidades relacionadas. Esto mantiene el patrón typed entity a lo largo de tu código cuando trabajas con referencias de entidades.
Lógica de entidades testeable
El patrón wrapper hace que la lógica de entidades sea altamente testeable a través de pruebas unitarias. Puedes mockear la entidad y cualquier servicio inyectado, luego probar tus métodos de lógica de negocio en aislamiento sin necesidad de un bootstrap completo de Drupal o base de datos.
Tips
- Usa Attributes de PHP 8 (#[TypedRepository]) en lugar de Annotations para una sintaxis más limpia y mejor soporte del IDE
- Crea clases wrapper base para tipos de entidad (ej. NodeBase) y extiéndelas para wrappers específicos de bundle
- Implementa AccessibleInterface en wrappers o repositorios para integrar lógica de acceso personalizada con el sistema de acceso de Drupal
- Usa CacheableDependencyWrappedEntityTrait para propagar fácilmente metadatos de caché desde entidades envueltas
- Habilita el submódulo Typed Entity UI durante el desarrollo para visualizar tu repositorio y jerarquía de clases
- Los IDs de repositorio siguen el patrón 'entity_type_id.bundle' (ej. 'node.article') - los repositorios sin bundle cubren todos los bundles de ese tipo de entidad
- Limpia cachés después de crear nuevos plugins TypedRepository para que sean descubiertos
- El método applies() en wrappers y renderers debe ser rápido ya que puede ser llamado frecuentemente durante la negociación de variantes
- Usa la función helper typed_entity_repository_manager() en archivos .module para fácil acceso al servicio RepositoryManager
- Los renderers coinciden con view modes a través de la constante VIEW_MODE - sobrescribe applies() para lógica de coincidencia más compleja
Technical Details
Admin Pages 2
/admin/config/development/typed-entity
Página administrativa para explorar y entender las definiciones de repositorios typed entity. Muestra una tabla con búsqueda de todos los plugins TypedRepository registrados con su tipo de entidad, bundle, descripción e información de clase. Permite filtrar por ID de plugin y proporciona enlaces a páginas de exploración detallada para cada repositorio.
/admin/config/development/typed-entity/{typed_entity_id}
Página de exploración detallada que muestra información completa sobre un plugin TypedRepository específico incluyendo la clase del repositorio, su clase padre, interfaces implementadas, wrappers de entidad (con fallback y variantes), y renderers de entidad (con fallback y variantes). Muestra resúmenes de clases PHP con ubicaciones de archivo y definiciones de attributes.
Permissions 1
Hooks 5
hook_typed_repository_info_alter
Permite a los módulos alterar las definiciones de plugins de repositorios tipados antes de que sean utilizadas.
hook_entity_view_alter (used by module)
Typed Entity implementa este hook para llamar a TypedEntityRenderer::viewAlter() para entidades envueltas.
hook_preprocess (used by module)
Typed Entity implementa este hook para llamar a TypedEntityRenderer::preprocess() para entidades envueltas.
hook_entity_display_build_alter (used by module)
Typed Entity implementa este hook para llamar a TypedEntityRenderer::displayBuildAlter() para entidades envueltas.
hook_entity_build_defaults_alter (used by module)
Typed Entity implementa este hook para llamar a TypedEntityRenderer::buildDefaultsAlter() para entidades envueltas.
Troubleshooting 4
Limpia todas las cachés usando drush cr. Los plugins TypedRepository son descubiertos por el gestor de plugins y se almacenan en caché. Después de crear una nueva clase de plugin, las cachés deben limpiarse para que sea reconocida.
Verifica que tu clase variante implementa el método estático applies() correctamente y devuelve TRUE para el contexto apropiado. Asegúrate de que la variante esté listada en el array 'variants' de ClassWithVariants en tu attribute/annotation de TypedRepository.
Asegúrate de estar llamando $repository->wrap($entity) o usando el servicio RepositoryManager para envolver entidades. El acceso directo a la entidad evita el sistema de wrapper. Usa typed_entity_repository_manager()->wrap($entity) para obtener instancias envueltas.
Verifica que tu annotation/attribute de plugin TypedRepository tenga los valores correctos de entity_type_id y bundle. Comprueba que el ClassWithVariants de wrappers tenga una clase fallback válida que exista y extienda WrappedEntityBase.
Security Notes 3
- El módulo no proporciona control de acceso adicional más allá del sistema de permisos estándar de Drupal. El permiso 'explore typed entity classes' debe restringirse a administradores de confianza ya que expone información de la estructura interna de clases.
- Al implementar AccessibleInterface en wrappers o repositorios, asegúrate de que las decisiones de acceso se almacenen correctamente en caché y no filtren información sensible.
- El módulo de ejemplo (typed_entity_example) es solo para propósitos de demostración y no debe habilitarse en sitios de producción.