Wie generiere ich programmgesteuert ein Entitätsformular?

7

Ich muss das Entitätsformular für meine benutzerdefinierte Entität programmgesteuert generieren - im Grunde die gleiche Ausgabe erhalten, die beim Aufrufen auftritt

\Drupal::formBuilder()->getForm('form namespace')

Wenn ich versuche, dies mit meinem Entitätsformular zu tun, wird folgende Fehlermeldung angezeigt :

Call to a member function getEntityTypeId() on null in Drupal\Core\Entity\EntityForm->getBaseFormId() (line 77 of core/lib/Drupal/Core/Entity/EntityForm.php).

Wirklich ratlos, wie es weitergehen soll.


Hier ist eine verschleierte Version des Codes, den ich verwende

Entitätsdefinition:

/**
 * Defines the My Entity entity.
 *
 * @ingroup my_module
 *
 * @ContentEntityType(
 *   id = "my_entity",
 *   label = @Translation("My Entity"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\my_module\MyEntityListBuilder",
 *     "views_data" = "Drupal\my_module\Entity\MyEntityViewsData",
 *     "translation" = "Drupal\my_module\MyEntityTranslationHandler",
 *
 *     "form" = {
 *       "default" = "Drupal\my_module\Form\MyEntityForm",
 *       "add" = "Drupal\my_module\Form\MyEntityForm",
 *       "edit" = "Drupal\my_module\Form\MyEntityForm",
 *       "delete" = "Drupal\my_module\Form\MyEntityDeleteForm",
 *     },
 *     "access" = "Drupal\my_module\MyEntityAccessControlHandler",
 *     "route_provider" = {
 *       "html" = "Drupal\my_module\MyEntityHtmlRouteProvider",
 *     },
 *   },
 *   base_table = "my_entity",
 *   data_table = "my_entity_field_data",
 *   translatable = TRUE,
 *   admin_permission = "administer as my_entity entities",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "name",
 *     "uuid" = "uuid",
 *     "uid" = "user_id",
 *     "langcode" = "langcode",
 *     "status" = "status",
 *   },
 *   links = {
 *     "canonical" = "/admin/structure/my_entity/{my_entity}",
 *     "add-form" = "/admin/structure/my_entity/add",
 *     "edit-form" = "/admin/structure/my_entity/{my_entity}/edit",
 *     "delete-form" = "/admin/structure/my_entity/{my_entity}/delete",
 *     "collection" = "/admin/structure/my_entity",
 *   },
 *   field_ui_base_route = "my_entity.settings",
 *   constraints = {
 *     "CustomContraint" = {}
 *   }
 * )
 */
class MyEntity extends ContentEntityBase implements MyEntityInterface {

Formular-Namespace

namespace Drupal\my_module\Form;

use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;

/**
 * Form controller for My Entity edit forms.
 *
 * @ingroup my_module
 */
class MyEntityForm extends ContentEntityForm {

Okay, das habe ich bisher gefunden. Die Antwort des Benutzers funktioniert teilweise. Ich musste meiner Container-Formularklasse auch eine Dummy-Funktion hinzufügen, um einen Rückruffehler zu umgehen, der folgendermaßen aussah:

