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,25 @@ 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 ();
159
163
160
- $ this ->addWebsiteGroupDateFilter ($ websiteId , $ customerGroupId , $ now );
161
- $ select = $ this ->getSelect ();
164
+ $ this ->prepareSelect ($ websiteId , $ customerGroupId , $ now );
162
165
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
- );
166
+ $ noCouponRules = $ this ->getNoCouponCodeSelect ();
167
+
168
+ if ($ couponCode ) {
169
+ $ couponRules = $ this ->getCouponCodeSelect ($ couponCode );
170
+
171
+ $ allAllowedRules = $ this ->getConnection ()->select ();
172
+ $ allAllowedRules ->union ([$ noCouponRules , $ couponRules ], \Zend_Db_Select::SQL_UNION_ALL );
173
+
174
+ $ wrapper = $ this ->getConnection ()->select ();
175
+ $ wrapper ->from ($ allAllowedRules );
176
+
177
+ $ this ->_select = $ wrapper ;
176
178
} else {
177
- $ this ->addFieldToFilter (
178
- 'main_table.coupon_type ' ,
179
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON
180
- );
179
+ $ this ->_select = $ noCouponRules ;
181
180
}
181
+
182
182
$ this ->setOrder ('sort_order ' , self ::SORT_ORDER_ASC );
183
183
$ this ->setFlag ('validation_filter ' , true );
184
184
}
@@ -187,72 +187,96 @@ public function setValidationFilter(
187
187
}
188
188
189
189
/**
190
- * Get rules ids related to coupon code
190
+ * Recreate the default select object for specific needs of salesrule evaluation with coupon codes.
191
191
*
192
- * @param string $couponCode
193
- * @return array
192
+ * @param $websiteId
193
+ * @param $customerGroupId
194
+ * @param $now
194
195
*/
195
- private function getCouponRelatedRuleIds ( string $ couponCode ): array
196
+ private function prepareSelect ( $ websiteId , $ customerGroupId , $ now )
196
197
{
197
- $ connection = $ this ->getConnection ();
198
- $ select = $ connection ->select ()->from (
199
- ['main_table ' => $ this ->getTable ('salesrule ' )],
200
- 'rule_id '
198
+ $ this ->getSelect ()->reset ();
199
+ parent ::_initSelect ();
200
+
201
+ $ this ->addWebsiteGroupDateFilter ($ websiteId , $ customerGroupId , $ now );
202
+ }
203
+
204
+ /**
205
+ * Return select object to determine all active rules not needing a coupon code.
206
+ *
207
+ * @return Select
208
+ */
209
+ private function getNoCouponCodeSelect ()
210
+ {
211
+ $ noCouponSelect = clone $ this ->getSelect ();
212
+
213
+ $ noCouponSelect ->where (
214
+ 'main_table.coupon_type = ? ' ,
215
+ Rule::COUPON_TYPE_NO_COUPON
201
216
);
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
- )
217
+
218
+ $ noCouponSelect ->columns ([Coupon::KEY_CODE => new \Zend_Db_Expr ('NULL ' )]);
219
+
220
+ return $ noCouponSelect ;
221
+ }
222
+
223
+ /**
224
+ * Determine all active rules that are valid for the given coupon code.
225
+ *
226
+ * @param $couponCode
227
+ * @return Select
228
+ */
229
+ private function getCouponCodeSelect ($ couponCode )
230
+ {
231
+ $ couponSelect = clone $ this ->getSelect ();
232
+
233
+ $ this ->joinCouponTable ($ couponCode , $ couponSelect );
234
+
235
+ $ notExpired = $ this ->getConnection ()->quoteInto (
236
+ '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?) ' ,
237
+ $ this ->_date ->date ()->format ('Y-m-d ' )
209
238
);
210
239
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 ,
240
+ $ isAutogenerated =
241
+ $ this ->getConnection ()->quoteInto ('main_table.coupon_type = ? ' , Rule::COUPON_TYPE_AUTO )
242
+ . ' AND ' .
243
+ $ this ->getConnection ()->quoteInto ('rule_coupons.type = ? ' , CouponInterface::TYPE_GENERATED );
244
+
245
+ $ isValidSpecific =
246
+ $ this ->getConnection ()->quoteInto ('(main_table.coupon_type = ?) ' , Rule::COUPON_TYPE_SPECIFIC )
247
+ . ' AND ( ' .
248
+ '(main_table.use_auto_generation = 1 AND rule_coupons.type = 1) '
249
+ . ' OR ' .
250
+ '(main_table.use_auto_generation = 0 AND rule_coupons.type = 0) '
251
+ . ') ' ;
252
+
253
+ $ couponSelect ->where (
254
+ "$ notExpired AND ( $ isAutogenerated OR $ isValidSpecific) " ,
250
255
null ,
251
256
Select::TYPE_CONDITION
252
257
);
253
- $ select ->group ('main_table.rule_id ' );
254
258
255
- return $ connection ->fetchCol ($ select );
259
+ return $ couponSelect ;
260
+ }
261
+
262
+ /**
263
+ * @param $couponCode
264
+ * @param Select $couponSelect
265
+ */
266
+ private function joinCouponTable ($ couponCode , Select $ couponSelect )
267
+ {
268
+ $ couponJoinCondition =
269
+ 'main_table.rule_id = rule_coupons.rule_id '
270
+ . ' AND ' .
271
+ $ this ->getConnection ()->quoteInto ('main_table.coupon_type <> ? ' , Rule::COUPON_TYPE_NO_COUPON )
272
+ . ' AND ' .
273
+ $ this ->getConnection ()->quoteInto ('rule_coupons.code = ? ' , $ couponCode );
274
+
275
+ $ couponSelect ->joinInner (
276
+ ['rule_coupons ' => $ this ->getTable ('salesrule_coupon ' )],
277
+ $ couponJoinCondition ,
278
+ [Coupon::KEY_CODE ]
279
+ );
256
280
}
257
281
258
282
/**
0 commit comments