Commit 74c655e8 authored by bojanz's avatar bojanz

Issue #2841910: Create a ConfigurableFieldManager

parent 1578f7f1
......@@ -3,6 +3,9 @@ services:
class: Drupal\commerce\BundlePluginInstaller
arguments: ['@entity_type.manager', '@entity_bundle.listener', '@field_storage_definition.listener', '@field_definition.listener']
commerce.configurable_field_manager:
class: Drupal\commerce\ConfigurableFieldManager
commerce.credentials_check_flood:
class: Drupal\commerce\CredentialsCheckFlood
arguments: ['@flood', '@entity_type.manager', '@config.factory']
......
......@@ -5,10 +5,9 @@
* Defines the Order entity and associated features.
*/
use Drupal\commerce\BundleFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Implements hook_theme().
......@@ -108,52 +107,23 @@ function commerce_order_theme_suggestions_commerce_order(array $variables) {
/**
* Adds the default order_items field to an order type.
*
* order items can't be a base field because the Views integration is broken.
* Order items can't be a base field because the Views integration is broken.
* Instead, it is created as a configurable field for each order type.
*
* @param \Drupal\commerce_order\Entity\OrderTypeInterface $order_type
* The order type.
*/
function commerce_order_add_order_items_field($order_type) {
$field_storage = FieldStorageConfig::loadByName('commerce_order', 'order_items');
$field = FieldConfig::loadByName('commerce_order', $order_type->id(), 'order_items');
if (empty($field_storage)) {
$field_storage = FieldStorageConfig::create([
'field_name' => 'order_items',
'entity_type' => 'commerce_order',
'type' => 'entity_reference',
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'commerce_order_item',
],
'locked' => TRUE,
'translatable' => FALSE,
]);
$field_storage->save();
}
if (empty($field)) {
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $order_type->id(),
'label' => 'Order items',
'required' => TRUE,
'settings' => [
'handler' => 'default',
'handler_settings' => [],
],
'translatable' => FALSE,
]);
$field->save();
$view_display = commerce_get_entity_display('commerce_order', $order_type->id(), 'view');
$view_display->setComponent('order_items', [
'type' => 'commerce_order_item_table',
'weight' => 0,
]);
$view_display->save();
$form_display = commerce_get_entity_display('commerce_order', $order_type->id(), 'form');
$form_display->setComponent('order_items', [
$field_definition = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('commerce_order')
->setTargetBundle($order_type->id())
->setName('order_items')
->setLabel('Order items')
->setCardinality(BundleFieldDefinition::CARDINALITY_UNLIMITED)
->setRequired(TRUE)
->setSetting('target_type', 'commerce_order_type')
->setSetting('handler', 'default')
->setDisplayOptions('form', [
'type' => 'inline_entity_form_complex',
'weight' => 0,
'settings' => [
......@@ -161,9 +131,14 @@ function commerce_order_add_order_items_field($order_type) {
'label_singular' => 'order item',
'label_plural' => 'order items',
],
])
->setDisplayOptions('view', [
'type' => 'commerce_order_item_table',
'weight' => 0,
]);
$form_display->save();
}
$configurable_field_manager = \Drupal::service('commerce.configurable_field_manager');
$configurable_field_manager->createField($field_definition);
}
/**
......
......@@ -5,16 +5,13 @@
* Defines the Product entity and associated features.
*/
use Drupal\commerce\BundleFieldDefinition;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Render\Element;
use Drupal\commerce_product\Entity\ProductType;
/**
* Implements hook_config_translation_info_alter().
......@@ -181,43 +178,22 @@ function template_preprocess_commerce_product_attribute_value(array &$variables)
* The product type.
*/
function commerce_product_add_stores_field($product_type) {
$field_storage = FieldStorageConfig::loadByName('commerce_product', 'stores');
$field = FieldConfig::loadByName('commerce_product', $product_type->id(), 'stores');
if (empty($field_storage)) {
$field_storage = FieldStorageConfig::create([
'field_name' => 'stores',
'entity_type' => 'commerce_product',
'type' => 'entity_reference',
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'commerce_store',
],
'locked' => TRUE,
'translatable' => FALSE,
]);
$field_storage->save();
}
if (empty($field)) {
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $product_type->id(),
'label' => 'Stores',
'required' => TRUE,
'settings' => [
'handler' => 'default',
'handler_settings' => [],
],
'translatable' => FALSE,
]);
$field->save();
$form_display = commerce_get_entity_display('commerce_product', $product_type->id(), 'form');
$form_display->setComponent('stores', [
$field_definition = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('commerce_product')
->setTargetBundle($product_type->id())
->setName('stores')
->setLabel('Stores')
->setCardinality(BundleFieldDefinition::CARDINALITY_UNLIMITED)
->setRequired(TRUE)
->setSetting('target_type', 'commerce_store')
->setSetting('handler', 'default')
->setDisplayOptions('form', [
'type' => 'commerce_entity_select',
'weight' => -10,
]);
$form_display->save();
}
$configurable_field_manager = \Drupal::service('commerce.configurable_field_manager');
$configurable_field_manager->createField($field_definition);
}
/**
......@@ -229,39 +205,23 @@ function commerce_product_add_stores_field($product_type) {
* (optional) The label for the body instance. Defaults to 'Body'.
*/
function commerce_product_add_body_field($product_type, $label = 'Body') {
$field_storage = FieldStorageConfig::loadByName('commerce_product', 'body');
$field = FieldConfig::loadByName('commerce_product', $product_type->id(), 'body');
if (empty($field_storage)) {
$field_storage = FieldStorageConfig::create([
'field_name' => 'body',
'entity_type' => 'commerce_product',
'type' => 'text_with_summary',
]);
$field_storage->save();
}
if (empty($field)) {
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $product_type->id(),
'label' => $label,
'settings' => ['display_summary' => FALSE],
]);
$field->save();
$view_display = commerce_get_entity_display('commerce_product', $product_type->id(), 'view');
$view_display->setComponent('body', [
$field_definition = BundleFieldDefinition::create('text_with_summary')
->setTargetEntityTypeId('commerce_product')
->setTargetBundle($product_type->id())
->setName('body')
->setLabel($label)
->setSetting('display_summary', FALSE)
->setDisplayOptions('form', [
'type' => 'text_textarea_with_summary',
'weight' => 1,
])
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'text_default',
]);
$view_display->save();
$form_display = commerce_get_entity_display('commerce_product', $product_type->id(), 'form');
$form_display->setComponent('body', [
'type' => 'text_textarea_with_summary',
'weight' => 1,
]);
$form_display->save();
}
$configurable_field_manager = \Drupal::service('commerce.configurable_field_manager');
$configurable_field_manager->createField($field_definition, FALSE);
}
/**
......@@ -274,49 +234,21 @@ function commerce_product_add_body_field($product_type, $label = 'Body') {
* The product type.
*/
function commerce_product_add_variations_field($product_type) {
$field_storage = FieldStorageConfig::loadByName('commerce_product', 'variations');
$field = FieldConfig::loadByName('commerce_product', $product_type->id(), 'variations');
if (empty($field_storage)) {
$field_storage = FieldStorageConfig::create([
'field_name' => 'variations',
'entity_type' => 'commerce_product',
'type' => 'entity_reference',
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'commerce_product_variation',
$field_definition = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('commerce_product')
->setTargetBundle($product_type->id())
->setName('variations')
->setLabel('Variations')
->setCardinality(BundleFieldDefinition::CARDINALITY_UNLIMITED)
->setRequired(TRUE)
->setSetting('target_type', 'commerce_product_variation')
->setSetting('handler', 'default')
->setSetting('handler_settings', [
'target_bundles' => [
$product_type->getVariationTypeId(),
],
'locked' => TRUE,
'translatable' => FALSE,
]);
$field_storage->save();
}
if (empty($field)) {
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $product_type->id(),
'label' => 'Variations',
'required' => TRUE,
'settings' => [
'handler' => 'default',
'handler_settings' => [
'target_bundles' => [
$product_type->getVariationTypeId(),
],
],
],
'translatable' => FALSE,
]);
$field->save();
$view_display = commerce_get_entity_display('commerce_product', $product_type->id(), 'view');
$view_display->setComponent('variations', [
'type' => 'commerce_add_to_cart',
'weight' => 10,
]);
$view_display->save();
$form_display = commerce_get_entity_display('commerce_product', $product_type->id(), 'form');
$form_display->setComponent('variations', [
])
->setDisplayOptions('form', [
'type' => 'inline_entity_form_complex',
'weight' => 10,
'settings' => [
......@@ -324,9 +256,14 @@ function commerce_product_add_variations_field($product_type) {
'label_singular' => 'variation',
'label_plural' => 'variations',
],
])
->setDisplayOptions('view', [
'type' => 'commerce_add_to_cart',
'weight' => 10,
]);
$form_display->save();
}
$configurable_field_manager = \Drupal::service('commerce.configurable_field_manager');
$configurable_field_manager->createField($field_definition);
}
/**
......
<?php
namespace Drupal\commerce;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
class ConfigurableFieldManager implements ConfigurableFieldManagerInterface {
/**
* {@inheritdoc}
*/
public function createField(BundleFieldDefinition $field_definition, $lock = TRUE) {
$field_name = $field_definition->getName();
$entity_type_id = $field_definition->getTargetEntityTypeId();
$bundle = $field_definition->getTargetBundle();
if (empty($field_name) || empty($entity_type_id) || empty($bundle)) {
throw new \InvalidArgumentException('The passed $field_definition is incomplete.');
}
// loadByName() is an API that doesn't exist on the storage classes for
// the two entity types, so we're using the entity classes directly.
$field_storage = FieldStorageConfig::loadByName($entity_type_id, $field_name);
$field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name);
if (!empty($field)) {
throw new \RuntimeException(sprintf('The field "%s" already exists on bundle "%s" of entity type "%s".', $field_name, $bundle, $entity_type_id));
}
// The field storage might already exist if the field was created earlier
// on a different bundle of the same entity type.
if (empty($field_storage)) {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => $entity_type_id,
'type' => $field_definition->getType(),
'cardinality' => $field_definition->getCardinality(),
'settings' => $field_definition->getSettings(),
'translatable' => $field_definition->isTranslatable(),
'locked' => $lock,
]);
$field_storage->save();
}
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $bundle,
'label' => $field_definition->getLabel(),
'required' => $field_definition->isRequired(),
'settings' => $field_definition->getSettings(),
'translatable' => $field_definition->isTranslatable(),
'default_value' => $field_definition->getDefaultValueLiteral(),
'default_value_callback' => $field_definition->getDefaultValueCallback(),
]);
$field->save();
// Show the field on default entity displays, if specified.
if ($view_display_options = $field_definition->getDisplayOptions('view')) {
$view_display = commerce_get_entity_display($entity_type_id, $bundle, 'view');
$view_display->setComponent($field_name, $view_display_options);
$view_display->save();
}
if ($form_display_options = $field_definition->getDisplayOptions('form')) {
$form_display = commerce_get_entity_display($entity_type_id, $bundle, 'form');
$form_display->setComponent($field_name, $form_display_options);
$form_display->save();
}
}
/**
* {@inheritdoc}
*/
public function deleteField(BundleFieldDefinition $field_definition) {
$field_name = $field_definition->getName();
$entity_type_id = $field_definition->getTargetEntityTypeId();
$bundle = $field_definition->getTargetBundle();
if (empty($field_name) || empty($entity_type_id) || empty($bundle)) {
throw new \InvalidArgumentException('The passed $field_definition is incomplete.');
}
$field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name);
if (empty($field)) {
throw new \RuntimeException(sprintf('The field "%s" does not exist on bundle "%s" of entity type "%s".', $field_name, $bundle, $entity_type_id));
}
$field->delete();
}
}
<?php
namespace Drupal\commerce;
/**
* Manages configurable fields based on field definitions.
*
* Allows for an easier way to create/delete configurable fields from code.
*/
interface ConfigurableFieldManagerInterface {
/**
* Creates a configurable field from the given field definition.
*
* @param \Drupal\commerce\BundleFieldDefinition $field_definition
* The field definition.
* @param bool $lock
* Whether the created field should be locked.
*
* @throws \InvalidArgumentException
* Thrown when given an incomplete field definition (missing name,
* target entity type ID, or target bundle).
* @throws \RuntimeException
* Thrown when a field with the same name already exists.
*/
public function createField(BundleFieldDefinition $field_definition, $lock = TRUE);
/**
* Deletes the configurable field created from the given field definition.
*
* @param \Drupal\commerce\BundleFieldDefinition $field_definition
* The field definition.
*
* @throws \InvalidArgumentException
* Thrown when given an incomplete field definition (missing name,
* target entity type ID, or target bundle).
* @throws \RuntimeException
* Thrown when no matching field was found.
*/
public function deleteField(BundleFieldDefinition $field_definition);
}
<?php
namespace Drupal\Tests\commerce\Kernel;
use Drupal\commerce\BundleFieldDefinition;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
/**
* Tests the ConfigurableFieldManager class.
*
* @group commerce
*/
class ConfigurableFieldManagerTest extends CommerceKernelTestBase {
/**
* The configurable field manager.
*
* @var \Drupal\commerce\ConfigurableFieldManagerInterface
*/
protected $configurableFieldManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->configurableFieldManager = \Drupal::service('commerce.configurable_field_manager');
}
/**
* Tests creating and deleting a field.
*/
public function testManager() {
$field_definition = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('entity_test')
->setTargetBundle('entity_test')
->setName('stores')
->setLabel('Stores')
->setCardinality(BundleFieldDefinition::CARDINALITY_UNLIMITED)
->setRequired(TRUE)
->setTranslatable(TRUE)
->setSetting('target_type', 'commerce_store')
->setSetting('handler', 'default')
->setDisplayOptions('form', [
'type' => 'commerce_entity_select',
'weight' => -10,
]);
$this->configurableFieldManager->createField($field_definition);
// Confirm that the field was created with the specifid options.
$entity_test = EntityTest::create();
$this->assertTrue($entity_test->hasField('stores'));
$created_definition = $entity_test->getFieldDefinition('stores');
$this->assertInstanceOf(FieldConfig::class, $created_definition);
$this->assertEquals($created_definition->getLabel(), $field_definition->getLabel());
$this->assertEquals($created_definition->isRequired(), $field_definition->isRequired());
$this->assertEquals($created_definition->isTranslatable(), $field_definition->isTranslatable());
$this->assertEquals('commerce_store', $field_definition->getSetting('target_type'));
$created_storage_definition = $created_definition->getFieldStorageDefinition();
$this->assertEquals($created_storage_definition->getCardinality(), $field_definition->getCardinality());
// Confirm that the field is functional.
$entity_test->get('stores')->appendItem($this->store);
$entity_test->save();
$entity_test = $this->reloadEntity($entity_test);
$this->assertEquals($this->store->id(), $entity_test->stores->target_id);
// Confirm that a form display was created and populated with the options.
$form_display = commerce_get_entity_display('entity_test', 'entity_test', 'form');
$component = $form_display->getComponent('stores');
$this->assertEquals('commerce_entity_select', $component['type']);
$this->assertEquals('-10', $component['weight']);
// Delete the field.
$this->configurableFieldManager->deleteField($field_definition);
$entity_test = EntityTest::create();
$this->assertFalse($entity_test->hasField('stores'));
}
/**
* Tests passing an invalid field definition.
*/
public function testInvalidDefinition() {
$field_definition = BundleFieldDefinition::create('entity_reference');
$field_definition->setName('stores');
$this->setExpectedException(\InvalidArgumentException::class);
$this->configurableFieldManager->createField($field_definition);
}
/**
* Tests trying to delete an unknown field.
*/
public function testInvalidDelete() {
$field_definition = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('entity_test')
->setTargetBundle('entity_test')
->setName('stores');
$expected_message = 'The field "stores" does not exist on bundle "entity_test" of entity type "entity_test".';
$this->setExpectedException(\RuntimeException::class, $expected_message);
$this->configurableFieldManager->deleteField($field_definition);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment