Skip to content

Commit 1c07f4f

Browse files
mglamanbojanz
authored andcommitted
Issue #2863860 by mglaman: Promotions need a sort order (#696)
1 parent f488a12 commit 1c07f4f

File tree

5 files changed

+270
-5
lines changed

5 files changed

+270
-5
lines changed

modules/promotion/src/Entity/Promotion.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,21 @@ public function setEnabled($enabled) {
315315
return $this;
316316
}
317317

318+
/**
319+
* {@inheritdoc}
320+
*/
321+
public function getWeight() {
322+
return (int) $this->get('weight')->value;
323+
}
324+
325+
/**
326+
* {@inheritdoc}
327+
*/
328+
public function setWeight($weight) {
329+
$this->set('weight', $weight);
330+
return $this;
331+
}
332+
318333
/**
319334
* {@inheritdoc}
320335
*/
@@ -536,6 +551,20 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
536551
'weight' => 20,
537552
]);
538553

554+
$fields['weight'] = BaseFieldDefinition::create('integer')
555+
->setLabel(t('Weight'))
556+
->setDescription(t('The weight of this promotion in relation to others.'))
557+
->setDefaultValue(0)
558+
->setDisplayOptions('view', [
559+
'label' => 'hidden',
560+
'type' => 'integer',
561+
'weight' => 0,
562+
])
563+
->setDisplayOptions('form', [
564+
'type' => 'number',
565+
'weight' => 4,
566+
]);
567+
539568
return $fields;
540569
}
541570

@@ -564,4 +593,26 @@ public static function getDefaultEndDate() {
564593
return gmdate('Y-m-d', time() + 31536000);
565594
}
566595

596+
/**
597+
* Helper callback for uasort() to sort promotions by weight and label.
598+
*
599+
* @param \Drupal\commerce_promotion\Entity\PromotionInterface $a
600+
* The first promotion to sort.
601+
* @param \Drupal\commerce_promotion\Entity\PromotionInterface $b
602+
* The second promotion to sort.
603+
*
604+
* @return int
605+
* The comparison result for uasort().
606+
*/
607+
public static function sort(PromotionInterface $a, PromotionInterface $b) {
608+
$a_weight = $a->getWeight();
609+
$b_weight = $b->getWeight();
610+
if ($a_weight == $b_weight) {
611+
$a_label = $a->label();
612+
$b_label = $b->label();
613+
return strnatcasecmp($a_label, $b_label);
614+
}
615+
return ($a_weight < $b_weight) ? -1 : 1;
616+
}
617+
567618
}

modules/promotion/src/Entity/PromotionInterface.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,24 @@ public function isEnabled();
244244
*/
245245
public function setEnabled($enabled);
246246

