Why is $form not upcast with the requested bundle's fields for the default Form Mode for GET requests; and with the entities' values for other method requests?

My form:

namespace Drupal\tam_api\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

use Drupal\node\NodeForm;
// use Drupal\Core\Form\FormBase; i've tried all of these as well...
// use Drupal\Core\Entity\ContentEntityForm;
// use Drupal\Core\Entity\EntityForm;
// Drupal\node\Form\NodeDeleteForm

class TamForm extends NodeForm {

  public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL) {
    $form = parent::buildForm($form, $form_state);
    $res = array('fields'=>json_encode($form, TRUE));
    return new JsonResponse($res);

  public function getFormId() {
    return 'tam_dynamic_form';

  public function validateForm(array &$form, FormStateInterface $form_state) {

  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);
    $module_data = system_rebuild_module_data();


My route:

namespace Drupal\tam_api\Routing;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class TamRoutes {

  protected $formMap = array(
    'node' =>array(
        'tracks'=> array(
     ) // and other entity types... 

  public function routes() {
    $route_collection = new RouteCollection();

    foreach($this->formMap as $entity_type=>$bundles) {
      foreach($bundles as $bundle=>$routes) {
        foreach($routes as $verb=>$path) {
          $route = new Route('/forms'.$path);

          $perms = array('_access'=>'TRUE'); // TEMP while testing

          $opts = array(
            '_format' => ['json'],
            '_auth' => ['oauth2']

          if ($verb != 'add') {
            $opts['parameters'] = array(
              $entity_type => array('type'=>'entity:'.$bundle)

          $defaults = ['_form' => '\Drupal\tam_api\Form\TamForm'];
          if ($entity_type == 'node') {
            if ($bundle === 'tracks') {
              $defaults = ['_entity_form' => 'node.tracks'];
          } else if ($entity_type == 'group_content') {
            $perms['_group_member'] = 'TRUE';            
            if ($verb != 'add') {
              $opts['parameters']['group_content'] = array('type'=>'entity:group_content');


          if ($verb === 'add') {
            $route->setMethods(['POST', 'GET']);
          } else if ($verb === 'edit') {
            $route->setMethods(['PATCH', 'GET']);
          } else if ($verb === 'delete')  {
            $route->setMethods(['DELETE', 'GET']);
          } else {

          $route_collection->add($entity_type.'.'.$bundle.'.'.$verb, $route);
    return $route_collection;


My module:

function tam_api_entity_type_build(array &$entity_types) {
  tam_api_module_entity_type_alter($entity_types); // _alter isn't seeming to fire by itself?
function tam_api_module_entity_type_alter(array &$entity_types) {
  $form_modes = \Drupal::service('entity_display.repository')->getAllFormModes();
  foreach ($form_modes as $entity_type => $display_modes) {
    if ($entity_type !== 'node') {
    $node_type = $entity_types[$entity_type];
    foreach ($display_modes as $machine_name => $form_display) {
      $default_handler_class = '\Drupal\tam_api\Form\TamForm';
      $node_type->setFormClass($machine_name, $default_handler_class);

ERRORS are as follows:

GET: /forms/node/playlists/31694?_format=json: (which routes through _entity_form) Error</em>: Call to a member function getEntityTypeId() on null in Drupal\Core\Entity\EntityForm-&gt;getBaseFormId() (line 79 of core/lib/Drupal/Core/Entity/EntityForm.php.

GET: /forms/node/tracks/32434?_format=json: (which routes through _form) Argument 1 passed to Drupal\Core\Entity\EntityForm::setEntity() must implement interface Drupal\Core\Entity\EntityInterface, string given, called in /Users/user/Developer/trackauthority/tam-d8/web/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php on line 74 in Drupal\Core\Entity\EntityForm-&gt;setEntity()

GET /forms/node/tracks?_format=json (which routes through _form Missing bundle for entity type node in Drupal\Core\Entity\ContentEntityStorageBase-&gt;doCreate()</em> (line 86 of core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php). >Drupal\Core\Entity\EntityStorageBase-&gt;create(Array) (Line: 381)

  • 2
    I'm surprised your form is working at all, given that you're overriding the constructor. NodeForm::__construct has 6 paramaters, but you're supplying only 1. Jul 9, 2019 at 21:00
  • @aaronbauman, thank you. that was lingering from when i tried extending FormBase and was using dependency injection to set the user. I've removed all of that to simplify the tests, and paste errors for each path i'm testing.
    – E.A.T
    Jul 9, 2019 at 21:24

1 Answer 1


When you return a response instead of a Form API array

  public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL) {
    return new JsonResponse($res);

the form is reduced to a request/response pattern of a controller. It is no longer possible to use it as a Drupal form.

  • as long as i can get the form object upcast with the bundle's fields, i'll flatten that to json myself (as i have here: drupal.stackexchange.com/questions/279305/…). of course, i'll also need the entity upcast to submitForm on post, delete and patch requests.
    – E.A.T
    Jul 10, 2019 at 21:21
  • drupal is decoupled here, i don't need drupal's front end (though ideally will keep the admin forms intact)
    – E.A.T
    Jul 10, 2019 at 22:07
  • I've answered why this specific form class is not working. For the overall picture see my comment in the previous question drupal.stackexchange.com/questions/283122/…
    – 4uk4
    Jul 11, 2019 at 6:35
  • i'm not sure how this answers anything about upcasting, but seems you're only concerned with the points. here ya go.
    – E.A.T
    Jul 12, 2019 at 9:52
  • 1
    Entities are upcasted through EntityConverter in the route options. Compare the routes you generate to a core route with this Drupal Console command: drupal debug:router entity.node.edit_form
    – 4uk4
    Jul 12, 2019 at 10:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.