mirror of
https://github.com/farmOS/farmOS.git
synced 2024-02-23 11:37:38 +01:00
Issue #2991394 by paul121: Move inventory fields to quantity measurements
This commit is contained in:
commit
d8443dfa56
|
@ -1,4 +1,8 @@
|
|||
services:
|
||||
farm_entity.bundle_plugin_installer:
|
||||
class: Drupal\farm_entity\BundlePlugin\BundlePluginInstaller
|
||||
decorates: entity.bundle_plugin_installer
|
||||
arguments: [ '@entity_type.manager', '@entity_bundle.listener', '@field_storage_definition.listener', '@field_definition.listener']
|
||||
plugin.manager.asset_type:
|
||||
class: Drupal\farm_entity\AssetTypeManager
|
||||
parent: default_plugin_manager
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity\BundlePlugin;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\entity\BundlePlugin\BundlePluginInstaller as EntityBundlePluginInstaller;
|
||||
|
||||
/**
|
||||
* Extends the entity BundlePluginInstaller service.
|
||||
*
|
||||
* Only removes field storage definitions when not in use by another module.
|
||||
* This allows field names to be reused across bundles.
|
||||
*
|
||||
* @see https://www.drupal.org/project/farm/issues/3200219
|
||||
*/
|
||||
class BundlePluginInstaller extends EntityBundlePluginInstaller {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uninstallBundles(EntityTypeInterface $entity_type, array $modules) {
|
||||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
$bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) {
|
||||
return in_array($bundle_info['provider'], $modules, TRUE);
|
||||
});
|
||||
|
||||
/**
|
||||
* We need to uninstall the field storage definitions in a separate loop.
|
||||
*
|
||||
* This way we can allow a module to re-use the same field within multiple
|
||||
* bundles, allowing e.g to subclass a bundle plugin.
|
||||
*
|
||||
* @var \Drupal\entity\BundleFieldDefinition[] $field_storage_definitions
|
||||
*/
|
||||
$field_storage_definitions = [];
|
||||
|
||||
// Field definitions that should persist after uninstalling these bundles.
|
||||
$field_definitions_to_persist = $this->getFieldDefinitionsToPersist($entity_type, array_keys($bundles));
|
||||
|
||||
foreach (array_keys($bundles) as $bundle) {
|
||||
$this->entityBundleListener->onBundleDelete($bundle, $entity_type->id());
|
||||
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
|
||||
$field_name = $definition->getName();
|
||||
$this->fieldDefinitionListener->onFieldDefinitionDelete($definition);
|
||||
|
||||
// Delete the field storage definition if it should not persist.
|
||||
if (!in_array($field_name, array_keys($field_definitions_to_persist))) {
|
||||
$field_storage_definitions[$field_name] = $definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($field_storage_definitions as $definition) {
|
||||
$this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($definition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field definitions from all remaining bundles.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type to check.
|
||||
* @param array $uninstalled_bundles
|
||||
* The bundles that will be uninstalled.
|
||||
*
|
||||
* @return array
|
||||
* Remaining field definitions.
|
||||
*/
|
||||
protected function getFieldDefinitionsToPersist(EntityTypeInterface $entity_type, array $uninstalled_bundles) {
|
||||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
$remaining_bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_name) use ($uninstalled_bundles) {
|
||||
return !in_array($bundle_name, $uninstalled_bundles, TRUE);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
$fields_to_persist = [];
|
||||
foreach (array_keys($remaining_bundles) as $bundle) {
|
||||
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
|
||||
$field_name = $definition->getName();
|
||||
if (!isset($fields_to_persist[$field_name])) {
|
||||
$fields_to_persist[$field_name] = $definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $fields_to_persist;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: second
|
||||
label: Second
|
||||
description: ''
|
||||
name_pattern: 'Second plan [plan:id]'
|
||||
workflow: plan_default
|
||||
new_revision: true
|
|
@ -0,0 +1,8 @@
|
|||
name: farmOS Bundle Fields Test
|
||||
description: Module for testing farmOS bundle fields behavior.
|
||||
type: module
|
||||
package: Testing
|
||||
core_version_requirement: ^9
|
||||
dependencies:
|
||||
- farm:farm_entity
|
||||
- farm:farm_entity_test
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity_bundle_fields_test\Plugin\Plan\PlanType;
|
||||
|
||||
use Drupal\entity\BundleFieldDefinition;
|
||||
use Drupal\farm_entity\Plugin\Plan\PlanType\FarmPlanType;
|
||||
|
||||
/**
|
||||
* Provides the second test plan type.
|
||||
*
|
||||
* @PlanType(
|
||||
* id = "second",
|
||||
* label = @Translation("Second"),
|
||||
* )
|
||||
*/
|
||||
class Second extends FarmPlanType {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFieldDefinitions() {
|
||||
|
||||
// Inherit all plan fields.
|
||||
$fields = parent::buildFieldDefinitions();
|
||||
|
||||
// Create a field for just this bundle.
|
||||
$fields['second_plan_field'] = BundleFieldDefinition::create('boolean')
|
||||
->setLabel($this->t('Test field for second plan type'));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\farm_entity\Functional;
|
||||
|
||||
use Drupal\Tests\farm\Functional\FarmBrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests that bundle fields are created during a postponed install.
|
||||
*
|
||||
* @group farm
|
||||
*/
|
||||
class EntityBundleFieldPostponedInstallTest extends FarmBrowserTestBase {
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The module installer service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleInstallerInterface
|
||||
*/
|
||||
protected $moduleInstaller;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'farm_entity',
|
||||
'farm_entity_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp():void {
|
||||
parent::setUp();
|
||||
$this->entityFieldManager = $this->container->get('entity_field.manager');
|
||||
$this->moduleInstaller = $this->container->get('module_installer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test installing the farm_entity_contrib_test module after farm_entity_test.
|
||||
*/
|
||||
public function testBundleFieldPostponedInstall() {
|
||||
|
||||
// Install the farm_entity_contrib_test module.
|
||||
$result = $this->moduleInstaller->install(['farm_entity_contrib_test'], TRUE);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Must clear the cache for the test environment.
|
||||
$this->entityFieldManager->clearCachedFieldDefinitions();
|
||||
|
||||
// Test log field storage definition.
|
||||
$fields = $this->entityFieldManager->getFieldStorageDefinitions('log');
|
||||
$this->assertArrayHasKey('test_contrib_hook_bundle_field', $fields);
|
||||
|
||||
// Test bundle field storage definition.
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test');
|
||||
$this->assertArrayHasKey('test_contrib_hook_bundle_field', $fields);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\farm_entity\Functional;
|
||||
|
||||
use Drupal\Tests\farm\Functional\FarmBrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests that bundle fields are created during a postponed install.
|
||||
*
|
||||
* @group farm
|
||||
*/
|
||||
class FarmEntityBundleFieldTest extends FarmBrowserTestBase {
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The module installer service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleInstallerInterface
|
||||
*/
|
||||
protected $moduleInstaller;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'farm_entity',
|
||||
'farm_entity_test',
|
||||
'farm_entity_bundle_fields_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp():void {
|
||||
parent::setUp();
|
||||
$this->entityFieldManager = $this->container->get('entity_field.manager');
|
||||
$this->entityTypeManager = $this->container->get('entity_type.manager');
|
||||
$this->database = $this->container->get('database');
|
||||
$this->moduleInstaller = $this->container->get('module_installer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test installing the farm_entity_contrib_test module after farm_entity_test.
|
||||
*/
|
||||
public function testBundleFieldPostponedInstall() {
|
||||
|
||||
// Install the farm_entity_contrib_test module.
|
||||
$result = $this->moduleInstaller->install(['farm_entity_contrib_test'], TRUE);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Must clear the cache for the test environment.
|
||||
$this->entityFieldManager->clearCachedFieldDefinitions();
|
||||
|
||||
// Test bundle field definition exists.
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test');
|
||||
$this->assertArrayHasKey('test_contrib_hook_bundle_field', $fields);
|
||||
|
||||
// Test log field storage definition exists.
|
||||
$this->assertFieldStorageDefinitionExists('log', 'test_contrib_hook_bundle_field');
|
||||
|
||||
// Save the contrib field storage definition for later.
|
||||
$installed_contrib_field_storage_definition = $this->entityFieldManager->getFieldStorageDefinitions('log')['test_contrib_hook_bundle_field'];
|
||||
|
||||
// Uninstall the farm_entity_contrib_test module.
|
||||
$result = $this->moduleInstaller->uninstall(['farm_entity_contrib_test']);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Must clear the cache for the test environment.
|
||||
$this->entityFieldManager->clearCachedFieldDefinitions();
|
||||
|
||||
// Test bundle field definition is deleted.
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test');
|
||||
$this->assertArrayNotHasKey('test_contrib_hook_bundle_field', $fields);
|
||||
|
||||
// Test log field storage definition is deleted.
|
||||
$this->assertFieldStorageDefinitionExists('log', 'test_contrib_hook_bundle_field', FALSE);
|
||||
|
||||
// Ensure the database table was deleted.
|
||||
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
||||
$table_mapping = $this->entityTypeManager->getStorage('log')->getTableMapping();
|
||||
$table = $table_mapping->getDedicatedDataTableName($installed_contrib_field_storage_definition);
|
||||
$this->assertFalse($this->database->schema()->tableExists($table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that bundle fields can be reused across bundles.
|
||||
*/
|
||||
public function testBundlePluginModuleUninstallation() {
|
||||
|
||||
// Test that database tables exist after uninstalling a bundle with
|
||||
// a field storage definition used by other bundles.
|
||||
$this->moduleInstaller->uninstall(['farm_entity_bundle_fields_test']);
|
||||
|
||||
// Must clear the cache for the test environment.
|
||||
$this->entityFieldManager->clearCachedFieldDefinitions();
|
||||
|
||||
// Test that correct field storage definitions and database tables exist.
|
||||
$test_fields = [
|
||||
'second_plan_field' => FALSE,
|
||||
'asset' => TRUE,
|
||||
'log' => TRUE,
|
||||
];
|
||||
foreach ($test_fields as $field_name => $exists) {
|
||||
$this->assertFieldStorageDefinitionExists('plan', $field_name, $exists);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check the existence of field storage definitions.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to check.
|
||||
* @param string $field_name
|
||||
* The field name to check.
|
||||
* @param bool $exists
|
||||
* If the field should exists, defaults to TRUE.
|
||||
*/
|
||||
protected function assertFieldStorageDefinitionExists(string $entity_type, string $field_name, bool $exists = TRUE) {
|
||||
$field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type);
|
||||
|
||||
// Test the field storage definition existence.
|
||||
$this->assertEquals($exists, array_key_exists($field_name, $field_storage_definitions));
|
||||
|
||||
// Test that the database table exists if the field storage definition
|
||||
// exists.
|
||||
if ($exists) {
|
||||
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
||||
$table_mapping = $this->entityTypeManager->getStorage($entity_type)->getTableMapping();
|
||||
|
||||
$table = $table_mapping->getDedicatedDataTableName($field_storage_definitions[$field_name]);
|
||||
$this->assertTrue($this->database->schema()->tableExists($table));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue