Comment puis-je modifier la classe de type d'entité?

9

Dans Drupal 8, vous pouvez charger une entité avec:

$node = \Drupal::entityManager()->getStorage('node')->load(123);

Cela recherche les définitions d'entité et constate que le nœud est défini par Drupal \ node \ Entity \ Node - donc (je suppose) Drupal \ node \ NodeStorage instanciera une nouvelle instance Drupal \ node \ Entity \ Node .

Ce que j'aimerais réaliser, c'est sous- classer Drupal \ node \ Entity \ Node et pouvoir instancier cette sous-classe quand c'est approprié. Par exemple, si j'ai un article sur un ensemble de nœuds, il y aurait une classe:

namespace Drupal\my_module\Entity\Article;
class Article extends Drupal\node\Entity\Node {
}

Et j'appellerais:

$node = \Drupal::entityManager()->getStorage('node_article')->load(123);

Et le retour serait ma Articlesous - classe.

Je peux y parvenir en créant un nouveau type d'entité et en le reliant à une autre définition d'entité existante, par exemple l'exemple d'article de nœud serait cette classe:

namespace Drupal\my_module\Entity;
use Drupal\node\Entity\Node;
/**
 * @ContentEntityType(
 *   id = "node_article",
 *   label = @Translation("Content"),
 *   bundle_label = @Translation("Content type"),
 *   handlers = {
 *     "storage" = "Drupal\node\NodeStorage",
 *     "storage_schema" = "Drupal\node\NodeStorageSchema",
 *     "view_builder" = "Drupal\node\NodeViewBuilder",
 *     "access" = "Drupal\node\NodeAccessControlHandler",
 *     "views_data" = "Drupal\node\NodeViewsData",
 *     "form" = {
 *       "default" = "Drupal\node\NodeForm",
 *       "delete" = "Drupal\node\Form\NodeDeleteForm",
 *       "edit" = "Drupal\node\NodeForm"
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\node\Entity\NodeRouteProvider",
 *     },
 *     "list_builder" = "Drupal\node\NodeListBuilder",
 *     "translation" = "Drupal\node\NodeTranslationHandler"
 *   },
 *   base_table = "node",
 *   data_table = "node_field_data",
 *   revision_table = "node_revision",
 *   revision_data_table = "node_field_revision",
 *   translatable = TRUE,
 *   list_cache_contexts = { "user.node_grants:view" },
 *   entity_keys = {
 *     "id" = "nid",
 *     "revision" = "vid",
 *     "bundle" = "type",
 *     "label" = "title",
 *     "langcode" = "langcode",
 *     "uuid" = "uuid",
 *     "status" = "status",
 *     "uid" = "uid",
 *   },
 *   bundle_entity_type = "node_type",
 *   field_ui_base_route = "entity.node_type.edit_form",
 *   common_reference_target = TRUE,
 *   permission_granularity = "bundle",
 *   links = {
 *     "canonical" = "/node/{node}",
 *     "delete-form" = "/node/{node}/delete",
 *     "edit-form" = "/node/{node}/edit",
 *     "version-history" = "/node/{node}/revisions",
 *     "revision" = "/node/{node}/revisions/{node_revision}/view",
 *   }
 * )
 */
class Article extends Node { }

// Results my Article sub type.
$node = \Drupal::entityManager()->getStorage('node_article')->load(123);

Cela fonctionne bien (autant que je peux voir); cependant, ça sent. Il ajoute un nouveau type d'entité, ce qui n'est pas vrai et pourrait entraîner d'autres problèmes à l'avenir.

Comment définir une sous-classe pour un ensemble d'entités afin que le chargement de l'entité renvoie un objet de cette classe?

itarato
la source
1
Je ne suis pas sûr que vous puissiez fournir une classe d'entité différente par bundle; vous pouvez utiliser hook_entity_type_alter()pour effectuer le changement plus proprement, mais je ne sais pas comment vous limiteriez cela à un ensemble spécifique
Clive
Merci Clive - ça a l'air d'un crochet prometteur pour enquêter!
itarato

Réponses:

10

Créez une nouvelle classe dans votre module qui s'étend \Drupal\node\Entity\Node.

use Drupal\node\Entity\Node as BaseNode;

