Hello! I am using the Group module and need to provide a different set of questions on the Group Membership form per Group and was hoping I could use your module to get closer to the solution.

I was thinking maybe I could:

  1. Create a Form Mode for each Group
  2. Create and add a Hidden Field on each Form Mode with a Default Value = Group ID
  3. Create a custom module that replaces the default form with the Form Mode where the Group ID = Current Group ID

The approach in this article appears to be what I am talking about.

What do you think? Does it make sense to use hook_entity_type_alter? Seems like this would not be done on load of the default form, but rather it changes the settings of particular forms. If so, is there a different hook to use for altering the default form on load of the page?

Thank you in advance for your help on this one.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tekNorah created an issue. See original summary.

woprrr’s picture

Hii @tekNorah

Lot of things to share with you here :) ! I love that !! First Like lot of entities Group have lot of specificities regardless others "standards" but that entity are really correctly generated I see it's so simple to tell Form Mode Manager API what is the Group specificities.

Group / Group Content not work "out of the box" because form handlers aren't standard (add/edit/delete) and common entities have following keys (add_form/edit/delete). It's not really a problems because the abstract level of Form Mode Manager allow you to inform form mode manager abstract factory with a plugin 'EntityRoutingMap' that allow you to build an object (plugin) to make a Bridge with entities routes and entity definition (that not exists in core).

The approach in this article appears to be what I am talking about.

What do you think? Does it make sense to use hook_entity_type_alter? Seems like this would not be done on load of the default form, but rather it changes the settings of particular forms. If so, is there a different hook to use for altering the default form on load of the page?

A large part of response for that is in Spion article and https://youtu.be/iZHsLo0m61c this video to show you how use Form Mode Manager with Form Class associated because Form Mode Manager work for you (enjoy :D)

AGAIN !! I think Form Mode Manager need more extendability think to directly work with Form Mode Manager API with events to make better control before needed tons of evil hooks, I need to think about that and I need really more feedback of developpers (GOD HELP ME :D)

woprrr’s picture

FileSize
4.43 KB

WOOPS ! Here you are the plugin file to make Group compatible :) feel free to add it in your custom module of in Group in future.

This is pretty similar to #2834749: It seems not works with Profile

tekNorah’s picture

Thanks for your prompt reply, woprrr!

So, ok, I generated the attached module. But, when I try to browse to: /admin/group/types/manage/open_group/form-display, I get the error: "The website encountered an unexpected error. Please try again later." with 500 (500 Service unavailable (with message)) in the console.

Do I need to configure more than this in this in my custom plugin to get it to work?

Also, should I be using the release or dev version of the Form Mode Manager module?

tekNorah’s picture

Hi woprrr! Just checking back here to see if you can provide some additional assistance? Also, to clarify, I am trying to provide working form modes for the group entity route: entity.group.join with the path: /group/{group}/join

So, I created the file GroupMembership.php and placed under {mymodule}/src/Plugin/EntityRoutingMap:

<?php

namespace Drupal\form_mode_manager\Plugin\EntityRoutingMap;

use Drupal\form_mode_manager\EntityRoutingMapBase;

/**
 * Class GroupMembership.
 *
 * @EntityRoutingMap(
 *   id = "group_membership",
 *   label = @Translation("Group Membership Routes properties"),
 *   targetEntityType = "group",
 *   defaultFormClass = "add",
 *   editFormClass = "edit",
 *   operations = {
 *     "group-join" = "entity.group.join",
 *     "group-leave" = "entity.group.leave",
 *   }
 * )
 */
class GroupMembership extends EntityRoutingMapBase {

}

I am able to add a form mode, add a field on the default form, but when I goto change the new form mode's configuration, get a 500 server response upon attempting to save. What am I doing wrong? Do I need to add an additional Controller or something? I am really lost here.

tekNorah’s picture

Hi woprrr!

I was able to successfully add the Group Content entity with the following GroupContent.php code and save changes to a Test form mode:

<?php

namespace Drupal\group_custom_form_mode_manager\Plugin\EntityRoutingMap;

use Drupal\form_mode_manager\EntityRoutingMapBase;

/**
 * Class GroupContent.
 *
 * @EntityRoutingMap(
 *   id = "group_content",
 *   label = @Translation("Group Content Routes properties"),
 *   targetEntityType = "group_content",
 *   defaultFormClass = "add",
 *   editFormClass = "edit",
 *   operations = {
 *     "add" = "entity.group_content.add_form",
 *     "edit" = "entity.group_content.edit_form",
 *     "delete" = "entity.group_content.delete_form",
 *     "group-join" = "entity.group.join",
 *     "group-leave" = "entity.group.leave"
 *   }
 * )
 */
