Problem/Motivation

The Group module currently only supports adding group member entities with integer IDs. While this covers most content entity types, some other entities utilizing string IDs (like config entities, external entities, etc) may not be added as a group content due to a fact the "group_content" "entity_id" entity reference field type is integer.

Proposed resolution

Below is a summary of solutions from #91:
Solution 1 (declined): Update entity_id column to varchar
Solution 2 (declined): Duplication of all code related to GroupContent as GroupConfig.
Solution 3 (A lot of refactoring): Create a new entity type for referring config entities. Create a Base class which GroupContent and GroupConfig will extend.
Solution 4 (Patch provided): Create a new column entity_config_id in the GroupContent.
Solution 5 (declined): Remove entity_id from BaseField and set it as BundleField. (edited)

Remaining tasks

One of solutions above should be accepted.
The solution should cover any entity type using string entity IDs, not just config entities.
Provide unit tests.

User interface changes

None.

API changes

The GroupContent class API may change, as long as GroupContentStorage, depending on the accepted solution.

Data model changes

Depending on the accepted solution, either a new field or a new entity type may be added.

Original report by kallehauge

Old issue title: Configuration entities as group content

Hi! I was curious if I missed a way to make a GroupContentEnabler for Config Entities (ConfigEntityBase/ConfigEntityBundleBase)? Examples include: Menus, YAML Forms, Entityqueues.

When I look at the schema for group_content_field_data, then the entity_id is required to be an Integer but because entities saved in configuration use their machine name as ID, then these will automatically be eliminated as possible references and the entity_id will be set as 0.

How core works:
If we take a look at how entity reference fields work in Drupal, then you select 1 type of entity (Node, comment, menu...) and then it creates a table for the field with an id of a machine name (Varchar) for config entities or an Integer for non-config entities.


Side-note: thank you for your good work on the module. We've been using it with tremendous joy :)
/ Kallehauge

CommentFileSizeAuthor
#306 2797793-306.patch87.59 KBZeiP
#279 interdiff-255-279.txt87.58 KBnmeegama
#279 2797793-279.patch87.58 KBnmeegama
#260 group-support_string_entity_ids-2797793-260.patch87.49 KBNigel Cunningham
#258 group-support_string_entity_ids-2797793-258.patch87.54 KBanish.a
#256 group-support_string_entity_ids-2797793-256.patch87.41 KBjacobbell84
#239 interdiff.txt561 bytesesolitos
#239 group-support_string_entity_ids-2797793-239.patch87.52 KBesolitos
#233 group-support_string_entity_ids-2797793-233.patch87.69 KBovidenov
#229 group-support_string_entity_ids-2797793-229.patch87.13 KBcarolpettirossi
#228 interdiff_215-228.txt1.47 KBcarolpettirossi
#228 group-support_string_entity_ids-2797793-228.patch87.2 KBcarolpettirossi
#216 interdiff_214-215.txt2.36 KBzipymonkey
#216 group-support_string_entity_ids-2797793-215.patch87.2 KBzipymonkey
#215 interdiff_213-214.txt3.62 KBsafetypin
#215 group-support_string_entity_ids-2797793-214.patch87.8 KBsafetypin
#213 group-support_string_entity_ids-2797793-213.patch87.68 KBsafetypin
#210 interdiff.txt5.12 KBzipymonkey
#210 group-support_string_entity_ids-2797793-210.patch86.17 KBzipymonkey
#204 group-support_string_entity_ids-2797793-204.patch75.64 KBsafetypin
#200 group-support_string_entity_ids-2797793-200.patch86.05 KBimalabya
#198 group-support_string_entity_ids-2797793-198.patch85.2 KBesolitos
#197 group-support_string_entity_ids-2797793-197.patch85.2 KBesolitos
#190 Screenshot from 2020-01-17 13-55-38.png140.37 KBdevreltomtom
#188 group-support_string_entity_ids-2797793-188.patch85.87 KBkyuubi
#186 group-support_string_entity_ids-2797793-186.patch85.55 KBkducharm
#185 interdiff_183-185.txt5.71 KBkducharm
#185 group-support_string_entity_ids-2797793-185.patch84.98 KBkducharm
#183 group-support_string_entity_ids-2797793-183.patch84.58 KBknowak
#182 group-support_string_entity_ids-2797793-182.patch74.17 KBknowak
#179 group-support_string_entity_ids-2797793-179.patch84.94 KBazinck
#178 group-support_string_entity_ids-2797793-178.patch84.94 KBazinck
#176 interdiff_168-172.txt720 bytesrichbs
#176 group-support_string_entity_ids-2797793-176.patch84.79 KBrichbs
#175 group-support_string_entity_ids-2797793-175.patch84.85 KBrichbs
#175 interdiff_171-175.txt1.37 KBrichbs
#173 group-support_string_entity_ids-2797793-172.patch84.99 KBrichbs
#171 group-support_string_entity_ids-2797793-171.patch84.24 KBazinck
#168 group-support_string_entity_ids-2797793-168.patch84.75 KBjoshuami
#165 group_content_field_data.png144.09 KBRuslan Piskarov
#164 interdiff_162-164.txt807 bytesRuslan Piskarov
#164 group-support_string_entity_ids-2797793-164.patch84.75 KBRuslan Piskarov
#162 interdiff_160-162.txt1.79 KBjidrone
#162 group-support_string_entity_ids-2797793-162.patch84.75 KBjidrone
#160 interdiff--2797793--152-160.txt1.22 KBesolitos
#160 group-support_string_entity_ids-2797793-160.patch84.81 KBesolitos
#153 group--interdiff_152-153.txt1.21 KBesolitos
#153 group-support_string_entity_ids-2797793-153.patch84.83 KBesolitos
#152 interdiff_146-152.txt2.58 KBjidrone
#152 group-support_string_entity_ids-2797793-152.patch84.76 KBjidrone
#146 interdiff-133-146.txt6.6 KBabramm
#146 interdiff-140-146.txt5.61 KBabramm
#146 group-support_string_entity_ids-2797793-146.patch83.52 KBabramm
#140 interdiff-138-140.txt430 bytesabramm
#140 interdiff-133-140.txt1.86 KBabramm
#140 group-support_string_entity_ids-2797793-140.patch82.97 KBabramm
#139 group-support_string_entity_ids-2797793-138.patch82.69 KBabramm
#138 interdiff-133-138.txt1.44 KBabramm
#133 group-2797793-133.patch85.18 KBkristiaanvandeneynde
#133 interdiff-130-133.txt32.04 KBkristiaanvandeneynde
#130 interdiff_125-130.txt13.28 KBjidrone
#130 group-support_string_entity_ids-2797793-130.patch80.81 KBjidrone
#125 interdiff_124-125.txt4.89 KBabramm
#125 group-support_string_entity_ids-2797793-125.patch81.38 KBabramm
#124 interdiff_122-124.txt10.96 KBabramm
#124 group-support_string_entity_ids-2797793-124.patch76.58 KBabramm
#123 interdiff_121-122.txt29.88 KBabramm
#123 group-support_string_entity_ids-2797793-122.patch66.34 KBabramm
#121 interdiff_120-121.txt20.66 KBabramm
#121 group-support_string_entity_ids-2797793-121.patch69.24 KBabramm
#120 interdiff_116-120.txt8.73 KBabramm
#120 group-support_string_entity_ids-2797793-120.patch49.37 KBabramm
#116 group-support_string_entity_ids-2797793-116.patch40.04 KBabramm
#115 interdiff-111-115.txt10.6 KBabramm
#115 group-support_string_entity_ids-2797793-115.patch40.06 KBabramm
#111 interdiff_103-111.txt1.3 KBjidrone
#111 group-support_string_entity_ids-2797793-111.patch34.03 KBjidrone
#110 interdiff_103-107.txt1.3 KBjidrone
#110 group-support_string_entity_ids-2797793-107.patch34.03 KBjidrone
#103 interdiff_101-103.txt544 bytesjidrone
#103 group-support_string_entity_ids-2797793-103.patch32.69 KBjidrone
#101 interdiff_100-101.txt12.46 KBabramm
#101 interdiff_93-101.txt22.91 KBabramm
#101 group-support_string_entity_ids_test_and_fix-2797793-101.patch32.97 KBabramm
#101 group-support_string_entity_ids_test_only-2797793-101.patch13.41 KBabramm
#100 interdiff_93-100.txt10.64 KBabramm
#100 group-support_string_entity_ids-2797793-100.patch19.07 KBabramm
#93 interdiff_92-93.txt488 bytesjidrone
#93 group-configurable-entities-as-group-content-2797793-93.patch15.12 KBjidrone
#92 interdiff_13-92.txt6.74 KBjidrone
#92 group-configurable-entities-as-group-content-2797793-92.patch15.54 KBjidrone
#58 group-configurable-entities-as-group-content-2797793-58.patch242.96 KBPerignon
#48 group-configurable-entities-as-group-content-2797793-48.patch242.54 KBedwardchiapet
#47 group-configurable-entities-as-group-content-2797793-47.patch219.46 KBedwardchiapet
#42 2797793-configurable-entities-as-group-content.patch28.69 KBSocialNicheGuru
#39 2797793-39.patch219.71 KBlawxen
#39 interdiff-2797793-38-39.txt561 byteslawxen
#38 interdiff-2797793-37-38.txt1.89 KBlawxen
#38 2797793-38.patch219.14 KBlawxen
#37 interdiff-2797793-20-37.txt223.6 KBlawxen
#37 2797793-37.patch219.95 KBlawxen
#23 nopatch.png35.45 KBlawxen
#23 applaypatch.png46.78 KBlawxen
#20 2797793-20.patch12.21 KBdravenk
#20 interdiff-13-20.txt2.32 KBdravenk
#6 2797793-6.patch6.99 KBseanB
#11 2797793-11.patch6.86 KBericras
#12 2797793-12.patch10.56 KBseanB
#12 interdiff-11-12.txt3.7 KBseanB
#13 2797793-13.patch9.89 KBseanB
#19 interdiff-19.txt2.28 KBdravenk
#19 2797793-19.patch12.17 KBdravenk

