9
9
use Magento \Framework \DB \Select ;
10
10
use Magento \Framework \Serialize \Serializer \Json ;
11
11
use Magento \Quote \Model \Quote \Address ;
12
+ use Magento \SalesRule \Api \Data \CouponInterface ;
13
+ use Magento \SalesRule \Model \Coupon ;
14
+ use Magento \SalesRule \Model \Rule ;
12
15
13
16
/**
14
17
* Sales Rules resource collection model.
@@ -107,12 +110,15 @@ protected function mapAssociatedEntities($entityType, $objectField)
107
110
108
111
$ associatedEntities = $ this ->getConnection ()->fetchAll ($ select );
109
112
110
- array_map (function ($ associatedEntity ) use ($ entityInfo , $ ruleIdField , $ objectField ) {
111
- $ item = $ this ->getItemByColumnValue ($ ruleIdField , $ associatedEntity [$ ruleIdField ]);
112
- $ itemAssociatedValue = $ item ->getData ($ objectField ) === null ? [] : $ item ->getData ($ objectField );
113
- $ itemAssociatedValue [] = $ associatedEntity [$ entityInfo ['entity_id_field ' ]];
114
- $ item ->setData ($ objectField , $ itemAssociatedValue );
115
- }, $ associatedEntities );
113
+ array_map (
114
+ function ($ associatedEntity ) use ($ entityInfo , $ ruleIdField , $ objectField ) {
115
+ $ item = $ this ->getItemByColumnValue ($ ruleIdField , $ associatedEntity [$ ruleIdField ]);
116
+ $ itemAssociatedValue = $ item ->getData ($ objectField ) ?? [];
117
+ $ itemAssociatedValue [] = $ associatedEntity [$ entityInfo ['entity_id_field ' ]];
118
+ $ item ->setData ($ objectField , $ itemAssociatedValue );
119
+ },
120
+ $ associatedEntities
121
+ );
116
122
}
117
123
118
124
/**
@@ -141,6 +147,7 @@ protected function _afterLoad()
141
147
* @param string $couponCode
142
148
* @param string|null $now
143
149
* @param Address $address allow extensions to further filter out rules based on quote address
150
+ * @throws \Zend_Db_Select_Exception
144
151
* @use $this->addWebsiteGroupDateFilter()
145
152
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
146
153
* @return $this
@@ -153,32 +160,24 @@ public function setValidationFilter(
153
160
Address $ address = null
154
161
) {
155
162
if (!$ this ->getFlag ('validation_filter ' )) {
156
- /* We need to overwrite joinLeft if coupon is applied */
157
- $ this ->getSelect ()->reset ();
158
- parent ::_initSelect ();
163
+ $ this ->prepareSelect ($ websiteId , $ customerGroupId , $ now );
159
164
160
- $ this ->addWebsiteGroupDateFilter ($ websiteId , $ customerGroupId , $ now );
161
- $ select = $ this ->getSelect ();
165
+ $ noCouponRules = $ this ->getNoCouponCodeSelect ();
162
166
163
- $ connection = $ this ->getConnection ();
164
- if (strlen ($ couponCode )) {
165
- $ noCouponWhereCondition = $ connection ->quoteInto (
166
- 'main_table.coupon_type = ? ' ,
167
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON
168
- );
169
- $ relatedRulesIds = $ this ->getCouponRelatedRuleIds ($ couponCode );
170
-
171
- $ select ->where (
172
- $ noCouponWhereCondition . ' OR main_table.rule_id IN (?) ' ,
173
- $ relatedRulesIds ,
174
- Select::TYPE_CONDITION
175
- );
167
+ if ($ couponCode ) {
168
+ $ couponRules = $ this ->getCouponCodeSelect ($ couponCode );
169
+
170
+ $ allAllowedRules = $ this ->getConnection ()->select ();
171
+ $ allAllowedRules ->union ([$ noCouponRules , $ couponRules ], Select::SQL_UNION_ALL );
172
+
173
+ $ wrapper = $ this ->getConnection ()->select ();
174
+ $ wrapper ->from ($ allAllowedRules );
175
+
176
+ $ this ->_select = $ wrapper ;
176
177
} else {
177
- $ this ->addFieldToFilter (
178
- 'main_table.coupon_type ' ,
179
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON
180
- );
178
+ $ this ->_select = $ noCouponRules ;
181
179
}
180
+
182
181
$ this ->setOrder ('sort_order ' , self ::SORT_ORDER_ASC );
183
182
$ this ->setFlag ('validation_filter ' , true );
184
183
}
@@ -187,72 +186,98 @@ public function setValidationFilter(
187
186
}
188
187
189
188
/**
190
- * Get rules ids related to coupon code
189
+ * Recreate the default select object for specific needs of salesrule evaluation with coupon codes.
191
190
*
192
- * @param string $couponCode
193
- * @return array
191
+ * @param int $websiteId
192
+ * @param int $customerGroupId
193
+ * @param string $now
194
194
*/
195
- private function getCouponRelatedRuleIds ( string $ couponCode ): array
195
+ private function prepareSelect ( $ websiteId , $ customerGroupId , $ now )
196
196
{
197
- $ connection = $ this ->getConnection ();
198
- $ select = $ connection ->select ()->from (
199
- ['main_table ' => $ this ->getTable ('salesrule ' )],
200
- 'rule_id '
197
+ $ this ->getSelect ()->reset ();
198
+ parent ::_initSelect ();
199
+
200
+ $ this ->addWebsiteGroupDateFilter ($ websiteId , $ customerGroupId , $ now );
201
+ }
202
+
203
+ /**
204
+ * Return select object to determine all active rules not needing a coupon code.
205
+ *
206
+ * @return Select
207
+ */
208
+ private function getNoCouponCodeSelect ()
209
+ {
210
+ $ noCouponSelect = clone $ this ->getSelect ();
211
+
212
+ $ noCouponSelect ->where (
213
+ 'main_table.coupon_type = ? ' ,
214
+ Rule::COUPON_TYPE_NO_COUPON
201
215
);
202
- $ select ->joinLeft (
203
- ['rule_coupons ' => $ this ->getTable ('salesrule_coupon ' )],
204
- $ connection ->quoteInto (
205
- 'main_table.rule_id = rule_coupons.rule_id AND main_table.coupon_type != ? ' ,
206
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON ,
207
- null
208
- )
216
+
217
+ $ noCouponSelect ->columns ([Coupon::KEY_CODE => new \Zend_Db_Expr ('NULL ' )]);
218
+
219
+ return $ noCouponSelect ;
220
+ }
221
+
222
+ /**
223
+ * Determine all active rules that are valid for the given coupon code.
224
+ *
225
+ * @param string $couponCode
226
+ * @return Select
227
+ */
228
+ private function getCouponCodeSelect ($ couponCode )
229
+ {
230
+ $ couponSelect = clone $ this ->getSelect ();
231
+
232
+ $ this ->joinCouponTable ($ couponCode , $ couponSelect );
233
+
234
+ $ notExpired = $ this ->getConnection ()->quoteInto (
235
+ '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?) ' ,
236
+ $ this ->_date ->date ()->format ('Y-m-d ' )
209
237
);
210
238
211
- $ autoGeneratedCouponCondition = [
212
- $ connection ->quoteInto (
213
- "main_table.coupon_type = ? " ,
214
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_AUTO
215
- ),
216
- $ connection ->quoteInto (
217
- "rule_coupons.type = ? " ,
218
- \Magento \SalesRule \Api \Data \CouponInterface::TYPE_GENERATED
219
- ),
220
- ];
221
-
222
- $ orWhereConditions = [
223
- "( " . implode ($ autoGeneratedCouponCondition , " AND " ) . ") " ,
224
- $ connection ->quoteInto (
225
- '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1) ' ,
226
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_SPECIFIC
227
- ),
228
- $ connection ->quoteInto (
229
- '(main_table.coupon_type = ? AND main_table.use_auto_generation = 0 AND rule_coupons.type = 0) ' ,
230
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_SPECIFIC
231
- ),
232
- ];
233
-
234
- $ andWhereConditions = [
235
- $ connection ->quoteInto (
236
- 'rule_coupons.code = ? ' ,
237
- $ couponCode
238
- ),
239
- $ connection ->quoteInto (
240
- '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?) ' ,
241
- $ this ->_date ->date ()->format ('Y-m-d ' )
242
- ),
243
- ];
244
-
245
- $ orWhereCondition = implode (' OR ' , $ orWhereConditions );
246
- $ andWhereCondition = implode (' AND ' , $ andWhereConditions );
247
-
248
- $ select ->where (
249
- '( ' . $ orWhereCondition . ') AND ' . $ andWhereCondition ,
239
+ $ isAutogenerated =
240
+ $ this ->getConnection ()->quoteInto ('main_table.coupon_type = ? ' , Rule::COUPON_TYPE_AUTO )
241
+ . ' AND ' .
242
+ $ this ->getConnection ()->quoteInto ('rule_coupons.type = ? ' , CouponInterface::TYPE_GENERATED );
243
+
244
+ $ isValidSpecific =
245
+ $ this ->getConnection ()->quoteInto ('(main_table.coupon_type = ?) ' , Rule::COUPON_TYPE_SPECIFIC )
246
+ . ' AND ( ' .
247
+ '(main_table.use_auto_generation = 1 AND rule_coupons.type = 1) '
248
+ . ' OR ' .
249
+ '(main_table.use_auto_generation = 0 AND rule_coupons.type = 0) '
250
+ . ') ' ;
251
+
252
+ $ couponSelect ->where (
253
+ "$ notExpired AND ( $ isAutogenerated OR $ isValidSpecific) " ,
250
254
null ,
251
255
Select::TYPE_CONDITION
252
256
);
253
- $ select ->group ('main_table.rule_id ' );
254
257
255
- return $ connection ->fetchCol ($ select );
258
+ return $ couponSelect ;
259
+ }
260
+
261
+ /**
262
+ * Join coupong table to select.
263
+ *
264
+ * @param string $couponCode
265
+ * @param Select $couponSelect
266
+ */
267
+ private function joinCouponTable ($ couponCode , Select $ couponSelect )
268
+ {
269
+ $ couponJoinCondition =
270
+ 'main_table.rule_id = rule_coupons.rule_id '
271
+ . ' AND ' .
272
+ $ this ->getConnection ()->quoteInto ('main_table.coupon_type <> ? ' , Rule::COUPON_TYPE_NO_COUPON )
273
+ . ' AND ' .
274
+ $ this ->getConnection ()->quoteInto ('rule_coupons.code = ? ' , $ couponCode );
275
+
276
+ $ couponSelect ->joinInner (
277
+ ['rule_coupons ' => $ this ->getTable ('salesrule_coupon ' )],
278
+ $ couponJoinCondition ,
279
+ [Coupon::KEY_CODE ]
280
+ );
256
281
}
257
282
258
283
/**
0 commit comments