-
-
Notifications
You must be signed in to change notification settings - Fork 266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #3314741: EntityFieldManager::getFieldMap() doesn't show farmOS bundle fields #583
base: 2.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,121 @@ use Drupal\entity\EntityPermissionProvider; | |
use Drupal\farm_entity\BundlePlugin\FarmEntityBundlePluginHandler; | ||
use Drupal\farm_entity\Routing\DefaultHtmlRouteProvider; | ||
|
||
/** | ||
* Implements hook_modules_installed(). | ||
*/ | ||
function farm_entity_modules_installed($modules, $is_syncing) { | ||
|
||
// Add bundle fields to the bundle field map. | ||
_farm_entity_rebuild_bundle_field_map('install', $modules); | ||
} | ||
|
||
/** | ||
* Implements hook_modules_uninstalled(). | ||
*/ | ||
function farm_entity_modules_uninstalled($modules, $is_syncing) { | ||
|
||
// Remove bundle fields from the bundle field map. | ||
_farm_entity_rebuild_bundle_field_map('uninstall', $modules); | ||
} | ||
|
||
/** | ||
* Helper function for rebuilding bundle field maps. | ||
* | ||
* This runs when modules are installed/uninstalled, loads bundle fields that | ||
* are defined by the module via hook_farm_entity_bundle_field_info(), and | ||
* updates the entity field map accordingly. | ||
* | ||
* @param string $op | ||
* Operation being performed. Must be either 'install' or 'uninstall'. | ||
* @param array $modules | ||
* The list of modules that are being installed/uninstalled. | ||
*/ | ||
function _farm_entity_rebuild_bundle_field_map(string $op, array $modules) { | ||
|
||
// If the operation is not "install" or "uninstall", bail. | ||
if (!in_array($op, ['install', 'uninstall'])) { | ||
return; | ||
} | ||
|
||
// If none of the modules implement hook_farm_entity_bundle_field_info(), | ||
// bail. | ||
if (!\Drupal::moduleHandler()->hasImplementations('farm_entity_bundle_field_info', $modules)) { | ||
return; | ||
} | ||
|
||
// Iterate through entity types. | ||
$entity_type_definitions = \Drupal::service('entity_type.manager')->getDefinitions(); | ||
foreach ($entity_type_definitions as $entity_type => $entity_type_definition) { | ||
|
||
// Only proceed for entity types that use bundle plugins. | ||
if (!in_array($entity_type, ['asset', 'log', 'plan', 'quantity'])) { | ||
continue; | ||
} | ||
|
||
// Get the bundle field map key value collection. | ||
$bundle_field_map = \Drupal::service('keyvalue')->get('entity.definitions.bundle_field_map')->get($entity_type) ?? []; | ||
|
||
// Get a list of installed bundles for this entity type. | ||
$bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type); | ||
|
||
// Iterate through bundles. | ||
foreach ($bundles as $bundle => $bundle_info) { | ||
|
||
// Iterate through the modules. | ||
foreach ($modules as $module) { | ||
|
||
// Invoke hook_farm_entity_bundle_field_info() to get bundle field | ||
// definitions added by the module. | ||
/** @var \Drupal\entity\BundleFieldDefinition[] $bundle_fields */ | ||
$bundle_fields = \Drupal::service('module_handler')->invoke($module, 'farm_entity_bundle_field_info', [$entity_type_definition, $bundle]) ?? []; | ||
|
||
// If bundle fields are empty, skip. | ||
if (empty($bundle_fields)) { | ||
continue; | ||
} | ||
|
||
// Iterate through the bundle field definitions to update the bundle | ||
// field map. This mimics the field_definition.listener service's | ||
// onFieldDefinitionCreate() and onFieldDefinitionDelete() behavior. | ||
// @see Drupal\Core\Field\FieldDefinitionListener::onFieldDefinitionCreate() | ||
// @see Drupal\Core\Field\FieldDefinitionListener::onFieldDefinitionDelete() | ||
foreach ($bundle_fields as $field_name => $bundle_field) { | ||
|
||
// If we are installing, add to the field map. | ||
if ($op == 'install') { | ||
if (!isset($bundle_field_map[$field_name])) { | ||
// This field did not exist yet, initialize it with the type and | ||
// empty bundle list. | ||
$bundle_field_map[$field_name] = [ | ||
'type' => $bundle_field->getType(), | ||
'bundles' => [], | ||
]; | ||
} | ||
$bundle_field_map[$field_name]['bundles'][$bundle] = $bundle; | ||
} | ||
|
||
// If we are uninstalling, remove from the field map. | ||
elseif ($op == 'uninstall') { | ||
unset($bundle_field_map[$field_name]['bundles'][$bundle]); | ||
if (empty($bundle_field_map[$field_name]['bundles'])) { | ||
unset($bundle_field_map[$field_name]); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Set the bundle field map key value collection. | ||
if (!empty($bundle_field_map)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if it became empty as part of the changes above? Shouldn't it still get set in that case? |
||
\Drupal::service('keyvalue')->get('entity.definitions.bundle_field_map')->set($entity_type, $bundle_field_map); | ||
} | ||
} | ||
|
||
// Clear the entity field map cache. | ||
\Drupal::service('cache.discovery')->delete('entity_field_map'); | ||
} | ||
|
||
/** | ||
* Implements hook_module_implements_alter(). | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Post update functions for farmOS entity module. | ||
*/ | ||
|
||
/** | ||
* Add hook_farm_entity_bundle_field_info() fields to bundle field maps. | ||
*/ | ||
function farm_entity_post_update_add_farm_entity_bundle_field_maps(&$sandbox = NULL) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this
Comment on lines
+8
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably just my Drupal naivete showing here, but does this implement a hook or is it called some other way? |
||
|
||
// Iterate through entity types. | ||
$entity_type_definitions = \Drupal::service('entity_type.manager')->getDefinitions(); | ||
foreach ($entity_type_definitions as $entity_type => $entity_type_definition) { | ||
|
||
// Only proceed for entity types that use bundle plugins. | ||
if (!in_array($entity_type, ['asset', 'log', 'plan', 'quantity'])) { | ||
continue; | ||
} | ||
|
||
// Get the bundle field map key value collection. | ||
$bundle_field_map = \Drupal::service('keyvalue')->get('entity.definitions.bundle_field_map')->get($entity_type) ?? []; | ||
|
||
// Get a list of installed bundles for this entity type. | ||
$bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type); | ||
|
||
// Iterate through bundles. | ||
foreach ($bundles as $bundle => $bundle_info) { | ||
|
||
// Invoke hook_farm_entity_bundle_field_info() on all modules with a | ||
// callback function to add bundle fields to the entity field map. | ||
\Drupal::service('module_handler')->invokeAllWith( | ||
'farm_entity_bundle_field_info', | ||
function (callable $hook) use ($entity_type_definition, $bundle, &$bundle_field_map) { | ||
|
||
// Get bundle fields defined by the module. | ||
$bundle_fields = $hook($entity_type_definition, $bundle) ?? []; | ||
|
||
// If bundle fields are empty, bail. | ||
if (empty($bundle_fields)) { | ||
return; | ||
} | ||
|
||
// Iterate through the bundle field definitions to add fields to the | ||
// bundle field map. This mimics the field_definition.listener | ||
// service's onFieldDefinitionCreate() behavior. | ||
// @see Drupal\Core\Field\FieldDefinitionListener::onFieldDefinitionCreate() | ||
foreach ($bundle_fields as $field_name => $bundle_field) { | ||
if (!isset($bundle_field_map[$field_name])) { | ||
// This field did not exist yet, initialize it with the type and | ||
// empty bundle list. | ||
$bundle_field_map[$field_name] = [ | ||
'type' => $bundle_field->getType(), | ||
'bundles' => [], | ||
]; | ||
} | ||
$bundle_field_map[$field_name]['bundles'][$bundle] = $bundle; | ||
} | ||
} | ||
); | ||
} | ||
|
||
// Set the bundle field map key value collection. | ||
if (!empty($bundle_field_map)) { | ||
\Drupal::service('keyvalue')->get('entity.definitions.bundle_field_map')->set($entity_type, $bundle_field_map); | ||
} | ||
} | ||
|
||
// Delete the entity field map cache entry. | ||
\Drupal::service('cache.discovery')->delete('entity_field_map'); | ||
Comment on lines
+12
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why implement this all again? Couldn't it just call |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,49 @@ protected function setUp():void { | |
$this->moduleInstaller = $this->container->get('module_installer'); | ||
} | ||
|
||
/** | ||
* Test that bundle field maps are updated on install/uninstall. | ||
*/ | ||
public function testBundleFieldMapUpdates() { | ||
|
||
// Get the entity field map. | ||
$field_map = $this->entityFieldManager->getFieldMap(); | ||
|
||
// Confirm that the 'test_default_bundle_field' exists in the log field map. | ||
$this->assertArrayHasKey('test_default_bundle_field', $field_map['log']); | ||
|
||
// Confirm that the 'test_contrib_hook_bundle_field' does NOT exist (yet). | ||
$this->assertArrayNotHasKey('test_contrib_hook_bundle_field', $field_map['log']); | ||
|
||
// Install the farm_entity_contrib_test module. | ||
$result = $this->moduleInstaller->install(['farm_entity_contrib_test']); | ||
$this->assertTrue($result); | ||
|
||
// Reload the entity field map. | ||
$this->entityFieldManager->setFieldMap([]); | ||
\Drupal::service('cache.discovery')->delete('entity_field_map'); | ||
$field_map = $this->entityFieldManager->getFieldMap(); | ||
|
||
// Confirm that the 'test_contrib_hook_bundle_field' exists in the log field | ||
// map, and exists in the 'test' bundle, but not in 'test_override'. | ||
$this->assertArrayHasKey('test_contrib_hook_bundle_field', $field_map['log']); | ||
$this->assertContains('test', $field_map['log']['test_contrib_hook_bundle_field']['bundles']); | ||
$this->assertNotContains('test_override', $field_map['log']['test_contrib_hook_bundle_field']['bundles']); | ||
|
||
// Uninstall the farm_entity_contrib_test module. | ||
$result = $this->moduleInstaller->uninstall(['farm_entity_contrib_test']); | ||
$this->assertTrue($result); | ||
|
||
// Reload the entity field map. | ||
$this->entityFieldManager->setFieldMap([]); | ||
\Drupal::service('cache.discovery')->delete('entity_field_map'); | ||
$field_map = $this->entityFieldManager->getFieldMap(); | ||
|
||
// Confirm that the 'test_contrib_hook_bundle_field' no longer exists in the | ||
// log field map. | ||
$this->assertArrayNotHasKey('test_contrib_hook_bundle_field', $field_map['log']); | ||
} | ||
Comment on lines
+62
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this also test the case where more than one module provides a given bundle field? i.e. that the bundle gets added, the field mapping doesn't get overwritten or prematurely deleted? |
||
|
||
/** | ||
* Test installing the farm_entity_contrib_test module after farm_entity_test. | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ class QuickFormTest extends KernelTestBase { | |
*/ | ||
protected static $modules = [ | ||
'asset', | ||
'farm_field', | ||
'farm_quantity_standard', | ||
'farm_quick', | ||
'farm_quick_test', | ||
|
@@ -94,13 +95,13 @@ public function testQuickFormSubmission() { | |
$this->assertNotEmpty($storage['assets'][0]->id()); | ||
|
||
// Confirm that the asset is linked to the quick form. | ||
$this->assertEquals('test', $storage['assets'][0]->quick[0]); | ||
$this->assertEquals('test', $storage['assets'][0]->get('quick')[0]->value); | ||
Comment on lines
-97
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this need to change? |
||
|
||
// Confirm that a log was created. | ||
$this->assertNotEmpty($storage['logs'][0]->id()); | ||
|
||
// Confirm that the log is linked to the quick form. | ||
$this->assertEquals('test', $storage['logs'][0]->quick[0]); | ||
$this->assertEquals('test', $storage['logs'][0]->get('quick')[0]->value); | ||
|
||
// Confirm that a quantity was created. | ||
$this->assertNotEmpty($storage['quantities'][0]->id()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing this array in a lot of places - sometimes in different orders or with/without the 'data_stream' entry.
Would it make sense to refactor it into a constant - or maybe just additional attributes on the entity definitions themselves?
e.g. here;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good idea! Didn't know about the
supportsBundlePlugins()
method!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could use
EntityTypeInterface::getBundleEntityType()