class GroupContent extends EntityRoutingMapBase {

}

Took me a bit to figure out where to locate the existing operations and routes for the Group Content entity and found the list of operations in the handlers>forms section under src/Entity/GroupContent.php in the Group module. I also forgot to rename the namespace to my custom module (duh!) and clear cache several times for it to take.

Now I am onto replacing the default form with the test form, programatically. I'm guessing I just need to get the default class, then set the class to use the specified form mode and default class?

Similar to this? https://www.webomelette.com/comment/1341#comment-1341

I'll tell you how it goes...

tekNorah’s picture

Ok, so, I was able to create and modify a form mode for the Group Membership form. However, the FMM didn't build a route for it.

I was expecting it to create for the test form mode:
- Route: entity.group.join.test
- Path: /group/{group}/join/test

However, per Devel, the only route and path showing up for the test form mode is:
- Route: form_mode_manager.group_content.add_page.test
- Path: /group_content/add-list/test

And this path is invalid (404).

I have a feeling I need to implement some additional customizations in order to handle the Group Content entity. I look forward to your response.

woprrr’s picture

Status: Active » Needs work
FileSize
2.66 KB

Hi @tekNorah,

After seen your tests and see group codes I can help you now.

I will give you my opinion on what you are trying to accomplish (make Form Mode Manager and Groups compatible via the Form Mode Manager plugin). To abstract the operations of entities which for each entities of the keys impossible to predict the plugin EntityRoutingMap was made available based on a common denominator between the basic entities and those of the core (node ​​/ media / term etc .. .). the keys that the code looks for within form mode manager are thus ('add_form' / 'edit_form' / 'delete_form') so you can not define a plugin with the keys (add / edit / delete).

Then Group seems to add a higher level of entity workflow (join / leave) and these are group-specific concepts so you have to implement them for Form Mode Manager to process them. I explain further down how to do

First with the attached code I can have

/group_content/add-list/{form_mode_name}
/group/add/{group_type}/{form_mode_name}
/group/{group}/content/add/{plugin_id}/{form_mode_name}
/group/{group}/content/{group_content}/edit/{form_mode_name}

/group/add-list/{form_mode_name}
/group/{group}/edit/{form_mode_name}

routes working correctly. With that I have ability to add/edit group and group_content

We have somes other form actions to Discover like :

entity.group.join
entity.group.leave
entity.group_content.create_page
entity.group_content.create_form
entity.group_content.group_node_add_page

To add that into Form Mode Manager you need to create new even_subscriber like Form Mode Manager (by extends)

  form_mode_manager.route_subscriber:
    class: Drupal\form_mode_manager\Routing\EventSubscriber\FormModesSubscriber
    arguments: ['@entity_type.manager', '@entity_display.repository', '@form_mode.manager', '@plugin.manager.entity_routing_map']
    tags:
      - { name: event_subscriber }

@see/@eg the following code :

  /**
   * Add a collection of route per form mode for current entity.
   *
   * Each entity need to define a collection of routes to be used by Drupal.
   * Each `entity operation` is a form handler present for all,
   * ContentEntityType plugins. Each operation represent a Form to be,
   * displayed by formBuilder service in routes controllers.
   * To respect core logic implemented by Drupal we need to add,
   * a route for each operations (add/edit/add_page/admin_add) dynamically and,
   * a transverse controller compatible to use form modes.
   *
   * Each routes generated by `setFormModeCollection` and,
   * `setAddPageCollection` respect the following naming,
   * `ENTITY_ROUTES_BASIS.FORM_MODE_NAME` eg for Node entity :
   *
   * @code
   *  $routes = [
   *    'add_form' => 'node.add.FORM_MODE_NAME',
   *    'edit_form' => 'entity.node.edit_form.FORM_MODE_NAME',
   *    'add_page' => 'form_mode_manager.node.add_page.FORM_MODE_NAME',
   *    'admin_add' => 'node.add.FORM_MODE_NAME',
   *  ];
   * @endcode
   *
   * @param array $form_modes
   *   All form-modes available for specified entity_type_id.
   */
  protected function addFormModesRoutes(array $form_modes) {
    foreach ($form_modes as $form_mode_infos) {
      $this->setFormModeCollection($form_mode_infos, 'join');
      $this->setFormModeCollection($form_mode_infos, 'leave');
      $this->setFormModeCollection($form_mode_infos, 'create_page');
      $this->setFormModeCollection($form_mode_infos, 'create_form');
      $this->setFormModeCollection($form_mode_infos, 'group_node_add_page');

      if (!empty($this->entityDefinition->getKey('bundle'))) {
        $this->setAddPageCollection($form_mode_infos);
      }
    }
  }