 public function processForm($element, FormStateInterface $form_state, $form)
  {
    return $element;
  }

Dies ist wahrscheinlich nicht der richtige Weg, aber es führt zu einem Formularobjekt, das in einem anderen Formular gerendert wird.

elstevenson
quelle
1
Gemäß den Dokumenten muss das Argument entweder die Formular-ID oder die Klasse sein, die das Formular implementiert. Dieses Formular muss FormInterface implementieren. api.drupal.org/api/drupal/…
Kevin
1
Ex. \ Drupal :: formBuilder () -> getForm (Drupal \ mymodule \ Form \ FormName)
Kevin
1
Ja, wie gesagt, ich habe es versucht.
Elstevenson
1
Mein Formular erstreckt sich von der ContentEntityForm-Klasse, die das ContentEntityFormInterface implementiert, das das EntityFormInterface erweitert, das das BaseFromInterface erweitert, das das FormInterface erweitert, wie Sie sagten.
Elstevenson

Antworten:

12

Sie können den Builder-Service für Entitätsformulare verwenden. Ich konnte das Benutzerformular auf diese Weise laden (wobei $ entity der aktuelle Benutzer ist):

$entity = User::load($uid);
$user_form = \Drupal::service('entity.form_builder')->getForm($entity, 'default');

Wenn Sie das Benutzerregisterformular erhalten möchten (bei dem es sich im Grunde um ein Entitätsformular ohne gespeicherte Entität handelt), können Sie einfach die Methode create für Ihre Entitätsklasse verwenden, bevor Sie es an das Formular übergeben:

$entity = User::create();
$user_form = \Drupal::service('entity.form_builder')->getForm($entity, 'default');
oknate
quelle
1
Okay, diese Antwort hat mich am nächsten gebracht. Wenn ich es versuche, wird die Seite geladen, aber es wird nur eine Warnung angezeigt, dass die Formularklasse, in die ich das Entitätsformular einfügen möchte, keine 'processForm'-Methode hat.
Elstevenson
1
Die Funktion 'processForm' scheint in der Klasse \ Drupal \ Core \ Entity \ EntityForm zu leben.
Elstevenson
1
Da die Funktion nur eines der empfangenen Argumente zurückgibt ($ element), habe ich in meiner Containerformularklasse eine Dummy-Version dieser Funktion erstellt. Danach wird das Entitätsformular DOES gerendert. Ich weiß jedoch nicht, welche Konsequenzen es hat, diesen Anruf zu umgehen.
Elstevenson
1
@oknate, danke für deinen Code, er funktioniert perfekt, aber ich möchte die Methode #after_build für Ajax-Operationen anhängen. Die Methode #after_build wird nicht ausgelöst / aufgerufen. Sind Sie auf diese Situation gestoßen? oder jemand hat eine Lösung, um #after_build Methode anzuhängen
allabakash.g
1
Wenn Sie ein Build-Array zum Speichern des Formulars verwenden und eine Vorlage verwenden, mit der das Formular ordnungsgemäß gerendert wird, sollte dies einwandfrei funktionieren. Wenn Sie Probleme damit haben, dass Ajax nicht geladen wird, rendern Sie das Formular möglicherweise vorab und es wird dann in der Vorlage zwischengespeichert, wo es nicht richtig signalisiert, das Javascript beim nachfolgenden Laden der Seite hinzuzufügen.
oknate
5

Entitätsformulare funktionieren ohne Entität nicht. Wenn Sie ein leeres Formular möchten, verwenden Sie eine neu erstellte Entitätsinstanz. Sie können dieses Beispiel aus der Knotenentität verwenden:

/**
   * Provides the node submission form.
   *
   * @param \Drupal\node\NodeTypeInterface $node_type
   *   The node type entity for the node.
   *
   * @return array
   *   A node submission form.
   */
  public function add(NodeTypeInterface $node_type) {
    $node = $this->entityManager()->getStorage('node')->create(array(
      'type' => $node_type->id(),
    ));

    $form = $this->entityFormBuilder()->getForm($node);

    return $form;
  }

Der Entity Form Builder wird in die Controller-Basisklasse eingefügt. Normalerweise verwenden Sie einen Controller, um Entitätsformulare zu erstellen.

Beim Erstellen eines Knotens müssen Sie ein Bundle (Inhaltstyp) definieren, für das das Entitätsformular erstellt wird. Wenn Ihre Entität keine Bundles hat, können Sie ein leeres Array verwenden.


Bemerkungen

Kommentare erfordern mehr Arbeit, da sie an ein Entitätsfeld angehängt sind.

Beispiel zum Erstellen eines Formulars für das Kommentarfeld eines Knotens:

$comment = $this->entityTypeManager()->getStorage('comment')->create([
  'entity_type' => 'node',
  'entity_id' => $node->id(),
  'field_name' => 'comment',
]);
$build = $this->entityFormBuilder()->getForm($comment);

Oder dasselbe außerhalb eines Controllers ohne injizierte Dienste:

$comment = \Drupal::entityTypeManager()->getStorage('comment')->create([
  'entity_type' => 'node',
  'entity_id' => $node->id(),
  'field_name' => 'comment',
]);
$build = \Drupal::service('entity.form_builder')->getForm($comment);
4k4
quelle
Vielen Dank für die Freigabe, wenn wir ein Formular geändert und #after_build hinzugefügt haben. Die Methode #after_build funktioniert nicht. Hast du eine Lösung?
allabakash.g
3

Zur Verdeutlichung funktioniert der folgende Code in Drupal 8.2.x für mich:

$form = \Drupal::formBuilder()->getForm(Drupal\user\Form\UserLoginForm::class);
Kevin
quelle
1
Als ich Ihren Code ausprobierte, funktionierte er anfangs nicht, aber durch Hinzufügen eines \ vor 'Drupal \ user \ Form \ UserLoginForm :: class' funktionierte er. Als ich es mit meiner benutzerdefinierten Klasse versuchte, bekam ich jedoch den gleichen Fehler. Ich habe versucht, meine Frage mit meiner Entitätsdefinition zu aktualisieren. Ich vermute, dass ich nichts implementiert habe, das ich implementieren muss, damit Ihr Code funktioniert.
Elstevenson
1
Okay, ich habe endlich bemerkt, dass das von Ihnen verwendete Beispielformular kein Entitätsformular ist, sondern sich nur von FormBase aus erstreckt. Ich muss ein Entitätsformular rendern (insbesondere eine Inhaltsentität, die sich von ContentEntityForm erstreckt)
elstevenson
1
Ja, für meinen Anwendungsfall ist dies falsch.
Elstevenson