Wie ändere ich die Entitätstypklasse?

9

In Drupal 8 können Sie eine Entität laden mit:

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

Dies sucht nach den Entitätsdefinitionen und stellt fest, dass der Knoten durch Drupal \ node \ Entity \ Node definiert ist. Drupal \ node \ NodeStorage instanziiert also (glaube ich) eine neue Drupal \ node \ Entity \ Node- Instanz.

Was ich erreichen möchte, ist die Unterklasse Drupal \ node \ Entity \ Node und die Möglichkeit, diese Unterklasse zu instanziieren, wenn es angebracht ist. Wenn ich beispielsweise einen Artikel zum Knotenpaket habe, gibt es eine Klasse:

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

Und ich würde anrufen:

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

Und die Rückkehr wäre meine ArticleUnterklasse.

Ich kann dies erreichen, indem ich einen neuen Entitätstyp erstelle und ihn mit anderen vorhandenen Entitätsdefinitionen verdrahtete. Das Beispiel für einen Knotenartikel wäre beispielsweise diese Klasse:

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);

Das funktioniert gut (so viel ich sehen kann); es riecht jedoch. Es wird ein neuer Entitätstyp hinzugefügt, der nicht wahr ist und in Zukunft andere Probleme verursachen kann.

Wie definiere ich eine Unterklasse für ein Entitätspaket, damit beim Laden der Entität ein Objekt dieser Klasse zurückgegeben wird?

itarato
quelle
1
Ich bin nicht sicher, ob Sie pro Bundle eine andere Entitätsklasse angeben können. Sie können verwenden hook_entity_type_alter(), um die Änderung sauberer zu machen, aber ich weiß nicht, wie Sie das auf ein bestimmtes Bundle beschränken würden
Clive
Vielen Dank, Clive - das scheint ein vielversprechender Haken zu sein!
Itarato

Antworten:

10

Erstellen Sie eine neue Klasse in Ihrem Modul, die erweitert wird \Drupal\node\Entity\Node.

use Drupal\node\Entity\Node as BaseNode;

class MyNode extends BaseNode {
}

Implementieren 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');
  }
}

Denken Sie daran, den Cache neu zu erstellen.

Es funktioniert einwandfrei, wenn Knoten über den Entity Type Manager-Dienst und den Knotenspeicher geladen werden. Es funktioniert sogar, wenn Sie es nur verwenden Drupal\node\Entity\Node::load($nid), da diese load()Funktion nur ein statischer Wrapper für den Serviceaufruf des Entity Type Managers ist, der von der EntityKlasse bereitgestellt wird, die von der Klasse erweitert wird Node.

// 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);
  }
}

Dies funktioniert auch gut mit der entity_load_multiple()Funktion, die bald entfernt werden soll. Ich denke, dies deckt alle Standardanwendungsfälle zum Laden von Knoten ab.

Wenn Ihr Modul dies tut und ein anderes Modul dasselbe tut, haben Sie natürlich ein Problem, aber ich denke, es ist kein allgemeines Szenario, und es ist nur für ganz bestimmte Anwendungsfälle sinnvoll.

SiliconMind
quelle
2
Entschuldigung, aber nein :) Die Frage war, eine andere Klasse pro Bundle zu haben . Sie ändern die Klasse für alle Bundles des Entitätstypknotens. Das ist nicht dasselbe
Berdir
@Berdir, Ihr Recht :( ... Wenn Klassen pro Bundle vorhanden sind, muss auch ein Entitätsspeicher für den Knoten erweitert werden, damit die Lademethoden überschrieben werden können, um die Klassen pro Bundle zu erzeugen. Dies ist im Grunde ein großes Problem.
SiliconMind
1
Ja. drupal.org/node/2570593 ist eines der Probleme, auf die ich verwiesen habe, die ich jedoch in meiner Antwort nicht verlinkt habe.
Berdir
Ich habe hook_entity_type_alter implementiert, um eine benutzerdefinierte Klasse festzulegen. Es funktioniert auch.
Yenya
Wenn ich diese Methode versuche, erhalte ich eine 'Drupal \ Component \ Plugin \ Exception \ PluginNotFoundException: Der Entitätstyp "Knoten" existiert nicht.' Botschaft. Ich verwende die Funktion ablecore_entity_type_build in meiner .module-Datei. Und ich habe meine AbleNode.php in / src / Entity / AbleNode / Ordner
Matt
2

Ich hatte das gleiche Problem und entschied mich, ein Modul zu erstellen, das die Entitätstypklasse von Drupal-Entitäten über das Plugin-System ändert. Derzeit unterstützt verändern sie das Node, Userund FileEntitätsklassen. Wenn Sie die NodeBerechtigung ändern, können Sie die Typklasse pro Knotenpaket ändern.

In der Modulbeschreibung finden Sie ein Beispiel:

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

Das Modul verwendet hook_entity_type_alter () , um eine Handlerklasse für Entitäten festzulegen , die Sie in Ihrer Plugin-Annotation angeben .

mvdgun
quelle
-1

Dies ist eine alte Frage, aber die wirkliche Antwort sollte sein:

Wenn Sie ein unterschiedliches Verhalten zwischen Bundles benötigen, sollten Sie unterschiedliche Entitätstypen verwenden, nicht unterschiedliche Bundles.

Benutzerdefinierte Inhaltseinheiten sind Bürger erster Klasse in D8. Wir schätzen, dass es ungefähr 30 Minuten dauert, bis eine neue benutzerdefinierte Inhaltsentität auf die Ebene des Knotens gebracht wird (was wirklich nur darauf hinausläuft, die Formular-Benutzeroberfläche hinzuzufügen, um das schöne Seitenfenster und die Alias- / Revisionsfelder zu erhalten) beinhaltet nicht das Hinzufügen der Übersetzungsseiten, aber das ist nicht viel mehr.

Wenn Sie es nicht gesehen haben, sehen Sie sich die Funktionen generate: custom: entity der Drupal Console an.

James
quelle