It's a good feature request to try to discover the EntityPluginMap 'operations' key to automatically add each operation available for entity. But that can be dangerous if people don't undestand implication of that ... Flexibility can be a trap sometimes.

woprrr’s picture

Hii again,

I have some information to you about compatibility of Groups + Form Mode Manager. First Groups seem's to implement some custom routes via RouteProvider and define her proper Controllers @see \Drupal\group\Entity\Controller\GroupContentController. Infact Form Mode Manager can work because the concept of Abstract Controller Factory is here to handle compatibility with ALL entities BUT you need to understand and make some additional code to do that.

For definition of routes you need to add some more parameters custom for group and not always exposed by group routes in your custom

To use Controller part uou need to make an extend of \Drupal\form_mode_manager\ComplexEntityFormModes Like \Drupal\form_mode_manager\TaxonomyEntityFormModes to expose how recover all informations in your \Drupal\group_custom_form_mode_manager\Routing\EventSubscriber\FormModesSubscriber

The concept and reason why Form Mode Manager is strongly abstract is that Content Entities can be really complex and unpredictable for Form Mode Manager. To take ability to use Form Modes with Form Mode Manager you need to expose how your entities works and implement your specific parameters with subscriber. After Route definition are good next step are using the Controller Factory provided by Form Mode Manager. This factory is based on similar concept as routes subscriber. Each entity can have some custom requirements and needed unpredictable then Form Mode Manager have identified Two Pattern (Complex entity (with bundle) And Unbundeled entity (without bundle)). In Complex Familly we have some custom example like Media / Taxonomy not unsing 'type' bundle key but more custom (vid || mid) then we have created a specific Object to expose how to make a controller compatible with theses entities. Please see the method Factory responsible of recovering of Correct controller ==> \Drupal\form_mode_manager\Controller\FormModeManagerEntityController::getEntityControllerObject

In your case Groups + Groups content + Form Mode Manager on every place that seem's to be a futur Contributed Module to handle FULL compatibility correctly.

ATM the Group / Group content entity works for simple usecases but for more Group specific usecases without more code that compatibility isn't revelant for Form Mode Manager.

I attach you a draft to show you the way to make this compatibility working. That a really interesting idea to make a new contributor module to be an "Interpreter" of two modules.

tekNorah’s picture

Thank you so much for all your help with this. So, wait, this is where I get confused. The join/leave routes appear to be under the group entity, but actually store data under the group_membership group_content type. Does that mean that group_membership is a group_content "bundle"?

I'm not the dev on the group module. So, when I examine the code, tbh, looks a bit confusing to me.

I guess, just not sure if I need to create a new event subscriber for the group_membership entity or just add the join/leave actions under the group_content subscriber you have in your example.

Also, I get that I need to create an GroupContentEntityFormMode, but, how do I get the main controller to call/use it?

Do I have to patch the main fmm module, modifying the existing controller, or can I extend it?

Your help is much appreciated!

woprrr’s picture

Hii @tekNorah,

Sorry for the delay again it's complicated to find time to see groups module code .. That's really not a simple module :D

The join/leave routes appear to be under the group entity, but actually store data under the group_membership group_content type. Does that mean that group_membership is a group_content "bundle"?

No, it's not a group_content bundle but group_membership exist to "Wrap" group_content entities
@see "\Drupal\group\GroupMembershipLoader"

/**
 * Loader for wrapped GroupContent entities using the 'group_membership' plugin.
 *
 * Seeing as this class is part of the main module, we could have easily put its
 * functionality in GroupContentStorage. We chose not to because other modules
 * won't have that power and we should provide them with an example of how to
 * write such a plugin-specific GroupContent loader.
 *
 * Also note that we don't simply return GroupContent entities, but wrapped
 * copies of said entities, namely \Drupal\group\GroupMembership. In a future
 * version we should investigate the feasibility of extending GroupContent
 * entities rather than wrapping them.
 */
I guess, just not sure if I need to create a new event subscriber for the group_membership entity or just add the join/leave actions under the group_content subscriber you have in your example.

&

Also, I get that I need to create an GroupContentEntityFormMode, but, how do I get the main controller to call/use it?

As already seen before, that's not an entity. For GroupContentEntityFormMode Why you need to add custom logic here ? Normally GroupContent are a ContentEntity (complex) and that does works IMHO (with attached sub-mobule join in that issue).
In all cases the FormModeManagerEntityController are an Abstract Factory and then support inherence or adding more Controller type without changing Fmm base code. In futur that would be great to delegate an Event XOR hook to add more controller in switch. To Add your own *EntityFormMode you should follow

