I just saw your code (beta 7) and was working on Ajax form Entity D8 (that will need for sure a module like yours to create walls for examples or mass creation for content), and it seems that there may be a more elegant manner to work with form entities.

/**
 * @file
 * Contains \Drupal\ajax_form_entity\Plugin\Block\FormBlock.
 */

namespace Drupal\ajax_form_entity\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 *
 * @Block(
 *   id = "entity_form_block",
 *   admin_label = @Translation("Block form"),
 *   category = @Translation("Forms")
 * )
 */
class FormBlock extends BlockBase {
  /**
   * {@inheritdoc}
   */
  public function build() {
    // Case of user
    $entity = entity_create('user');
    // Case of node type
    $entity = entity_create('node', array('type' => 'page'));
    // Case of taxonomy
    $entity = entity_create('taxonomy_term', array('vid' => 'forums'));
    
    // Call the form entity.
    $output['form'] = \Drupal::service('entity.form_builder')->getForm($entity, 'default');
    return $output;
  }
}

So in one line
\Drupal::service('entity.form_builder')->getForm($entity, 'default');
we can get any form entity in a block. Issue is that entity_create should be custom as the parameters are not always the same (sad that we do not have a "bundle" parameter instead of vid / type or whatever). I tested with node / taxonomy / user and I must say it is not that complex !

Next question is how to make a block per entity / bundle. I do not see the solution as we need one file per block it seems in D8 so you can't declare as many block as you wish programatically like in D7. We may need to implement a new block type (?). Any other solution is welcome !

Regards,

Comments

mikey_p’s picture

Yup, I realized this as well and started work on this in a branch: http://cgit.drupalcode.org/formblock/log/?h=genericentityform.

It's not working perfectly for all entity types, but you can see the attempt at http://cgit.drupalcode.org/formblock/tree/src/Plugin/Block/EntityFormBlo...

eme’s picture

Oh, I did not see your reply. I worked on my own also a bit meantime, and got some interesting results : I created a new project today that you can test out (https://www.drupal.org/project/entity_forms_in_blocks) but we need to merge rapidly the two projects I think.

I have the solution for the dynamic block system : we need to use the derivative system , where I use entity_get_bundles to get all bundles for each entity :


/**
 * @file
 * Contains \Drupal\entity_forms_in_blocks\Plugin\Derivative\DerivativeFormBlock.
 */

namespace Drupal\entity_forms_in_blocks\Plugin\Derivative;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;


/**
 * Creates all the form block variations per entity / bundle type.
 *
 * @see \Drupal\entity_forms_in_blocks\Plugin\Block\FormBlock
 */
class FormBlock extends DeriverBase implements ContainerDeriverInterface {


  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, $base_plugin_id) {
    return new static();
  }


  /**
   * Implements DeriverInterface::getDerivativeDefinitions().
   *
   * Returns the actual list of available blocks.
   */
  public function getDerivativeDefinitions($base_plugin_definition) {
    // Get all entity types and bundles.
    $all_entity_types  = entity_get_bundles();
    // Define which entity types to create form block(s) for.
    $entity_types = array('node', 'taxonomy_term', 'user');
    // Loop across all entity types.
    foreach ($all_entity_types as $entity_type => $bundles) {
      // If it match the specified entity type defined above.
      if (array_search($entity_type, $entity_types) !== FALSE) {
        // Loop across each bundle and create a form block for it.
        foreach($bundles AS $bundle => $informations) {
          $block_id = $entity_type . '__' . $bundle;
          $this->derivatives[$block_id] = $base_plugin_definition;
          $this->derivatives[$block_id]['admin_label'] = t('@form_type form', array('@form_type' => $informations['label']));
          $this->derivatives[$block_id]['cache'] = DRUPAL_NO_CACHE;
        }
      }
    }
    return $this->derivatives;
  }
}

With that, I create one bloc for each entity form (better than having a single block I think).

Then you can use the block ID to render the right block (even if I think some more methode should be used instead of my switch above) :


/**
 * @file
 * Contains \Drupal\entity_forms_in_blocks\Plugin\Block\FormBlock.
 */

namespace Drupal\entity_forms_in_blocks\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\EntityOwnerInterface;


/**
 *
 * @Block(
 *   id = "entity_form_block",
 *   admin_label = @Translation("Hello !"),
 *   category = @Translation("Forms"),
 *   deriver = "\Drupal\entity_forms_in_blocks\Plugin\Derivative\FormBlock"
 * )
 */
class FormBlock extends BlockBase {
  /**
   * {@inheritdoc}
   */
  public function build() {
    // Get derivative ID.
    $block_id = $this->getDerivativeId();

    // Create a blank entity.
    $entity = $this->create_entity_per_bundle($block_id);

    // Call the form entity and return.
    // @todo : settings to define the form view mode (not just default).
    $output['form'] = \Drupal::service('entity.form_builder')->getForm($entity, 'default');
    return $output;
  }