247+
/**
248+
* Gets the weight.
249+
*
250+
* @return int
251+
* The weight.
252+
*/
253+
public function getWeight();
254+
255+
/**
256+
* Sets the weight.
257+
*
258+
* @param int $weight
259+
* The weight.
260+
*
261+
* @return $this
262+
*/
263+
public function setWeight($weight);
264+
247265
/**
248266
* Checks whether the promotion entity can be applied.
249267
*

modules/promotion/src/PromotionListBuilder.php

Lines changed: 146 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,171 @@
44

55
use Drupal\Core\Entity\EntityInterface;
66
use Drupal\Core\Entity\EntityListBuilder;
7+
use Drupal\Core\Form\FormInterface;
8+
use Drupal\Core\Form\FormStateInterface;
79

810
/**
9-
* Defines the list builder for promotions.
11+
* Defines the list builder for shipping methods.
1012
*/
11-
class PromotionListBuilder extends EntityListBuilder {
13+
class PromotionListBuilder extends EntityListBuilder implements FormInterface {
14+
15+
/**
16+
* The key to use for the form element containing the entities.
17+
*
18+
* @var string
19+
*/
20+
protected $entitiesKey = 'promotions';
21+
22+
/**
23+
* The entities being listed.
24+
*
25+
* @var \Drupal\commerce_promotion\Entity\PromotionInterface[]
26+
*/
27+
protected $entities = [];
28+
29+
/**
30+
* Whether tabledrag is enabled.
31+
*
32+
* @var bool
33+
*/
34+
protected $hasTableDrag = TRUE;
35+
36+
/**
37+
* The form builder.
38+
*
39+
* @var \Drupal\Core\Form\FormBuilderInterface
40+
*/
41+
protected $formBuilder;
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function getFormId() {
47+
return 'commerce_promotions';
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
public function load() {
54+
$entity_ids = $this->getEntityIds();
55+
$entities = $this->storage->loadMultiple($entity_ids);
56+
// Sort the entities using the entity class's sort() method.
57+
uasort($entities, [$this->entityType->getClass(), 'sort']);
58+
59+
return $entities;
60+
}
1261

1362
/**
1463
* {@inheritdoc}
1564
*/
1665
public function buildHeader() {
17-
$header['name'] = t('Name');
18-
$header['status'] = t('Enabled');
66+
$header['name'] = $this->t('Name');
67+
$header['status'] = $this->t('Enabled');
68+
if ($this->hasTableDrag) {
69+
$header['weight'] = $this->t('Weight');
70+
}
1971
return $header + parent::buildHeader();
2072
}
2173

2274
/**
2375
* {@inheritdoc}
2476
*/
2577
public function buildRow(EntityInterface $entity) {
78+
/** @var \Drupal\commerce_shipping\Entity\ShippingMethodInterface $entity */
79+
$row['#attributes']['class'][] = 'draggable';
80+
$row['#weight'] = $entity->getWeight();
2681
$row['name'] = $entity->label();
2782
$row['status'] = $entity->isEnabled() ? $this->t('Enabled') : $this->t('Disabled');
83+
if ($this->hasTableDrag) {
84+
$row['weight'] = [
85+
'#type' => 'weight',
86+
'#title' => $this->t('Weight for @title', ['@title' => $entity->label()]),
87+
'#title_display' => 'invisible',
88+
'#default_value' => $entity->getWeight(),
89+
'#attributes' => ['class' => ['weight']],
90+
];
91+
}
2892

2993
return $row + parent::buildRow($entity);
3094
}
3195

96+
/**
97+
* {@inheritdoc}
98+
*/
99+
public function render() {
100+
return \Drupal::formBuilder()->getForm($this);
101+
}
102+
103+
/**
104+
* {@inheritdoc}
105+
*/
106+
public function buildForm(array $form, FormStateInterface $form_state) {
107+
$this->entities = $this->load();
108+
if (count($this->entities) <= 1) {
109+
$this->hasTableDrag = FALSE;
110+
}
111+
$delta = 10;
112+
// Dynamically expand the allowed delta based on the number of entities.
113+
$count = count($this->entities);
114+
if ($count > 20) {
115+
$delta = ceil($count / 2);
116+
}
117+
118+
$form[$this->entitiesKey] = [
119+
'#type' => 'table',
120+
'#header' => $this->buildHeader(),
121+
'#empty' => $this->t('There are no @label yet.', ['@label' => $this->entityType->getPluralLabel()]),
122+
];
123+
foreach ($this->entities as $entity) {
124+
$row = $this->buildRow($entity);
125+
if (isset($row['name'])) {
126+
$row['name'] = ['#markup' => $row['name']];
127+
}
128+
if (isset($row['status'])) {
129+
$row['status'] = ['#markup' => $row['status']];
130+
}
131+
if (isset($row['weight'])) {
132+
$row['weight']['#delta'] = $delta;
133+
}
134+
$form[$this->entitiesKey][$entity->id()] = $row;
135+
}
136+
137+
if ($this->hasTableDrag) {
138+
$form[$this->entitiesKey]['#tabledrag'][] = [
139+
'action' => 'order',
140+
'relationship' => 'sibling',
141+
'group' => 'weight',
142+
];
143+
$form['actions']['#type'] = 'actions';
144+
$form['actions']['submit'] = [
145+
'#type' => 'submit',
146+
'#value' => t('Save'),
147+
'#button_type' => 'primary',
148+
];
149+
}
150+
151+
return $form;
152+
}
153+
154+
/**
155+
* {@inheritdoc}
156+
*/
157+
public function validateForm(array &$form, FormStateInterface $form_state) {
158+
// No validation.
159+
}
160+
161+
/**
162+
* {@inheritdoc}
163+
*/
164+
public function submitForm(array &$form, FormStateInterface $form_state) {
165+
foreach ($form_state->getValue($this->entitiesKey) as $id => $value) {
166+
if (isset($this->entities[$id]) && $this->entities[$id]->getWeight() != $value['weight']) {
167+
// Save entity only when its weight was changed.
168+
$this->entities[$id]->setWeight($value['weight']);
169+
$this->entities[$id]->save();
170+
}
171+
}
172+
}
173+
32174
}

modules/promotion/src/PromotionStorage.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ protected function buildLoadQuery(OrderTypeInterface $order_type, StoreInterface
6868
->condition('start_date', gmdate('Y-m-d'), '<=')
6969
->condition('status', TRUE)
7070
->condition($or_condition);
71+
$query->sort('weight', 'ASC');
7172
return $query;
7273
}
7374

modules/promotion/tests/src/Kernel/PromotionStorageTest.php

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function testLoadValid() {
120120
}
121121

122122
/**
123-
* Tests that promotions with coupons do not get loaded PromotionStorage::loadValid.
123+
* Tests that promotions with coupons do not get loaded.
124124
*/
125125
public function testValidWithCoupons() {
126126
$order_type = OrderType::load('default');
@@ -198,4 +198,57 @@ public function testLoadByCoupon() {
198198
$this->assertEquals($promotion1->label(), $valid_promotion->label());
199199
}
200200

201+
/**
202+
* Tests that promotions are loaded by weight.
203+
*
204+
* @group debug
205+
*/
206+
public function testWeight() {
207+
$order_type = OrderType::load('default');
208+
209+
$promotion1 = Promotion::create([
210+
'name' => 'Promotion 1',
211+
'order_types' => [$order_type],
212+
'stores' => [$this->store->id()],
213+
'status' => TRUE,
214+
'weight' => 4,
215+
]);
216+
$this->assertEquals(SAVED_NEW, $promotion1->save());
217+
$promotion2 = Promotion::create([
218+
'name' => 'Promotion 2',
219+
'order_types' => [$order_type],
220+
'stores' => [$this->store->id()],
221+
'status' => TRUE,
222+
'weight' => 2,
223+
]);
224+
$this->assertEquals(SAVED_NEW, $promotion2->save());
225+
$promotion3 = Promotion::create([
226+
'name' => 'Promotion 3',
227+
'order_types' => [$order_type],
228+
'stores' => [$this->store->id()],
229+
'status' => TRUE,
230+
'weight' => -10,
231+
]);
232+
$this->assertEquals(SAVED_NEW, $promotion3->save());
233+
$promotion4 = Promotion::create([
234+
'name' => 'Promotion 4',
235+
'order_types' => [$order_type],
236+
'stores' => [$this->store->id()],
237+
'status' => TRUE,
238+
'weight' => 1,
239+
]);
240+
$this->assertEquals(SAVED_NEW, $promotion4->save());
241+
242+
$promotions = $this->promotionStorage->loadValid($order_type, $this->store);
243+
244+
$promotion = array_shift($promotions);
245+
$this->assertEquals($promotion3->label(), $promotion->label());
246+
$promotion = array_shift($promotions);
247+
$this->assertEquals($promotion4->label(), $promotion->label());
248+
$promotion = array_shift($promotions);
249+
$this->assertEquals($promotion2->label(), $promotion->label());
250+
$promotion = array_shift($promotions);
251+
$this->assertEquals($promotion1->label(), $promotion->label());
252+
}
253+
201254
}

0 commit comments

Comments
 (0)