1/ Extends \Drupal\form_mode_manager\Controller\FormModeManagerEntityController and overide getEntityControllerObject() method.
2/ Adapt \Drupal\form_mode_manager\Routing\EventSubscriber\FormModesSubscriber if you need to change all fmm routes OR if you use the code join in that issue (or custom EventSubscriber) define \Drupal\form_mode_manager\Routing\EventSubscriber\FormModesSubscriber::FORM_MODE_DEFAULT_CONTROLLER constant to your XxxxEntityController created previously (this controller extends FormModeManagerEntityController and contain your own getEntityControllerObject() method factory.
3/ Add you usecases like

      case 'my_entity_type_id':
        $object = new GroupContentEntityFormMode(
          $this->renderer,
          $this->account,
          $this->formModeManager,
          $this->entityFormBuilder,
          $this->entityRoutingMap,
          $this->formBuilder,
          $this->entityTypeManager
        );

        break;

And Enjoy you can now use your group_content entities with the dynamic controller factory. I admit I don't have more time to change the Factory to build a discovery or plugin to be more dynamic and transparent. This module are the best friend of developper :D

That's help you ?

tekNorah’s picture

Hmmm.. I extended the Base Form Mode Manager controller and added all the stuff you suggested, but I'm back to the same error:

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Entity\EntityStorageException</em>: Missing bundle for entity type group_content in <em class="placeholder">Drupal\Core\Entity\ContentEntityStorageBase-&gt;doCreate()</em> (line <em class="placeholder">74</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php</em>). <pre class="backtrace">Drupal\Core\Entity\EntityStorageBase-&gt;create(Array) (Line: 29)
Drupal\group_custom_form_mode_manager\GroupContentEntityFormModes-&gt;getEntityFromRouteMatch(Object) (Line: 159)
Drupal\form_mode_manager\AbstractEntityFormModesFactory-&gt;checkAccess(Object) (Line: 206)
Drupal\form_mode_manager\Controller\FormModeManagerEntityController-&gt;getEntityControllerResponse(&#039;checkAccess&#039;, Object) (Line: 149)
Drupal\form_mode_manager\Controller\FormModeManagerEntityController-&gt;checkAccess(Object)
call_user_func_array(Array, Array) (Line: 68)
Drupal\Core\Access\CustomAccessCheck-&gt;access(Object, Object, Object)
call_user_func_array(Array, Array) (Line: 159)
Drupal\Core\Access\AccessManager-&gt;performCheck(&#039;access_check.custom&#039;, Object) (Line: 135)
Drupal\Core\Access\AccessManager-&gt;check(Object, Object, Object, 1) (Line: 112)
Drupal\Core\Access\AccessManager-&gt;checkRequest(Object, Object, 1) (Line: 107)
Drupal\Core\Routing\AccessAwareRouter-&gt;checkAccess(Object) (Line: 92)
Drupal\Core\Routing\AccessAwareRouter-&gt;matchRequest(Object) (Line: 115)
Symfony\Component\HttpKernel\EventListener\RouterListener-&gt;onKernelRequest(Object, &#039;kernel.request&#039;, Object)
call_user_func(Array, Object, &#039;kernel.request&#039;, Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher-&gt;dispatch(&#039;kernel.request&#039;, Object) (Line: 127)
Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel-&gt;handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle-&gt;handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache-&gt;pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware-&gt;handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware-&gt;handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel-&gt;handle(Object, 1, 1) (Line: 666)
Drupal\Core\DrupalKernel-&gt;handle(Object) (Line: 19)
</pre>

See my attached code, plz.

tekNorah’s picture

To be honest, I don't understand why you suggested to extend the controller. The Group Content bundles use the standard "type" key.

Sooooo.. why am I getting this missing bundle for Group Content Entity error?

tekNorah’s picture

Any more progress on this? Need help extending the Group Membership join form as it is using a non-standard route.

I looked at the profile entity example, but seems like there is A LOT that is different from the group_content entity

So, just trying to iron out which classes/methods I need to be extending and if I actually need and/or how to define the \Drupal\form_mode_manager\Routing\EventSubscriber\FormModesSubscriber::FORM_MODE_DEFAULT_CONTROLLER constant in my custom group form mode manager module?

If you don't have time to help atm, is there anyone else you can refer me to?

tekNorah’s picture

Hi woprrr!

Here is the current code I am working with (attached) and see this corresponding explainer vid of the problem I am encountering at this point: https://share.vidyard.com/watch/GygypkNU4yT785ZBZYvZ3q?

Basically, looks like I am encountering a "non-standard route" issue. I believe the FMM is expecting the route reference/pointer to be entity.group_content.join ,but it is entity.group.join??

I looked at the Profile entity example, but seems like there are a lot of differences between how profile and group_content are managed, that I am not sure how much/what needs to be extended/overridden.

tekNorah’s picture

Sorry, made a mistake at the end of that vid. the default join url is: group/{group number}/join, not off the stream page, lol

I expect the a form mode to be group/{group number}/join/{form mode name}
This is what I get when I try to goto that page:

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Entity\EntityStorageException</em>: Missing bundle for entity type group_content in <em class="placeholder">Drupal\Core\Entity\ContentEntityStorageBase-&gt;doCreate()</em> (line <em class="placeholder">74</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php</em>). <pre class="backtrace">Drupal\Core\Entity\EntityStorageBase-&gt;create(Array) (Line: 143)
Drupal\form_mode_manager\ComplexEntityFormModes-&gt;getEntityFromRouteMatch(Object) (Line: 159)
Drupal\form_mode_manager\AbstractEntityFormModesFactory-&gt;checkAccess(Object) (Line: 206)
Drupal\form_mode_manager\Controller\FormModeManagerEntityController-&gt;getEntityControllerResponse(&#039;checkAccess&#039;, Object) (Line: 149)
Drupal\form_mode_manager\Controller\FormModeManagerEntityController-&gt;checkAccess(Object)
call_user_func_array(Array, Array) (Line: 68)
Drupal\Core\Access\CustomAccessCheck-&gt;access(Object, Object, Object)
call_user_func_array(Array, Array) (Line: 159)
Drupal\Core\Access\AccessManager-&gt;performCheck(&#039;access_check.custom&#039;, Object) (Line: 135)
Drupal\Core\Access\AccessManager-&gt;check(Object, Object, Object, 1) (Line: 112)
Drupal\Core\Access\AccessManager-&gt;checkRequest(Object, Object, 1) (Line: 107)
Drupal\Core\Routing\AccessAwareRouter-&gt;checkAccess(Object) (Line: 92)
Drupal\Core\Routing\AccessAwareRouter-&gt;matchRequest(Object) (Line: 115)
Symfony\Component\HttpKernel\EventListener\RouterListener-&gt;onKernelRequest(Object, &#039;kernel.request&#039;, Object)
call_user_func(Array, Object, &#039;kernel.request&#039;, Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher-&gt;dispatch(&#039;kernel.request&#039;, Object) (Line: 127)
Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel-&gt;handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle-&gt;handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache-&gt;pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware-&gt;handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware-&gt;handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel-&gt;handle(Object, 1, 1) (Line: 666)
Drupal\Core\DrupalKernel-&gt;handle(Object) (Line: 19)
</pre>

Screenshot

Sorry for any confusion here. But, I believe this error is because the join form uses a "non-standard" route. Does that make sense?

woprrr’s picture

Status: Needs work » Needs review
FileSize
7.96 KB

In attachament the new version of sub module to be compatible with Group content (membership). In future that's would be great this module become a REAL module with fmm dependency and cleanup to take ability to use group content membership with form modes.

@tekNorah : Can you post a small feedback and what works or not works as excepted please ?

If anyone (tekNorah i hope) need to help or become co maintainer of futur bridge module (for maintain usability with group content).

woprrr’s picture

Status: Needs review » Postponed

I flag this issue postponed and waiting sub module project to continue the compatibility effort.

tekNorah’s picture

Hi woprrr!

Your last version was VERY close! I made the following adjustments:

1. Remove group-leave operation - the group-leave form doesn't use fields configured on manage form fields
2. Modified the hook_entity_type_alter() function to apply to all form modes managed by this module

I would be more than happy to be a co-maintainer for this sub-module. Please go ahead and set that up. Thanks!

tekNorah’s picture

I have added back the core Group entity functionality as form modes are also often needed on Group, not just Group Content.

woprrr’s picture

Status: Postponed » Fixed

H @tekNorah !

Thank you to continue to work on this sub module :) It's now time to this project to have a classique life cycle and to you to become a co maintainer of https://www.drupal.org/project/group_form_mode_manager !! Congratulation :) !!

Now you can continue all dev in that repository and start to correctly describe our project (and you english are very better than me :D).

I go to create a follow-up issue to continue and after we can speak about roadmap and others cool subject arroud FMM + group.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.