Skip to content

Commit 409f48a

Browse files
committed
Issue #2842924 by mglaman, jkuma, niko-: Add a new promotion condition to filter by product
1 parent 9bddff6 commit 409f48a

File tree

5 files changed

+672
-0
lines changed

5 files changed

+672
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
namespace Drupal\commerce_product\Plugin\Commerce\PromotionCondition;
4+
5+
use Drupal\commerce_promotion\Plugin\Commerce\PromotionCondition\PromotionConditionBase;
6+
use Drupal\Component\Utility\NestedArray;
7+
use Drupal\Core\Entity\EntityTypeManagerInterface;
8+
use Drupal\Core\Form\FormStateInterface;
9+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
12+
/**
13+
* Provides an 'Order item: product equals' condition.
14+
*
15+
* @CommercePromotionCondition(
16+
* id = "commerce_product_equals",
17+
* label = @Translation("Product equals"),
18+
* target_entity_type = "commerce_order_item",
19+
* )
20+
*/
21+
class ProductEquals extends PromotionConditionBase implements ContainerFactoryPluginInterface {
22+
23+
/**
24+
* The product storage.
25+
*
26+
* @var \Drupal\Core\Entity\EntityStorageInterface
27+
*/
28+
protected $productStorage;
29+
30+
/**
31+
* Constructs a new ProductEquals object.
32+
*
33+
* @param array $configuration
34+
* The plugin configuration, i.e. an array with configuration values keyed
35+
* by configuration option name. The special key 'context' may be used to
36+
* initialize the defined contexts by setting it to an array of context
37+
* values keyed by context names.
38+
* @param string $plugin_id
39+
* The plugin_id for the plugin instance.
40+
* @param mixed $plugin_definition
41+
* The plugin implementation definition.
42+
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
43+
* The entity type manager.
44+
*/
45+
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
46+
parent::__construct($configuration, $plugin_id, $plugin_definition);
47+
$this->productStorage = $entity_type_manager->getStorage('commerce_product');
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
54+
return new static(
55+
$configuration,
56+
$plugin_id,
57+
$plugin_definition,
58+
$container->get('entity_type.manager')
59+
);
60+
}
61+
62+
/**
63+
* {@inheritdoc}
64+
*/
65+
public function defaultConfiguration() {
66+
return [
67+
'product_id' => NULL,
68+
] + parent::defaultConfiguration();
69+
}
70+
71+
/**
72+
* {@inheritdoc}
73+
*/
74+
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
75+
$form = parent::buildConfigurationForm($form, $form_state);
76+
77+
$product = $this->productStorage->load($this->configuration['product_id']);
78+
$form['product_id'] = [
79+
'#type' => 'entity_autocomplete',
80+
'#title' => $this->t('Product'),
81+
'#default_value' => $product,
82+
'#target_type' => 'commerce_product',
83+
];
84+
return $form;
85+
}
86+
87+
/**
88+
* {@inheritdoc}
89+
*/
90+
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
91+
$values = $form_state->getValue($form['#parents']);
92+
$this->configuration['product_id'] = $values['product_id'];
93+
parent::submitConfigurationForm($form, $form_state);
94+
}
95+
96+
/**
97+
* {@inheritdoc}
98+
*/
99+
public function evaluate() {
100+
$product_id = $this->configuration['product_id'];
101+
if (empty($product_id)) {
102+
return FALSE;
103+
}
104+
105+
/** @var \Drupal\commerce_product\Entity\ProductInterface $current_product */
106+
$current_product = $this->getTargetEntity()->getPurchasedEntity()->getProduct();
107+
108+
return $current_product->id() == $product_id;
109+
}
110+
111+
/**
112+
* {@inheritdoc}
113+
*/
114+
public function summary() {
115+
return $this->t('Compares the purchased product.');
116+
}
117+
118+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<?php
2+
3+
namespace Drupal\commerce_product\Plugin\Commerce\PromotionCondition;
4+
5+
use Drupal\commerce_order\Entity\OrderItemInterface;
6+
use Drupal\commerce_promotion\Plugin\Commerce\PromotionCondition\PromotionConditionBase;
7+
use Drupal\Component\Utility\Html;
8+
use Drupal\Component\Utility\NestedArray;
9+
use Drupal\Core\Form\FormStateInterface;
10+
use Drupal\taxonomy\TermInterface;
11+
use Drupal\taxonomy\TermStorage;
12+
13+
/**
14+
* Provides an 'Order: Total amount comparison' condition.
15+
*
16+
* @CommercePromotionCondition(
17+
* id = "commerce_promotion_product_field_equals",
18+
* label = @Translation("Product field equals"),
19+
* target_entity_type = "commerce_order_item",
20+
* )
21+
*/
22+
class ProductFieldEquals extends PromotionConditionBase {
23+
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function defaultConfiguration() {
28+
return [
29+
'bundle' => NULL,
30+
'field' => NULL,
31+
] + parent::defaultConfiguration();
32+
}
33+
34+
/**
35+
* {@inheritdoc}
36+
*/
37+
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
38+
$form += parent::buildConfigurationForm($form, $form_state);
39+
$ajax_wrapper_id = Html::getUniqueId('ajax-wrapper');
40+
// Prefix and suffix used for Ajax replacement.
41+
$form['#prefix'] = '<div id="' . $ajax_wrapper_id . '">';
42+
$form['#suffix'] = '</div>';
43+
44+
$selected_bundle = isset($this->configuration['bundle']) ? $this->configuration['bundle'] : NULL;
45+
$bundles = \Drupal::service("entity_type.bundle.info")->getBundleInfo('commerce_product');
46+
$bundle_options = [];
47+
foreach ($bundles as $bundle => $label) {
48+
$bundle_options[$bundle] = $label['label'];
49+
}
50+
51+
$form['bundle'] = [
52+
'#type' => 'select',
53+
'#options' => $bundle_options,
54+
'#title' => t('Product bundle'),
55+
'#default_value' => $selected_bundle,
56+
'#required' => TRUE,
57+
'#ajax' => [
58+
'callback' => [$this, 'bundleAjaxCallback'],
59+
'wrapper' => $ajax_wrapper_id,
60+
],
61+
];
62+
if (!$selected_bundle) {
63+
return $form;
64+
}
65+
66+
$fields = \Drupal::service("entity_field.manager")->getFieldDefinitions('commerce_product', $selected_bundle);
67+
$selected_field = isset($this->configuration['field']) ? $this->configuration['field'] : NULL;
68+
69+
$filed_options = [];
70+
foreach ($fields as $field_id => $field_definition) {
71+
$filed_options[$field_id] = $field_definition->getLabel();
72+
}
73+
74+
$form['field'] = [
75+
'#type' => 'select',
76+
'#title' => t('Field'),
77+
'#options' => $filed_options,
78+
'#default_value' => $selected_field,
79+
'#required' => TRUE,
80+
'#ajax' => [
81+
'callback' => [$this, 'bundleAjaxCallback'],
82+
'wrapper' => $ajax_wrapper_id,
83+
],
84+
];
85+
86+
if (!$selected_field) {
87+
return $form;
88+
}
89+
90+
//Create an empty representative entity
91+
$commerce_product = \Drupal::service('entity_type.manager')->getStorage('commerce_product')->create(array(
92+
'type' => $selected_bundle,
93+
$selected_field => $this->configuration[$selected_field],
94+
)
95+
);
96+
97+
//Get the EntityFormDisplay (i.e. the default Form Display) of this content type
98+
$entity_form_display = \Drupal::service('entity_type.manager')->getStorage('entity_form_display')
99+
->load('commerce_product.' . $selected_bundle . '.default');
100+
101+
//Get the body field widget and add it to the form
102+
if ($widget = $entity_form_display->getRenderer($selected_field)) { //Returns the widget class
103+
$items = $commerce_product->get($selected_field); //Returns the FieldItemsList interface
104+
$items->filterEmptyItems();
105+
$form[$selected_field] = $widget->form($items, $form, $form_state); //Builds the widget form and attach it to your form
106+
$form[$selected_field]['widget']['#required'] = TRUE;
107+
}
108+
109+
return $form;
110+
}
111+
112+
113+
public function bundleAjaxCallback(array $form, FormStateInterface $form_state) {
114+
$triggering_element = $form_state->getTriggeringElement();
115+
$parents = array_slice($triggering_element['#array_parents'], 0, -1);
116+
$form_element = NestedArray::getValue($form, $parents);
117+
return $form_element;
118+
}
119+
120+
/**
121+
* {@inheritdoc}
122+
*/
123+
public function evaluate() {
124+
$bundle_id = $this->configuration['bundle'];
125+
if (empty($bundle_id)) {
126+
return FALSE;
127+
}
128+
$field_id = $this->configuration['field'];
129+
if (empty($field_id)) {
130+
return FALSE;
131+
}
132+
/** @var OrderItemInterface $order_item */
133+
$order_item = $this->getContextValue('commerce_order_item');
134+
135+
/** @var \Drupal\commerce_product\Entity\ProductInterface $current_product */
136+
$current_product = $order_item->getPurchasedEntity()->getProduct();
137+
if ($current_product->bundle() != $bundle_id) {
138+
return FALSE;
139+
}
140+
141+
if (!$current_product->hasField($field_id)) {
142+
return FALSE;
143+
}
144+
145+
$field_type = $current_product->get($field_id)->getFieldDefinition()->getType();
146+
$target_type = NULL;
147+
if ($field_type == 'entity_reference') {
148+
$target_type = $current_product->get($field_id)->getFieldDefinition()->getFieldStorageDefinition()->getSetting('target_type');
149+
}
150+
151+
if ($target_type == 'taxonomy_term') {
152+
if ($current_product->get($field_id)->getValue() == $this->configuration[$field_id]) {
153+
return TRUE;
154+
}
155+
else {
156+
/** @var TermInterface $term */
157+
$term = \Drupal::service('entity_type.manager')
158+
->getStorage("taxonomy_term")->load($this->configuration[$field_id][0]['target_id']);
159+
$tree = \Drupal::service('entity_type.manager')
160+
->getStorage("taxonomy_term")
161+
->loadTree($term->getVocabularyId(), $term->id());
162+
$found = FALSE;
163+
foreach ($tree as $item) {
164+
if ($item->tid == $current_product->get($field_id)->getValue()[0]['target_id']) {
165+
$found = TRUE;
166+
break;
167+
}
168+
}
169+
return $found;
170+
}
171+
}
172+
elseif ($current_product->get($field_id)->getValue() != $this->configuration[$field_id]) {
173+
return FALSE;
174+
}
175+
176+
return TRUE;
177+
}
178+
179+
/**
180+
* {@inheritdoc}
181+
*/
182+
public function summary() {
183+
return $this->t('Compares the product entity.');
184+
}
185+
186+
}

0 commit comments

Comments
 (0)