Skip to content

Commit 2acf4bc

Browse files
lisastreeterbojanz
authored andcommitted
Issue #2911346 by lisastreeter, mglaman, bojanz: When creating a product type, add option to create new variation type
1 parent 24776b8 commit 2acf4bc

3 files changed

Lines changed: 195 additions & 5 deletions

File tree

modules/product/src/Form/ProductTypeForm.php

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Drupal\commerce\EntityHelper;
66
use Drupal\commerce\EntityTraitManagerInterface;
77
use Drupal\commerce\Form\CommerceBundleEntityFormBase;
8+
use Drupal\commerce_order\Entity\OrderItemTypeInterface;
89
use Drupal\Core\Entity\EntityTypeManagerInterface;
910
use Drupal\Core\Entity\EntityFieldManagerInterface;
1011
use Drupal\Core\Entity\EntityTypeInterface;
@@ -99,9 +100,12 @@ public function form(array $form, FormStateInterface $form_state) {
99100
'#title' => $this->t('Product variation type'),
100101
'#default_value' => $product_type->getVariationTypeId(),
101102
'#options' => EntityHelper::extractLabels($variation_types),
102-
'#required' => TRUE,
103103
'#disabled' => !$product_type->isNew(),
104104
];
105+
if ($product_type->isNew()) {
106+
$form['variationType']['#empty_option'] = $this->t('- Create new -');
107+
$form['variationType']['#description'] = $this->t('If an existing product variation type is not selected, a new one will be created.');
108+
}
105109
$form['injectVariationFields'] = [
106110
'#type' => 'checkbox',
107111
'#title' => $this->t('Inject product variation fields into the rendered product.'),
@@ -139,6 +143,47 @@ public function form(array $form, FormStateInterface $form_state) {
139143
*/
140144
public function validateForm(array &$form, FormStateInterface $form_state) {
141145
$this->validateTraitForm($form, $form_state);
146+
147+
if (empty($form_state->getValue('variationType'))) {
148+
$id = $form_state->getValue('id');
149+
if (!empty($this->entityTypeManager->getStorage('commerce_product_variation_type')->load($id))) {
150+
$form_state->setError($form['variationType'], $this->t('A product variation type with the machine name @id already exists. Select an existing product variation type or change the machine name for this product type.', [
151+
'@id' => $id,
152+
]));
153+
}
154+
155+
if ($this->moduleHandler->moduleExists('commerce_order')) {
156+
$order_item_type_ids = $this->getOrderItemTypeIds();
157+
if (empty($order_item_type_ids)) {
158+
$form_state->setError($form['variationType'], $this->t('A new product variation type cannot be created, because no order item types were found. Select an existing product variation type or retry after creating a new order item type.'));
159+
}
160+
}
161+
}
162+
}
163+
164+
/**
165+
* {@inheritdoc}
166+
*/
167+
public function submitForm(array &$form, FormStateInterface $form_state) {
168+
parent::submitForm($form, $form_state);
169+
170+
/** @var \Drupal\commerce_product\Entity\ProductTypeInterface $product_type */
171+
$product_type = $this->entity;
172+
// Create a new product variation type.
173+
if (empty($form_state->getValue('variationType'))) {
174+
/** @var \Drupal\commerce_product\Entity\ProductVariationTypeInterface $variation_type */
175+
$variation_type = $this->entityTypeManager->getStorage('commerce_product_variation_type')->create([
176+
'id' => $form_state->getValue('id'),
177+
'label' => $form_state->getValue('label'),
178+
]);
179+
if ($this->moduleHandler->moduleExists('commerce_order')) {
180+
$order_item_type_ids = $this->getOrderItemTypeIds();
181+
$order_item_type_id = isset($types['default']) ? 'default' : reset($order_item_type_ids);
182+
$variation_type->setOrderItemTypeId($order_item_type_id);
183+
}
184+
$variation_type->save();
185+
$product_type->setVariationTypeId($form_state->getValue('id'));
186+
}
142187
}
143188

144189
/**
@@ -165,4 +210,23 @@ public function save(array $form, FormStateInterface $form_state) {
165210
}
166211
}
167212

213+
/**
214+
* Gets the available order item type IDs.
215+
*
216+
* Only order item types that can be used to purchase product variations
217+
* are included.
218+
*
219+
* @return string[]
220+
* The order item type IDs.
221+
*/
222+
protected function getOrderItemTypeIds() {
223+
$order_item_type_storage = $this->entityTypeManager->getStorage('commerce_order_item_type');
224+
$order_item_types = $order_item_type_storage->loadMultiple();
225+
$order_item_types = array_filter($order_item_types, function (OrderItemTypeInterface $type) {
226+
return $type->getPurchasableEntityTypeId() == 'commerce_product_variation';
227+
});
228+
229+
return array_keys($order_item_types);
230+
}
231+
168232
}

modules/product/src/ProductTypeListBuilder.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,77 @@
44

55
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
66
use Drupal\Core\Entity\EntityInterface;
7+
use Drupal\Core\Entity\EntityStorageInterface;
8+
use Drupal\Core\Entity\EntityTypeInterface;
9+
use Drupal\Core\Entity\EntityTypeManagerInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
711

812
/**
913
* Defines the list builder for product types.
1014
*/
1115
class ProductTypeListBuilder extends ConfigEntityListBuilder {
1216

17+
/**
18+
* The variation type storage.
19+
*
20+
* @var \Drupal\Core\Entity\EntityStorageInterface
21+
*/
22+
protected $variationTypeStorage;
23+
24+
/**
25+
* Constructs a new ProductTypeListBuilder object.
26+
*
27+
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
28+
* The entity type definition.
29+
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
30+
* The entity storage.
31+
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
32+
* The entity type manager.
33+
*/
34+
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, EntityTypeManagerInterface $entity_type_manager) {
35+
parent::__construct($entity_type, $storage);
36+
37+
$this->variationTypeStorage = $entity_type_manager->getStorage('commerce_product_variation_type');
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
44+
return new static(
45+
$entity_type,
46+
$container->get('entity.manager')->getStorage($entity_type->id()),
47+
$container->get('entity_type.manager')
48+
);
49+
}
50+
1351
/**
1452
* {@inheritdoc}
1553
*/
1654
public function buildHeader() {
1755
$header['name'] = $this->t('Product type');
1856
$header['type'] = $this->t('Machine name');
57+
$header['product_variation_type'] = $this->t('Product variation type');
1958
return $header + parent::buildHeader();
2059
}
2160

2261
/**
2362
* {@inheritdoc}
2463
*/
2564
public function buildRow(EntityInterface $entity) {
65+
$variation_type = $this->variationTypeStorage->load($entity->getVariationTypeId());
2666
$row['name'] = $entity->label();
2767
$row['type'] = $entity->id();
68+
if (empty($variation_type)) {
69+
$row['product_variation_type'] = $this->t('N/A');
70+
}
71+
else {
72+
$row['product_variation_type']['data'] = [
73+
'#type' => 'link',
74+
'#title' => $variation_type->label(),
75+
'#url' => $variation_type->toUrl('edit-form'),
76+
];
77+
}
2878
return $row + parent::buildRow($entity);
2979
}
3080

modules/product/tests/src/Functional/ProductTypeTest.php

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Drupal\Tests\commerce_product\Functional;
44

5+
use Drupal\commerce_order\Entity\OrderItemType;
56
use Drupal\commerce_product\Entity\ProductType;
7+
use Drupal\commerce_product\Entity\ProductVariationType;
68

79
/**
810
* Ensure the product type works correctly.
@@ -54,10 +56,84 @@ public function testProductTypeCreation() {
5456
];
5557
$this->submitForm($edit, t('Save'));
5658
$product_type = ProductType::load($edit['id']);
57-
$this->assertNotEmpty(!empty($product_type), 'The new product type has been created.');
58-
$this->assertEquals($product_type->label(), $edit['label'], 'The new product type has the correct label.');
59-
$this->assertEquals($product_type->getDescription(), $edit['description'], 'The new product type has the correct label.');
60-
$this->assertEquals($product_type->getVariationTypeId(), $edit['variationType'], 'The new product type has the correct associated variation type.');
59+
$this->assertNotEmpty($product_type);
60+
$this->assertEquals($product_type->label(), $edit['label']);
61+
$this->assertEquals($product_type->getDescription(), $edit['description']);
62+
$this->assertEquals($product_type->getVariationTypeId(), $edit['variationType']);
63+
64+
// Automatic variation type creation option.
65+
$this->drupalGet('admin/commerce/config/product-types/add');
66+
$edit = [
67+
'id' => strtolower($this->randomMachineName(8)),
68+
'label' => $this->randomMachineName(),
69+
'description' => 'My even more random product type',
70+
'variationType' => '',
71+
];
72+
$this->submitForm($edit, t('Save'));
73+
$product_type = ProductType::load($edit['id']);
74+
$this->assertNotEmpty($product_type);
75+
$this->assertEquals($product_type->label(), $edit['label']);
76+
$this->assertEquals($product_type->getDescription(), $edit['description']);
77+
$this->assertEquals($product_type->getVariationTypeId(), $edit['id']);
78+
$product_variation_type = ProductVariationType::load($edit['id']);
79+
$this->assertNotEmpty($product_variation_type);
80+
$this->assertEquals($product_variation_type->label(), $edit['label']);
81+
$this->assertEquals($product_variation_type->getOrderItemTypeId(), 'default');
82+
83+
// Confirm that a conflicting product variation type ID is detected.
84+
$product_type_id = $product_type->id();
85+
$product_type->delete();
86+
$this->drupalGet('admin/commerce/config/product-types/add');
87+
$edit = [
88+
'id' => $product_type_id,
89+
'label' => $this->randomMachineName(),
90+
'description' => 'My even more random product type',
91+
'variationType' => '',
92+
];
93+
$this->submitForm($edit, t('Save'));
94+
$this->assertSession()->pageTextContains(t('A product variation type with the machine name @name already exists. Select an existing product variation type or change the machine name for this product type.', ['@name' => $product_type_id]));
95+
96+
// Confirm that the form can't be submitted with no order item types.
97+
$default_order_item_type = OrderItemType::load('default');
98+
$this->assertNotEmpty(!empty($default_order_item_type), 'The default order item type has been loaded.');
99+
$default_order_item_type->delete();
100+
101+
$this->drupalGet('admin/commerce/config/product-types/add');
102+
$edit = [
103+
'id' => strtolower($this->randomMachineName(8)),
104+
'label' => $this->randomMachineName(),
105+
'description' => 'Another random product type',
106+
'variationType' => '',
107+
];
108+
$this->submitForm($edit, t('Save'));
109+
$this->assertSession()->pageTextContains(t('A new product variation type cannot be created, because no order item types were found. Select an existing product variation type or retry after creating a new order item type.'));
110+
111+
// Confirm that a non-default order item type can be selected.
112+
$default_order_item_type->delete();
113+
OrderItemType::create([
114+
'id' => 'test',
115+
'label' => 'Test',
116+
'orderType' => 'default',
117+
'purchasableEntityType' => 'commerce_product_variation',
118+
])->save();
119+
120+
$this->drupalGet('admin/commerce/config/product-types/add');
121+
$edit = [
122+
'id' => strtolower($this->randomMachineName(8)),
123+
'label' => $this->randomMachineName(),
124+
'description' => 'My even more random product type',
125+
'variationType' => '',
126+
];
127+
$this->submitForm($edit, t('Save'));
128+
$product_type = ProductType::load($edit['id']);
129+
$this->assertNotEmpty($product_type);
130+
$this->assertEquals($product_type->label(), $edit['label']);
131+
$this->assertEquals($product_type->getDescription(), $edit['description']);
132+
$this->assertEquals($product_type->getVariationTypeId(), $edit['id']);
133+
$product_variation_type = ProductVariationType::load($edit['id']);
134+
$this->assertNotEmpty($product_variation_type);
135+
$this->assertEquals($product_variation_type->label(), $edit['label']);
136+
$this->assertEquals($product_variation_type->getOrderItemTypeId(), 'test');
61137
}
62138

63139
/**

0 commit comments

Comments
 (0)