class MyNode extends BaseNode {
}

Mettre en œuvre hook_entity_type_build().

use Drupal\Core\Entity\EntityTypeInterface;

/**
 * @param EntityTypeInterface[] $entity_types
 */
function my_module_entity_type_build(&$entity_types) {
  if (isset($entity_types['node'])) {
    $entity_types['node']->setClass('Drupal\my_module\Entity\MyNode');
  }
}

N'oubliez pas de reconstruire le cache.

Cela fonctionne très bien lors du chargement des nœuds via le service de gestionnaire de type d'entité et le stockage des nœuds. Cela fonctionne même lorsque vous utilisez simplement Drupal\node\Entity\Node::load($nid)grâce au fait que cette load()fonction est juste un wrapper statique pour l'appel de service du gestionnaire de type d'entité fourni par la Entityclasse qui est étendue à partir de la Nodeclasse.

// Part of Entity class for reference
abstract class Entity implements EntityInterface {
  /**
   * Loads an entity.
   *
   * @param mixed $id
   *   The id of the entity to load.
   *
   * @return static
   *   The entity object or NULL if there is no entity with the given ID.
   */
  public static function load($id) {
    $entity_manager = \Drupal::entityManager();
    return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->load($id);
  }
}

Cela fonctionne également très bien avec la fonction qui sera bientôt supprimée entity_load_multiple(), donc je suppose que cela couvre tous les cas d'utilisation standard pour le chargement de nœuds.

Bien sûr, si votre module fait cela et qu'un autre module fait de même, vous aurez un problème, mais je suppose que ce n'est pas un scénario courant, et cela n'a de sens que pour des cas d'utilisation très spécifiques.

SiliconMind
la source
2
Désolé mais non :) La question était d'avoir une classe différente par paquet . Vous changez la classe pour tous les bundles du nœud de type d'entité. Ce n'est pas pareil.
Berdir
@Berdir, votre droite :( ... Avoir des classes par bundle signifie qu'un stockage d'entité pour le nœud devrait également être étendu afin que ses méthodes de chargement puissent être remplacées pour produire celles par classe de bundle. Ce qui est fondamentalement un énorme casse-tête.
SiliconMind
1
Ouais. drupal.org/node/2570593 est l'un des problèmes auxquels j'ai fait référence, mais j'ai oublié de créer un lien dans ma réponse.
Berdir
J'ai implémenté hook_entity_type_alter pour définir une classe personnalisée. Ça marche aussi.
Yenya
Lorsque j'essaie cette méthode, j'obtiens un 'Drupal \ Component \ Plugin \ Exception \ PluginNotFoundException: le type d'entité "node" n'existe pas.' message. J'utilise la fonction ablecore_entity_type_build dans mon fichier .module. Et j'ai mon AbleNode.php dans / src / Entity / AbleNode / folder
Matt
2

J'ai rencontré le même problème et j'ai décidé de créer un module qui modifie la classe de type d'entité des entités Drupal via le système de plugin. Il supporte actuellement la modification de la Node, Useret les Fileclasses d'entités. Lorsque Nodevous modifiez le droit, vous pouvez modifier la classe de type par groupe de nœuds.

Consultez la description du module pour un exemple:

https://www.drupal.org/project/entity_type_class

Le module utilise hook_entity_type_alter () pour définir une classe de gestionnaire sur les entités que vous fournissez dans votre annotation de plug-in.

mvdgun
la source
-1

C'est une vieille question, mais la vraie réponse devrait être:

Si vous avez besoin d'un comportement différent entre les bundles, vous devez utiliser différents types d'entités, pas différents bundles.

Les entités de contenu personnalisé sont des citoyens de première classe en D8. En fait, nous estimons qu'il faut environ 30 minutes pour obtenir une nouvelle entité de contenu personnalisé au niveau de ce nœud (ce qui revient vraiment à ajouter l'interface utilisateur du formulaire pour obtenir le joli panneau latéral et les champs d'alias / révision.) n'inclut pas l'ajout des pages de traduction, mais ce n'est pas beaucoup plus.

Si vous ne l'avez pas vu, jetez un œil aux fonctionnalités d'entité generate: custom: de la console Drupal.

James
la source