  /**
   * Check user access to the forms.
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account) {
    // Get derivative ID.
    $block_id = $this->getDerivativeId();

    // Create a void entity to test and return the right access behind.
    $entity = $this->create_entity_per_bundle($block_id);
    if( $entity->access('create')) {
      return AccessResult::allowed();
    }
    else {
      return AccessResult::forbidden();
    }
  }

  /**
   * Takes care of creating blank entities with entity-type specificities.
   */
  protected function create_entity_per_bundle($block_id) {

    // Get entity type and bundle from block ID.
    $entity_type = $this->get_entity_type($block_id);
    $entity_bundle = $this->get_entity_bundle($block_id);

    // Initialize entity default values as it may be different depending
    // on entity type, especially for the bundle definition (compulsory).
    // @todo : add contact forms and others (entityforms, eck...).
    $args = array();
    switch($entity_type) {
      // Case of node types
      case 'node':
        $args['type'] = $entity_bundle;
        break;
      // Case of taxonomy term creation
      case 'taxonomy_term':
        $args['vid'] = $entity_bundle;
        break;
    }

    return entity_create($entity_type, $args);
  }

  /**
   * Returns entity type from the block ID.
   *
   */
  function get_entity_type($block_id) {
    // Get entity type and associated bundle.
    $explode = explode('__', $block_id);
    return $explode[0];
  }

  /**
   * Returns entity bundle from the block ID.
   */
  function get_entity_bundle($block_id) {
    // Get entity type and associated bundle.
    $explode = explode('__', $block_id);
    return $explode[1];
  }

}

Pretty straightforward and amazingly simple compared to D7 !

eme’s picture

Version: 8.x-1.0-beta7 » 8.x-1.x-dev

Well I studied a bit your code and I must say that the config solution is more elegant with the ability of D8 to add as many block as needed.

Important change : we now need to change the access part by using use Drupal\Core\Access\AccessResult;

  /**
   * Implements \Drupal\block\BlockBase::blockAccess().
   */
  public function blockAccess(AccountInterface $account) {
    $access_control_handler = $this->entityManager->getAccessControlHandler($this->configuration['entity_type']);
    if($access_control_handler->createAccess($this->configuration['bundle'], $account)) {
      return AccessResult::allowed();
    }
    else {
      return AccessResult::forbidden();
    }
  }

There is another issue : the cache of the block is not functionnal (once submitted, the form expires). We may cache per user only I think. For now we can specify no cache :


  /**
   * {@inheritdoc}
   *
   */
  public function getCacheMaxAge() {
    return 0;
  }

Could you release it as a dev version so that the whole community can elaborate on this ?

eme’s picture

We've released a new version for the module with part of your code (see last dev version of https://www.drupal.org/project/entity_forms_in_blocks). We replaced the "operation" part that does not seem to have real interest and replace it with the form mode for any entity.

There are still some work remaining of course ! Some entity forms not working (especially comment forms).

Of course, if you wanna merge it into FormBlock you're welcome and we can put Entity Forms in Blocks as obsolete.

Regards.

Greg Boggs’s picture

Hey Mikey_p, do you plan to include this in form_block? We're considering using this in RedHen to build out contact collections to allow for email subscription that's plugable with MailChimp and other email providers.

mikey_p’s picture

Update: I have some old code in a branch for this, I would like to check with the core maintainers to see how 'operation' was intended to be used before we remove that completely (maybe it should just have a default and be under an "advanced options" section that is hidden).

The biggest problem seems to be that some entity types don't work, and we need a way to whitelist the ones that we know to work correctly. Once this is in place we can have a generic EntityFormBlock class and I think we can try to find a way to subclass it for each individual entity types that need to add custom behavior (like contact form access/flood control/etc)

Patches welcome.

Greg Boggs’s picture

Do you know off the top of your head an entity type that this fails with?

mikey_p’s picture

Comments for one, I know there are several others. It's kinda surprising how many different entity types there are in D8.

mikey_p’s picture

Category: Task » Feature request

Now that I've got all the base bugs worked out for a stable 1.0 release for D8, I'm planning on adding this as well as form mode support in a 1.1 release.

phjou’s picture

Any news on this?

AndyD328’s picture

Version: 8.x-1.x-dev » 2.0.0-beta2
Assigned: Unassigned » AndyD328

Hi phjou,
I intend to look at this once the features & bug fixes in the issue queue are resolved.
I think if we aim to do this generic solution first it will slow down getting a solid D9 version released.
So this is definitely on the road map and I'll aim to use as much of the above work as possible - so additional patches etc are very welcome.
Thanks!