Issue fork group-2797793

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

    Support from Acquia helps fund testing for Drupal Acquia logo

    Comments

    kallehauge created an issue. See original summary.

    kristiaanvandeneynde’s picture

    Hi there!

    Currently Group only supports content entities because of the restriction of entity reference fields. I guess it would be possible to write an update to change this so we can allow menus to be added to groups as well. It used to be possible in Group 7, we just haven't built in support yet in D8 because no-one seemed to have a valid use case.

    Either we try to get this in now before we hit RC1, but that might introduce a BC break. Or we try to get this done in 8.2.x. I'd love to support it, though. It's just that Group currently uses the same trick Comment does to have "dynamic" entity reference fields and that functionality is far from perfect in core either.

    ericras’s picture

    I'd someday like to hook up groups with https://www.drupal.org/project/asset_injector which uses ConfigEntityBase. This would be a nice feature.

    mhmhartman’s picture

    Title: Configurable entities » Configurable entities as group content

    +1 for adding menu's as group content. Do you have any pointers? We might have time to work on this for our current project.

    I changed the issue title to make this easier to find.

    seanB’s picture

    I don't think there is a restriction on entity reference fields? For example:

        $fields['user_role'] = BaseFieldDefinition::create('entity_reference')
          ->setLabel(t('User role'))
          ->setDescription(t('The role of the associated user.'))
          ->setSetting('target_type', 'user_role')
          ->setSetting('handler', 'default');
    

    Or in the webform module:

        $fields['webform_id'] = BaseFieldDefinition::create('entity_reference')
          ->setLabel(t('Webform'))
          ->setDescription(t('The associated webform.'))
          ->setSetting('target_type', 'webform');
    

    I think this can be done without major changes.

    seanB’s picture

    Status: Active » Needs review
    FileSize
    6.99 KB

    While working on the groupmenu module for D8 this patch worked for me. The only problems I found where:

    • No views integration for config entities. You have to provide a custom list.
    • The autocomplete when relating entities doesn't work. The form needs to be overridden to use a custom autocomplete callback.

    Please review!

    kristiaanvandeneynde’s picture

    The patch looks good, but the functionality is still something I'm not sure about. This patch would also need an update hook for the existing websites and would be a backwards compatibility break API-wise. I'd definitely reconsider this for an 8.x-2.x branch, but right now it's a bit too risky to work on right before we ship a release candidate.

    Regarding the group menu module: Have you considered the suggestion of making menu links group content and using one big menu?

    seanB’s picture

    Thanks for your feedback. I've considered using 1 big menu, but I'm currently working on a site with 4300 groups (most of them have menu's). Having a big menu with thousand of links introduced issues in whole bunch of other area's in the interface (as well as permissions). Besides that you can't have multiple menu's per groups (like a main menu and a footer menu for example).

    I think breaking the API now, vs maintaining 2 branches is something to consider. It's not a big change in the API, modules calling the methods don't need to change, only modules implementing/overriding them. Reimplementing Group or GroupContent is probably not a common use case. In the end it is your call off course, but for me it would seem this change is not worth the trouble of maintaining an extra branch.

    Plus this patch makes it possible to add webforms as group content, one of the things I was going to work on next. The ability to add config entities makes a lot of new things possible, so even though it is risky, the high impact of this change in terms of new possibilities could make it worth while. So I would kindly ask you to reconsider, as the D8 beta change policy states:

    Certain other issues with high impact and low disruption at committer discretion only.

    Will work on the update hook asap.

    kristiaanvandeneynde’s picture

    I see your point. I can definitely see the benefit but the implementation might be tricky. Config entities tend to have dependencies for instance. For the Group Menu module: Having thousands of menus also means having thousands of config entities that need exporting when syncing sites. There's all sorts of cans of worms we might be opening here.

    About maintaining two branches, I need to clarify something.

    Group 7.x-1.x was ported to Group 8.x-1.x from scratch. Some concepts that didn't work were scrapped, some were added, some things were completely rewritten (e.g.: content plugins). But the main idea behind the module remained the same and the goal was to get something working rather sooner than later.

    Group 8.x-2.x will not be an additional branch, but a replacement of Group 8.x-1.x. It will have the exact same functionality to begin with, but the classes will follow PHP principles better to allow for unit testing. The second release will be a developer release: Better API, cleaner code, more abstraction, ...

    So once 8.x-2.0 is out, 1.x may stop receiving updates and (sub)module maintainers may be asked to upgrade. Their code will generally not have to change except for how they use the Group API.

    seanB’s picture

    Yes, adding config is indeed tricky (no views support, import / export issues, dependencies). There is some discussion on this topic in the groupmenu module as well. As I stated over there.

    The performance should not be a big issue, the webform module has a test for this (see the discussion in #2828461: Change config entity to content entity and the test in http://cgit.drupalcode.org/webform/commit/?id=f82477e). This should be fine. The same goes for the configuration issues. Webform has the same issues. I think using config_split and/or config_ignore can solve these issues if you want to (most advanced sites use these modules anyway to deal with block_content and webform issues).

    Thanks for explaining the plans for 2.x. Sounds like this is something that could take some time. It would be great to have groupmenu working for the 1.x branch as well (I could patch the module, but still). I just posted a working version of groupmenu depending on this patch in #2716443: Drupal 8 version (also see the sandbox https://www.drupal.org/sandbox/seanpenn079/2868316). You could test that to see how it is working if you like.

    ericras’s picture

    Here's a re-roll of #6 that will apply to the latest dev state. No changes.

    For anyone wanting to use it: you either have to apply the patch and install Group from scratch -- or if you already have Group installed: alter the `group_content_field_data` table to change entity_id to VARCHAR(32).

    seanB’s picture

    Added extra changes to new methods in GroupContentStorage.
    Also see #2716443-51: Drupal 8 version .

    seanB’s picture

    idebr’s picture

    Status: Needs review » Needs work

    The patch works as expected, however:

    For anyone wanting to use it: you either have to apply the patch and install Group from scratch -- or if you already have Group installed: alter the `group_content_field_data` table to change entity_id to VARCHAR(32).

    There should be an upgrade path for this.

    dravenk’s picture

    The patch could not be apply
    `sudo git apply -v 2797793-13.patch`
    ---
    Skipped patch 'group.module'.
    Skipped patch 'src/Entity/Group.php'.
    Skipped patch 'src/Entity/GroupContent.php'.
    Skipped patch 'src/Entity/GroupContentInterface.php'.
    Skipped patch 'src/Entity/GroupInterface.php'.
    Skipped patch 'src/Entity/Storage/GroupContentStorage.php'.
    Skipped patch 'src/Entity/Storage/GroupContentStorageInterface.php'.

    dravenk’s picture

    `cd modules/group/`
    `git init`
    `git add .`
    `git commit -m 'init'`
    `git apply -v 2797793-13.patch`
    `rm 2797793-13.patch`

    dravenk’s picture

    I create a module also needs this patch
    This patch #2900635: Group Vocabulary provide you add taxonomy vocabulary to group type content.

    dravenk’s picture

    Assigned: Unassigned » dravenk
    dravenk’s picture

    Status: Needs work » Needs review
    FileSize
    12.17 KB
    2.28 KB

    I create a upgrade patch change implements

    alter the `group_content_field_data` table to change entity_id to VARCHAR(32)
    dravenk’s picture

    skyredwang’s picture

    Assigned: dravenk » Unassigned
    Priority: Critical » Normal

    Feature request cannot be critical

    seanB’s picture

    Just found a serious possibly HUGE performance issue with this patch:

    • The patch changes the entity_id column for the table that saves the relationship between a group and an entity
    • Let's say you create a view (for example a media browser) that filters by group content, defining a relation between the media table and the group_content_field_data table
    • You now added a join between a numeric field (media: mid) and a varchar field (group_content_field_data: entity_id)
    • MySQL ensures that your indexes are not used if the data types of the columns do not match (https://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html, search the page for 'Comparison of dissimilar columns')
    • When no indexes are used for 12,000+ records, the query slows down from 90ms to about 60 seconds!

    We are probably going to solve this by creating a media browser using a search API based view, but this is something to seriously consider!

    lawxen’s picture

    FileSize
    46.78 KB
    35.45 KB

    MySQL ensures that your indexes are not used if the data types of the columns do not match (https://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html, search the page for 'Comparison of dissimilar columns')

    I have some test on this:
    Building two site, execute the same sql

    EXPLAIN
    select * from
    group_content_field_data gf
    join node n on
    gf.entity_id = n.nid
    where gf.entity_id = 12

    Result:

    1. apply patch 2797793-20.patch:
    2. no patch :

    conclusion:

    The useless index on diffrent mysql field type when use join operation really making drupal extremely slow.

    I think we should redesign the structure of groups on content entity and config entity based on

    How core works:
    If we take a look at how entity reference fields work in Drupal, then you select 1 type of entity (Node, comment, menu...) and then it creates a table for the field with an id of a machine name (Varchar) for config entities or an Integer for non-config entities.

    lawxen’s picture

    Status: Needs review » Needs work
    dravenk’s picture

    This patch is so wrong. When you request resources use include multiple level property , It will cash 500 server error. The e.g url like that

    ?include=entity_id,entity_id.uid

    steps to reproduce:

    with JSON API 8.1.3 , Group 8.x-1.x
    1. create group
    2. enable install gnode plugin to group
    3. create one article
    4. relate one article to the group
    4. request /jsonapi/group_content/{group}-group_node-article?include=entity_id,entity_id.uid

    After applyed this patch, you will got the error in the response.

    dravenk’s picture

    lawxen’s picture

    How core works:
    If we take a look at how entity reference fields work in Drupal, then you select 1 type of entity (Node, comment, menu...) and then it creates a table for the field with an id of a machine name (Varchar) for config entities or an Integer for non-config entities.

    This let's group imitate entity reference's design, but it can't

    Reason:

    \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem
    entityreference-propertyDefinition
    [entityreference-schema]
    Entity reference do the work of choosing between string or integer based on field, It's field who create new table, not entity reference, So if group want to use the design, it should learn the architecture of **field, not entity reference**.
    The design can be used on three level:

    1. (bundle level)can't work if directly flow it's design, If imitating it, means group do sth on bundle(group_content_type or group_type):gnode_article, gnode_basic_page group_type_teacher,
      It dosn't has 'config_entity' effect on entity:group or group content
    2. (entity level)If group applies the field's design for entity:group and group_content arbitrarily, it means create a table on every entity instance. But group's cose idea(repalce og) is entity type which make it conforming to core's other component, new table on entity break it's origin idea, make it can't work with drupal core's views、 import、export, dependencies, so this level can't work.
    3. (table level)If group just use 'new table' idea on group_content_field_data, can't work with core's views、import、export..... can't work either

    So entity reference can't be used as an example to resolving group-entity-level-config-entity problem.

    Besides:

    1. newset drupal7's group dev version can't add bundle(vocabulary) ethier.
    2. drupal field's base_field_override method need to be investigated, But I can't find a proper example use case in drupal core
    lawxen’s picture

    let's figure out the drupal8's content_entity and config_entity, use node for example:
    Configuration entities use the new Drupal 8 entity API in order to keep configuration in the database.

    1. node is content_entity_type
    2. content_type is Config_entity
    3. bundle is subset or variant of content_entity_type
    4. node.type.article is a content_type instance, this config_entity instance store the information of bundle article, Config_entity can't has fields, but article can has fields.
    5. bundle != config_entity instance
    6. But bundle and config_entity instance has consanguineous relationships.

    what group need do is make each group type's instance has some restrictions on some bundle, not config_entity.

    How to do this?

    This may be a good direction

    Create a new config_entity type called group_config or group_restrict or whatever else;
    This entity type has two properties that group need:

    1. group
    2. bundle----->because bundle is defined by confige entity, so this point to corresponded config_entity instance to get it's information.
    lawxen’s picture

    The entity_id is a BaseField of group_content_type, so it can't like bundle's entity reference field: every entity reference field create a new table
    So if we move entity_id from BaseField to BundleField, we can solve #22 's performance problem.

    Because it's a very different implementation, so I created a new issue: https://www.drupal.org/node/2920474

    lawxen’s picture

    kristiaanvandeneynde’s picture

    Comment #22 is serious enough for me to be extremely hesitant about implementing this. It might be better to start working with a GroupConfig/GroupConfigType instead to follow the model set out by GroupContent/GroupContentType instead of having one column in the database trying to do both.

    lawxen’s picture

    @kristiaanvandeneynde
    I agree with #31 saied, what i think on #28 is very similar with this, but I just consider one level: group_config config entity type, #31 furthermore define it as GroupConfig/GroupConfigType

    kristiaanvandeneynde’s picture

    Every relation between a group and its content/config needs to be fieldable, hence content entities.

    lawxen’s picture

    Assigned: Unassigned » lawxen
    seanB’s picture

    #31 in general +1 to having another GroupConfig / GroupConfigType. Not sure what the implication are, but I guess that is a very serious change.

    @kristiaanvandeneynde, could you provide some insight on the work that needs to be done to implement this? I might be able to help push this forward, and since I have some sites running Group Menu 8.x, it is also important for me to provide an upgrade path.

    kristiaanvandeneynde’s picture

    This is actually work I had planned for an 8.2.0 release, but here's the basic idea: A GroupConfig entity that is almost exactly like GroupContent, except it links config entities instead of content entities. The entity_id field would obviously be a string base field in this case.

    This has several potential downsides, which I'll list below, so there may be a better way to tackle it. I just hadn't gotten around to fleshing out this idea yet because it was rather low on my priority list.

    Potential downsides:

    • Two entities that do the same thing: Connect something to a group.
      • I had thought of adding another base field to GroupContent to reference config entities with, but that would imply dynamically selecting a database column on many levels: Direct field access, views, ...
      • I had also thought about a shared base class and shared handler classes. This might make the code more readable and clearly outline what parts are shared and what parts are specific to one or the other.
    • A code distinction also implies a UI distinction. I.e.: People need to know when they're adding config vs content. We might need to duplicate our views handlers for relationships, filters, etc.
    • Our group content enabler plugins should ideally not become two systems as well. Instead, we should dynamically find out whether the enabled entity type is config or content and then create the right Group(Content/Config)Type based on that.

    As you can see, it can get real messy real quick. A cleaner way would be to figure out a way to dynamically select one of two columns and feed that into the entity_id field based on whether the target type is config or content. Perhaps with a custom field storage?

    Core suffers from the same problem with comments, but because hardly anyone adds comments to config entities it doesn't really show :) If we were to come up with a custom field storage for the above use case, we could contrib that back to core for comments.

    lawxen’s picture

    Implement #31's GroupConfig / GroupConfigType, and a note_type plugin GroupConfig instance
    Functional work well, but

    1. No hook_update yet
    2. No test code yet

    Maintainer shouldn't spend time review the code before completing above two tasks.
    But hope groups users can test this patch.

    lawxen’s picture

    1. Add hook_update
    2. Remove two test purpose file in gnode submodule

    Todo:

    1. Create and modify dozens of test code:
    lawxen’s picture

    Change

    1. Add group_config crud menu tab.

    Todo:

    1. Create and modify dozens of test code.
    blakemorgan’s picture

    When installing this patch with composer, the following error appears:

    - Applying patches for drupal/group
        https://www.drupal.org/files/issues/2797793-39.patch (Configurable entities as group content)
       Could not apply patch! Skipping. The error was: Cannot apply patch https://www.drupal.org/files/issues/2797793-39.patch
    

    And in composer.json:

            "patches": {
                "drupal/group": {
                    "Configurable entities as group content": "https://www.drupal.org/files/issues/2797793-39.patch"
                }
            }
    SocialNicheGuru’s picture

    #39 has to be rerolled

    Hunk #2 FAILED at 235.
    1 out of 2 hunks FAILED -- saving rejects to file src/Access/GroupPermissionHandler.php.rej

    the lines are:
    more src/Access/GroupPermissionHandler.php.rej
    --- src/Access/GroupPermissionHandler.php
    +++ src/Access/GroupPermissionHandler.php
    @@ -235,7 +244,7 @@
    $permission = ['title' => $permission];
    }

    - // Set the provider if none was spec
    + // Set the provider if none was spec.
    $permissions[$permission_name] = $permission + ['provider' => $provider];
    }

    SocialNicheGuru’s picture

    This is just a reroll of 39 for composer.

    I am getting this error:

    Symfony\Component\DependencyInjection\Exception\LogicException: Service [error]
    'group_config_type.breadcrumb' for consumer 'breadcrumb' does not implement
    Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface. in
    drupal-8.4.2/html/core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php:164

    dravenk’s picture

    After update group latest. This module can't be apply into repertoty. and depend on this patch build will broken.
    see:
    https://www.drupal.org/project/group/issues/2810131#comment-12364263

    @seanB
    Do you have any good idea?

    seanB’s picture

    @dravenk I guess the patch should be properly rerolled against the latest version?

    alexd73’s picture

    Version: 8.x-1.0-beta1 » 8.x-1.0-rc1

    I have this error

    patch -p1 < 2797793-configurable-entities-as-group-content.patch
    patching file group.install
    patching file group.links.action.yml
    patching file group.links.task.yml
    patching file group.module
    patching file group.routing.yml
    patching file group.services.yml
    Hunk #2 FAILED at 106.
    1 out of 2 hunks FAILED -- saving rejects to file group.services.yml.rej
    patching file src/Access/GroupPermissionHandler.php
    Hunk #1 succeeded at 148 (offset -5 lines).
    patching file src/Entity/Controller/GroupTypeController.php
    patching file src/Entity/GroupType.php
    patching file src/Entity/GroupTypeInterface.php
    
    SocialNicheGuru’s picture

    the patch introduced this error:
    drush cr
    Symfony\Component\DependencyInjection\Exception\LogicException: Service [error]
    'group_config_type.breadcrumb' for consumer 'breadcrumb' does not implement
    Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface. in
    drupal-8.4.3/html/core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php:164

    edwardchiapet’s picture

    This is a re-roll of #42 against dev that resolves some patch errors.

    edwardchiapet’s picture

    Missed some updates from the patch in #13 in my last comment #47. This updated patch should work against dev.

    kristiaanvandeneynde’s picture

    While the approach in the latest patch seems okay, it essentially cope-pastes everything regarding group content and names it groyp config :) We might want to look into getting rid of code duplication there.

    For Group 8.2.0 I was thinking of a common interface between GroupConfig and GroupContent entities for the most part, methods on GroupInterface that will retrieve any entity regardless of type and opening up the access checks to handle both types of relations. We shouldn't be copying this much code :)

    scotwith1t’s picture

    Version: 8.x-1.0-rc1 » 8.x-1.0-rc2
    Assigned: lawxen » Unassigned

    Been spending a bunch of time recently looking at the pros and cons of Group and wondered how much of a priority this particular issue has in the future development path? It seems that many of the biggest missing pieces are being able to connect Group to a config entity (taxonomy, menus, webform, core workflows?) In D7 using OG, additional OG contrib modules handled all of these cases for us and worked really well, but I would love to see Kristian do his thing, writing beautiful, clean code that does this "the Group way", instead of a big copy/paste job as he pointed out. I have no doubt he will ultimately come up with an awesome solution for this. Thanks, and looking forward to seeing this module continue to grow, improve, and expand!

    seanB’s picture

    Recently a similar problem was solved in the entity usage module (in the same way as dynamic entity reference module). See #2951093-4: Support alphanumeric entity IDs and config entities for more info.

    Basically, the idea is to add a separate column for string IDs to the table and make the module handle this dynamically. This would prevent a lot of the duplicate code.

    kyuubi’s picture

    Hi,
    Any updates on this? Our use case is a decoupled Drupal CMS powering a bunch of react frontends and using Group module for relationships between content and said groups.
    Works beautifully with nodes, but we also need to have menus per group (menus of each website).
    This would be truly amazing to have in Group (along other things like vocabs for ex).
    Thanks for such a great module!
    Cheers,

    shortspoken’s picture

    I am also interested if there are any updates in this issue as Menus per Group would be very useful when using the Group module to manage subpages. Or are there other good ways to achieve this? Cheers.

    adamfranco’s picture

    In testing patch 48 I noticed an error in the GroupConfigCardinalityValidator where it was calling an undefined method on the Group on line 119 of src/Plugin/Validation/Constraint/GroupConfigCardinalityValidator.php:

    It is calling

          $entity_instances = $group->getConfigByEntityId($plugin->getPluginId(), $entity->id());
    

    which doesn't exist.

    Should that missing method be added or should this call be changed to something like the following since this is looking up a relationship entity rather than the config itself?

          $entity_instances = $group->getContentByEntityId($plugin->getPluginId(), $entity->id());
    
    SocialNicheGuru’s picture

    @adamfranco I can confirm that getConfigByEntityId is not defined in the group module but getContentByEntityId is.
    should the patch be re-rolled?

    adamfranco’s picture

    It needs to be re-rolled to not get a fatal error when relating a config entity like a menu using groupmenu. See #2990525: Not compatible with latest "Configurable entities as group content" for a patch to groupmenu to make it compatible with the latest patches here.

    As mentioned in #54, I think getConfigByEntityId() needs to be defined but I'm not totally sure.

    kyuubi’s picture

    Guessing it's probably related to @adamfranco's comment.
    But when I apply #48 my group content Search API index fields no longer work.

    Perignon’s picture

    andrenoronha’s picture

    Hi, thanks for the patches.

    I need to use Group Menu module which says I have to apply this patch so it can work.

    I tried patch #58 but I got this:

    patching file group.module
    Hunk #6 FAILED at 183.
    Hunk #7 FAILED at 202.
    Hunk #8 succeeded at 223 (offset -8 lines).
    Hunk #9 succeeded at 311 (offset -8 lines).
    Hunk #10 succeeded at 563 (offset -12 lines).
    Hunk #11 succeeded at 579 (offset -12 lines).
    2 out of 11 hunks FAILED -- saving rejects to file group.module.rej
    

    When I install the menu component at a group type I got the "The website encountered an unexpected error. Please try again later." white page bug.

    :(

    Perignon’s picture

    You have to apply the patch to the development branch and not the RC.

    andrenoronha’s picture

    Thanks, Perignon! It's working now.

    SocialNicheGuru’s picture

    Can I use this to allow custom entities to be group content?

    MLZR’s picture

    Hello, what a work to make all this happen! Good to see.

    I am want to use ECK and group. Unforunately group only support nodes, not a thing as ECK.

    So I see this thread and when I read this, this wil solve my problem. Yeah!

    So I installed the patch with no issues. Now I see "Group content type - Adds content type to groups both publicly and privately."
    When I do this and just enter for what it is, then I go to the 'content' page (Home>Administration>Groups>Group types>Edit Group one ) I was expecting to see the ECK fields. Bud they are not there.

    I think I have to do something else to set a relation between 'group' and my ECK types. Bud reading this thread + fiddling arround, I can't figure out what I have to do to make this work.

    Please can someone explain how to configure this? I think the module + patch is ok, bud I have to config somewhere a 'missing link'

    Thanks,
    Marchello

    scotwith1t’s picture

    @SocialNicheGuru See #5 on this list https://www.deeson.co.uk/labs/9-reasons-group-drupal-8-awesome, that should help.

    @marchellodepello unless your custom ECK-based entity is a config entity and not a "real" entity (I believe ECK builds the latter?), I'm not sire this issue pertains to your situation. I believe you may also need to do #5 above to connect your custom entity type to Group.

    MLZR’s picture

    @scotself, thank you for picking up my post!
    I do not understand what you mean by 'your custom ECK-based entity is a config entity and not a "real" entity'. Ahum, what is a custom or real entity? A entity is a entity to me. Als as far my knowledge goes a ECK creates entity's.
    Regarding the #5 I have a look at the patch, and compare this to the patch I applyed (#58) and it seems to me things in #5 ar still also in #58. So when I apply the pach #58 I cover the code in #5.
    Let me say; I am not a coder bud with my level of knowledge this is what i see. Excuse me when I am wrong.

    I was searching for some way to get the ECK content into groups I stumbled over issue #2723911
    I think this is what I have to to. And I was thinking the patch of this thread is to do this bud now in IU (remember I am not a coder). ( have this link from https://www.deeson.co.uk/labs/9-reasons-group-drupal-8-awesome on the second comment "Great Module ... There is.." wich also is the question how to reach the ECK fields.)

    ...and still I think there is a missing link; I have to config somewhere somewhat, but I just don't know what. Again I search the Drupal UI what this could be. Bud no luck. Only I think I have to make a reference field in '/admin/group/config/manage/group_[group name]group_node_type/fields'. ? Or somewhere else.

    I hope you can help me a step forward. I have the feeling i am almost there, but I have to find this missing piece...

    kyuubi’s picture

    Hi guys,
    What would be the next steps to getting this in?
    We are in terrible need of this (and group menus as consequence).
    I can see this has been going since 2016 and I'm adamant of putting this patch in the build without some idea of where we are at.
    Happy to put in the work, just need to know what next steps would be?
    Cheers

    scotwith1t’s picture

    Sorry for the confusion @marchellodepello, but there is definitely a difference between an Entity like a Node or User ("real" entity? not sure what else to call it to differentiate...non-config entity? database entity?) v. something like a Menu or View, which is entirely configuration. There are many articles about this out there, here are a few:

    https://www.drupal.org/docs/8/api/configuration-api/overview-of-configur...
    https://www.drupal.org/docs/8/api/entity-api/configuration-entity
    https://www.drupal.org/node/1818734
    https://drupal.stackexchange.com/questions/236622/what-are-configuration...

    More confusion came from me referencing "#5", which would normally refer to comment #5 on this thread, but I was specifically pointing to the same thing you were, which is mentioned in item #5 in the deeson.co.uk blog post I mentioned; that is the simple idea of extending Group Content support for a custom (content) entity with a plugin which extends GroupContentEnablerBase. This is, as you said, not very useful if you "don't code" (in my experience, you can't really harness the power of Drupal completely without getting into the code, at least at some level). Unfortunately, it isn't possible at this point to extend Group Content support to other entities (including ones you make using ECK) via the UI, you have to have that small bit of custom code mentioned in that post and the d.o post you mentioned.

    If ECK doesn't build configuration entities (don't think it does), following this issue will not help you unfortunately. Again, this thread only deals with configuration entities like Menus and making them available as Group Content. Hope that clears up things a bit.

    scotwith1t’s picture

    @kyuubi et al, I think that @kristianvandeneynde is right and that this patch, while well-intentioned, isn't really the right approach as it duplicates the GroupContentEnabler code in the form of GroupConfigEnabler and creates 2 places to maintain what is essentially the same code. I made a similar mistake in trying to get sub-groups to work, basically copying all of gnode as ggroup and trying to make that work. This approach also, unless I'm missing something still requires a plugin for each type of config entity too; the only one available initially is the content type config entity (provided by updates in the patch to gnode), so one would still have to create a plugin in order to extend this to other config entities?

    To take it a step further, it doesn't make sense in my mind either to require the user to be comfortable coding in order to tie existing entity type bundles to a Group type. Seems like the better approach would be to offer a UI which would:

    1. allow both content AND config entities as "Group Content"
    2. just work for all available entity types in the system

    I think the second point is important because folks like @marchello above will not be comfortable writing a custom module with a plugin file just to add the capability to, in his instance, a content entity type created by ECK module. Ideally, you'd go to a single UI page, see all available Config Entity (Menus, Contact forms, etc) types and Content Entity types (Commerce Products/Orders/etc if your site has Drupal Commerce, for example) that Drupal knows about and choose which ones to allow/create the Group Relation entity between. You could even set some options (group and entity cardinality) from the UI. Just throwing this idea out there, but if we're going to keep going down this path I can instead provide feedback on the patch. Just feels like we should step back and think about it a bit more before plowing ahead with this.

    To be sure, I'm just as anxious as anyone to get this (as well as #2949408: Allow group admins to create user account and add to group) going as I have a large site we're starting to build out which, put simply, will be next to impossible to do without this piece being figured out and working well. Group-specific menus (this issue) and Group assignment during account creation are critical to our project.

    kyuubi’s picture

    Hi @scotself I tend to agree with you although I'm not sure about the route of hosting both config and content entities in the same process.

    Although I am not familiar with this module's code, I imagine this approach would require a significant rewrite to the already stable process of plugging in content entities. It's also likely that we would need to diverge the implementation a bit to handle both.

    However, the concept of having a base (or core) plugin system for both config and content entities, that can then be extended for each with it's own variants makes a lot of sense to me.

    From a UI perspective, I agree the separation might not make as much sense. Content and Config entities are more of a technical concept and a site manager won't really distinguish between them.

    Bottom line, I would vote for an approach that either:

    a) Implements a separate config entity plugin (what this issue tries to do) without unnecessary code replication.
    b) Gut out the current content entity plugin system to a base that works for both content and config (shared code) and then provide extended classes to deal with each entity type specificities.

    Hope that makes sense and keen to hear everybody's thoughts.

    We are also extremely dependant on the viability of this for our project and could potentially allocate some resources to helping this become a reality, provided we can pair up with someone to give us some insights into how Group works.

    MLZR’s picture

    Hi @scotself,
    yes, this clears up some confusion... about the 'config' and 'content' entities, and about the #5 patch :-)
    Regarding config/content entities, now I see the difference.
    Content entities are the content, config entities are holding configuration things.
    As an example, when I install plain Drupal and create an 'article', I create a content-entity which is a node of type atricle.
    Somewhere there is a defenetion of what a 'node' is. This defenition is a config- entity.

    Am I right?

    Now, when I look back to my case, then things are a bit confusing.. when I make a new ECK entity type, ( /admin/structure/eck/add ) there I configure a new ECK- entity type. It looks to me I am making a 'config-entity' on that spot? And when I add content to that ECK-type, then I make content.
    Regarding to your text "this thread only deals with configuration entities", it should deal with the config-entity I made on /admin/structure/eck/add ?

    Probably I am mistaken here :-|
    Bud here it is confusing for me. Is 'making a new ECK type' not equivalent to making a 'config-entity'?

    scotwith1t’s picture

    @kyuubi I agree with all of your comments completely. It is nice from a coding perspective to be able to just create a plugin and extend or alter the functionality of GroupContentEnablerBase, but I would imagine it would be possible to both support known entity (content AND config) types out of the box (a UI of some sort) while still allowing for Plugin files which can override the base functionality.

    We're in the same boat and I've reached out to @kristiaanvandeneynde to recruit his help but he has recently had a baby and job situation change, so it may be tough to get him engaged at the level that it sounds like we need here, but I have high hopes he'll rediscover his love for this module soon and jump in :)

    @marchellodepello I have actually never worked with ECK module, but it seems to work very similar to core Entities: the Entity type itself is DB-based (and thus should be able to integrate with the GroupContentEnablerBase) but the definition of each bundle of your ECK entity type is a config entity (which can be exported via the new Config Mgmt system).

    To all who have been working to try and get this working, I totally applaud the effort, but I honestly don't think @kristiaanvandeneynde will merge it in using the approach in the patch here. The code for this module is meticulously well-written and I don't think code duplication like this will fly (see his comments in #31, #36, and #49).

    MLZR’s picture

    Hi @scotself, thanks for your reply's.
    In this moment I am thinking for my own case a go an other direction and not use ECK.

    Bud for the ones reading this and want to have a solution for the problem I came along, look ad:

    -> issue 2775119 where kristiaanvandeneynde wrote at point #4 he wants to enable all kinds of entities bud this is 'parked' untill an other issue is checked. So hopefully this is gonna happen.

    -> and Add example for how to expose custom entity to Group module for insight and learning how to get entities into the scope of Group.

    -> and this also as an example of how to.. Create a custom content entity.

    Maybe I help someone with this POI's.

    Marchello

    kyuubi’s picture

    Hi @scotself,

    Been looking a bit more into the module's code and I feel introducing a similar concept like we have for GroupContent would always lead to some duplication as well as considerable level of complexity, since we probably would need to refactor a lot of existing code to support both of these things.

    It's starting to feel to me that an abstract level that we should consider the "Content" in GroupContent to encompass all type of entities. This would mean we can leverage the existing system we have and just add in support to both entities where needed.

    As @kristiaanvandeneynde pointed, out maybe a way of dynamically selecting the id based on the entity type. The Group Content entity supports an entity reference field which by itself is able to target both config and content entities and all the other concepts that currently exist (like the plugin system, two step adding process, fieldable group content etc) all make sense to exist for config entities.

    Looking at the approach the Group Menu module is taking seems to fall well with the above. It obviously relies on there having some support for config entities (currently with this patch), but it extends the GroupContentEnabler plugin. Using this module as a reference could actually be a nice way to try to develop this support (keep refactoring the GroupContent system until the Group Menu module works).

    Then each contrib module could take a similar approach to extending GroupContentEnabler to provide support for their config entity of choice.

    Bottom line, I think the original patches were more inline with the correct approach, we just need to come up with a way to handle the entity_id without changing the database column which proves to be a performance issue. Either having two fields (with a process to switch based on the entity type) or something similar to the quoted approach core takes.

    Not sure this makes sense or is 100% viable, given my still lack of knowledge of the module's internals, but conceptually seems like a reasonable approach.

    Thoughts?

    PS: If there are a number of interested parties to getting this done, maybe we should get together on slack or something (pm me @kyuubi) and discuss further. I feel that in the pace of an issue queue this will take a long long time to get done.

    larowlan’s picture

    Also #2931932: Explore adding support for config entities demonstrates that workbench_access (doesn't require workbench moderation or content moderation) works with config entities.

    You could wire up a group - to workbench access bridge

    larowlan’s picture

    Oh, but workbench access doesn't have an opinion on view access, while group does. Nevermind

    kyuubi’s picture

    I think at this stage I see only two viable approaches:

    First we apply the changes from the initial patch (except the schema changes) that basically generalise behaviour across the code to all entities not just content. Then either:

    a) Create GroupConfig content entity (that uses a string id in storage) and GroupConfigType class, then refactor code on existing system accordingly. Or:
    b) Try GroupContent for both types of entities by having 2 columns in the schema and some sort of logic to switch between them.

    The a) approach has the advantage of being more standard. We can probably minimise duplication to an extent by creating some base classes to share code where possible, but the approach will likely involve a higher amount of code refactoring.

    The approach b) on the other hand, if achievable should be involve less messing around with Group's code, but seems quite more challenging and somewhat less elegant to an extent.

    The Dynamic Entity Reference module solves this problem, but unfortunately relies on triggers which don't work on any hosting environment (e.g Acquia).

    Also from conversation with @larowlan it seems Comments limits itself to integer entity ids, even for content entities, and as he pointed out Group as it stands probably doesn't support custom content entities with string entity ids, so the issue isn't completely scoped to configuration entities.

    At this stage it would be awesome if @kristiaanvandeneynde could chip in if there is any of these directions that wouldn't be considered for this module, just to avoid us spending time in the wrong direction. As soon as we have a clear path to move forward I can probably allocate some time to getting this done, maybe with some other interested parties help?

    scotwith1t’s picture

    I've reached out to Kristiaan, hopefully we will have his input sooner than later. As you said @kyuubi, I don't think any decision should be made without his input so as to not waste time on approaches that won't be merged in. I'd love to work through this in a group setting on slack, maybe he will be able to set some time aside to do that.

    SocialNicheGuru’s picture

    is there a way for me to uninstall just this patch ie. the update 8015 that it introduced? I can't uninstall and reinstall the group module within my site. thanks.

    kristiaanvandeneynde’s picture

    I am still on the fence whether I actually want this behavior at all. I see a lot of mentions for group menus: A use case I have solved myself about a year ago but never got around to contributing back. And that code is out of my hands as I work at a different agency now. I am curious as to what other use cases people might have that require you to have config entities being grouped.

    But let's assume there are good use cases and that we want this in Group.

    The first and foremost thing you should all be aware of is that Group uses the same hack core uses for comments to allow our entity (GroupContent vs Comment) to reference any content entity. This is behavior that Commerce uses as well and is at best to be described as "vague". See #2346347: Finalize API for creating, overriding, and altering code-defined bundle fields

    Secondly, I may have closed caseylau's issue too quickly, because it might be worth investigating the ramifications of moving the entity_id field to a bundle field. If that means it gets dedicated tables where content entities have a numerical ID and config entities a varchar, then great! Might be worth exploring that route. The issue I'm talking about can be found here: #2920474: Move group_content entity type's entity_id field from base_field to bundle_field

    If that is not a good solution, then seanB's mention of trying to use two columns instead of one might work, but I dread the amount of confusing code and bugs that will probably bring us. (see #2951093: Support alphanumeric entity IDs and config entities)

    Finally, the more I think about it, the more I am opposed to copy-pasting a ton of code just to reproduce functionality we already have elsewhere. Copying that much code is a code smell and we should avoid it at all cost. I deeply apologize if I encouraged people to try and pursue this route, but I have had very little investment in this issue so far.

    So what I'm trying to say is this:

    1. Does anyone have a good use case for adding config entities to groups, other than menus? If not, I'll gladly go into detail as to how I solved that use case using what we already have (if I can remember properly).
    2. If so, can anyone investigate caseylau's proposal in order to facilitate this without changing too much code?

    With regard to having to write plugins to support certain entity types: This can be easily solved by writing a deriver that generated basic plugins based on what you select in a simple admin UI. I've been meaning to add such a thing for content entities, but have yet to get around to that.

    I am currently in the process of finalizing some tests for new cache contexts which will allow me to tag Group 1.0 in the very near future. I sadly can't spend any time on this issue for that very reason. Sponsored time could work, but as it stands I am stretched thin trying to finally get a full release out the door.

    scotwith1t’s picture

    Thanks for getting back to us, and hope the new job and kid are both going well!

    For us, there are multiple critical, indispensable (like we really can't launch the site without this type of functionality) use cases:

    • Menus - each group needs to be able to define and control menus for their pages, which only they can place on a page and edit but are still available for public display.
    • Taxonomy - each group should be able to define and control its own vocabularies and terms which it can use to tag Group Content and is still available for public display.
    • Contact forms - each group should be able to define and control its own contact forms which they can use for receiving public input for their Group.
    • Workflows/moderation (core) - each group should be able to define and control its own Workflows/transitions (all config entities, i believe) to customize the workflow to be used in conjunction with that Group's roles/permissions for controlling the publishing of Group content.
    • Blocks - each group should be able to define and control its own Blocks.
    • (not config entity-related, but still mission-critical) User Registration #2949408: Allow group admins to create user account and add to group - must be able to create new accounts and assign them to a Group at the same time.

    All this creates the possibility to use Group as a "microsite" building tool, with each Group kind of having its own little, self-contained world, with everything it needs to control the creation of its content while higher level stuff shows lists of Groups to join, links to contact forms for each group, lists of Group content by Group term, etc. Bonus: config entities can be exported/imported (read: deployed via git) via Config Mgmt (yml files).

    I don't know about caseylau's proposal but I will try to make some time to look at that soon.

    Chris Burge’s picture

    @scotself does an excellent job of summing up what we would need to use Group to build microsites. I'm currently maintaining a private Drupal 7 distribution for an enterprise client, and it makes use of Organic Groups and the various add-on modules to support group-specific menus, webforms, FPP's, etc. Webforms are the big one, however. And they're config in D8. The client has 100+ webforms across dozens of groups.

    kyuubi’s picture

    Hi @kristiaanvandeneynde,
    Thanks for chipping in, your guidance is really appreciated.

    In terms of use cases, the others have done a good job explaining the common ways where config entities become critical for people that use Group.

    I find that a good portion of Group users, choose this module in order to scope parts of a website as a collection (can be microsites as some mentioned).

    For my use case, Drupal is used as a content service API, powering multiple ReactJS applications. The way we segment content for each application is via Groups. This functionality is essential for us, mainly for menus (so that we can scope sets of menus to each group and serve them to the apps) as well as taxonomies (each application can have its own taxonomies). Editors are then also segmented into the various groups so it's important that editor from Group A can manage menus and taxonomies of that Group without accessing others.

    I think we all agree copy pasting code is to be avoided. I still think it's likely possible to create a Config type and Config entity and then adjust the other classes to accommodate for them both, using shared based classes where needed, but let's investigate the issue you raised first and go from there.

    Thanks for the input!

    kristiaanvandeneynde’s picture

    Okay so I'm starting to see a need for supporting config entities. I just have two major concerns in addition to #79:

    1. Is core capable of supporting fine-grained config entity access? For example: Would the menu or taxonomy page actually check individual view access before listing the available taxonomies/menus?
    2. What about collisions and data leaks? If a member of Group A wants to create a 'seasons' taxonomy and then a member of Group B wants to do the same, they might get an error stating that machine name is already taken. This has two downsides:
      • They might need to choose annoying machine names such as seasons_0. Group has this too when it comes to plugins and group roles and "fixes" it by hashing the machine name. Not my finest solution :(
      • They could use this to figure out what taxonomies, menus, etc. already exist in other groups (data disclosure vulnerability)
    kyuubi’s picture

    Hi @kristiaanvandeneynde,
    Not sure about 1), would need to check, but I imagine it's possible.

    As for 2) you are right, but I think that is something we just can't avoid. This is something that can probably handled by developers extending Group functionality if there is a need for total isolation between groups.

    For example, this already happens with urls. You can't have groups using the same url for two separate content items.
    We have worked around that by extending a prefix (invisible to site editors) so that all urls are in fact prefixed by the group id. Again this is something I don't think should be handled by Group as it will depend a lot case by case.

    I think you can see that if we go down that road we might start questioning many things, several outside the scope of this issue.

    From my end, for what it's worth, I think if there is the ability to control access and scope content and config to a Group that would be enough and clearly in much need. There will be issues we can't fix as they are inherent to core, but most of the time developers can adapt the solution to their use case to fix them where necessary.

    imclean’s picture

    Point 2. could be something Group (or a related module) could resolve more generically. The Group module is providing some separation of content (and hopefully config) within a single website. Without knowing exactly how this is currently achieved, namespacing or prefixing, as suggested in #84, would be one way.

    This could lead to a fair bit more complexity but also a lot of flexibility. For example, if you create a vocabulary called "Seasons", the title is "Seasons" while the machine name behind the scenes is "group_name_seasons". I don't see much of a need for group admins to change the machine names of their vocabularies or other config entities.

    adamfranco’s picture

    2. What about collisions ...

    My use-case is micro-sites for university departments where I need a menu per department micro-site. The individual menus would be created by an admin prefixed with the department name at setup time and then associated with the group, so collisions wouldn't a problem. After that initial setup, group members could edit their menu as needed. Adding/removing users from a group is all that is needed to grant editing access and is something that could be delegated to users with an elevated Group-management role.

    Until this feature is available and the menu can be associated with the group, my ugly work around is to use Group to manage editorial access of content and then maintain a separate Drupal role alongside every Group that restricts access to menus using menu_admin_per_menu. This means that adding a new department member to a Group isn't sufficient to allow them to edit the microsite, they also have to be added to the department role by a site-wide admin in order to add nodes to the menu. This will probably become less and less workable as the number of departments passes 50. I look forward to this feature greatly simplifying our user/role management.

    SocialNicheGuru’s picture

    In the group view, there is an option 'Group content type'. Currently it only selects nodes.

    How can I filter by custom entity types within a group view?

    geek-merlin’s picture

    @kristiaanvandeneynde #79:
    As of the question "bundle field vs 2 base fields"... i can remember when i dug the source code of Dynamic Entity Reference and they have 2 fields on that reference field. This then has a lot of not-really-nice consequences. So my gut feeling is definitely towards bundle fields.

    geek-merlin’s picture

    Title: Configurable entities as group content » Configuration entities as group content
    scotwith1t’s picture

    FYI to all those interested in this and other D8 Group issues, I just created #d8-group channel in Drupal slack http://drupalslack.herokuapp.com. I've invited a few folks initially, but feel free to jump in there. It would be ideal if we could get Kristiaan to set aside a dedicated time slot ("Group module office hours"?) to working with us all to get this and other major Group module issues worked on, but I know how challenging being a new parent and a new job at the same time can be! Hope to hear from some of you there. :)

    carolpettirossi’s picture

    Hi all,

    I'm trying to understand the very very long drupal.org thread and the proposed/implemented solutions. This is what I've understood so far:

    Summary
    We need that Group module supports config entities in the same way that it does support content. For example, it should be possible to relate a menu to a specific group.
    Main problem: Config entity id is a string which differs from a content entity, which has an integer id.

    Solution 1 (declined)
    Update entity_id column to varchar which would allow referencing any kind of entity (config or content) (patch from comment #20)
    Reason why it was declined: MySQL performance issues (comment #22)

    Solution 2 (declined)
    Duplication of all code related to GroupContent as GroupConfig.
    Reason why it was declined: Maintenance. Imagine if something has to change on GroupContent and you need to change in GroupConfig too...

    Solution 3
    Create a Base class which GroupContent and GroupConfig will extend. We need to take a deep look at the code and figure out what is common and what are the differences. So far, the only difference we know is that we need a varchar entity_id for the GroupConfig.
    This is basically the approach from solution 2 but without lots of duplicated code.
    I see that this would require heaps of coding. Even considering a Base class reusing code from GroupContent, a refactor in the GroupContent would be necessary too.

    Solution 4
    Create a new column entity_config_id (BaseField) in the GroupContent and let the submodules to handle where to get the id (from the integer or varchar column). How the submodule would define that? Does someone have any idea about the next steps for this proposed solution?

    Solution 5
    Remove entity_id from BaseField and add as a field into the Plugin. I think that what caseylau suggested here: https://www.drupal.org/project/group/issues/2920474
    - Remove entity_id column from group_content_field_data table
    - Add entity_id as a reference field to the Plugin (e.g Membership, Article, Basic Page, Menu)
    -- In this approach, we would need that each submodule (gnode, groupmenu) to be responsible for creating the field automatically when the plugin is installed.
    - Refactor GroupContent entity_id references in the code to get the field defined (How do we know the field?)
    I can see that this approach would require much less coding than the others, but since we need to restructure by moving the entity_id (baseField) to an entity reference field, it's still not clear how we can manage this from the parent module, since I assumed to define in the other modules (gnode, groupmenu, etc).
    Can anyone help me to see better what are the next steps in this one too?

    Can you guys point out if I misunderstood or if I am missing something here? Before moving forward with any implementation (solution 3, 4 or 5) I would like to hear your suggestions and concerns.

    I have time allocated for this issue but I need some brains with me. Would be very happy to work together to get this done sooner rather than later. =D

    Ps. Also sent this is the slack channel mentioned by @scotself

    jidrone’s picture

    Hi everyone,

    The approach number 3 looks the best one, but it requires a lot of code refactoring and however it will need the new column "entity_config_id" because the entity_reference field is defined based on the "target_type" setting so forcing it to be varchar was already discarted by performarce reasons.

    I think the code changes in patch #13 were ok but removing the change of the entity_id field to varchar, for that reason I took that patch as base to do the solution 4 mentioned by @carolpettirossi and making the GroupContent entity smart enough to detect which field to use.

    This is an initial work to detect how many methods we will need to override in case we decide to go for solution 3, please check the patch and let's continue the discussion.

    jidrone’s picture

    Sorry, I forgot to remove the target type on entity_id field in previous patch.

    kristiaanvandeneynde’s picture

    I'm more inclined to look at the "purely bundle fields" solution (move it from GroupContent::baseFieldDefinitions to GroupContent::bundleFieldDefinitions altogether). If that fails, we could look at the custom storage (2 columns for 1 field), but as mentioned in #88 that might be nasty.

    geek-merlin’s picture

    OK and we may want to take note of #2935932: Add a FieldDefinition class for defining bundle fields in code. which is going to land soon.

    kristiaanvandeneynde’s picture

    Oh shit, sweet! Did not know that was in the works. Great to know, thanks!

    abramm’s picture

    This issue should cover not only config entities, but other entity types using string entity IDs. A good example is external entities.

    For instance, here's how entity reference handles content/integer vs config/string references.
    From \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::propertyDefinitions():

        $target_id_data_type = 'string';
        if ($target_type_info->entityClassImplements(FieldableEntityInterface::class)) {
          $id_definition = \Drupal::entityManager()->getBaseFieldDefinitions($settings['target_type'])[$target_type_info->getKey('id')];
          if ($id_definition->getType() === 'integer') {
            $target_id_data_type = 'integer';
          }
        }
    

    As you can see, there's no content vs config entity type check. Instead, the ID field definition is used to determine the correct data type.

    abramm’s picture

    According to \Drupal\Core\Field\FieldStorageDefinitionInterface doc:

    While field definitions may differ by entity bundle, all of those
    bundle fields have to share the same common field storage definition. Thus,
    the storage definitions can be defined by entity type only.
    

    That is also confirmed by the fact that the field storage config entity only refers entity type.
    It is possible that some future core version will provide bundle-specific field storage solution but seems like it's not possible at the moment, even with all these patches floating around.

    I've spent half a day finding a way to do this and it the "purely bundle fields" approach seems to be a dead end unfortunately.
    Seems like the only viable solution is using two entity_id fields, one for integer, another one for string IDs.

    abramm’s picture

    Title: Configuration entities as group content » Entities identified by strings as group content
    Issue summary: View changes
    abramm’s picture

    Here's a patch for supporting both content and config entities, identified by either integers or strings.

    The patch is based on #93, thus it's adding a new field for string IDs (the field name is entity_id_str).

    Unit tests aren't updated yet (string IDs aren't tested) by at least regression tests should pass.

    abramm’s picture

    I've added a unit test for the previous patch.
    Also, found a bug (missing entity_id field handing in createForEntityInGroup(), came from previous patches) while writing the test, so fixed it as well.

    I'm attaching two patches, one with test only (should fail), another one with test + fix (should pass).
    Tests aren't specific for a current solution and may be used for other approaches as well.

    Can someone review please?

    Thanks.

    jidrone’s picture

    The patch looks good, just an small fix to support entities with IDs longer than 32 characters.

    carolpettirossi’s picture

    Status: Needs review » Reviewed & tested by the community

    Hi all,

    Amazing job in getting this done. =)
    I've just applied the patch and it works properly. The groupmenu module also works with this patch.

    Thanks,
    Carol

    kyuubi’s picture

    Just gave this a spin, works nicely.
    Great job guys!

    geek-merlin’s picture

    Status: Reviewed & tested by the community » Needs work

    regarding # 97:
    > [also content entities can have string IDs]

    correct. thanks for enlighening us with this.

    regarding #98:
    > I've spent half a day finding a way to do this and it the "purely bundle fields" approach seems to be a dead end unfortunately.

    The comments in the issue mentioned in #95 (#2935932: Add a FieldDefinition class for defining bundle fields in code.) prove that this is done already and will be even easier with that patch.

    I guess one person "did not find a way" is not what the maintainer meant in #94 so setting NW.

    abramm’s picture

    Hi @axel.rutz,
    Sorry, I may have been not precise.
    The core issue you're referring to makes it possible to define bundle fields easily, but not per bundle field storages unfortunately. Bundle fields still shares the same storage, thus making bundle fields not usable for us since we want to store both integers and strings depending on the bundle.

    scotwith1t’s picture

    This patch needs an implementation of hook_update_N so as to not break existing sites. I had previously applied other patches from this issue so the schema was not able to be updated to what this latest patch requires. @Jidrone is aware and working on it :)

    jidrone’s picture

    Version: 8.x-1.0-rc2 » 8.x-1.x-dev
    Status: Needs work » Needs review
    FileSize
    34.03 KB
    1.3 KB

    Adding the hook_update_N for sites already using the module.

    jidrone’s picture

    Just setting the right name for the patch file.

    kristiaanvandeneynde’s picture

    Patch is starting to look very nice! Bonus points for having tests.

    I'm still wondering whether we can't use bundle fields along with this approach. While the following is true:

    The core issue you're referring to makes it possible to define bundle fields easily, but not per bundle field storages unfortunately. Bundle fields still shares the same storage, thus making bundle fields not usable for us since we want to store both integers and strings depending on the bundle.

    We could define both entity_id and entity_id_str as bundle fields only and define a storage for each one. That way, we don't ship every GroupContent entity with at least one field it will never use (either entity_id or entity_id_str).

    We'd have to keep an eye on some bugs regarding manually defining field storages, though. I know I ran into #2584729: SqlContentEntityStorage::getFieldStorageDefinitions() is poorly named and should not be public a while back. The group_roles field works by happy accident because I mistakenly defined its storage as a config entity. Turns out I circumvented a core bug by doing so :) I'd rather define it properly through the right hook, though, as that's where people will look.

    P.S.: I'm happy to make this work for now and then have a thorough look into all the other options again when refactoring for Group 8.2.0; who knows what core might offer/fix by then :)

    +++ b/src/Entity/Form/GroupContentForm.php
    @@ -65,8 +77,12 @@ class GroupContentForm extends ContentEntityForm {
    +    $form['entity_id']['#access'] = FALSE;
    +    $form['entity_id_str']['#access'] = FALSE;
    +
    +    if ($this->operation === 'add' && !$form_state->get('group_wizard')) {
    +      $entity_id_field = $this->getContentEntityField();
    +      $form[$entity_id_field]['#access'] = TRUE;
    

    Can't we do this in a field access hook? Otherwise it only applies to this one form instead of all invocations.

    +++ b/group.install
    @@ -424,3 +425,28 @@ function group_update_8014() {
    +  ¶
    

    Whitespace error in group_update_8015 (near the bottom on the blank line)

    abramm’s picture

    Status: Needs review » Needs work

    We could define both entity_id and entity_id_str as bundle fields only and define a storage for each one. That way, we don't ship every GroupContent entity with at least one field it will never use (either entity_id or entity_id_str).

    AFAIR there's no way to _undefine_ the bundle field if it's already defined as an entity type base field.
    On the other hand, there's no way to *reliably* define field storage for an entity field other than defining it as base field. So we'll always have two fields.
    Another solution could be using `hook_entity_field_storage_info` to define field storages (and then define fields per bundle) but as you've mentioned the hook is broken.

    I think the Entity API contrib module has an API for that but it seems to be using an approach which is different from what the core is going to use so I'm doubt we should be using it, also, it would introduce an unnecessary dependency.

    kristiaanvandeneynde’s picture

    There's an easy workaround mentioned in the issue I linked. I'd rather not declare base fields that will never be used by certain bundles. That just shouts "bundle field!" in my opinion :)

    abramm’s picture

    Let's try bundle fields now!

    I was able to make bundle specific fields work by following all recommendations and suggested workarounds.
    I've seen some tests failures on my local machine; uploading the work-in-progress patch to check what Drupal.org CI say.

    Remaining issues:
    - Failing (?) tests.
    - Upgrade path. The group_update_8015() doesn't seem to be sufficient anymore; the entity_id field should be moved to a new database table.

    abramm’s picture

    Re-rolled #115 against latest 8.x-1.x since the last patch failed to apply.
    group_update_8015() is now group_update_8016().

    I'm not making an interdiff since it won't be useful anyway; the 111-115 is mostly the same.

    kristiaanvandeneynde’s picture

    I had a few minutes to read part of the patch and found some quick fixes and the field access thingy I mentioned #112. Starting to look really nice, especially loving the core workarounds being thoroughly documented. Keep up the good work!

    1. +++ b/group.module
      @@ -519,6 +518,64 @@ function group_content_entity_submit($form, FormStateInterface $form_state) {
      +    // Field to support entities identified by strings.
      +    $fields['entity_id'] = GroupContentReferenceDefinition::create('entity_reference')
      

      By integers

    2. +++ b/src/Entity/Form/GroupContentForm.php
      @@ -41,6 +52,7 @@ class GroupContentForm extends ContentEntityForm {
      +      $container->get('entity_type.manager')->getStorage('group_content'),
      

      We already inject the deprecated entity manager one line below. Why not call for the storage in the constructor? Saves us one dependency injection.

    3. +++ b/src/Entity/Form/GroupContentForm.php
      @@ -65,8 +77,12 @@ class GroupContentForm extends ContentEntityForm {
      +      $form[$entity_id_field]['#access'] = TRUE;
      

      Should ideally use the field access system instead of manually manipulating #access.

    anairamzap’s picture

    Hi, just a quick note to say that I've tried the last patch (https://www.drupal.org/project/group/issues/2797793#comment-12846247) and I get the following error on the "Members" Group tab:

    Drupal\Core\Database\DatabaseExceptionWrapper: Exception in Group members[group_members]: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'group_content_field_data.entity_id' in 'on clause': SELECT COUNT(*) AS expression FROM (SELECT 1 AS expression FROM {group_content_field_data} group_content_field_data INNER JOIN {users_field_data} users_field_data_group_content_field_data ON group_content_field_data.entity_id = users_field_data_group_content_field_data.uid AND group_content_field_data.type = :views_join_condition_0 WHERE (group_content_field_data.gid = :group_content_field_data_gid)) subquery; Array ( [:group_content_field_data_gid] => 1 [:views_join_condition_0] => test_gtype-group_membership ) in Drupal\views\Plugin\views\query\Sql->execute() (line 1543 of /home/eu/git-mbm/d8clean/docroot/web/core/modules/views/src/Plugin/views/query/Sql.php).

    I've checked the db structure, and in fact that column "entity_id" is not present on `group_content_field_data` table:

    MariaDB [drupal]> show columns from group_content_field_data;
    +------------------+------------------+------+-----+---------+-------+
    | Field            | Type             | Null | Key | Default | Extra |
    +------------------+------------------+------+-----+---------+-------+
    | id               | int(10) unsigned | NO   | PRI | NULL    |       |
    | type             | varchar(32)      | NO   | MUL | NULL    |       |
    | langcode         | varchar(12)      | NO   | PRI | NULL    |       |
    | gid              | int(10) unsigned | YES  | MUL | NULL    |       |
    | label            | varchar(255)     | YES  |     | NULL    |       |
    | uid              | int(10) unsigned | YES  | MUL | NULL    |       |
    | created          | int(11)          | YES  |     | NULL    |       |
    | changed          | int(11)          | YES  |     | NULL    |       |
    | default_langcode | tinyint(4)       | NO   |     | NULL    |       |
    +------------------+------------------+------+-----+---------+-------+
    9 rows in set (0.00 sec)
    

    Same happens with the "Nodes" tab.

    abramm’s picture

    Hi @anairamzap!
    Thank you for your feedback.

    This is currently a known issue; the bug you've experienced is caused by the Views integration broken due to the field schema change. This case is already covered by unit tests and we know it's failing; you can see 4 test failures in #116.

    I'm working on resolving this issue. Also, it's been discussed at #d8-group on Slack.
    I believe the last problems are failing tests from #116, upgrade path and few coding standards/typos issue from #117.

    abramm’s picture

    Posting results of what I did with Views integration so far. We should at least get less failures.

    This patch may break your code, burn your house or eat your cat. Or even burn your cat, what's even worse.
    You've been warned.

    abramm’s picture

    Here's a new patch.
    Even more dirty hacks, broken coding standards, incomplete code etc. Sorry but I had to upload this in order to check what CI say this time.

    jidrone’s picture

    Hi @abramm,
    I tested the last one, the members view is working for me but the nodes view is not, I will start checking the code to see if I can give a hand.

    abramm’s picture

    FileSize
    66.34 KB
    29.88 KB

    Another patch with my results.

    All tests fixed.
    All coding standards issues and typos from #117 are fixed.
    @kristiaanvandeneynde as for the field #access, the new code is now following the same approach used in dev branch; the only difference is the field name. Let's raise a new issue if you think this should be changed since my patch is already 67kb (sic!).

    Remaining tasks

    Upgrade path. The group_update_8016() is not enough anymore since we now need to move entity_id to a new storage.

    abramm’s picture

    Fixed View reverse relationship plugin thanks to @jidrone.
    The group_nodes view is not covered by unit tests. Views relationship handler changes broke contextual filters in #121/#123.

    I'm going to raise a followup task for that.

    As for this issue, the remaining task is still an upgrade path.

    abramm’s picture

    Status: Needs work » Needs review
    FileSize
    81.38 KB
    4.89 KB

    Finally, the patch with all functionality, all fixes, upgrade path and everything.
    Ready for review.

    kristiaanvandeneynde’s picture

    Some initial questions going over the first part of the patch, should have more time later this week. Update hooks are looking nice so far. Don't we need an update hook to add the entity_id_str bundle field to the known fields/storages using the entity definition update manager, though?

    1. +++ b/group.install
      @@ -451,3 +452,176 @@ function group_update_8015() {
      +  // We're now inserting new fields data which may be tricky. We're updating
      +  // group_content entities instead of inserting fields data directly to make
      +  // sure field data is stored correctly.
      +  $rows_per_operation = 50;
      +  $query->condition('id', $sandbox['current'], '>');
      +  $query->range(0, $rows_per_operation);
      +  $query->orderBy('id', 'ASC');
      +
      +  $rows = $query->execute()->fetchAllKeyed();
      +  if ($rows) {
      +    /** @var \Drupal\group\Entity\GroupContentInterface[] $group_contents */
      +    $group_contents = \Drupal::entityTypeManager()
      +      ->getStorage('group_content')
      +      ->loadMultiple(array_keys($rows));
      +
      +    foreach ($group_contents as $id => $group_content) {
      +      $group_content->entity_id->target_id = $rows[$id];
      +      $group_content->save();
      +    }
      

      You can't do that, sadly. Saving entities is a bad idea in update hooks; it can only be done safely in post-update hooks.

    2. +++ b/group.views.inc
      @@ -5,44 +5,58 @@
      +  if (!$group_content_storage instanceof SqlEntityStorageInterface) {
      +    // Only process views data if Group content entities are stored in the
      +    // database.
      +    return;
      +  }
      

      Are we sure we want to do this?

    3. +++ b/group.views.inc
      @@ -5,44 +5,58 @@
           $entity_type_id = $plugin->getEntityTypeId();
      -    $entity_type = $entity_types[$entity_type_id];
      +    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
           $entity_data_table = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
      

      Why use this over loading the entire list just once?

    4. +++ b/group.views.inc
      @@ -5,44 +5,58 @@
      -    // This relationship will allow a content entity to easily map to the group
      -    // content entity that ties it to a group, optionally filtering by plugin.
      +    // This relationship will allow a content entity to easily map to the
      +    // group content entity that ties it to a group, optionally filtering by
      +    // plugin.
      

      Unnecessary change?

    5. +++ b/group.views.inc
      @@ -5,44 +5,58 @@
             'relationship' => [
               'group' => t('Group content'),
      -        'base' => $data_table,
      -        'base field' => 'entity_id',
      +        'base' => 'group_content_field_data',
      +        'base field' => 'id',
      +        'entity_type' => 'group_content',
      +        'field table' => $table_mapping->getFieldTableName($content_ref_field),
      +        'field field' => $table_mapping->getFieldColumnName($field_def->getFieldStorageDefinition(), 'target_id'),
      +        'field_name' => $content_ref_field,
               'relationship field' => $entity_type->getKey('id'),
      

      I'm not sure this is right, could you explain what you're doing here?

    abramm’s picture

    1. I wasn't sure about this; post update hook seems to be an option.
    2. I don't remember exactly, I think I've added this since some method we are using (getting SQL table map?) really belongs to SqlEntityStorageInterface.
    3. Questionable; I could ask the same. Both approaches works, it's a question of taste. Probably I've changed this while re-writing the code few times, trying different ways etc. I can revert this if you prefer.
    4. Yes, you're right, it's unnecessary change. The comment didn't fit the line when I moved this code to a more indented block so I changed it; then I probably moved it back no the same indentation level.
    5. I've replaced the logic of the reverse entity reference relationship handler with the code copied from the core entity_reverse handler since the one found in module wasn't handling field data in separate table; the relationship definition was changed as well. The GroupContentToEntityReverse class is now almost the same as core EntityReverse, the only difference is bundles (group content types) filter.
    The reason behind this change is issue at #122.

    carolpettirossi’s picture

    I'm testing the latest patch provided on #125 and I'm facing the following error when installing gnode module:

    Drupal\Core\Entity\Sql\SqlContentEntityStorageException: Table information not available for the 'entity_id' field. in Drupal\Core\Entity\Sql\DefaultTableMapping->getFieldTableName() (line 375 of /app/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php).

    carolpettirossi’s picture

    Status: Needs review » Needs work
    jidrone’s picture

    Status: Needs work » Needs review
    FileSize
    80.81 KB
    13.28 KB

    Hi Everyone,

    Thanks @abramm for all the progress on this issue.

    This patch fixes the error reported in #128, it also have the following adjustments based on comment #126:
    1. Moved the saving of the entities to a post update.
    2. Removed those conditions, I think it is always true because the group content is a "Content entity".
    3. Reverted change, I also consider it was overloading.
    4. Reverted change.
    5. Nothing changed, because the approach followed by @abramm is the correct for entity reference reverse relationship based core documentation.

    kristiaanvandeneynde’s picture

    Nice progress once again. I'm currently seeing progress on #2584729: SqlContentEntityStorage::getFieldStorageDefinitions() is poorly named and should not be public so let's definitely keep an eye on that!

    abramm’s picture

    I don't remember exactly but I think I've added a workaround for broken hook and it should work as is even after the hook fix is committed so it's not blocking us. Also, relying on a hook will force us to require a newer Drupal core version.

    kristiaanvandeneynde’s picture

    Here's a more thorough review of the patch already. I left some @todos in the code and will comment on some changes here:

    • Moved the field name logic away from the storage, it has no place there. Not sure GroupContent is the right place, but the storage definitely isn't.
    • The naming for both methods to retrieve the field name are awful, we need something better :)
    • With the custom field definition class we can reduce a lot of clutter by moving the related code there.
    • From what I've seen, it is safe to assume Views uses an SQL back-end, but let's double-check.
    • Made sure the string reference schema column is always 255 characters long.

    Feel free to quote parts of the interdiff you don't immediately understand and I'll explain further.

    Status: Needs review » Needs work

    The last submitted patch, 133: group-2797793-133.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

    kristiaanvandeneynde’s picture

    Status: Needs work » Needs review

    Failures show that the views need to use the new relation field names. It might need an update hook that loads all views, then all relations/fields and replaces those occurrences with the right configuration/name.

    Also, GroupContentStorageTest apparently still needs the group_test_content module. Oh, I guess this needed to be (re)moved as well:

        $this->installConfig(['group_test_content']);
        $storage->createFromPlugin($group_type, 'integer_content_entity_as_content')->save();
        $storage->createFromPlugin($group_type, 'string_config_entity_as_content')->save();
        $storage->createFromPlugin($group_type, 'string_content_entity_as_content')->save();
    
        $this->installEntitySchema('group_test_content_entity_int');
        $this->installEntitySchema('group_test_config_entity_string');
        $this->installEntitySchema('group_test_content_entity_string');
    

    But GroupContentTest goes green even if we don't call $this->installConfig(['group_test_content']);... That might need to be looked into as well.

    kristiaanvandeneynde’s picture

    Status: Needs review » Needs work
    abramm’s picture

    Assigned: Unassigned » abramm
    Status: Needs work » Active

    Hi kristiaanvandeneynde,

    Thank you for you review!

    I've noticed you've changed Views field names in #133 which broke tests:

    -      // Create a unique field name for this views field.
    -      $field_name = 'gc__' . $entity_type_id;
    +      $field_name = GroupContent::getEntityFieldNameForEntityType($entity_type_id);
    +      // @todo Can't we read this from a table or field mapping instead?
    +      $field_table = 'group_content__' . $field_name;
    

    I've used the 'gc__' prefix since it's the way how it was working before my changes.
    We can't use the entity field name name since different bundles of group_content refers to different entity types, e.g. a simple 'entity_id' field may refer both users and nodes and so there should be one field for each referenced entity type.

    I'd suggest keeping the same gc__ENTITY_TYPE naming we're currently using since making changes here doesn't look necessary for me.

    I'm going to revert your changes in GroupContentViewsData (only those related to Views field names) and see if that helps with unit test.

    abramm’s picture

    FileSize
    1.44 KB

    Here we go. Unit tests now pass locally. I didn't run all tests suite, let's see how CI reacts on this.

    abramm’s picture

    Ancient manuscripts says CI would work better if I'd uploaded the patch as well.

    abramm’s picture

    Not sure what was wrong with CI but GroupContentStorageTest started failing (which was expected because of the group_test_content module dependency removal) once I fixed Views tests. Re-enabling the module fixed those failures for me.
    Uploading a new patch.
    I've added interdiffs for both #133 and #138 for convenience.

    EDIT:
    It's definitely not a good day for CI.
    PHP 7 test failed with mkdir(): File exists error in random functional test, then worked fine when I re-ran it.
    PHP 5.6 test failed with same error but in a different test.
    PHP 5.5 passed just fine.

    kristiaanvandeneynde’s picture

    The tests started failing because setUp() still tries to install the now missing module's config/schema, as mentioned in #135, those lines need to go.

    kristiaanvandeneynde’s picture

    "PHP 7 test failed with mkdir(): File exists error in random functional test, then worked fine when I re-ran it." Yeah, we sometimes get that. Should track that down some day. Never seems to happen locally.

    abramm’s picture

    Let me summarize what was discussed in Slack.

    1) Regarding non-SQL backends in Views.
    Seems like it's only needed in group_views_data_alter. I've added the condition to group_views_data_alter first and then copy-pasted it to GroupContentViewsData.
    TODO: I think I'll keep it in group_views_data_alter (since there's no guarantee entity is using SQL storage backend and it would be safer to simply skip the alter). As for GroupContentViewsData, it appears that it would be never called at all if storage is not SQL.

    2) Regarding usage of

    $field_table = 'group_content__' . $field_name;
    

    instead of table mapping.

    I was using `$table_mapping->getFieldTableName` but it broke gnode module install. According to @jidrone, the module gnode is importing the views config, in that moment it goes to group_views_data_alter, but in the table_mapping the bundle fields does not appear and the installation fails.

    This looks like a Views bug, so I'll have to check it.

    3) views_field_default_views_data() code copy.
    I wish there was another way. Core Views code (views_field_default_views_data) is a long meaningless piece of code.
    I've tried calling Views core methods but it didn't work; the method expects FieldStorageConfigInterface (instead of FieldStorageDefinitionInterface) which we don't have and I didn't want to mock the config entity just to be able to re-use core method.

    4) GroupContentStorageTest is almost fine now; we've figured out why the group_test_content module dependency is required.
    I need to add '@covers' tags to tests and update tests/data providers doc to be consistent with other tests in the module.

    SocialNicheGuru’s picture

    Status: Active » Needs review
    abramm’s picture

    Status: Needs review » Active

    I'm still working on the patch, it's not ready for review yet.

    abramm’s picture

    I believe I've fixed all issues in reviews starting from #133.

    1) Cleared non-SQL storage backend conditions.
    2) Fixed fetching table mapping. Looks like it's a core bug, I've raised #3016059: SqlContentEntityStorage assumes field storages are static and provided a workaround.
    3) Unfortunately, it's not possible to avoid copy-paste. Actually, we shouldn't provide Views data at all (or at least we shouldn't build data from scratch), Views should do that. I've raised #3016026: No Views data provided for bundle fields.
    4) Tests docs fixed.

    The patch has grown so I've provided interdiffs for both #140 (my latest patch) and #133 (kristiaanvandeneynde's latest patch).

    Ready for review.

    ronaldtebrake’s picture

    Status: Needs review » Needs work

    Great job on the latest progress @abramm, thanks for the interdiffs & great documentation, makes reviewing a lot smoother. Seems like you've addressed everything since #133 nicely!

    The only thing that isn't working for me is the update path. Existing custom views seem to error after the update, due to their existing group_content_field_data > node related relationships & fields requiring this relationship. Would be great to have this covered in an update hook.

    Feel free to contact me on slack for any examples.

    Mohamed.Osama’s picture

    The patch failed to apply on the latest version.
    Please help.

    nitesh624’s picture

    patch #146 didn't apply on current version

    fdefeyter@gmail.com’s picture

    Up! waiting fro the update of this patch :-)

    kducharm’s picture

    I'm having an issue with groupmenu module even with a patch that has it use the GroupContent::getEntityFieldNameForEntityType (https://www.drupal.org/files/issues/2018-12-10/groupmenu-change_referenc...) - even though that is working for new menus, the form to relate existing menus runs a validator which eventually calls GroupContentCardinalityValidator::validate, then Group::getContentByEntityId(). That function has a hard coded 'entity_id' => $id.

    In the GroupContentCardinalityValidator class, doesn't the section below where patch #146 does the $ref_field changes need the following change from:
    $entity_instances = $group->getContentByEntityId($plugin->getPluginId(), $entity->id());

    to
    $entity_instances = $group->getContent($plugin->getPluginId(), [$ref_field => $entity->id()]);

    ?

    jidrone’s picture

    Hello, here is a patch with the following:

    • Post update for views as proposed in 147
    • Fix for the getContentByEntityId method to handle the field in the right way, issue found by @kducharm in 151
    esolitos’s picture

    With patch #152 lots of entity relationships in views were lost, quite a bit of debugging lead me to do a couple of changes on GroupContentViewsData, please check for regressions.

    Status: Needs review » Needs work

    The last submitted patch, 153: group-support_string_entity_ids-2797793-153.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

    LOBsTerr’s picture

    It looks like the tests fail in the same place not related to the issue. In many other tickets I see the same result

    esolitos’s picture

    That's possible, however my patch might have introduced regressions, as I get errors here and there.

    I can't work with it right now, hopefully in the next days...

    u_tiwari’s picture

    I see last two patches are failing, which one are we supposed to use with the latest version of group menu module. Is it still patch #20 as mentioned in the group menu module's page.

    esolitos’s picture

    I recommend #152, as it seems that my patch might be creating other regressions.

    Patch failures seems unrelated.

    jidrone’s picture

    Hi @u_tiwari,

    For groupmenu I recommend you this patch also #3010909: Change references to "entity_id" field in group content..

    esolitos’s picture

    I think I fixed the regression which I caused and solved the initial issue: the content of $data[$field_table] in GroupContentViewsData, which handles the views integration was overridden each time.

    Status: Needs review » Needs work

    The last submitted patch, 160: group-support_string_entity_ids-2797793-160.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

    jidrone’s picture

    Here is a new patch adding a comment to the condition added by @esolitos because I think what he did is ok and added some small improvements.

    jidrone’s picture

    Failed tests caused by changes introduced in core 8.8.x-dev as explained in the following issue #3061353: Replace deprecated EntityManager and add EntityFieldManager in GroupContentViewsData.

    Ruslan Piskarov’s picture

    I have tried to apply #162 to the latest dev and got:
    PHP Fatal error: Cannot redeclare group_update_8016() (previously declared in /modules/contrib/group/group.install:461).
    Providing a new patch with minor changes in two lines.

    Ruslan Piskarov’s picture

    FileSize
    144.09 KB

    Could someone help me? Maybe I missed something.
    I have a site with groupmenu module. Applied the latest patch from this issue and as mentioned @jidrone in #159 the latest from #3010909: Change references to "entity_id" field in group content.. However in this patch at group_update_8017() I see:

    +        'entity_id' => [
    +          'type' => 'int',
    +          'unsigned' => TRUE,
    +          'not null' => TRUE,
    +        ],

    but groupmenu uses strings
    strings
    so when I run drush updb I get a fatal error via: entity_id must be integer.
    Is it knows issue?
    .

    jidrone’s picture

    Hi @RuslanP,

    You need to run database updates.

    kyuubi’s picture

    Status: Needs review » Reviewed & tested by the community

    Hi guys,
    This patch is working for me (fixing the duplicate hook updates).
    Marking as RTBC.

    joshuami’s picture

    Rerolling the patch against Group 1.0.0-rc4 to address duplicate group_update_8016().

    jidrone’s picture

    Hi @joshuami,

    It was done in #164 and please provide an interdiff with future patches to find easier the changes you are proposing.

    joshuami’s picture

    Apologies for the extra reroll. Didn't pay attention to the interdiff.

    I finally have a working version of this change, but I ran into an issue where function group_post_update_fix_group_content_views_relations() tries to update all the view relationships that point to group_content_field_data and have them point to group_content__entity_id instead. This is fine for the user relationships, but for views of content, the group relation registers as a broken handler.

    azinck’s picture

    Status: Reviewed & tested by the community » Needs review
    FileSize
    84.24 KB

    Re-rolled against latest 8.x-1.x-dev

    Marking back to Needs Review to get eyes on it again.

    richbs’s picture

    I had great success with patch #168 but noticed that some content was not appearing in the groups after migration to the group_content__entity_id table.

    We had over 7000 group content items and only 6500 were migrated to the new table.

    Essentially the group_update_8017 function in group.install function assumes that the highest id in the group_content_field_data table will never exceed the total number of rows in that table.

    If you delete and create new group content items, then the id is incremented beyond the count of rows.

    There's a similar slight flaw in the logic in group_post_update_restore_entity_id_data in group.post_update.php.

    I'll attach a new patch and interdiff to illustrate. Patch and interdiff against 8.x-1.0-rc4

    richbs’s picture

    richbs’s picture

    richbs’s picture

    This patch takes the patch described in #172 and re-rolls it against latest 8.x-1.x-dev using #171 as a base.

    richbs’s picture

    Apologies for the comment spam but my last patch crossed in mid-air with the latest commit to 8.x-1.x-dev.

    To summarise:

    To ensure that all group content is migrated to the new table structure: see #172

    If you are using 8.x-1.0-rc4: Patch 172, Interdiff 168-172

    If you are using 8.x-1.x-dev: Patch 176 (Interdiff doesn't work.)

    teddyvermeulin’s picture

    Hi, with 8.x-1.x-dev, Patch 176 failed for me (Drupal 8.7.9 / PHP 7.3 / MySQL 5.5)

    azinck’s picture

    Re-rolled 176 against 8.x-1.x-dev

    azinck’s picture

    Fixing 178. I didn't realize there were 2 update hooks added and I failed to rename one of them.

    azinck’s picture

    In trying to test 179 it appears some things may have changed in 1.x-dev that may require bigger changes to this patch. I'm currently getting this error when trying to use the patch and update from an older dev version:

    Error: Call to a member function getDefinitions() on null in Drupal\group\Entity\Views\GroupContentViewsData->getViewsData() (line 94 of /var/www/html/web/modules/cont  
      rib/group/src/Entity/Views/GroupContentViewsData.php). 
    richbs’s picture

    An update to my previous comment as the link to patch #172 appears to have broken. Correct link and info below…

    To ensure that all group content is migrated to the new table structure: see #172

    If you are using 8.x-1.0-rc4: Patch 172, Interdiff 168-172

    knowak’s picture

    #180 I think the change to dev that is causing the issue is #3061353 Replace deprecated EntityManager and add EntityFieldManager in GroupContentViewsData

    I created a new patch that doesn't declared the entityManager in GroupContentViewsData so it matches #3061353.

    *Edit: No, I guess more needs to be done to fix it...

    knowak’s picture

    Trying this again because I missed adding some files in #182. Sorry still learning the ropes!

    elya’s picture

    #181Unfortunately, I get an error when trying to use this patch:
    Update #8017
    Failed: Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'pantheon.group_content_field_data' doesn't exist: SELECT COUNT(*) AS expression FROM (SELECT 1 AS expression FROM {group_content_field_data} g) subquery; Array ( ) in group_update_8017() (line 503 of /srv/bindings/6db4cd2728164b26a921b5c01e4dfb27/code/modules/contrib/group/group.install).

    kducharm’s picture

    It appears the update hooks have been renumbered (8018 was 8016), so it was trying to re-run it. If you've already run the updates before, it would fail because it wasn't checking if the field exists. This patch should resolve that.

    kducharm’s picture

    Trying this patch which has the index removed on entity_id field, seems to cause problems on a fresh install.

    kyuubi’s picture

    Hi,
    This patch doesn't seem to be applying to the latest rc-5.

    kyuubi’s picture

    Hi everyone,

    It looks like this issue is due to the changes in Issue #3029878 where we start statically caching loadByEntity(). This change replaces the `loadByProperties` method with loading from the entity cache.

    I've adjusted the change by introducing the field name variable rather than having a hardcoded `entity_id`.

    Similarly `getCacheTagsToInvalidate` was introduced in GroupContent which also references a hardcoded `entity_id`. I also addressed this.

    Patch attached.

    I'd like to ask @kristiaanvandeneynde (or other maintainers), when are we likely to finally merge this? It's been more than 3 years that this issue was since opened and a lot of effort has been put into this patch to support what is now a very large number of use cases (and other contrib modules like group menu, group taxonomy, etc). It's becoming challenging to constantly keep up with new changes from the main codebase so I'd like to ask what is stopping us from finally committing this (obviously now pending the review and feedback of the latest patches).

    Thanks!

    bajah1701’s picture

    This latest patch #188 seem to work fine with version rc-5.

    devreltomtom’s picture

    Hi everyone,
    there seems to be a bug in GroupMenuService.php in method: public function getGroupMenus() {} on line 200 and 204.
    There is no property with name entity_id in $group_content object but entity_id_str.
    When i change the code to:

            foreach ($group_contents as $group_content) {
              // Make sure the group and entity IDs are set in the group content
              // entity.
              if (!isset($group_content->gid->target_id) || !isset($group_content->entity_id_str->target_id)) {
                continue;
              }
              /** @var \Drupal\group\Entity\GroupContentInterface $group_content */
              $this->groupMenus[$group_content->gid->target_id][$group_content->entity_id_str->target_id] = $menus[$group_content->entity_id_str->target_id];
            }
    

    the menu is being shown correctly in the group context.

    Please check my screen below where i'm debugging the code:

    xxx

    bajah1701’s picture

    You might want to post this under groupmenu module. I to have errors showing with respect to groupmenu which might be unrelated. Also it would be good to know what exactly was changed, from what to what.

    This issue might help: https://www.drupal.org/project/groupmenu/issues/3010909

    fabianderijk’s picture

    I've been updating a really outdated version of group (rc-2) that contained the patch in #13. When I update to rc-5 and apply the patch in #188 the patch applies perfectly, however when I try to run the database updates i throws an error that the table "group_content__entity_id_str" doesn't exist.

    When I look at the patch, this table is being created in the update hook that it tries to run. Has anybody else ran into this issue? It looks like the issue in #184. But it is not :(

    TravisJohnston’s picture

    Same issue as reported in #192. I applied the patch in #188 which was successful. However site crashed. Cache clear showed missing group_post_update_restore_entity_id_data table and also shows missing group_content__entity_id_str table.
    I was able to create the group_post_update_restore_entity_id_data manually based on what I saw in the patch but I see no reference to group_content__entity_id_str anywhere.

    TravisJohnston’s picture

    Curious, based on looking at the patch #188 changes to .install, is this best designed to be applied before we install group on the site? Vs patch and update? That may be why there are issues.

    willabby’s picture

    I am experiencing same as #192/#193 on site with Drupal 8.8.5 and Group -rc5, would appreciate any updates on patch status. Thank you.

    esolitos’s picture

    Priority: Normal » Major

    Increasing the priority of this patch because it's an essential piece for other modules integrating with Group (see Group Menu as example).

    That said, i'm right now very confused on the situation, I'm running the latest dev and I cannot understand which patch is to be used and due to the lack of interdiff for most of the recent patches figuring out what's the status is quite hard (also given the size of the patch).

    It would be great if a maintainer of Group would pick this up and give it a spin so we can have this in at the least in the Dev snapshot of the module, because as mentioned it's a contib blocker.

    If patches are over complicate and introduce great deal of complexity I would vote to reconsider Solution 1 (declined): Update entity_id column to varchar, which was initially discarded, having this issue dragging on for over 4 years is really not healthy for the whole project IMHO.

    esolitos’s picture

    Meanwhile: here's a re-roll of #188 that applies cleanly on 8.x-1.x#0dcdcaf.
    I couldn't do an inter-diff because #188 does not apply cleanly, however it's only applied to match.

    esolitos’s picture

    Sorry, didn't realise that in #197 I forgot to rename the update hooks, now they are 8019 and 8020.

    Only changes from #197:

    -function group_update_8018(&$sandbox) {
    +function group_update_8019(&$sandbox) {
    
    -function group_update_8019(&$sandbox) {
    +function group_update_8020(&$sandbox) {
    
    brockdray’s picture

    Are there plans to commit this patch?

    imalabya’s picture

    Status: Needs review » Needs work

    The last submitted patch, 200: group-support_string_entity_ids-2797793-200.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

    zipymonkey’s picture

    I ran into when updating to group:8.x-1.0. I created an issue for it over here but it seems like it might be related to this patch. I am seeing the following error message:

    Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'entity_id' in 'where clause': SELECT id from {group_content_field_data} WHERE entity_id = :entity_id AND type IN (:types__0, :types__1, :types__2, :types__3); Array ( [:entity_id] => 3 [:types__0] => group_content_type_1bb2d37721849 [:types__1] => group_content_type_4fca91baa24dd [:types__2] => group_content_type_78deb0fd0f040 [:types__3] => group_content_type_f4bd2cb5a4621 [:types__4] ) in Drupal\group\Entity\Storage\GroupContentStorage->loadByEntity() (line 115 of /Users/.../www/modules/contrib/group/src/Entity/Storage/GroupContentStorage.php).
    

    The performance change added in https://www.drupal.org/project/group/issues/3158906 replaces the entity query in GroupContentStorage->loadByEntity() with a plain query (SELECT id from {{$this->dataTable}} WHERE entity_id = :entity_id AND type IN (:types[])). However, I believe this patch removes the entity_id field from the group_content_field_data table.

    Any help would be appreciated.

    safetypin’s picture

    I'm not able to apply patch #200 to the dev branch with composer. I spent a lot of time manually applying all the changes, but then I accidentally thew away all those changes before generating a new patch. I'm running into a problem with the Group Menu module, that might also be related to your problem, @zipymonkey:

        $group_contents = $this->entityTypeManager->getStorage('group_content')
          ->loadByProperties([
            'type' => array_keys($group_content_types)[0],
            'entity_id' => $menu->id(),
          ]);
    

    Doesn't seem to work as expected. I can see that the entity and the relation entity exists in the database, but the entity_id property condition in this load method isn't working. If I remove the 'entity_id' element from the properties array, then the content gets loaded, but it seems like the storage object isn't able to access entity_id anymore.

    safetypin’s picture

    I've rerolled the patch against the current dev branch. Hope it works.

    lawxen’s picture

    #204 has the same crash error of #202

    lawxen’s picture

    @zipymonkey Do you have time to test #205 again?

    zipymonkey’s picture

    Had to reinstall my local dev environment and getting the following error when trying to install. I'm using the group:dev-1.x and the #204 patch.

    Error: Class 'Drupal\group\Field\GroupContentReferenceDefinition' not found in group_entity_field_storage_info() (line 755 of /Users/.../www/modules/contrib/group/group.module).
    
    safetypin’s picture

    I know I had it working with one of the previous patches applied manually, so I'm attempting to reroll 188 against the current dev branch now. At least I had it working in a clean install, not on my current dev environment.

    zipymonkey’s picture

    It looks like the GroupContentReferenceDefinition class isn't included in the #204 patch.

    zipymonkey’s picture

    I rerolled the patch from #200 and added an interdiff. This installs the group module but I'm not able to verify that this fixes my issue. I will report back as soon as I have more info.

    safetypin’s picture

    I'm attempting to go through and reroll patch #198 again, and I'm seeing a problem in src/Entity/Storage/GroupContentStorage.php, in the loadByEntityId method. This is the current state of the method:

      /**
       * {@inheritdoc}
       */
      public function loadByEntity(ContentEntityInterface $entity) {
        // An unsaved entity cannot have any group content.
        $entity_id = $entity->id();
        if ($entity_id === NULL) {
          throw new EntityStorageException("Cannot load GroupContent entities for an unsaved entity.");
        }
    
        $entity_type_id = $entity->getEntityTypeId();
        if (!isset($this->loadByEntityCache[$entity_type_id][$entity_id])) {
          /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */
          $storage = $this->entityTypeManager->getStorage('group_content_type');
          $group_content_types = $storage->loadByEntityTypeId($entity_type_id);
    
          // Statically cache all group content IDs for the group content types.
          if (!empty($group_content_types)) {
            // Use an optimized plain query to avoid the overhead of entity and SQL
            // query builders.
            $query = "SELECT id from {{$this->dataTable}} WHERE entity_id = :entity_id AND type IN (:types[])";
            $this->loadByEntityCache[$entity_type_id][$entity_id] = $this->database
              ->query($query, [
                ':entity_id' => $entity_id,
                ':types[]' => array_keys($group_content_types),
              ])
              ->fetchCol();
          }
          // If no responsible group content types were found, we return nothing.
          else {
            $this->loadByEntityCache[$entity_type_id][$entity_id] = [];
          }
        }
    
        if (!empty($this->loadByEntityCache[$entity_type_id][$entity_id])) {
          return $this->loadMultiple($this->loadByEntityCache[$entity_type_id][$entity_id]);
        }
        else {
          return [];
        }
      }
    

    and the patch wants to make this change:

       /**
        * {@inheritdoc}
        */
    -  public function loadByEntity(ContentEntityInterface $entity) {
    +  public function loadByEntity(EntityInterface $entity) {
         // An unsaved entity cannot have any group content.
         if ($entity->id() === NULL) {
           throw new EntityStorageException("Cannot load GroupContent entities for an unsaved entity.");
    @@ -99,9 +103,11 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten
     
           // Statically cache all group content IDs for the group content types.
           if (!empty($group_content_types)) {
    +        // Retrieve the entity reference field name.
    +        $field_name = GroupContent::getEntityFieldNameForEntityType($entity->getEntityTypeId());
             $this->loadByEntityCache[$entity->getEntityTypeId()][$entity->id()] = $this->getQuery()
               ->condition('type', array_keys($group_content_types), 'IN')
    -          ->condition('entity_id', $entity->id())
    +          ->condition($field_name, $entity->id())
               ->execute();
           }
           // If no responsible group content types were found, we return nothing.
    

    It looks to me like this query has been completely rewritten using a different technique. I don't know enough to know which is right, or how to modify this alternate sql query to match the new entity_id system. But, this is my best guess:

      /**
       * {@inheritdoc}
       */
      public function loadByEntity(EntityInterface $entity) {
        // An unsaved entity cannot have any group content.
        $entity_id = $entity->id();
        if ($entity_id === NULL) {
          throw new EntityStorageException("Cannot load GroupContent entities for an unsaved entity.");
        }
    
        $entity_type_id = $entity->getEntityTypeId();
        if (!isset($this->loadByEntityCache[$entity_type_id][$entity_id])) {
          /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */
          $storage = $this->entityTypeManager->getStorage('group_content_type');
          $group_content_types = $storage->loadByEntityTypeId($entity_type_id);
    
          // Statically cache all group content IDs for the group content types.
          if (!empty($group_content_types)) {
            // Retrieve the entity reference field name.
            $field_name = GroupContent::getEntityFieldNameForEntityType($entity->getEntityTypeId());
            // Use an optimized plain query to avoid the overhead of entity and SQL
            // query builders.
            $query = "SELECT id from {{$this->dataTable}} WHERE {{$field_name}} = :entity_id AND type IN (:types[])";
            $this->loadByEntityCache[$entity_type_id][$entity_id] = $this->database
              ->query($query, [
                $field_name => $entity_id,
                ':types[]' => array_keys($group_content_types),
              ])
              ->fetchCol();
          }
          // If no responsible group content types were found, we return nothing.
          else {
            $this->loadByEntityCache[$entity_type_id][$entity_id] = [];
          }
        }
    
        if (!empty($this->loadByEntityCache[$entity_type_id][$entity_id])) {
          return $this->loadMultiple($this->loadByEntityCache[$entity_type_id][$entity_id]);
        }
        else {
          return [];
        }
      }
    

    I am wondering if the problem may stem from "{{$this->dataTable}}" in the plain, optimized sql query - I'm guessing this part doesn't take into account that with this patch, the entity_ids are stored in 2 different tables now.

    safetypin’s picture

    Ok, I think that was the problem; I rerolled patch #198, but I reverted the query inside the loadByEntity() method I mentioned previously to match exactly what was in the patch, and no longer getting errors. So, I guess we need to figure out how to rewrite the changes in this alternate query format. I'm not an sql expert, but I'll see if I can figure it out.

    safetypin’s picture

    I think I've rewritten the query to find the entity_id in the correct table when using string and numeric IDs. I'm not 100% sure I've reworked the query correctly, but it appears to be working. I'm not sure if I should do an interdiff.

    I went back and reapplied patch #198, and then instead of applying the patch for the loadByEntity() method I pasted over the full function from the #198 version of the patch into the loadByEntity method. After confirming that the function worked with the #198 version of the method, I attempted to rework the updated query that was incompatible with the #198 patch.

    safetypin’s picture

    It looks like I got distracted in the middle of the process and accidentally left a bunch of commented out debugging code in that patch. But I've also run into another sql error related to entity_id in the src/QueryAccess/EntityQueryAlter.php file - several lines are looking for an entity_id field on the group_content__field_data table, so I'll try to figure out what those queries are trying to load and how to get the data from the alternative fields.

    safetypin’s picture

    I think the EntityQueryAlter.php method seems like it's not actually using the entity_id column from these queries, but simply trying to determine if there is content currently existing for each GroupContent type. So, if I rename the entity_id column to just id, that will accomplish the same thing. So, I'm attaching a new patch (with the debug code removed) and rewriting these queries.

    zipymonkey’s picture

    Thanks @safetypin. I'm no longer seeing the error message I reported above.

    I fixed an indentation issues in the query and uploaded the patch (added an interdiff as well).

    Status: Needs review » Needs work

    The last submitted patch, 216: group-support_string_entity_ids-2797793-215.patch, failed testing. View results
    - codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

    JasonLuttrell’s picture

    Hi, some help would be greatly appreciated. Since beginning to migrate to Drupal 9, this patch now no longer works for me on Group module 1.2. I used a previous iteration and have since tried to apply all of the above more recent patches, to no avail. This error appears to be coming from GroupContentViewsData.php, getFieldTableName() function:

    $table_mapping = $this->storage->getTableMapping($this->entityManager->getFieldStorageDefinitions('group_content'));
        return $table_mapping->getFieldTableName($field_name);

    It seems there may need to be some better error handling in the function above, but TBH I'm unable to really trace the cause of the error:

    The website encountered an unexpected error. Please try again later.
    Error: Call to a member function getFieldStorageDefinitions() on null in Drupal\group\Entity\Views\GroupContentViewsData->getFieldTableName() (line 484 of modules/contrib/group/src/Entity/Views/GroupContentViewsData.php).
    Drupal\group\Entity\Views\GroupContentViewsData->getFieldTableName('entity_id') (Line: 111)
    Drupal\group\Entity\Views\GroupContentViewsData->getViewsData() (Line: 178)
    views_views_data()
    call_user_func_array('views_views_data', Array) (Line: 392)
    Drupal\Core\Extension\ModuleHandler->invoke('views', 'views_data') (Line: 237)
    Drupal\views\ViewsData->getData() (Line: 154)
    Drupal\views\ViewsData->get('aggregator_feed') (Line: 91)
    Drupal\ds\Plugin\Derivative\DsEntityRow->getDerivativeDefinitions(Array) (Line: 101)
    Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDerivatives(Array) (Line: 87)
    Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDefinitions() (Line: 284)
    Drupal\Core\Plugin\DefaultPluginManager->findDefinitions() (Line: 175)
    Drupal\Core\Plugin\DefaultPluginManager->getDefinitions() (Line: 146)
    views_theme(Array, 'module', 'views', 'core/modules/views') (Line: 454)
    Drupal\Core\Theme\Registry->processExtension(Array, 'views', 'module', 'views', 'core/modules/views') (Line: 341)
    Drupal\Core\Theme\Registry->build() (Line: 240)
    Drupal\Core\Theme\Registry->get() (Line: 88)
    Drupal\Core\Utility\ThemeRegistry->initializeRegistry() (Line: 69)
    Drupal\Core\Utility\ThemeRegistry->__construct('theme_registry:runtime:seven', Object, Object, Array, 1) (Line: 260)
    Drupal\Core\Theme\Registry->getRuntime() (Line: 142)
    Drupal\Core\Theme\ThemeManager->render('html', Array) (Line: 431)
    Drupal\Core\Render\Renderer->doRender(Array, 1) (Line: 200)
    Drupal\Core\Render\Renderer->render(Array, 1) (Line: 144)
    Drupal\Core\Render\Renderer->Drupal\Core\Render\{closure}() (Line: 573)
    Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 145)
    Drupal\Core\Render\Renderer->renderRoot(Array) (Line: 66)
    Drupal\Core\Render\BareHtmlPageRenderer->renderBarePage(Array, Object, 'maintenance_page', Array) (Line: 76)
    Drupal\Core\ProxyClass\Render\BareHtmlPageRenderer->renderBarePage(Array, Object, 'maintenance_page', Array) (Line: 195)
    Drupal\system\Controller\DbUpdateController->handle('info', Object)
    call_user_func_array(Array, Array) (Line: 114)
    Drupal\Core\Update\UpdateKernel->handleRaw(Object) (Line: 75)
    Drupal\Core\Update\UpdateKernel->handle(Object) (Line: 28)

    JasonLuttrell’s picture

    Also, to follow up on #218, when I attempted to remove the patch and revert to the original Group module, I get the following error message as well, which also relates to group_content storage. Please help, I cannot get past this to the admin screen or anything. I'm dead in the water and tempted to start from scratch.

    The website encountered an unexpected error. Please try again later.
    TypeError: Argument 1 passed to Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}() must implement interface Drupal\Core\Field\FieldStorageDefinitionInterface, instance of __PHP_Incomplete_Class given in Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}() (line 174 of core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php).
    Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}(Object)
    array_filter(Array, Object) (Line: 176)
    Drupal\Core\Entity\Sql\DefaultTableMapping::create(Object, Array, '') (Line: 381)
    Drupal\Core\Entity\Sql\SqlContentEntityStorage->getCustomTableMapping(Object, Array) (Line: 356)
    Drupal\Core\Entity\Sql\SqlContentEntityStorage->getTableMapping() (Line: 203)
    Drupal\Core\Entity\Sql\SqlContentEntityStorage->initTableLayout() (Line: 188)
    Drupal\Core\Entity\Sql\SqlContentEntityStorage->__construct(Object, Object, Object, Object, Object, Object, Object, Object) (Line: 156)
    Drupal\Core\Entity\Sql\SqlContentEntityStorage::createInstance(Object, Object) (Line: 285)
    Drupal\Core\Entity\EntityTypeManager->createHandlerInstance('Drupal\group\Entity\Storage\GroupContentStorage', Object) (Line: 274)
    Drupal\Core\Entity\EntityTypeManager->getHandler('group_content', 'storage') (Line: 208)
    Drupal\Core\Entity\EntityTypeManager->getStorage('group_content') (Line: 106)
    Drupal\views\EntityViewsData::createInstance(Object, Object) (Line: 26)
    Drupal\group\Entity\Views\GroupContentViewsData::createInstance(Object, Object) (Line: 285)
    Drupal\Core\Entity\EntityTypeManager->createHandlerInstance('Drupal\group\Entity\Views\GroupContentViewsData', Object) (Line: 274)
    Drupal\Core\Entity\EntityTypeManager->getHandler('group_content', 'views_data') (Line: 177)
    views_views_data()
    call_user_func_array('views_views_data', Array) (Line: 392)
    Drupal\Core\Extension\ModuleHandler->invoke('views', 'views_data') (Line: 237)
    Drupal\views\ViewsData->getData() (Line: 154)
    Drupal\views\ViewsData->get('aggregator_feed') (Line: 91)
    Drupal\ds\Plugin\Derivative\DsEntityRow->getDerivativeDefinitions(Array) (Line: 101)
    Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDerivatives(Array) (Line: 87)
    Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDefinitions() (Line: 284)
    Drupal\Core\Plugin\DefaultPluginManager->findDefinitions() (Line: 175)
    Drupal\Core\Plugin\DefaultPluginManager->getDefinitions() (Line: 146)
    views_theme(Array, 'module', 'views', 'core/modules/views') (Line: 454)
    Drupal\Core\Theme\Registry->processExtension(Array, 'views', 'module', 'views', 'core/modules/views') (Line: 341)
    Drupal\Core\Theme\Registry->build() (Line: 240)
    Drupal\Core\Theme\Registry->get() (Line: 88)
    Drupal\Core\Utility\ThemeRegistry->initializeRegistry() (Line: 69)
    Drupal\Core\Utility\ThemeRegistry->__construct('theme_registry:runtime:seven', Object, Object, Array, 1) (Line: 260)
    Drupal\Core\Theme\Registry->getRuntime() (Line: 142)
    Drupal\Core\Theme\ThemeManager->render('html', Array) (Line: 431)
    Drupal\Core\Render\Renderer->doRender(Array, 1) (Line: 200)
    Drupal\Core\Render\Renderer->render(Array, 1) (Line: 144)
    Drupal\Core\Render\Renderer->Drupal\Core\Render\{closure}() (Line: 573)
    Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 145)
    Drupal\Core\Render\Renderer->renderRoot(Array) (Line: 66)
    Drupal\Core\Render\BareHtmlPageRenderer->renderBarePage(Array, Object, 'maintenance_page', Array) (Line: 76)
    Drupal\Core\ProxyClass\Render\BareHtmlPageRenderer->renderBarePage(Array, Object, 'maintenance_page', Array) (Line: 195)
    Drupal\system\Controller\DbUpdateController->handle('info', Object)
    call_user_func_array(Array, Array) (Line: 114)
    Drupal\Core\Update\UpdateKernel->handleRaw(Object) (Line: 75)
    Drupal\Core\Update\UpdateKernel->handle(Object) (Line: 28)

    carolpettirossi’s picture

    Hi @jasonluttrell,

    Patch provided on #215 works well for me on 1.2.

    Are you facing issue applying the patch or when you access a page?

    Please make sure to:
    1. Check if you have this in composer.json require section: "drupal/group": "^1.0@RC",
    2. Update composer.json adding the patch like this:

    "patches": {
                "drupal/group": {
                    "Group content Path automated pattern and tokens for actual node (say: Title)": "https://www.drupal.org/files/issues/group-content-entity-tokens-2916907-7.patch",
                    "Entities identified by strings as group content": "https://www.drupal.org/files/issues/2020-08-13/group-support_string_entity_ids-2797793-215.patch"
                }
    },

    3. Run composer install to apply the patch
    4. Run drush updb -y => VariationCache will be installed
    5. Run drush cr

    JasonLuttrell’s picture

    Sorry, to clarify: The issue isn't applying the patch. It's when migrating from Drupal 8 to 9. I am unable to access the site. I wonder if it was because previous data was not migrated. I will try your recommendations and report back.

    JasonLuttrell’s picture

    Hi @carolpettirossi - I applied the patch #215 and there was no problem with that. And I cleared the cache. And I believe VariationCache is already installed.

    But it won't let me access drush updb. I get the same message.

    [error] Error: Call to a member function getFieldStorageDefinitions() on null in Drupal\group\Entity\Views\GroupContentViewsData->getFieldTableName() (line 484...

    Again, to be clear, this is from an import of a Drupal 8 site. I'm sure it works fine fresh out of the box, but that's not what I'm doing here.

    This is a major blocker for me as my site heavily depends on Groups. Any help would be greatly appreciated! Thanks.

    ...In the meantime, I'm going to try re-installing Groups and starting over with it for Drupal 9 as it looks like I may have no choice. :( :(

    JasonLuttrell’s picture

    This issue has become a major blocker for me. Especially as I'm unable to uninstall the module either to workaround it!

    Issue related to not being able to uninstall:
    https://www.drupal.org/project/group/issues/3159157#comment-13801895

    I wonder if this patch has something to do with that issue?

    safetypin’s picture

    Can you post the full error message? line 484.. of which file?

    Have you removed the patch as well as uninstalling the groupmenu module? I don't know the install/uninstall system backwards and forwards, so I can imagine it's possible the module is leaving a table behind, or something else, but that error message means that there's some data that is empty that the code assumes exists.

    JasonLuttrell’s picture

    @safetypin, I posted the entire messages above. I think it said something about "line 484 of modules/contrib/group/src/Entity/Views/GroupContentViewsData.php"

    I think you may be right about the data--possibly data that was never migrated from one format to another. I see one of the previous updates had something to do with that? I tried the module with and without the patches, and I uninstalled and reinstalled the module, to no avail.

    Ultimately, I just uninstalled the group module, and all related modules, and will have to rebuild the group functionality. It's somewhat of a "loss" in terms of time spent having to redo things but, for a Drupal 9 migration, if it's the only real functionality that I lost, it's not that big of a deal.

    Flotsam’s picture

    Hi - I'm working on a site I recently inherited that was not well patched, though we've brought Drupal core up to 8.9.3 and most other modules are up-to-date. The site is built around the Group module (currently 8.x-1.0-rc5), Group Menu, and Group Menu Block (both at 8.x-1.0-alpha1). Attempts to upgrade Group have been difficult. The site has been running ok until today.

    Today, authenticated editors receive the following error when attempting to access any group content listing, group menu, or group block:
    Error: Call to a member function id() on null in modules/contrib/groupmenu/src/GroupMenuConfigOverrides.php on line 214
    and they cannot access most pages while logged in (though unauthenticated views of the site are functional).

    The problem seems to boil down to the issue here. I've attempted many of the combinations above including the patch from #200 which had not been previously installed. At the moment, I can successfully update the 1.2 or the 1.x-dev version with the patch from #215 (much as suggested by @carolpettirossi) - but at the point of running "drush updb" I get:

    SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal.group_content__entity_id_str' doesn't exist (same as noted in #192 and #193).

    The site has also (since before I inherited it) suffered from the follow issue in the status report that may be worth noting:

    Mismatched entity and/or field definitions
    The following changes were detected in the entity type and field definitions.

    Group content
    The Content field needs to be updated.

    Any suggestions for how to proceed would be welcome!

    carolpettirossi’s picture

    @jasonluttrell,

    I've started to face the Uncaught Error: Call to a member function getFieldStorageDefinitions() on null in /var/www/html/web/modules/contrib/group/src/Entity/Views/GroupContentViewsData.php:484
    error too.

    I was on 8.9.3 and patch #215 worked well with that version. Now, I'm upgrading D8 to D9 and started to face the error.

    carolpettirossi’s picture

    @jasonluttrell,

    I've updated #215 changing entityManager to entityTypeManager and using getFieldStorageDefinitions from EntityViewsData instead of EntityManager. (See interdiff)

    Hopefully, this patch will fix the issues you are facing.

    carolpettirossi’s picture

    Here's a new patch with a small update on group_test_content.info.yml adding core_version_requirement: ^8.8 || ^9 and removing core.

    Flotsam’s picture

    @carolpettirossi et al - I've had no trouble applying the most recent patches, or those suggested in https://www.drupal.org/project/groupmenu/issues/3010909#comment-13188662 (or #12 from there), but at each attempt to update the DB, I get blocked with the missing table (same as noted in #192 and #193 above).

    SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal.group_content__entity_id_str' doesn't exist

    I have my suspicions that this issue may go back to the move from core 8.6 to 8.7 in which entities were not properly massaged, but I didn't manage the site when that update took place (perhaps a year ago). I'm also new to the Group module. Our site is fully architected around it, and I'd really like to get Group (and Group Menu) up-to-date.

    I don't see above how #192 or #193 resolved their issue, and working my way down the patches produces the same missing table error. Could anyone provide a pointer as to what might be best to look at, or how I might create the table above?

    As a side note, when I run "drush entup" using Devel Entity Updates to see if it's possible to post-massage the entities into shape, my very first error is "The SQL storage cannot change the schema for an existing field (entity_id in group_content entity) with data." How do I go about determining what schema changes are even needed?

    (Sorry for the green questions: I've worked with Drupal for over a decade, and I've never had an issue this troublesome...largely because I kept my sites religiously up-to-date.)

    anruether’s picture

    @flotsam please keep in mind that this issue is at #231 with 100 users getting notified. For the sake of keeping this issue focus please isolate the problem, reproduce it and come back to this issue only if it it helps to resolve it.

    Flotsam’s picture

    Thanks @anruether - and sorry for the thrashing about in my previous messages. I was just getting up to speed and saw a lot of the same issues I was facing in the comments above. The issue was repeatable in the environment I inherited which had group-8.x-1.0-rc5 and groupmenu-8.x-1.0-alpha1 running together without any patching -- having worked through the issue now, I'm surprised these worked together at all without the patches!

    We've solved the issues we were having, and in the off chance anyone else arrives at this page with back-level Group and Group Menu modules and no upgrade path, I'll post what we did to get around it.

    But first, many, many thanks to all of you who've worked on this patch to get the Group and Group Menu modules working together: it's a godsend that makes managing our enterprise site significantly more elegant.

    Our solution:

    • To allow the Group module a clean upgrade path*, we needed to uninstall Group Menu. This required writing two patches, one for Group and one for Group Menu that disabled entity lookups that were broken for us.
    • With our patches applied, we were able to visit the Group menu pages and delete the relation for each menu (which of course still leaves the menu intact). We chose to do this through the web interface -- we drove the process using Selenium -- rather than with hooks because it was a) easier and b) we were uncertain what state of affairs our data was in at this point, and it seemed safest.
    • With all menus disassociated from the groups, we were able to uninstall Group Menu Block and Group Menu.
    • We then upgraded Group (to version 8.x-1.2) and Group Menu (to version 8.x-beta2) using the patch from #229 above for Group and the patch from #9 at https://www.drupal.org/project/groupmenu/issues/3010909 for Group Menu.
    • After reinstalling Group Menu we were able to re-associate all the menus with their Groups.

    *Prior to taking this approach we spent a lot of time trying to get the patches above to work with our existing modules (particularly #188 which applied well to rc5) - and we found no upgrade path that worked with our data. We modified the #188 patch to allow the group_content_entity_id_update table to accept varchar entity_id values from the group_content_field_data table - since the Group Menu entity IDs were stored as strings. However the new group_content__entity_id and group_content__entity_id_str tables would not get created during the update process (no error - just didn't get made). So we created these manually. We were able to get the non-string entities to load, but we never got the string entities to load during the group_post_update_restore_entity_id_data. These would fail when attempting to do any kind of entity id or label lookup. So at this point, it seemed wisest to reconstruct the relations to make sure our data was in a better state, which is what we did.

    The new modules patched with the latest patches are working very well. Thanks again for the hard work on this. (And apologies for being verbose - I included the details in the off chance it may help someone in a similar situation to ours.)

    ovidenov’s picture

    For me patch 229 didn't work entirely.
    Scenario:

    1) Visiting group nodes view as group member (and not admin).
    2) Some of the unpublished nodes were not visible

    The patch is tested against group, version 1.3.

    Possible solution.

    File: src/QueryAccess/EntityQueryAlter.php

    I replaced one join and added other join in the doAlter function (line 231)

     $query->leftJoin(
          'group_content__entity_id',
          'gcfdss',
          "$base_table.$id_key=gcfdss.entity_id_target_id AND gcfdss.bundle IN (:group_content_type_ids_in_use[])",
          [':group_content_type_ids_in_use[]' => $group_content_type_ids_in_use]
        );
    
        $query->innerJoin(
          'group_content_field_data',
          'gcfd',
          "gcfdss.entity_id=gcfd.id AND gcfd.type IN (:group_content_type_ids_in_use[])",
          [':group_content_type_ids_in_use[]' => $group_content_type_ids_in_use]
        );
    
    AdamEvertsson’s picture

    I agree with many of the commenters above: Great job with this module, it is a marvel in handling groups and all.

    I was wondering if there is any progress with this issue, though, since this i blocking a project of mine. Is there anything that I, a non-coder, can do to help out here?

    // Adam

    safetypin’s picture

    @AdamEvertsson, You can add some things to your composer.json file and have composer automatically apply this patch for you. If there is a new release of the module, it's possible that the patch won't apply automatically. In that case, you'll need to replace the URL for the patch file with an updated patch posted here. If you have trouble, ask in the drupal support slack channel, and hopefully someone will help. I'm usually in there, but i don't always pay close attention. if you @ me, i'll try to help.

    First, run composer require cweagans/composer-patches.

    Next, look for the section of composer.json that starts with "extra": {. It might look something like this:

        "extra": {
            "drupal-scaffold": {
                "locations": {
                    "web-root": "docroot/"
                }
            },
            "installer-paths": {
                "docroot/core": ["type:drupal-core"],
                "docroot/libraries/{$name}": ["type:drupal-library"],
                "docroot/modules/contrib/{$name}": ["type:drupal-module"],
                "docroot/profiles/contrib/{$name}": ["type:drupal-profile"],
                "docroot/themes/contrib/{$name}": ["type:drupal-theme"],
                "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
                "docroot/modules/custom/{$name}": ["type:drupal-custom-module"],
                "docroot/themes/custom/{$name}": ["type:drupal-custom-theme"]
            }
        }
    

    and you want to make it look like this:
    (You need to add a comma after the last curly brace, and add the "patches" section.)

        "extra": {
            "drupal-scaffold": {
                "locations": {
                    "web-root": "docroot/"
                }
            },
            "installer-paths": {
                "docroot/core": ["type:drupal-core"],
                "docroot/libraries/{$name}": ["type:drupal-library"],
                "docroot/modules/contrib/{$name}": ["type:drupal-module"],
                "docroot/profiles/contrib/{$name}": ["type:drupal-profile"],
                "docroot/themes/contrib/{$name}": ["type:drupal-theme"],
                "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
                "docroot/modules/custom/{$name}": ["type:drupal-custom-module"],
                "docroot/themes/custom/{$name}": ["type:drupal-custom-theme"]
            },
            "patches": {
              "drupal/group": {
                "Entities identified by strings as group content": "https://www.drupal.org/files/issues/2020-10-12/group-support_string_entity_ids-2797793-233.patch"
              }
            }
    
    gotenhansan’s picture

    For those who have applied the large patch with refactoring and database structure updates: How confident are you that future updates of the module won't present problems with the patched code?

    Our fears for a current project is that we apply the patch, but future updates of the group module will end up requiring a completely new migration of the data due to incompatible database structure.

    I'm sure - looking at the activity and hard work with this issue - that there will be community-led update paths for those with the patch applied, but we just have to make sure that we won't have to spend hours and hours on a new migration path.

    Kind regards,
    Hans@Kodamera

    Berdir’s picture

    Not the maintainer and not involved here, but yes, this is a massive change and applying patches with update functions is always a very big risk. Even if the patch itself doesn't change much, all it takes is one new update function and then every site using this is in deep trouble. You will miss out on the new official update unless you manually run them and this update will try to rerun which might or might not be a problem.

    azinck’s picture

    Yes, I agree that this is something keeping me up at night. I'd love to see a maintainer comment as to the likelihood of this getting committed since it's going to be very hard to minimize damage without maintainer support.

    esolitos’s picture

    Status: Needs work » Needs review
    FileSize
    87.52 KB
    561 bytes

    I encountered an issue with patch #233 where all content not belonging to a group was not visible for anonymous user, after quite some debugging it turned out to be this patch and the innerJoin added in #233.

    I have changed this to leftJoin which makes more logical sense (see interdiff attached) and that seems to have solved the issue.

    Review is extremely welcome.

    j-vee’s picture

    My issue explained over here https://www.drupal.org/project/group/issues/3162259 got fixed by your patch esolitos. Didn't realise the issue was stemming from here but after updating to this patch, all my nodes appear as they should again.

    Thank you!

    Mike.Conley’s picture

    +1 for input from a maintainer here. I need this patch in order to get to D9, but I don't want to make my site incompatible w/ future releases in the process.

    JasonLuttrell’s picture

    @esolitos thanks. What you described is exactly what I encountered as posted in the issue here, a possible duplicate:
    https://www.drupal.org/project/group/issues/3182228

    I also was using the patch in #233. I will have to check out your patch to see if it fixes the problem and then we can close the other issue.

    JasonLuttrell’s picture

    Finally getting around to it... I can confirm that updating my patch from #233 to #239 (a simple change of query->innerJoin() to query->leftJoin()) helped all my views to reappear again (without needing to check the View's Query Settings - disable rewrite SQL). So, definitely use 239 instead of 233.

    (Thus, I can also close issue #3182228).

    Thanks again, @esolitos.

    KarimBou’s picture

    Using Drupal 8.9 menugroup and group latest 1.3 so using this patch #239 i'm getting this error on drupal update.

    SQLSTATE[42S02]: Base table or view not found: 1146 Table 'default.group_content__entity_id_str' doesn't exist: SELECT t.*                                                                                      
      FROM                                                                                                                                                                                                            
      {group_content__entity_id_str} t                                                                                                                                                                                
      WHERE (entity_id IN (:db_condition_placeholder_0, :db_condition_placeholder_1, :db_condition_placeholder_2, :db_condition_placeholder_3, :db_condition_placeholder_4, :db_condition_placeholder_5, :db_conditi  
      on_placeholder_6, :db_condition_placeholder_7, :db_condition_placeholder_8, :db_condition_placeholder_9, :db_condition_placeholder_10, :db_condition_placeholder_11, :db_condition_placeholder_12, :db_conditi  
      on_placeholder_13, :db_condition_placeholder_14, :db_condition_placeholder_15, :db_condition_placeholder_16, :db_condition_placeholder_17, :db_condition_placeholder_18, :db_condition_placeholder_19, :db_con  
      dition_placeholder_20, :db_condition_placeholder_21, :db_condition_placeholder_22, :db_condition_placeholder_23, :db_condition_placeholder_24, :db_condition_placeholder_25, :db_condition_placeholder_26, :db  
      _condition_placeholder_27, :db_condition_placeholder_28, :db_condition_placeholder_29, :db_condition_placeholder_30, :db_condition_placeholder_31, :db_condition_placeholder_32, :db_condition_placeholder_33)  
      ) AND (deleted = :db_condition_placeholder_34) AND (langcode IN (:db_condition_placeholder_35, :db_condition_placeholder_36, :db_condition_placeholder_37, :db_condition_placeholder_38))                       
      ORDER BY delta ASC; Array                                                                                                                                                                                       
      (                                                                                                                                                                                                               
          [:db_condition_placeholder_0] => 9                                                                                                                                                                          
          [:db_condition_placeholder_1] => 23                                                                                                                                                                         
          [:db_condition_placeholder_2] => 28                                                                                                                                                                         
          [:db_condition_placeholder_3] => 38                                                                                                                                                                         
          [:db_condition_placeholder_4] => 51                                                                                                                                                                         
          [:db_condition_placeholder_5] => 63                                                                                                                                                                         
          [:db_condition_placeholder_6] => 67                                                                                                                                                                         
          [:db_condition_placeholder_7] => 71                                                                                                                                                                         
          [:db_condition_placeholder_8] => 75                                                                                                                                                                         
          [:db_condition_placeholder_9] => 79                                                                                                                                                                         
          [:db_condition_placeholder_10] => 83                                                                                                                                                                        
          [:db_condition_placeholder_11] => 87                                                                                                                                                                        
          [:db_condition_placeholder_12] => 91                                                                                                                                                                        
          [:db_condition_placeholder_13] => 95                                                                                                                                                                        
          [:db_condition_placeholder_14] => 99                                                                                                                                                                        
          [:db_condition_placeholder_15] => 103                                                                                                                                                                       
          [:db_condition_placeholder_16] => 107                                                                                                                                                                       
          [:db_condition_placeholder_17] => 111                                                                                                                                                                       
          [:db_condition_placeholder_18] => 115                                                                                                                                                                       
          [:db_condition_placeholder_19] => 119                                                                                                                                                                       
          [:db_condition_placeholder_20] => 123                                                                                                                                                                       
          [:db_condition_placeholder_21] => 127                                                                                                                                                                       
          [:db_condition_placeholder_22] => 131                                                                                                                                                                       
          [:db_condition_placeholder_23] => 135                                                                                                                                                                       
          [:db_condition_placeholder_24] => 139                                                                                                                                                                       
          [:db_condition_placeholder_25] => 143                                                                                                                                                                       
          [:db_condition_placeholder_26] => 147                                                                                                                                                                       
          [:db_condition_placeholder_27] => 151                                                                                                                                                                       
          [:db_condition_placeholder_28] => 155                                                                                                                                                                       
          [:db_condition_placeholder_29] => 159                                                                                                                                                                       
          [:db_condition_placeholder_30] => 163                                                                                                                                                                       
          [:db_condition_placeholder_31] => 170                                                                                                                                                                       
          [:db_condition_placeholder_32] => 214                                                                                                                                                                       
          [:db_condition_placeholder_33] => 223                                                                                                                                                                       
          [:db_condition_placeholder_34] => 0                                                                                                                                                                         
          [:db_condition_placeholder_35] => fr                                                                                                                                                                        
          [:db_condition_placeholder_36] => en                                                                                                                                                                        
          [:db_condition_placeholder_37] => und                                                                                                                                                                       
          [:db_condition_placeholder_38] => zxx                                                                                                                                                                       
      )                                                                                                                                                                                                               
                                                                                                                                                                                                                      
    
    In Statement.php line 59:
                                                                                                                      
      SQLSTATE[42S02]: Base table or view not found: 1146 Table 'default.group_content__entity_id_str' doesn't exist  
                                                                                                                      
    safetypin’s picture

    @KarimBou, did you run database updates after applying the patch?

    kristiaanvandeneynde’s picture

    I've discussed this with @kyuubi on Slack (you can read it in the #d8-group channel) and I'll try to formulate my findings tomorrow. But it basically boils down to this:

    GroupContentEnablers and their handlers were really made with content entities in mind: bundles, query access, ... Trying to duckpunch this system into one that also serves config just keeps feeling off to me. Having said that, config does not need as much as content, so maybe a "light" version of the plugins will do.

    So I'll try to summarize my points tomorrow and discuss what a system we can commit might look like. I know the specs of this patch have changed a lot over the years, but it's a really difficult topic and finding the time to deal with this issue is hard. Stay tuned.

    kristiaanvandeneynde’s picture

    Okay so here we go.

    First of all, I want to point out that I've given the current architecture a lot of thought over the past 24 hours and found that I had good reasons to build the whole GroupContent, -Type, -Enablers system and I still think it was the right call. The UX can be horrible under some circumstances, but just using plain ER fields or a custom table was not an option. I also still believe plugins are the way to go.

    Which leads us to the following point: If the current architecture, using plugins, works so well, why can't we just use the same thing for config entities? The answer is that config entities behave vastly different to content entities.

    Examples (content vs config):

    • Stored in normalized DB vs stored in single files
    • Has list access vs does not
    • Has indexes vs does not (well, not really)
    • Does not use universally unique IDs out of the box vs does (as machine names)

    The plugins as they stand

    Because I don't want my code base riddled with if ($foo instance of ContentEntityInterface) elseif ($foo instance of ConfigEntityInterface), I really dislike the idea of trying to duckpunch the current system into also supporting config entities. Having said that, I did take an approach when writing the Subgroup module that might work well here.

    In Subgroup, the entire tree metadata is handled by a handler. But GroupType entities have their metadata stored as third party settings, whereas Group entities have them stored as field data. The solution here was to write an abstract handler that shares all of the logic, but offloads the actual storage interaction to a few abstract methods that subclasses must implement.

    Maybe we can do something similar here. In recent versions of Group, you'll notice that some functionality (permission defining and access checks) has moved to so-called plugin handlers. What if we start abstracting all of the monolithical cruft out of the plugin base class and into handlers?

    At that point, we have options:

    • Either we have two plugin types that can share a lot of handlers but have differing default handlers where we encounter one of these config/vs/content differences.
    • Or, we keep one plugin type, but in the base class set different default handlers depending on whether the entity type implements the content or config interface.

    I'm not entirely sure about the second scenario because it's more error prone whereas the first scenario would have a much clearer scope. But scenario one forces us to check plugin type whereas scenario two is more like Subgroup in a sense that we don't have to care what type it is because the handlers are supposed to tread carefully in your stead.

    The storage issue

    Here we face the biggest challenge. We cannot use the GroupContent DB table for both config and content as we need the ID column to be int so that we can have fast joins in the already expensive entity query access joins. So the above solves part of the puzzle, but we still need to figure out how we will store things.

    The whole GC DB table is joined when checking for query access so, ideally, we keep config entities out of there to make these joins faster. Here, we have several options to explore, some of which were mentioned in this issue before:

    Custom storage handler for ER base field that creates two columns instead of one

    • Pro: One entity type for all things, handlers will take care of the differences, column to join remains INT for content.
    • Con: Config entities in GC DB table, leading to (slightly?) slower joins.

    Two entity types (GroupContent/GroupConfig) sharing same bundle (GroupContentType, to be renamed)

    • Pro: Separate tables
    • Con: 90% Copy-pasted annotation and nightmare routes unless we write a custom upcaster that allows {group_relation} to translate into either a GContent or GConfig entity. While interesting, this might lead to a lot of pain.

    Current approach

    • Pro: Already implemented
    • Con: Really not a fan of getEntityFieldNameForEntityType as it puts the onus on the entity rather than the field storage. Also not a fan that the patch does not handle content vs config.

    So in summary

    If we "handlerize" the plugin system more and give storage another round of thought, considering the table needs to be joinable and ID columns may not use the wrong type, we could land on a solution that works well all-around without the implementer needing to worry too much about whether they are grouping config or content.

    kscheirer’s picture

    What about entities that are neither config nor content? That's my use case, though I admit we're probably the only ones that do this.
    Drupal Core - #3042467: Support entities that are neither content nor config entities

    kristiaanvandeneynde’s picture

    Re #248 while that issue you pointed to seems really interesting, the sad reality is that core is not the fastest open source project out there when it comes to picking up these type of feature requests. So it would seem like a lot of hassle for Group to already (try and) support something that is still iffy in core itself.

    kristiaanvandeneynde’s picture

    I just spent some time combing over EntityReferenceItem again and I'm truly wondering if no-one here got the following to work yet:

    • Class that extends EntityReferenceItem and when we define our entity_reference BaseFieldDefinition, we call setClass() on it and set this class.
    • schema() defines two columns: target_id_int and target_id_str
    • propertyDefinitions() defines two properties: target_id_int and target_id_str, with an additional computed property of target_id
    • Make sure that onChange(), setValue(), etc. properly grab the right column and assign to computed target_id property when loading or take what is in the computed target_id and write to either target_id_int or target_id_str when saving

    This would lead to two columns being in the DB, but for all intents and purposes Drupal only seeing one computed target_id property and therefore all other code surrounding entity references should keep on working. The only major downside I see is that content entities with string IDs will still not be supported. Which isn't any different from the current scenario where our entity_id base field is always an int column.

    Some background: GroupContent uses the same approach as Comment when it comes to dynamic entity reference targets and core has been ignoring content entities with string IDs for years. It throws an exception when _comment_entity_uses_integer_id() returns FALSE and basically tells entities with string IDs to get lost. See #2496699: Allow comments to be attached to entities using a string primary key

    So maybe the solution is to also tell content entities with string IDs to get lost and take it up with core instead? Seems weird to try and support a scenario core itself can't/won't even support right now.


    I still see a few major drawbacks to supporting config entities, though:

    • The entity reference suggestion above is bound to break in combination with some contrib (or core) code that doesn't use the API properly. Maybe not often, but it will break with some code
    • Config entities do not support list queries at all, but people have come to expect this of Group. Why is a list of nodes secure when a list of menus isn't?
    • String vs integer remains a hot topic. But as mentioned above: if core doesn't support it, how can we?

    Another idea I've been pondering, but would lead to an ugly data architecture is yet another interstitial entity when dealing with config:
    Group <-> GroupContent <-> ConfigWrapper <-> Config

    Where ConfigWrapper is a content entity so we don't have to change GroupContent at all, with the added benefit that config entity queries could have list access by proxy of their wrappers. Then your GroupMenu plugin for example wouldn't directly wrap menu entities, but a specific bundle of ConfigWrapper entities designed to wrap menus, and only menus.

    The hard part would be to tie that all together, but I'm sure a central service or two could take care of the heavy lifting. The big benefit being that the core of Group remains: "You gather content entities with int IDs"

    abramm’s picture

    Hi kristiaanvandeneynde,

    I think the approach you've described is exactly what we discussed in Slack and what I've implemented in my patch series (how long time ago it was? 2 years?).

    As far as I remember, the development was almost complete, there was a single failing test in Views or edge case somewhere (I don't remember exactly tbh).

    kristiaanvandeneynde’s picture

    Hi @abramm, I was wondering whether we hadn't tried that before, but with the limited time I can spend on this issue I can't go over all 250 comments :P What was the reason we abandoned that approach? Keep in mind we only want one base field, which internally behaves as two.

    abramm’s picture

    I'm sorry but my customer switched to a different language/framework so my work was never complete. The last patch I posted was at #146 I believe.
    I haven't been following the issue too much but I know someone have updated it further and people were using those patches; try looking starting from #146 :).

    I'm not entirely sure if it was one field behaving differently or two fields selected conditionally.

    kristiaanvandeneynde’s picture

    Yeah the current patch seems to be using two fields and then switching between them.

    safetypin’s picture

    I can confirm that the current patch utilizes two fields and switches between them. I encountered and fixed a permissions issue, and used a conditional to load the permissions of a config entity, in exactly the sort of way you mentioned not wanting to do. This seems like a reasonable way to handle a less than ideal situation to me. However, I like the sound of several of your ideas. I will be glad to help out any way I can once we've decided how to proceed; do we need to stay in a holding pattern on this issue to wait for more people to come into agreement?

    In case you didn't see it in the patches:

    @@ -78,13 +80,17 @@ class GroupContentCardinalityValidator extends ConstraintValidator implements Co
           return;
         }
     
    -    // Get the entity_id field label for error messages.
    -    $field_name = $group_content->getFieldDefinition('entity_id')->getLabel();
    +    // Get the content reference field name. May be either entity_id or
    +    // entity_id_str depending on the referenced content type.
    +    $ref_field = $this->getContentReferenceField($plugin);
    

    This maps to separate tables in the database: group_content__entity_id and group_content__entity_id_str.

    jacobbell84’s picture

    Just doing a quick re-roll to support the latest dev branch.

    anish.a’s picture

    anish.a’s picture

    Rerolled

    anish.a’s picture

    Is there any solution to the issue raised in #170 by @joshuami ? I am encountering the same issue. All my group content related views are broken.

    Nigel Cunningham’s picture

    Reroll of #258 against current dev.

    tstoeckler’s picture

    Stumbled on this issue, as well. In our case, we are not actually storing configuration entities as group content but (custom) content entities that have string IDs, but the general issue is the same. As we will (almost) exclusively have group content with string IDs I don't think we will be hitting the performance issues discussed here, but I understand that they make just changing the column to type varchar unfeasible.

    I am not at all opposed to the "two-column" approach in the recent patches, but since I'm not sure when that will land and due to the significant complexity involved there and also because I want to avoid an update hook in a patch I carry for a project (due to reasons also noted above) I wrote a small module that performs the schema change as part of the module installation. It also supports reverting the change as part of the unintallation.

    For now, I've created it as a sandbox project, but I would like to promote it to a full project unless there is significant interest in having something like it as part of this or another module and unless someone points out a significant flaw with the module. Feel free to check it out and I would appreciate any feedback! (If it's strictly related to the module, please use the issue queue there, in order to not make this thread any longer than it needs to be ;-))

    Module page: https://www.drupal.org/sandbox/tstoeckler/3221218

    joshuami’s picture

    In response to @anish.a, we ended up taking a different approach to group menus after running into that error creating views with group content relationships. Basically, we use a view to create a menu of pages marked as "show in menu" (boolean field) and allow pages to have a parent page (entity reference). This gives us a 2-layer menu for groups with no dependencies to the menu system. It's a trade off as we had to create our own active trail handling in the view with twig, but it kept us from needing the extra patch.

    kristiaanvandeneynde’s picture

    While I don't have time to review it, the solution offered in #261 might be a great alternative to trying to keep this long issue going. It would be in everyone's best interest to review that small module and see if it works as expected.

    Thanks Tobias

    kyuubi’s picture

    Thanks @tstoeckler for the module!

    Unfortunately if I understand correctly, contrary to the patch which allows for both int and string, the solution in the module handles ids as strings exclusively. This means for us for example, who have almost 2million group content entities, 90% of which are ints, the performance impact will be severe I think.

    @kristiaanvandeneynde Any progress on alternative approaches as we discussed a while back? Given it's been 5 years already and the issue still holds traction I think there is no denying this needs to move ahead. I'm personally totally okay with the two fields. I can vouch for it working well at scale, we have hundreds of groups and millions of group content entities and it's still going strong.

    My vote would be to leverage the current solution in the patch but potentially try to clean up the code a bit.

    kristiaanvandeneynde’s picture

    @kyuubi The changes would be too drastic for this major version. I have started work on 2.0.0 and maybe we can fit that in there, but my first goal would be to finish what was planned for 2.0.0, then start investigating which modules rely on this patch and whether they could function using the available API, rather than supporting string IDs. Also, until core properly supports string IDs for content entities, there's not much we can do there either.

    My main concern is list access and performance. If Group promises to hide a few menus/vocabularies/etc. and you then visit the overview page for said config entity only to see all of them, that's a no go. Similarly, if adding support for config entities means the whole module becomes even slower than it already can be on really large websites, that's also a big nope.

    Having said that, my one day a week will currently go to the work on 2.0.0 for two reasons:

    • The ideas I have for it are awesome and I want to see how well they do
    • The module could become even more extensible, meaning less maintenance on my end

    So anything else will either have to be sponsored or picked up by a co-maintainer. I found one recently and he's been slowly (but steadily) making himself familiar with the project. Critical bugs will obviously still get their due attention.

    Flotsam’s picture

    I've installed tstoeckler's module from #261 and tried it out on a standard drupal/recommended-project composer install of Drupal 9.2 with Group 1.3. I agree this does seem a promising approach.

    Our interest in this issue's patch is to provide support for the GroupMenu module - and so using only the patch (and approach) found at https://www.drupal.org/project/groupmenu/issues/3144604 to make GroupMenu Drupal 9 ready, we were able to get the GroupMenu module installed alongside tstoeckler's module (#261), though it has some trouble.

    At this point, when we visit /admin/structure/menu we see the following error:
    [php7:notice] ...TypeError: Argument 1 passed to Drupal\\group\\Entity\\Storage\\GroupContentStorage::loadByEntity() must implement interface Drupal\\Core\\Entity\\ContentEntityInterface, instance of Drupal\\system\\Entity\\Menu given, called in [...]/web/modules/contrib/group/group.module on line 321 in [...]/web/modules/contrib/group/src/Entity/Storage/GroupContentStorage.php

    So we'd have work to do, but this seems like a possibly better way to go.

    In the meantime, we have two questions:

    1. For those of us running this patch who'd like to stay up-to-date with the production Group release schedule while development is ongoing, which is the best patch to re-roll against the Group 1.4 release? We're currently running #239 of this patch successfully with Group 1.3. Is this the right one to rework for the 1.4 release? I'd prefer to stay with the official releases for production deployment (as much as possible - even if the amount of patching we're doing to the Group modules makes that a little moot).
    2. If we want to stop using this patch in the future, has anyone already done the work to unwind the schema changes this patch introduces?
    azinck’s picture

    For those using this patch only to support Group Menu, I would suggest moving to Group Content Menu (https://www.drupal.org/project/group_content_menu). It stores its data in content entities rather than config entities, obviating the need for this patch.

    kristiaanvandeneynde’s picture

    #267 +1. It must be pretty obvious by now that this patch won't go in soon, so please weigh your options carefully when choosing which module to use. Subgroup and Group Content Menu both managed to work fine without this patch, so I'm fairly sure some (but not all) use cases can take a similar approach.

    Flotsam’s picture

    Thank you for the feedback on Group Content Menu - we took a close look at it as we've been transitioning to Drupal 9, and had it working with our production data as a preliminary test. The transition from Group Menu to Group Content Menu seemed non-trivial, and we needed one patch to make the menus appear (see: https://www.drupal.org/project/group_content_menu/issues/3170013). It seemed somewhat likely we'd need to reconstruct the menu trees for ~ 150 groups so we were hesitant to take the plunge when Group Menu continued to work.

    At the time, I was uncertain if Group Content Menu was any safer a bet - it has about 238 sites reporting use vs. about 229 for Group Menu - so not a large install base, but if it gets a thumbs up here, then we'll look at it closely again! :-) In particular, I'll look closely at what the transition process looks like. It seemed possible to pull in at least some submenus when moving nodes. I'll report back what we learn.

    Our alternative was to not use either of the two menu modules, and rather use the default Drupal menuing since Group Menu already had a standard menu established for each group (these remain when uninstalling the module). We would create new menus and menu blocks by convention when a group was created. We were considering the Menu Select module to help make adding nodes to menus easier to manage as well since this approach produces a huge list when selecting a node's menu parent. So, if your groups themselves don't require the ability to manipulate the menu directly, this is another alternative. (Most of our group members do not need the ability to manipulate the menus: that's left to a team of specialists who build and hand-off sites to these groups. We were comfortable with downstream editors requesting menu/IA updates to this team. I realize that's not a solution for everyone. ;-)

    Uninstalling this patch is still non-trivial: we have production data that has this patch's schema changes in it. I'd love to know if someone has already pulled this patch out of production and rewound the schema to get things back to normal. If not, I'll document the process here if we successfully manage it. (And I'm most certainly open to tips for how best to proceed!)

    noman_297’s picture

    How to get paragraphs entities attached to a group programatically
    Like for nodes I follow this https://drupal.stackexchange.com/questions/238880/how-to-load-paragraph-...
    by using this method if I pass $group entity its not working. I visited the group object the paragraphs are attached in field definitions.
    Any help will be appreciated. Thanks

    dasginganinja’s picture

    I had a problem upgrading to Drupal 9 because of a patch that was previously used for Solution 2. I've posted a solution to this in https://www.drupal.org/project/group/issues/3223028

    Flotsam’s picture

    I also ran into this problem @dasginganinja which I worked through using the solution at https://www.drupalcenter.de/node/60218. The solution you point at is clearer (and better).

    tstoeckler’s picture

    Just as an FYI I promoted my module to a full project now: https://www.drupal.org/project/group_content_string_id

    Re @Flotsam #266: Those typehints in Group module are not something that can be fixed/altered from the outside. I wasn't aware of that, so that means that my module as of now cannot be used for Group Menu.

    Maybe, though, as a quick win here it would be possible to broaden those typehints from Group module to allow anything implementing EntityInterface? As far as I can tell from a cursory glance there's nothing content-entity-specific being used there?

    kristiaanvandeneynde’s picture

    Try changing just those hints and see if it breaks any tests :)

    tstoeckler’s picture

    Fair enough ;-)

    Opened a separate issue for that.

    Flotsam’s picture

    Looking for a way to uninstall this patch, we opted to test out a blunt hammer approach that shuffles the data back into proper form using SQL. This seemed the quickest way to get out from under the patch without having to reverse-engineer it too much. We've been testing the results, and they seem promising: we've been able to uninstall the modules that require this patch (in our case the Group Menu and Group Menu Block) and then upgrade Group 1.3 to version 1.4.

    Here's what we've done (in a test environment).

    A. We removed dependencies on this patch:

    1. We deleted all rows in the group_content_field_data table that correspond to the rows of the group_content__entity_id_str table. This disconnects the relations from the groups...
    2. ...allowing us to uninstall the modules that used this patch (Group Menu Block, Group Menu).
    delete from group_content_field_data where exists (select 1 from group_content__entity_id_str where group_content_field_data.id=group_content__entity_id_str.entity_id);
    

    B. We restored the Group data to its state without the patch:

    1. We restored the entity_id column to the group_content_field_data table;
    2. We ported all the references from group_content__entity_id.entity_id_target_id back into group_content_field_data.entity_id;
    3. We updated several values in the key_value table related to the Group module to reflect a vanilla install state of our current version (Group 1.3 for us). ...for example the values for 'group_content.field_storage_definitions';
    4. And finally we dropped the group_content__entity_id and group_content__entity_id_str tables (since they are no longer used).

    At this point we are able to uninstall the patch, upgrade to Group 1.4, and we've not seen any errors.

    What dangers are we courting if we do this? Is there something significant I've missed? Is there a different way of dealing with step B.3 (restoring several values in the key_value table)?

    Here's most of the SQL for B (excluding B.3 which is long - though I can provide that if someone needs it):

    -- 1. restore the entity_id column
    ALTER TABLE group_content_field_data ADD entity_id int(10) unsigned DEFAULT NULL COMMENT 'The ID of the target entity.' AFTER gid;
    
    -- 2. port the references back to the group_content_field_data table
    UPDATE
        group_content_field_data A
        inner join group_content__entity_id B on
            A.id = B.entity_id
    SET
        A.entity_id = B.entity_id_target_id;
    
    -- 3. update a few key_value rows (this is just a reference)
    update key_value set value = 'a_value_from_a_reference_install_of_Group' where name = 'group_stuff_that_needs_changing_back';
    
    -- 4. drop the deprecated tables
    DROP TABLE group_content__entity_id;
    DROP TABLE group_content__entity_id_str;
    
    kyuubi’s picture

    Almost 5 years into this issue and still rocking! :D

    I'm curious of anyone using the module in #273 found any considerable performance degradation with integer ids.

    Our use case means we need both types of entities, but the vast majority are still nodes (ints) so an approach that forces strings on everything is likely going to be a bit too drastic.

    Unfortunately we're quite dependant on this patch by now (I suspect many others given the size of this thread) so even though I would love to take another approach like some options @kristiaanvandeneynde suggested, I think we can only do so when there is a viable option. We can probably help in migration paths when that happens as we will need to do that ourselves as well.

    Also thanks @Flotsam for sharing the process, I suspect having a good way to detatch ourselves from this patch will become quite an essential part of this issue.

    Thanks to all who are keeping this issue alive.

    kyuubi’s picture

    Also, as far as I can see the patch in #26 seems to work with 1.4? Or is anyone having issues?

    nmeegama’s picture

    New patch for 8.x-1.4

    nmeegama’s picture

    Status: Needs review » Needs work

    The patch in #279 applies and works for sites that are applying it after using 1.4.x for a while, but it has a conflict with hook_update_N if you are already using the patch since version 1.3.x
    So to explain, The earlier patch had updates 22 and 23 (schema versions for the module) but the module in version 1.4.x also introduce updates 22 and 23.
    SO sites already using the patch will not get the updates brought forward by 1.4.x since the module schema version is already 23. Therefore the solution for sites already using the patch is to change the updates from the module 1.4.x to 24 and 25 and then run updb (probably using a local patch).
    Hope all this makes sense.

    nmeegama’s picture

    Status: Needs work » Needs review
    jomsy’s picture

    Group webform gets removed from list when translated

    While creating a translation for a webform under group error occurs. "entity_id_str_target_id" under "group_content__entity_id_str" table becomes empty. The details are updated in the below ticket.

    https://www.drupal.org/project/group_webform/issues/3230456

    safetypin’s picture

    Component: Group (group) » Code

    Patch #279 worked for me after altering the key_value table entry for the group schema version. I manually set it to 8021, and then ran updates and everything ran smoothly. Haven't thoroughly tested yet.

    kristiaanvandeneynde’s picture

    Status: Needs review » Needs work

    I've given this a lot of thought again recently, and came to a solution using wrappers. Then I checked the comments and noticed that I had already had that idea way back in #250:

    Another idea I've been pondering, but would lead to an ugly data architecture is yet another interstitial entity when dealing with config:
    Group <-> GroupContent <-> ConfigWrapper <-> Config

    I no longer think this would be an ugly solution, on the contrary.

    Let's look at where config entities differ greatly from content entities:

    • They do not really use the database (they do, but they actually rely on the file system)
    • They do not support query access at all
    • They're not fieldable

    But they do support individual access checks using an access control handler.

    Therefore, I suggest the following solution for Group 2.1.0:

    1. We create a content entity type called GroupConfig (or whatever)
    2. This entity type has almost zero UI, routes, operations, etc.; it only exists in code
    3. For each config entity type, we automatically create a bundle for GroupConfig
    4. Then, if you want to be able to group certain config, you write a plugin that targets the group_config entity type and bundle that matches your config entity
    5. We do not write a UI yet that allows you to add config entities to a group, modules for Menu, Webform, etc. will have to write their own UI
    6. When you want to group a config entity, you need to wrap it using the right wrapper bundle and then add said wrapper to the group
    7. Query access checks if the target entity type is config and then does absolutely nothing
    8. Regular access checks can figure out if config is grouped by running a simply entity query on the GroupConfig entity, checking the config name because we know that name must be unique

    This should lead to minimal impact and actually be fully backwards compatible. The only two places that would need a change are the query alter (to ignore config entities) and access control handler (to figure out whether something's grouped based on config/content entity type).

    Some important things of note:

    • The wrapper would literally be that, a reference to a config entity
    • It would not have any other fields
    • If the target config entity is removed, so is the wrapper (and GroupContent entity)
    • If the GroupContent entity is removed, so is the wrapper

    In essence: A wrapper cannot exist unless the wrapped config is grouped, therefore leading to the valid assumption that if a GroupConfig entity exists, the config is grouped.

    I'm finalizing the features for 2.0.0, so we can try and land this in 2.1.0. Content entities with string IDs will still be shit out of luck, but that's still the same in core so essentially not my problem. The fact that we won't have to write a UI would also do wonders for the speed of development. Given the size of this endeavor, it would be awesome if some companies that rely heavily on this feature could chip in and sponsor some of my time. You can contact Factorial for that.

    TL;DR: Current approach almost zero chance of making it in, approach from #250 heavily considered. Nothing for 8.x-1.x, planned for 2.1.x.

    The reason I still don't like the current approach is because it just feels too damn icky to have two fields essentially do the same thing and then juggle between said fields based on the scenario at hand. Let's keep the API to group something simple and just accept the fact that config entities are a whole different ball game. By wrapping them, we can make them fall in line with the current API.

    kristiaanvandeneynde’s picture

    Status: Needs work » Closed (won't fix)

    Closing as won't fix for 8.x-1.x in favor of #3292139: [META] Support grouping of config entities, credited everyone who contributed in some form there.

    ras-ben’s picture

    I used the patch in #279 with 1.x - Now I'm trying to update to 2.x, and it's failing hard.
    After a lot of trial and error, I can see that it's that patch, that is causing issues.

    If I stay on 1.x, and remove the patch, it fails in the same way.

    This is a long shot - but is there any smart people here, who can figure out what is going wrong? 🙏

    On drush cr / drush updb, after remvoing the patch

    [14-Nov-2022 19:42:15 UTC] PHP Fatal error:  Uncaught TypeError: Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}(): Argument #1 ($definition) must be of type Drupal\Core\Field\FieldStorageDefinitionInterface, __PHP_Incomplete_Class given in /var/www/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php:174
    Stack trace:
    #0 [internal function]: Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}()
    #1 /var/www/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php(176): array_filter()
    #2 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(381): Drupal\Core\Entity\Sql\DefaultTableMapping::create()
    #3 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(356): Drupal\Core\Entity\Sql\SqlContentEntityStorage->getCustomTableMapping()
    #4 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(203): Drupal\Core\Entity\Sql\SqlContentEntityStorage->getTableMapping()
    #5 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(188): Drupal\Core\Entity\Sql\SqlContentEntityStorage->initTableLayout()
    #6 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(156): Drupal\Core\Entity\Sql\SqlContentEntityStorage->__construct()
    #7 /var/www/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(269): Drupal\Core\Entity\Sql\SqlContentEntityStorage::createInstance()
    #8 /var/www/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(258): Drupal\Core\Entity\EntityTypeManager->createHandlerInstance()
    #9 /var/www/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(192): Drupal\Core\Entity\EntityTypeManager->getHandler()
    #10 /var/www/web/core/modules/field/field.module(176): Drupal\Core\Entity\EntityTypeManager->getStorage()
    #11 /var/www/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(588): field_entity_field_storage_info()
    #12 /var/www/web/core/lib/Drupal/Core/Extension/ModuleHandler.php(405): Drupal\Core\Entity\EntityFieldManager->Drupal\Core\Entity\{closure}()
    #13 /var/www/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(601): Drupal\Core\Extension\ModuleHandler->invokeAllWith()
    #14 /var/www/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(462): Drupal\Core\Entity\EntityFieldManager->buildFieldStorageDefinitions()
    #15 /var/www/web/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php(354): Drupal\Core\Entity\EntityFieldManager->getFieldStorageDefinitions()
    #16 /var/www/web/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php(233): Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider->getEntityTypeIdKeyType()
    #17 /var/www/web/modules/contrib/group/src/Entity/Routing/GroupContentRouteProvider.php(148): Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider->getCanonicalRoute()
    #18 /var/www/web/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php(86): Drupal\group\Entity\Routing\GroupContentRouteProvider->getCanonicalRoute()
    #19 /var/www/web/modules/contrib/group/src/Entity/Routing/GroupContentRouteProvider.php(21): Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider->getRoutes()
    #20 /var/www/web/core/lib/Drupal/Core/EventSubscriber/EntityRouteProviderSubscriber.php(47): Drupal\group\Entity\Routing\GroupContentRouteProvider->getRoutes()
    #21 [internal function]: Drupal\Core\EventSubscriber\EntityRouteProviderSubscriber->onDynamicRouteEvent()
    #22 /var/www/web/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()
    #23 /var/www/web/core/lib/Drupal/Core/Routing/RouteBuilder.php(184): Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch()
    #24 /var/www/web/core/lib/Drupal/Core/ProxyClass/Routing/RouteBuilder.php(83): Drupal\Core\Routing\RouteBuilder->rebuild()
    #25 /var/www/web/core/includes/common.inc(587): Drupal\Core\ProxyClass\Routing\RouteBuilder->rebuild()
    #26 /var/www/web/core/includes/utility.inc(41): drupal_flush_all_caches()
    #27 /var/www/vendor/drush/drush/src/Commands/core/CacheCommands.php(234): drupal_rebuild()
    #28 [internal function]: Drush\Commands\core\CacheCommands->rebuild()
    #29 /var/www/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array()
    #30 /var/www/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
    #31 /var/www/vendor/consolidation/annotated-command/src/CommandProcessor.php(176): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
    #32 /var/www/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(390): Consolidation\AnnotatedCommand\CommandProcessor->process()
    #33 /var/www/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
    #34 /var/www/vendor/symfony/console/Application.php(1039): Symfony\Component\Console\Command\Command->run()
    #35 /var/www/vendor/symfony/console/Application.php(275): Symfony\Component\Console\Application->doRunCommand()
    #36 /var/www/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun()
    #37 /var/www/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run()
    #38 /var/www/vendor/drush/drush/src/Runtime/Runtime.php(48): Drush\Runtime\Runtime->doRun()
    #39 /var/www/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run()
    #40 /var/www/vendor/drush/drush/drush(4): require('...')
    #41 /var/www/vendor/bin/drush(120): include('...')
    #42 {main}
      thrown in /var/www/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php on line 174
    
    Fatal error: Uncaught TypeError: Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}(): Argument #1 ($definition) must be of type Drupal\Core\Field\FieldStorageDefinitionInterface, __PHP_Incomplete_Class given in /var/www/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php:174
    Stack trace:
    #0 [internal function]: Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}()
    #1 /var/www/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php(176): array_filter()
    #2 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(381): Drupal\Core\Entity\Sql\DefaultTableMapping::create()
    #3 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(356): Drupal\Core\Entity\Sql\SqlContentEntityStorage->getCustomTableMapping()
    #4 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(203): Drupal\Core\Entity\Sql\SqlContentEntityStorage->getTableMapping()
    #5 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(188): Drupal\Core\Entity\Sql\SqlContentEntityStorage->initTableLayout()
    #6 /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(156): Drupal\Core\Entity\Sql\SqlContentEntityStorage->__construct()
    #7 /var/www/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(269): Drupal\Core\Entity\Sql\SqlContentEntityStorage::createInstance()
    #8 /var/www/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(258): Drupal\Core\Entity\EntityTypeManager->createHandlerInstance()
    #9 /var/www/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(192): Drupal\Core\Entity\EntityTypeManager->getHandler()
    #10 /var/www/web/core/modules/field/field.module(176): Drupal\Core\Entity\EntityTypeManager->getStorage()
    #11 /var/www/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(588): field_entity_field_storage_info()
    #12 /var/www/web/core/lib/Drupal/Core/Extension/ModuleHandler.php(405): Drupal\Core\Entity\EntityFieldManager->Drupal\Core\Entity\{closure}()
    #13 /var/www/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(601): Drupal\Core\Extension\ModuleHandler->invokeAllWith()
    #14 /var/www/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(462): Drupal\Core\Entity\EntityFieldManager->buildFieldStorageDefinitions()
    #15 /var/www/web/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php(354): Drupal\Core\Entity\EntityFieldManager->getFieldStorageDefinitions()
    #16 /var/www/web/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php(233): Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider->getEntityTypeIdKeyType()
    #17 /var/www/web/modules/contrib/group/src/Entity/Routing/GroupContentRouteProvider.php(148): Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider->getCanonicalRoute()
    #18 /var/www/web/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php(86): Drupal\group\Entity\Routing\GroupContentRouteProvider->getCanonicalRoute()
    #19 /var/www/web/modules/contrib/group/src/Entity/Routing/GroupContentRouteProvider.php(21): Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider->getRoutes()
    #20 /var/www/web/core/lib/Drupal/Core/EventSubscriber/EntityRouteProviderSubscriber.php(47): Drupal\group\Entity\Routing\GroupContentRouteProvider->getRoutes()
    #21 [internal function]: Drupal\Core\EventSubscriber\EntityRouteProviderSubscriber->onDynamicRouteEvent()
    #22 /var/www/web/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(142): call_user_func()
    #23 /var/www/web/core/lib/Drupal/Core/Routing/RouteBuilder.php(184): Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch()
    #24 /var/www/web/core/lib/Drupal/Core/ProxyClass/Routing/RouteBuilder.php(83): Drupal\Core\Routing\RouteBuilder->rebuild()
    #25 /var/www/web/core/includes/common.inc(587): Drupal\Core\ProxyClass\Routing\RouteBuilder->rebuild()
    #26 /var/www/web/core/includes/utility.inc(41): drupal_flush_all_caches()
    #27 /var/www/vendor/drush/drush/src/Commands/core/CacheCommands.php(234): drupal_rebuild()
    #28 [internal function]: Drush\Commands\core\CacheCommands->rebuild()
    #29 /var/www/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array()
    #30 /var/www/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
    #31 /var/www/vendor/consolidation/annotated-command/src/CommandProcessor.php(176): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
    #32 /var/www/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(390): Consolidation\AnnotatedCommand\CommandProcessor->process()
    #33 /var/www/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
    #34 /var/www/vendor/symfony/console/Application.php(1039): Symfony\Component\Console\Command\Command->run()
    #35 /var/www/vendor/symfony/console/Application.php(275): Symfony\Component\Console\Application->doRunCommand()
    #36 /var/www/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun()
    #37 /var/www/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run()
    #38 /var/www/vendor/drush/drush/src/Runtime/Runtime.php(48): Drush\Runtime\Runtime->doRun()
    #39 /var/www/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run()
    #40 /var/www/vendor/drush/drush/drush(4): require('...')
    #41 /var/www/vendor/bin/drush(120): include('...')
    #42 {main}
      thrown in /var/www/web/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php on line 174
    
    SocialNicheGuru’s picture

    You can also use this module for Group 1.x to achieve what the patch does: https://www.drupal.org/project/group_content_string_id

    If you use the module, there is no use for the patch in this issue.

    ras-ben’s picture

    @SocialNicheGuru

    But, even if i add that module, when i remove the patch, i still get the error ^

    yojohnyo’s picture

    I'm having the same issue as #286. Has anyone found a solution for this?

    ras-ben’s picture

    @yojohnyo Yes and no..

    I've been able to finally remove the patch, after running these SQL commands first:

    ALTER TABLE group_content_field_data ADD entity_id INT(11);
    DELETE FROM key_value WHERE name='group_content.field_schema_data.entity_id_str';
    DROP TABLE group_content__entity_id_str;
    DELETE FROM key_value WHERE name='group_content.field_storage_definitions';
    

    BUT BARE IN MIND - These are destructive commands! I have not tested fully, and I'm not sure yet if this causes new issues.

    yojohnyo’s picture

    It didn't work to delete the group_content_field_data. I was able to uninstall the patch with code like this:

    $entity_id_definition = \Drupal\Core\Field\BaseFieldDefinition::create('entity_reference')
            ->setLabel(t('Content'))
            ->setDescription(t('The entity to add to the group.'))
            ->setDisplayOptions('form', [
              'type' => 'entity_reference_autocomplete',
              'weight' => 5,
              'settings' => [
                'match_operator' => 'CONTAINS',
                'size' => '60',
                'placeholder' => '',
              ],
            ])
            ->setDisplayConfigurable('view', TRUE)
            ->setDisplayConfigurable('form', TRUE)
            ->setRequired(TRUE);
    caesius’s picture

    Tried removing patch #279 and sure enough ran into the DefaultTableMapping error in Groups 1.5. I haven't delved into trying to run SQL statements mentioned in #290 and #276 yet, mainly because I inherited this project and can't figure out what this patch is even used for (it's not groupmenu -- it might be workflows?)

    @yojohnyo Could you clarify where you put this code and how you came up with it? Do you add it to an install/alter hook in a custom module or do you patch it into the Group module somewhere?

    edit: Checked the table group_content__entity_id_str and it's empty, which I assume means the patch was installed but never actually needed/used.

    edit 2: Ran queries from #290 and removed the patch but one of our view pages WSOD'd due to a "Broken/missing handler". Comparing to live and recreating the handler worked. I believe this was because of group_post_update_fix_group_content_views_relations() in the patch.

    edit 3: Now I'm seeing that Group Content Menu functionality is no longer working, e.g. WSOD at /group/123/menus and an Access Denied as user 1 at group/123/menu/123/edit -- not sure yet if this is directly and solely due to this patch yet, but I'm surprised since I thought it was "Groupmenu" that needed this patch, not "Group Content Menu." Pasting error below for the benefit of Google:

    Drupal\group_content_menu\GroupContentMenuListBuilder::buildRow(): Argument #1 ($entity) must be of type Drupal\Core\Entity\EntityInterface, null given, called in /var/www/html/web/core/lib/Drupal/Core/Entity/EntityListBuilder.php on line 219 in Drupal\group_content_menu\GroupContentMenuListBuilder->buildRow() (line 88 of modules/contrib/group_content_menu/src/GroupContentMenuListBuilder.php).

    ras-ben’s picture

    In addition to #290 - I've had better luck with this:

    /**
     * Alter the database, to not have broken Groups module config.
     *
     * In the past, we used the group_entityqueue module on the site, however that
     * has later been removed as it was not necessary. In order to get
     * group_entityqueue to work, we needed to include a patch for groups:
     * https://www.drupal.org/files/issues/2021-08-24/2797793-279.patch
     * https://www.drupal.org/project/group/issues/2797793.
     *
     * This patch basically refactored groups, to save group content IDs as strings
     * instead of ints. This causes an ungodly amount of trouble with updating to
     * groups 2.x / getting groups 1.x ready for D10.
     * The problem now is that we cannot just remove the patch, as it didnt include
     * any uninstall hooks (or, atleast not any that works). If we just remove
     * the patch, Drupal will refuse to do anything, including CR and UPDB.
     * So, before removing the patch, we need to undo the mess that it has left
     * in the database. We'll port the data back to group_content_field_data, and
     * remove the entity_id_str references.
     */
    function my_module_deploy_fix_groups_db() {
      $database = \Drupal::database();
    
      // We need to re-add the entity_id column to group_content_field_data.
      // Get data from the table that the patch added ('group_content__entity_id').
      $database->query("ALTER TABLE group_content_field_data ADD entity_id INT(11);");
      $database->query("
        UPDATE group_content_field_data
        JOIN group_content__entity_id ON group_content_field_data.id = group_content__entity_id.entity_id
        SET group_content_field_data.entity_id = group_content__entity_id.entity_id_target_id;
      ");
    
      // Now, we can remove all the messed up config, from the old field.
      $database->query("DELETE FROM key_value WHERE name='group_content.field_schema_data.entity_id_str';");
      $database->query("DROP TABLE group_content__entity_id_str;");
      $database->query("DELETE FROM key_value WHERE name='group_content.field_storage_definitions';");
    }
    caesius’s picture

    I reset my database and code back to before I removed the patch to try that^ as an update hook in a custom module. Ensuring it runs before removing the patch fixed the issue I was having with Group Menus, thanks!

    Obviously I still needed to update the views that had broken handlers before, but I already had a config export to deal with those. The diffs for the views were only one line:

    --- a/config/views.view.group_members.yml
    +++ b/config/views.view.group_members.yml
    @@ -689,7 +689,7 @@ display:
           relationships:
             gc__user:
               id: gc__user
    -          table: group_content__entity_id
    +          table: group_content_field_data
               field: gc__user
               relationship: none
               group_type: group

    I still need to do more testing and possibly debugging but this looks promising for my use case.

    PierreEmmanuel’s picture

    Hello @ras-ben,

    I use your code in a HOOK_update_N() before making the upgrade, it works fine. Thanks !

    I had one more issue about an unexpected value on a field content_translation_uid. In fact I had few elements with a very high content_translation_uid, once I deleted them all updates were running OK.

    I am not sure why those elements are not ok, il will dig up this issue. I just check a few things, but group have theirs contents.

    caesius’s picture

    I'm trying to update to Group 2.1.0 now and am running into an issue with updatedb; I can't tell if it's because of the removed patch or not. Does this look familiar to anyone?

    group    9203        hook_update_n   9203 - Update group_content DB table, fields and indexes.
    
    >  [notice] Update started: group_update_9203
    >  [error]  Missing langcode field.
    >  [error]  Update failed: group_update_9203
     [error]  Update aborted by: group_update_9203
     [error]  Finished performing updates.
    Failed to run drush updb -y: exit status 1
    
    safetypin’s picture

    This patch #3381602: field definition empty in group_update_9203 worked to get me past the langcode field error. I had some strange errors and outputs that went away after some cache rebuilding. But I've run into another issue: #3381703: Error after upgrading from 1.5 -> 2.1.

    ras-ben’s picture

    In addition to #293, I found out that I didnt have the correct table structure afterwards.
    It seemed to cause issues with views that had groups relationship.

    Here is the expanded deploy hook, that *seems* to work better - but, please, use it at your own risk :)

    /**
     * Alter the database, to not have broken Groups module config.
     *
     * In the past, we used the group_entityqueue module on the site, however that
     * has later been removed as it was not necessary. In order to get
     * group_entityqueue to work, we needed to include a patch for groups:
     * https://www.drupal.org/files/issues/2021-08-24/2797793-279.patch
     * https://www.drupal.org/project/group/issues/2797793.
     *
     * This patch basically refactored groups, to save group content IDs as strings
     * instead of ints. This causes an ungodly amount of trouble with updating to
     * groups 2.x / getting groups 1.x ready for D10.
     * The problem now is that we cannot just remove the patch, as it didnt include
     * any uninstall hooks (or, atleast not any that works). If we just remove
     * the patch, Drupal will refuse to do anything, including CR and UPDB.
     * So, before removing the patch, we need to undo the mess that it has left
     * in the database. We'll port the data back to group_content_field_data, and
     * remove the entity_id_str references.<em></em>
     */
    function MY_MODULE_deploy_fix_groups_db() {
      $database = \Drupal::database();
    
      // We need to re-add the entity_id column to group_content_field_data.
      // Get data from the table that the patch added ('group_content__entity_id').
      $database->query("ALTER TABLE group_content_field_data ADD entity_id INT(11);");
      $database->query("
        UPDATE group_content_field_data
        JOIN group_content__entity_id ON group_content_field_data.id = group_content__entity_id.entity_id
        SET group_content_field_data.entity_id = group_content__entity_id.entity_id_target_id;
      ");
    
      // Make sure the entity_id column has the correct index keys and properties.
      $database->query("
        ALTER TABLE `group_content_field_data`
        MODIFY COLUMN `entity_id` int(10) unsigned DEFAULT NULL COMMENT 'The ID of the target entity.',
        DROP INDEX IF EXISTS `group_content_field__entity_id__target_id`,
        ADD INDEX `group_content_field__entity_id__target_id` (`entity_id`),
        DROP INDEX IF EXISTS `group_content__entity_fields`,
        ADD INDEX `group_content__entity_fields` (`type`,`entity_id`);
      ");
    
      // Now, we can remove all the messed up config, from the old field.
      $database->query("DELETE FROM key_value WHERE name='group_content.field_schema_data.entity_id_str';");
      $database->query("DROP TABLE group_content__entity_id_str;");
    
      // If we don't delete the whole field_storage_definition, we run into errors,
      // when we remove the patch.
      $database->query("DELETE FROM key_value WHERE name='group_content.field_storage_definitions';");
    
      // Alternatively to removing the whole field storage definition, I'd prefer
      // to just remove the reference to entity_id_str, but I couldnt get that
      // to work.
      /** @var \Drupal\Core\KeyValueStore\DatabaseStorage $key_value */
      /*
        $key_value = \Drupal::service('keyvalue')->get('entity.definitions.installed');
        $group_field_defs_key = 'group_content.field_storage_definitions';
        $group_field_defs = $key_value->get($group_field_defs_key);
        unset($group_field_defs['entity_id_str']);
        $key_value->set($group_field_defs_key, $group_field_defs);
      */
    }
    
    ras-ben’s picture

    And, in addition to that, I've also added this deploy hook to run **AFTER** the patch has been removed.
    Again - use at your own risk, but it seems to work well for me.

    /**
     * Update invalid entity updates.
     */
    function MY_MODULE_deploy_entity_updates() {
      $entity_type_manager = \Drupal::entityTypeManager();
      $entity_type_manager->clearCachedDefinitions();
    
      $entity_type_ids = [];
      $change_summary = \Drupal::service('entity.definition_update_manager')->getChangeSummary();
      foreach ($change_summary as $entity_type_id => $change_list) {
        $entity_type = $entity_type_manager->getDefinition($entity_type_id);
        \Drupal::entityDefinitionUpdateManager()->installEntityType($entity_type);
    
        $entity_type_ids[] = $entity_type_id;
      }
    
      drupal_flush_all_caches();
    
      return t("Installed/Updated the entity type(s): @entity_type_ids", [
        '@entity_type_ids' => implode(', ', $entity_type_ids),
      ]);
    }
    
    gwvoigt’s picture

    @kristiaanvandeneynde If I'm in Group 1.4 and I'm using this patch, but it want to update to Group 1.5, what is the workaround? The patches do not work with 1.5 and this issue https://www.drupal.org/project/group/issues/3292139 is for Group 2.0.

    kristiaanvandeneynde’s picture

    @gwvoigt, sadly I never found a patch here that was architecturally sound enough to want to risk having to maintain for years to come. Over the years, I have looked at patches, reviewed some of them and kept second guessing the approach (even if suggested by me) until things started moving more in one direction around comments #246, #250 and #284.

    Ultimately, I've often warned people on Slack and in other issues against using the patch here because it was still so heavily in flux and there was no guarantee that it would ever land. So as far as telling you what the workaround is, the honest answer is: I don't know.

    After almost 6 years of discussion, someone (ANNAI) finally came along and wanted to sponsor this issue, accepting that we would probably have to break BC and therefore release a new major version. The result of that is that after weeks of work we finally have Group releases that fully support config entities being grouped in a way that doesn't heavily impact performance and doesn't warp the API by needing to inspect what we're trying to group.

    So as far as I am concerned -and as the issue status shows- I consider this issue fixed and I would advise anyone still relying on this patch to carefully consider their options:

    • Do you want to keep relying on a patch that will never get committed and using modules that depend on this patch to even work, or
    • Do you want to invest that same time in looking for a way to upgrade to v2 and get this supported functionality out of the box

    Upgrading to v2 should be as simple as downloading the latest versions of your submodules and running drush updb, following the change records to see if any of your custom code needs updating too. However, undoing this patch might be a huge pain in the ass, as demonstrated by the latest comments. You could, perhaps, get away with a same-site migration for the grouped config entities.

    gwvoigt’s picture

    @kristiaanvandeneynde Thank you for your detailed reply. We are going to try to undo the patch and upgrade to 2.0.

    safetypin’s picture

    I think I've successfully extricated this patch from my installation of Group 1.5, at least that's which version I *thought* was installed. When I look at my production module list, I see 8.x-1.0-beta3. I removed all menu relationships manually. I manually added the functions from #298 & #299 as group_update_8026 & group_update 8027 to group.install, and executed database updates. This didn't result in errors, and I was able to uninstall the groupmenu module afterwards. Then I went into a couple of group entities' edit menus, and was able to click around a bit without errors.

    So, I'd like to replicate this process in my production environment, but in order to do that, I need a patch file that will add these update methods to the group.install file. However, I'm not sure which version of Group I should check out locally in order to create that patch file. I would expect to work with 8.x-1.x, but that is missing some database updates. As an added complication, if I update group, I now get a 1.6 version. Composer gives the following output:
    - Upgrading drupal/group (1.5.0 => 1.6.0): Extracting archive

    And then the patch I've been using doesn't apply properly. So, I'm confused on a couple of counts: why does drupal's GUI report that I'm using 8.x-1.0-beta3 but composer says I'm using 1.5.0?

    What is the best way for me to generate a patch against 1.5.0, so I can add these group_update_xxxx functions and undo the database updates that this patch made?

    safetypin’s picture

    @ras-ben, do you have any insight into why this command might not work?

    $database->query("DELETE FROM key_value WHERE name='group_content.field_storage_definitions';");
    

    I applied your patches (#298 & #299) as database updates to group v1.5, but I was still running into errors. I looked at a the key_value table, and found an entry named 'group_content.field_storage_definitions', so it appears that command didn't work. I removed that record manually and the error went away, so other than that, everything appears to be working.

    safetypin’s picture

    Has anyone started working on a new module to integrate menu config entities as group content for 2.x? Can we start a drupal/group_menu module?

    I figured out how to get it working. For some reason, as mentioned in my last post, the sql command to remove the storage definition didn't get removed, but this command worked when I executed directly on the database server. This patch should apply cleanly to 1.5; you might need to change the update numbers to get it to apply to 1.6. If anyone else needs it, this is the content of the patch file I'm using:

    diff --git a/group.install b/group.install
    index d05dbb1..38c3f0c 100644
    --- a/group.install
    +++ b/group.install
    @@ -722,3 +722,69 @@ function group_update_8025(&$sandbox) {
       \Drupal::entityDefinitionUpdateManager()
         ->installFieldStorageDefinition('entity_id_str', 'group_content', 'group_content', $entity_id_str_definition);
     }
    +
    +function group_update_8026(&$sandbox) {
    +  $database = \Drupal::database();
    +
    +// We need to re-add the entity_id column to group_content_field_data.
    +// Get data from the table that the patch added ('group_content__entity_id').
    +$database->query("ALTER TABLE group_content_field_data ADD entity_id INT(11);");
    +$database->query("
    +  UPDATE group_content_field_data
    +  JOIN group_content__entity_id ON group_content_field_data.id = group_content__entity_id.entity_id
    +  SET group_content_field_data.entity_id = group_content__entity_id.entity_id_target_id;
    +");
    +
    +// Make sure the entity_id column has the correct index keys and properties.
    +$database->query("
    +  ALTER TABLE `group_content_field_data`
    +  MODIFY COLUMN `entity_id` int(10) unsigned DEFAULT NULL COMMENT 'The ID of the target entity.',
    +  DROP INDEX IF EXISTS `group_content_field__entity_id__target_id`,
    +  ADD INDEX `group_content_field__entity_id__target_id` (`entity_id`),
    +  DROP INDEX IF EXISTS `group_content__entity_fields`,
    +  ADD INDEX `group_content__entity_fields` (`type`,`entity_id`);
    +");
    +
    +// Now, we can remove all the messed up config, from the old field.
    +$database->query("DELETE FROM key_value WHERE name='group_content.field_schema_data.entity_id_str';");
    +$database->query("DROP TABLE group_content__entity_id_str;");
    +
    +// If we don't delete the whole field_storage_definition, we run into errors,
    +// when we remove the patch.
    +$database->query("DELETE FROM key_value WHERE name='group_content.field_storage_definitions';");
    +
    +// Alternatively to removing the whole field storage definition, I'd prefer
    +// to just remove the reference to entity_id_str, but I couldnt get that
    +// to work.
    +/** @var \Drupal\Core\KeyValueStore\DatabaseStorage $key_value */
    +/*
    +  $key_value = \Drupal::service('keyvalue')->get('entity.definitions.installed');
    +  $group_field_defs_key = 'group_content.field_storage_definitions';
    +  $group_field_defs = $key_value->get($group_field_defs_key);
    +  unset($group_field_defs['entity_id_str']);
    +  $key_value->set($group_field_defs_key, $group_field_defs);
    +*/
    +}
    +
    +/**
    + * Alter the database, to not have broken Groups module config.
    + */
    +function group_update_8027(&$sandbox) {
    +  $entity_type_manager = \Drupal::entityTypeManager();
    +  $entity_type_manager->clearCachedDefinitions();
    +
    +  $entity_type_ids = [];
    +  $change_summary = \Drupal::service('entity.definition_update_manager')->getChangeSummary();
    +  foreach ($change_summary as $entity_type_id => $change_list) {
    +    $entity_type = $entity_type_manager->getDefinition($entity_type_id);
    +    \Drupal::entityDefinitionUpdateManager()->installEntityType($entity_type);
    +
    +    $entity_type_ids[] = $entity_type_id;
    +  }
    +
    +  drupal_flush_all_caches();
    +
    +  return t("Installed/Updated the entity type(s): @entity_type_ids", [
    +    '@entity_type_ids' => implode(', ', $entity_type_ids),
    +  ]);
    +}
    
    ZeiP’s picture

    FileSize
    87.59 KB

    Rerolled the patch on 1.6, this fixed the PHP Fatal error: Uncaught TypeError: Drupal\Core\Entity\Sql\DefaultTableMapping::Drupal\Core\Entity\Sql\{closure}(): Argument #1 ($definition) must be of type Drupal\Core\Field\FieldStorageDefinitionInterface, __PHP_Incomplete_Class given in /app/public/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php:174 for us.

    dinazaur’s picture

    So to fix it properly you need to make the same steps as in the patch.
    Note we used #279 patch.
    Note2 we upgraded to v2 of group module.

    First deployment iteration -- backup all fields

    In our database entity_id_str was empty so I didn't include it inside copiying update, but if you have you should include this field too.

    /**
     * Copy the entity_id field to temporary table and remove unneeded fields.
     */
    function MY_MODULE_update_9001(&$sandbox) {
      $query = \Drupal::database()
        ->select('group_content__entity_id', 'g');
    
      $query->addField('g', 'entity_id', 'id');
      $query->addField('g', 'entity_id_target_id', 'entity_id');
    
      // Initialize the update process, create a temporary table.
      if (!isset($sandbox['total'])) {
        $sandbox['total'] = $query->countQuery()->execute()->fetchField();
        $sandbox['current'] = 0;
    
        \Drupal::database()
          ->schema()
          ->createTable('group_content_entity_id_update', [
            'fields' => [
              'id' => [
                'type' => 'int',
                'unsigned' => TRUE,
                'not null' => TRUE,
              ],
              'entity_id' => [
                'type' => 'int',
                'unsigned' => TRUE,
                'not null' => TRUE,
              ],
            ],
            'primary key' => [
              'id',
            ],
            'description' => 'Temporary storage for group_content entity_id field migration',
          ]);
      }
    
      $rows_per_operation = 500;
      $query->condition('entity_id', $sandbox['current'], '>');
      $query->range(0, $rows_per_operation);
      $query->orderBy('g.entity_id', 'ASC');
    
      $rows = $query->execute()->fetchAll(PDO::FETCH_ASSOC);
      if ($rows) {
        $insert = \Drupal::database()
          ->insert('group_content_entity_id_update')
          ->fields(['id', 'entity_id']);
    
        foreach ($rows as $row) {
          $insert->values($row);
          $sandbox['current'] = $row['id'];
        }
    
        $insert->execute();
        $temp_count = \Drupal::database()
          ->select('group_content_entity_id_update')
          ->countQuery()->execute()->fetchField();
        $sandbox['#finished'] = ($temp_count / $sandbox['total']);
      }
      else {
        $sandbox['#finished'] = 1;
      }
    
      if ($sandbox['#finished'] >= 1) {
        // Uninstall obsolete fields.
        $entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
        $entityDefinitionUpdateManager
          ->uninstallFieldStorageDefinition($entityDefinitionUpdateManager->getFieldStorageDefinition('entity_id', 'group_content'));
    
        $entityDefinitionUpdateManager
          ->uninstallFieldStorageDefinition($entityDefinitionUpdateManager->getFieldStorageDefinition('entity_id_str', 'group_content'));
      }
    }
    

    Second iteration

    1. Uninstall patch

    2. Re-install entity-id field

      /**
       * Re-install entity_id field.
       */
      function MY_MODULE_update_9002(&$sandbox) {
        // Borrowed this logic from the Comment module.
        // Warning! May change in the future: https://www.drupal.org/node/2346347
        $definition = \Drupal\Core\Field\BaseFieldDefinition::create('entity_reference')
          ->setLabel(t('Content'))
          ->setTargetEntityTypeId('group_content')
          ->setDescription(t('The entity to add to the group.'))
          ->setDisplayOptions('form', [
            'type' => 'entity_reference_autocomplete',
            'weight' => 5,
            'settings' => [
              'match_operator' => 'CONTAINS',
              'size' => '60',
              'placeholder' => '',
            ],
          ])
          ->setDisplayConfigurable('view', TRUE)
          ->setDisplayConfigurable('form', TRUE)
          ->setRequired(TRUE);
        \Drupal::entityDefinitionUpdateManager()
          ->installFieldStorageDefinition('entity_id', 'group_content', 'group_content', $definition);
      }
      
      
    3. Restore the data for group_content entity_id.

      /**
       * Restore the data for group_content entity_id.
       */
      function MY_MODULE_post_update_restore_entity_id_data(&$sandbox) {
        $query = \Drupal::database()
          ->select('group_content_entity_id_update', 'g')
          ->fields('g', ['id', 'entity_id']);
      
        // Initialize the update process, install the field schema.
        if (!isset($sandbox['total'])) {
          $sandbox['total'] = $query->countQuery()->execute()->fetchField();
          $sandbox['current'] = 0;
        }
      
        // We're now inserting new fields data which may be tricky. We're updating
        // group_content entities instead of inserting fields data directly to make
        // sure field data is stored correctly.
        $rows_per_operation = 50;
        $query->condition('id', $sandbox['current'], '>');
        $query->range(0, $rows_per_operation);
        $query->orderBy('id', 'ASC');
      
        $rows = $query->execute()->fetchAllKeyed();
        if ($rows) {
          /** @var \Drupal\group\Entity\GroupContentInterface[] $group_contents */
          $group_contents = \Drupal::entityTypeManager()
            ->getStorage('group_content')
            ->loadMultiple(array_keys($rows));
      
          foreach ($group_contents as $id => $group_content) {
            $group_content->entity_id->target_id = $rows[$id];
            $group_content->save();
          }
      
          end($rows);
          $sandbox['current'] = key($rows);
          $moved_rows = Drupal::database()
            ->select('group_content_field_data')
            ->isNotNull('entity_id')
            ->countQuery()->execute()->fetchField();
          $sandbox['#finished'] = ($moved_rows / $sandbox['total']);
        }
        else {
          $sandbox['#finished'] = 1;
        }
      
        if ($sandbox['#finished'] >= 1) {
          // Delete the temporary table once data is copied.
          \Drupal::database()->schema()->dropTable('group_content_entity_id_update');
        }
      }
      
      
    4. Fix Views

      /**
       * Fix existing group content views relationships.
       */
      function MY_MODULE_post_update_fix_group_content_views_relations() {
        $config_factory = \Drupal::configFactory();
        foreach ($config_factory->listAll('views.view.') as $name) {
          $view = $config_factory->getEditable($name);
          $changed = FALSE;
          foreach ($view->get('display') as $display_id => $display) {
            if (isset($display['display_options']['relationships'])) {
              foreach ($display['display_options']['relationships'] as $relation_id => $relation) {
                if ($relation['table'] === 'group_content__entity_id') {
                  $trail = "display.$display_id.display_options.relationships.$relation_id.table";
                  $view->set($trail, 'group_relationship_field_data')->save();
                  $changed = TRUE;
                }
              }
            }
          }
          if ($changed) {
            $view->save();
          }
        }
      }
      
      
    tatewaky’s picture

    I am confuse, is this fix for version 2.x?

    Jaypan’s picture

    I am confuse, is this fix for version 2.x?

    No. It's outdated.

    SocialNicheGuru’s picture