PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.36.0
MailPoet – Newsletters, Email Marketing, and Automation v5.36.0
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Segments / DynamicSegments / FilterDataMapper.php
mailpoet / lib / Segments / DynamicSegments Last commit date
Exceptions 3 months ago Filters 1 day ago DynamicSegmentFilterRepository.php 4 years ago DynamicSegmentsListingRepository.php 1 month ago FilterDataMapper.php 1 day ago FilterFactory.php 1 day ago FilterHandler.php 3 months ago SegmentSaveController.php 2 years ago index.php 3 years ago
FilterDataMapper.php
776 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Segments\DynamicSegments;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\DynamicSegmentFilterData;
9 use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException;
10 use MailPoet\Segments\DynamicSegments\Filters\AutomationsEvents;
11 use MailPoet\Segments\DynamicSegments\Filters\DateFilterHelper;
12 use MailPoet\Segments\DynamicSegments\Filters\EmailAction;
13 use MailPoet\Segments\DynamicSegments\Filters\EmailActionClickAny;
14 use MailPoet\Segments\DynamicSegments\Filters\EmailOpensAbsoluteCountAction;
15 use MailPoet\Segments\DynamicSegments\Filters\EmailsReceived;
16 use MailPoet\Segments\DynamicSegments\Filters\FilterHelper;
17 use MailPoet\Segments\DynamicSegments\Filters\MailPoetCustomFields;
18 use MailPoet\Segments\DynamicSegments\Filters\NumberOfClicks;
19 use MailPoet\Segments\DynamicSegments\Filters\SubscriberDateField;
20 use MailPoet\Segments\DynamicSegments\Filters\SubscriberScore;
21 use MailPoet\Segments\DynamicSegments\Filters\SubscriberSegment;
22 use MailPoet\Segments\DynamicSegments\Filters\SubscriberSubscribedViaForm;
23 use MailPoet\Segments\DynamicSegments\Filters\SubscriberTag;
24 use MailPoet\Segments\DynamicSegments\Filters\SubscriberTextField;
25 use MailPoet\Segments\DynamicSegments\Filters\SubscriberTrackingConsent;
26 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceAverageSpent;
27 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceCategory;
28 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceCountry;
29 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceCustomerTextField;
30 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceFirstOrder;
31 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceMembership;
32 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceNumberOfOrders;
33 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceNumberOfReviews;
34 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceProduct;
35 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceProductVariation;
36 use MailPoet\Segments\DynamicSegments\Filters\WooCommercePurchaseDate;
37 use MailPoet\Segments\DynamicSegments\Filters\WooCommercePurchasedWithAttribute;
38 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSingleOrderValue;
39 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
40 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTag;
41 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
42 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedCouponCode;
43 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod;
44 use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedShippingMethod;
45 use MailPoet\WP\Functions as WPFunctions;
46
47 class FilterDataMapper {
48 public const MAX_GROUPS = 5;
49 public const MAX_FILTERS_PER_GROUP = 10;
50
51 private WPFunctions $wp;
52
53 private DateFilterHelper $dateFilterHelper;
54
55 private WooCommerceNumberOfReviews $wooCommerceNumberOfReviews;
56
57 private FilterHelper $filterHelper;
58
59 private WooCommerceUsedCouponCode $wooCommerceUsedCouponCode;
60
61 private WooCommerceTag $wooCommerceTag;
62
63 private WooCommercePurchasedWithAttribute $wooCommercePurchasedWithAttribute;
64
65 public function __construct(
66 WPFunctions $wp,
67 DateFilterHelper $dateFilterHelper,
68 FilterHelper $filterHelper,
69 WooCommerceNumberOfReviews $wooCommerceNumberOfReviews,
70 WooCommerceUsedCouponCode $wooCommerceUsedCouponCode,
71 WooCommercePurchasedWithAttribute $wooCommercePurchasedWithAttribute,
72 WooCommerceTag $wooCommerceTag
73 ) {
74 $this->wp = $wp;
75 $this->dateFilterHelper = $dateFilterHelper;
76 $this->filterHelper = $filterHelper;
77 $this->wooCommerceNumberOfReviews = $wooCommerceNumberOfReviews;
78 $this->wooCommerceUsedCouponCode = $wooCommerceUsedCouponCode;
79 $this->wooCommercePurchasedWithAttribute = $wooCommercePurchasedWithAttribute;
80 $this->wooCommerceTag = $wooCommerceTag;
81 }
82
83 /**
84 * @param array $data
85 * @return DynamicSegmentFilterData[]
86 */
87 public function map(array $data = []): array {
88 if (!isset($data['filters']) || count($data['filters'] ?? []) < 1) {
89 throw new InvalidFilterException('Filters are missing', InvalidFilterException::MISSING_FILTER);
90 }
91 $this->validateGroups($data['filters'], $data['filters_connect'] ?? null);
92 $processFilter = function ($filter, $data) {
93 $filter['connect'] = $data['filters_connect'] ?? DynamicSegmentFilterData::CONNECT_TYPE_AND;
94 return $this->withGroupingParams($this->createFilter($filter), $filter);
95 };
96 $wpFilterName = 'mailpoet_dynamic_segments_filters_map';
97 if ($this->wp->hasFilter($wpFilterName)) {
98 $filtered = $this->wp->applyFilters($wpFilterName, $data, $processFilter);
99 if (is_array($filtered)) {
100 return array_values(array_filter($filtered, function ($f) {
101 return $f instanceof DynamicSegmentFilterData;
102 }));
103 }
104 }
105 $filter = reset($data['filters']);
106 return [$processFilter($filter, $data)];
107 }
108
109 /**
110 * @throws InvalidFilterException
111 */
112 private function validateGroups(array $filters, ?string $filtersConnect): void {
113 $groupCounts = [];
114 foreach ($filters as $filter) {
115 if (!isset($filter['group_id'])) {
116 continue;
117 }
118 $groupId = (int)$filter['group_id'];
119 $groupCounts[$groupId] = ($groupCounts[$groupId] ?? 0) + 1;
120 }
121 if (count($groupCounts) > self::MAX_GROUPS) {
122 throw new InvalidFilterException(
123 'Too many filter groups (max ' . self::MAX_GROUPS . ')',
124 InvalidFilterException::TOO_MANY_GROUPS
125 );
126 }
127 foreach ($groupCounts as $count) {
128 if ($count > self::MAX_FILTERS_PER_GROUP) {
129 throw new InvalidFilterException(
130 'Too many filters in a group (max ' . self::MAX_FILTERS_PER_GROUP . ')',
131 InvalidFilterException::TOO_MANY_FILTERS_PER_GROUP
132 );
133 }
134 }
135 if (count($groupCounts) > 1 && $filtersConnect === DynamicSegmentFilterData::CONNECT_TYPE_NONE) {
136 throw new InvalidFilterException(
137 'Outer connector must be and/or for grouped segments',
138 InvalidFilterException::INVALID_OUTER_CONNECTOR
139 );
140 }
141 }
142
143 /**
144 * Stamps group_id/group_operator from the raw filter row onto the constructed
145 * filter data so the entity can group filters at query time.
146 */
147 private function withGroupingParams(DynamicSegmentFilterData $filterData, array $rawFilter): DynamicSegmentFilterData {
148 if (!isset($rawFilter['group_id'])) {
149 return $filterData;
150 }
151 $existingData = $filterData->getData() ?? [];
152 $existingData['group_id'] = (int)$rawFilter['group_id'];
153 $existingData['group_operator'] = $this->normalizeGroupOperator($rawFilter['group_operator'] ?? null);
154 return new DynamicSegmentFilterData(
155 (string)$filterData->getFilterType(),
156 (string)$filterData->getAction(),
157 $existingData
158 );
159 }
160
161 private function normalizeGroupOperator($value): string {
162 if (
163 $value === DynamicSegmentFilterData::CONNECT_TYPE_AND
164 || $value === DynamicSegmentFilterData::CONNECT_TYPE_OR
165 || $value === DynamicSegmentFilterData::CONNECT_TYPE_NONE
166 ) {
167 return $value;
168 }
169 return DynamicSegmentFilterData::CONNECT_TYPE_AND;
170 }
171
172 private function createFilter(array $filterData): DynamicSegmentFilterData {
173 if (isset($filterData['days']) && !isset($filterData['timeframe'])) {
174 // Backwards compatibility for filters created before time period component had "over all time" option
175 $filterData['timeframe'] = DynamicSegmentFilterData::TIMEFRAME_IN_THE_LAST;
176 }
177 switch ($this->getSegmentType($filterData)) {
178 case DynamicSegmentFilterData::TYPE_AUTOMATIONS:
179 return $this->createAutomations($filterData);
180 case DynamicSegmentFilterData::TYPE_USER_ROLE:
181 return $this->createSubscriber($filterData);
182 case DynamicSegmentFilterData::TYPE_EMAIL:
183 return $this->createEmail($filterData);
184 case DynamicSegmentFilterData::TYPE_WOOCOMMERCE:
185 return $this->createWooCommerce($filterData);
186 case DynamicSegmentFilterData::TYPE_WOOCOMMERCE_MEMBERSHIP:
187 return $this->createWooCommerceMembership($filterData);
188 case DynamicSegmentFilterData::TYPE_WOOCOMMERCE_SUBSCRIPTION:
189 return $this->createWooCommerceSubscription($filterData);
190 default:
191 throw new InvalidFilterException('Invalid type', InvalidFilterException::INVALID_TYPE);
192 }
193 }
194
195 /**
196 * @throws InvalidFilterException
197 */
198 private function getSegmentType(array $data): string {
199 if (!isset($data['segmentType'])) {
200 throw new InvalidFilterException('Segment type is not set', InvalidFilterException::MISSING_TYPE);
201 }
202 return $data['segmentType'];
203 }
204
205 private function createAutomations(array $data): DynamicSegmentFilterData {
206 if (empty($data['action'])) {
207 throw new InvalidFilterException('Missing automations filter action', InvalidFilterException::MISSING_ACTION);
208 }
209
210 if (in_array($data['action'], AutomationsEvents::SUPPORTED_ACTIONS)) {
211 if (
212 !isset($data['operator']) || !in_array($data['operator'], [
213 DynamicSegmentFilterData::OPERATOR_ANY,
214 DynamicSegmentFilterData::OPERATOR_ALL,
215 DynamicSegmentFilterData::OPERATOR_NONE,
216 ])
217 ) {
218 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
219 }
220 if (
221 !isset($data['automation_ids'])
222 || !is_array($data['automation_ids'])
223 || count($data['automation_ids']) < 1
224 ) {
225 throw new InvalidFilterException('Missing automation IDs', InvalidFilterException::MISSING_VALUE);
226 }
227 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_AUTOMATIONS, $data['action'], [
228 'action' => $data['action'],
229 'automation_ids' => $data['automation_ids'],
230 'operator' => $data['operator'],
231 'connect' => $data['connect'],
232 ]);
233 }
234
235 throw new InvalidFilterException('Unknown automations action', InvalidFilterException::MISSING_ACTION);
236 }
237
238 private function createSubscriber(array $data): DynamicSegmentFilterData {
239 if (empty($data['action'])) {
240 $data['action'] = DynamicSegmentFilterData::TYPE_USER_ROLE;
241 }
242 if ($data['action'] === SubscriberScore::TYPE) {
243 if (!isset($data['value'])) {
244 throw new InvalidFilterException('Missing engagement score value', InvalidFilterException::MISSING_VALUE);
245 }
246 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
247 'value' => $data['value'],
248 'operator' => $data['operator'] ?? SubscriberScore::HIGHER_THAN,
249 'connect' => $data['connect'],
250 ]);
251 }
252 if ($data['action'] === SubscriberSegment::TYPE) {
253 if (empty($data['segments'])) {
254 throw new InvalidFilterException('Missing segments', InvalidFilterException::MISSING_VALUE);
255 }
256 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
257 'segments' => array_map(function ($segmentId) {
258 return intval($segmentId);
259 }, $data['segments']),
260 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
261 'connect' => $data['connect'],
262 ]);
263 }
264 if ($data['action'] === MailPoetCustomFields::TYPE) {
265 if (empty($data['custom_field_id'])) {
266 throw new InvalidFilterException('Missing custom field id', InvalidFilterException::MISSING_VALUE);
267 }
268 if (empty($data['custom_field_type'])) {
269 throw new InvalidFilterException('Missing custom field type', InvalidFilterException::MISSING_VALUE);
270 }
271 if (!isset($data['value'])) {
272 throw new InvalidFilterException('Missing value', InvalidFilterException::MISSING_VALUE);
273 }
274 $filterData = [
275 'value' => $data['value'],
276 'custom_field_id' => $data['custom_field_id'],
277 'custom_field_type' => $data['custom_field_type'],
278 'connect' => $data['connect'],
279 ];
280 if (!empty($data['date_type'])) {
281 $filterData['date_type'] = $data['date_type'];
282 }
283 if (!empty($data['operator'])) {
284 $filterData['operator'] = $data['operator'];
285 }
286 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], $filterData);
287 }
288 if ($data['action'] === SubscriberTag::TYPE) {
289 if (empty($data['tags'])) {
290 throw new InvalidFilterException('Missing tags', InvalidFilterException::MISSING_VALUE);
291 }
292 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
293 'tags' => array_map(function ($tagId) {
294 return intval($tagId);
295 }, $data['tags']),
296 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
297 'connect' => $data['connect'],
298 ]);
299 }
300 if ($data['action'] === SubscriberSubscribedViaForm::TYPE) {
301 if (!isset($data['form_ids']) || empty($data['form_ids'])) {
302 throw new InvalidFilterException('Missing at least one form ID', InvalidFilterException::MISSING_VALUE);
303 }
304 if (!isset($data['operator']) || !in_array($data['operator'], [DynamicSegmentFilterData::OPERATOR_ANY, DynamicSegmentFilterData::OPERATOR_NONE])) {
305 throw new InvalidFilterException('Missing valid operator', InvalidFilterException::MISSING_VALUE);
306 }
307 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
308 'form_ids' => array_map(function($formId) {
309 return intval($formId);
310 }, $data['form_ids']),
311 'operator' => $data['operator'],
312 'connect' => $data['connect'],
313 ]);
314 }
315 if ($data['action'] === SubscriberTrackingConsent::TYPE) {
316 if (!isset($data['value']) || !in_array($data['value'], SubscriberTrackingConsent::VALID_VALUES, true)) {
317 throw new InvalidFilterException('Missing valid tracking consent value', InvalidFilterException::MISSING_VALUE);
318 }
319 if (!isset($data['operator']) || !in_array($data['operator'], SubscriberTrackingConsent::VALID_OPERATORS, true)) {
320 throw new InvalidFilterException('Missing valid operator', InvalidFilterException::MISSING_OPERATOR);
321 }
322 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
323 'value' => $data['value'],
324 'operator' => $data['operator'],
325 'connect' => $data['connect'],
326 ]);
327 }
328 if (in_array($data['action'], SubscriberTextField::TYPES)) {
329 if (empty($data['value'])) {
330 throw new InvalidFilterException('Missing value', InvalidFilterException::MISSING_VALUE);
331 }
332 if (empty($data['operator'])) {
333 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
334 }
335 if (!in_array($data['operator'], DynamicSegmentFilterData::TEXT_FIELD_OPERATORS)) {
336 throw new InvalidFilterException('Invalid operator', InvalidFilterException::MISSING_OPERATOR);
337 }
338 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
339 'value' => $data['value'],
340 'operator' => $data['operator'],
341 'action' => $data['action'],
342 'connect' => $data['connect'],
343 ]);
344 }
345 if (in_array($data['action'], SubscriberDateField::TYPES)) {
346 if (empty($data['value'])) {
347 throw new InvalidFilterException('Missing date value', InvalidFilterException::MISSING_VALUE);
348 }
349 if (empty($data['operator'])) {
350 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
351 }
352 if (!in_array($data['operator'], $this->dateFilterHelper->getValidOperators())) {
353 throw new InvalidFilterException('Invalid operator', InvalidFilterException::MISSING_OPERATOR);
354 }
355 $filterData = [
356 'value' => $data['value'],
357 'operator' => $data['operator'],
358 'connect' => $data['connect'],
359 ];
360 if ($data['operator'] === DateFilterHelper::BETWEEN) {
361 $betweenData = [
362 'timeframe' => DynamicSegmentFilterData::TIMEFRAME_BETWEEN,
363 'value' => $filterData['value'],
364 'value2' => $data['value2'] ?? null,
365 ];
366 if (is_string($betweenData['value']) && is_string($betweenData['value2'])) {
367 [$betweenData['value'], $betweenData['value2']] = $this->orderDateRange($betweenData['value'], $betweenData['value2']);
368 }
369 try {
370 $this->filterHelper->validateDaysPeriodData($betweenData);
371 } catch (InvalidFilterException $e) {
372 throw $e;
373 } catch (\Throwable $e) {
374 throw new InvalidFilterException('Invalid date value', InvalidFilterException::INVALID_DATE_VALUE);
375 }
376 $filterData['value'] = $betweenData['value'];
377 $filterData['value2'] = $betweenData['value2'];
378 } elseif (isset($data['value2'])) {
379 $filterData['value2'] = $data['value2'];
380 }
381 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], $filterData);
382 }
383 if (empty($data['wordpressRole'])) {
384 throw new InvalidFilterException('Missing role', InvalidFilterException::MISSING_ROLE);
385 }
386 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
387 'wordpressRole' => $data['wordpressRole'],
388 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
389 'connect' => $data['connect'],
390 ]);
391 }
392
393 /**
394 * @throws InvalidFilterException
395 */
396 private function createEmail(array $data): DynamicSegmentFilterData {
397 if (empty($data['action'])) {
398 throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
399 }
400 if (!in_array($data['action'], EmailAction::ALLOWED_ACTIONS)) {
401 throw new InvalidFilterException('Invalid email action', InvalidFilterException::INVALID_EMAIL_ACTION);
402 }
403 if (
404 ($data['action'] === EmailOpensAbsoluteCountAction::TYPE)
405 || ($data['action'] === EmailOpensAbsoluteCountAction::MACHINE_TYPE)
406 ) {
407 return $this->createEmailOpensAbsoluteCount($data);
408 }
409 if ($data['action'] === EmailActionClickAny::TYPE) {
410 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_EMAIL, $data['action'], [
411 'connect' => $data['connect'],
412 ]);
413 }
414
415 $filterData = [
416 'connect' => $data['connect'],
417 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
418 ];
419
420 if ($data['action'] === EmailsReceived::ACTION) {
421 if (!isset($data['emails'])) {
422 throw new InvalidFilterException('Missing email count value', InvalidFilterException::MISSING_VALUE);
423 }
424 $filterData['emails'] = $data['emails'];
425 $filterData['operator'] = $data['operator'];
426 $filterData['connect'] = $data['connect'];
427 $this->copyDatePeriodData($data, $filterData);
428 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_EMAIL, $data['action'], $filterData);
429 }
430
431 if ($data['action'] === NumberOfClicks::ACTION) {
432 if (!isset($data['clicks'])) {
433 throw new InvalidFilterException('Missing click count value', InvalidFilterException::MISSING_VALUE);
434 }
435 $filterData['clicks'] = $data['clicks'];
436 $filterData['operator'] = $data['operator'];
437 $filterData['connect'] = $data['connect'];
438 $this->copyDatePeriodData($data, $filterData);
439 return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_EMAIL, $data['action'], $filterData);
440 }
441
442 if (($data['action'] === EmailAction::ACTION_CLICKED)) {
443 if (empty($data['newsletter_id'])) {
444 throw new InvalidFilterException('Missing newsletter id', InvalidFilterException::MISSING_NEWSLETTER_ID);
445 }
446 $filterData['newsletter_id'] = $data['newsletter_id'];
447 } else {
448 if (empty($data['newsletters']) || !is_array($data['newsletters'])) {
449 throw new InvalidFilterException('Missing newsletter', InvalidFilterException::MISSING_NEWSLETTER_ID);
450 }
451 $filterData['newsletters'] = array_values(
452 array_map('intval', array_filter($data['newsletters'], 'is_scalar'))
453 );
454 }
455
456 $filterType = DynamicSegmentFilterData::TYPE_EMAIL;
457 $action = $data['action'];
458 if (isset($data['link_ids']) && is_array($data['link_ids'])) {
459 $filterData['link_ids'] = $this->normalizeEmailLinkIds($data['link_ids']);
460 if (!isset($data['operator'])) {
461 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
462 }
463 $filterData['operator'] = $data['operator'];
464 }
465 return new DynamicSegmentFilterData($filterType, $action, $filterData);
466 }
467
468 private function normalizeEmailLinkIds(array $linkIds): array {
469 $normalizedLinkIds = [];
470 foreach ($linkIds as $linkId) {
471 if (is_int($linkId)) {
472 $normalizedLinkIds[] = $linkId;
473 continue;
474 }
475 if (is_float($linkId) && floor($linkId) === $linkId) {
476 $normalizedLinkIds[] = (int)$linkId;
477 continue;
478 }
479 if (!is_string($linkId)) {
480 continue;
481 }
482 $linkId = trim($linkId);
483 if ($linkId === '') {
484 continue;
485 }
486 $normalizedLinkIds[] = ctype_digit($linkId) ? (int)$linkId : $linkId;
487 }
488 return $normalizedLinkIds;
489 }
490
491 /**
492 * @throws InvalidFilterException
493 */
494 private function createEmailOpensAbsoluteCount(array $data): DynamicSegmentFilterData {
495 if (!isset($data['opens'])) {
496 throw new InvalidFilterException('Missing number of opens', InvalidFilterException::MISSING_VALUE);
497 }
498 $filterData = [
499 'opens' => $data['opens'],
500 'operator' => $data['operator'] ?? 'more',
501 'connect' => $data['connect'],
502 ];
503 $this->copyDatePeriodData($data, $filterData);
504 $filterType = DynamicSegmentFilterData::TYPE_EMAIL;
505 $action = $data['action'];
506 return new DynamicSegmentFilterData($filterType, $action, $filterData);
507 }
508
509 /**
510 * @throws InvalidFilterException
511 */
512 private function createWooCommerce(array $data): DynamicSegmentFilterData {
513 if (empty($data['action'])) {
514 throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
515 }
516 $filterData = [
517 'connect' => $data['connect'],
518 ];
519 $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE;
520 $action = $data['action'];
521 if ($data['action'] === WooCommerceCategory::ACTION_CATEGORY) {
522 if (!isset($data['category_ids'])) {
523 throw new InvalidFilterException('Missing category', InvalidFilterException::MISSING_CATEGORY_ID);
524 }
525 if (!isset($data['operator'])) {
526 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
527 }
528 $filterData['operator'] = $data['operator'];
529 $filterData['category_ids'] = $data['category_ids'];
530 $this->copyDatePeriodData($data, $filterData, DynamicSegmentFilterData::TIMEFRAME_ALL_TIME);
531 } elseif ($data['action'] === WooCommerceProduct::ACTION_PRODUCT) {
532 if (!isset($data['product_ids'])) {
533 throw new InvalidFilterException('Missing product', InvalidFilterException::MISSING_PRODUCT_ID);
534 }
535 if (!isset($data['operator'])) {
536 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
537 }
538 $filterData['operator'] = $data['operator'];
539 $filterData['product_ids'] = $data['product_ids'];
540 $this->copyDatePeriodData($data, $filterData, DynamicSegmentFilterData::TIMEFRAME_ALL_TIME);
541 } elseif ($data['action'] === WooCommerceProductVariation::ACTION_PRODUCT_VARIATION) {
542 if (!isset($data['variation_ids']) || !is_array($data['variation_ids']) || count($data['variation_ids']) === 0) {
543 throw new InvalidFilterException('Missing variation', InvalidFilterException::MISSING_VARIATION_ID);
544 }
545 if (
546 !isset($data['operator']) || !in_array($data['operator'], [
547 DynamicSegmentFilterData::OPERATOR_ANY,
548 DynamicSegmentFilterData::OPERATOR_ALL,
549 DynamicSegmentFilterData::OPERATOR_NONE,
550 ], true)
551 ) {
552 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
553 }
554 $filterData['operator'] = $data['operator'];
555 $filterData['variation_ids'] = $data['variation_ids'];
556 $this->copyDatePeriodData($data, $filterData, DynamicSegmentFilterData::TIMEFRAME_ALL_TIME);
557 } elseif ($data['action'] === WooCommerceCountry::ACTION_CUSTOMER_COUNTRY) {
558 if (!isset($data['country_code'])) {
559 throw new InvalidFilterException('Missing country', InvalidFilterException::MISSING_COUNTRY);
560 }
561 $filterData['country_code'] = $data['country_code'];
562 $filterData['operator'] = $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY;
563 } elseif (in_array($data['action'], WooCommerceNumberOfOrders::ACTIONS)) {
564 if (
565 !isset($data['number_of_orders_type'])
566 || !isset($data['number_of_orders_count']) || $data['number_of_orders_count'] < 0
567 ) {
568 throw new InvalidFilterException('Missing required fields', InvalidFilterException::MISSING_NUMBER_OF_ORDERS_FIELDS);
569 }
570 $filterData['number_of_orders_type'] = $data['number_of_orders_type'];
571 $filterData['number_of_orders_count'] = $data['number_of_orders_count'];
572 $this->copyDatePeriodData($data, $filterData);
573 } elseif ($data['action'] === WooCommerceNumberOfReviews::ACTION) {
574 $this->wooCommerceNumberOfReviews->validateFilterData($data);
575 $filterData['count_type'] = $data['count_type'];
576 $filterData['count'] = $data['count'];
577 $filterData['rating'] = $data['rating'];
578 $this->copyDatePeriodData($data, $filterData);
579 } elseif ($data['action'] === WooCommerceTotalSpent::ACTION_TOTAL_SPENT) {
580 if (
581 !isset($data['total_spent_type'])
582 || !isset($data['total_spent_amount']) || $data['total_spent_amount'] < 0
583 ) {
584 throw new InvalidFilterException('Missing required fields', InvalidFilterException::MISSING_TOTAL_SPENT_FIELDS);
585 }
586 $filterData['total_spent_type'] = $data['total_spent_type'];
587 $filterData['total_spent_amount'] = $data['total_spent_amount'];
588 $this->copyDatePeriodData($data, $filterData);
589 } elseif ($data['action'] === WooCommerceSingleOrderValue::ACTION_SINGLE_ORDER_VALUE) {
590 if (
591 !isset($data['single_order_value_type'])
592 || !isset($data['single_order_value_amount']) || $data['single_order_value_amount'] < 0
593 ) {
594 throw new InvalidFilterException('Missing required fields', InvalidFilterException::MISSING_SINGLE_ORDER_VALUE_FIELDS);
595 }
596 $filterData['single_order_value_type'] = $data['single_order_value_type'];
597 $filterData['single_order_value_amount'] = $data['single_order_value_amount'];
598 $this->copyDatePeriodData($data, $filterData);
599 } elseif (in_array($data['action'], [WooCommercePurchaseDate::ACTION, WooCommerceFirstOrder::ACTION])) {
600 $filterData['operator'] = $data['operator'];
601 $filterData['value'] = $data['value'];
602 if ($data['operator'] === DateFilterHelper::BETWEEN) {
603 $betweenData = [
604 'timeframe' => DynamicSegmentFilterData::TIMEFRAME_BETWEEN,
605 'value' => $filterData['value'],
606 'value2' => $data['value2'] ?? null,
607 ];
608 if (is_string($betweenData['value']) && is_string($betweenData['value2'])) {
609 [$betweenData['value'], $betweenData['value2']] = $this->orderDateRange($betweenData['value'], $betweenData['value2']);
610 }
611 try {
612 $this->filterHelper->validateDaysPeriodData($betweenData);
613 } catch (InvalidFilterException $e) {
614 throw $e;
615 } catch (\Throwable $e) {
616 throw new InvalidFilterException('Invalid date value', InvalidFilterException::INVALID_DATE_VALUE);
617 }
618 $filterData['value'] = $betweenData['value'];
619 $filterData['value2'] = $betweenData['value2'];
620 }
621 } elseif ($data['action'] === WooCommerceAverageSpent::ACTION) {
622 if (
623 !isset($data['average_spent_type'])
624 || !isset($data['average_spent_amount']) || $data['average_spent_amount'] < 0
625 ) {
626 throw new InvalidFilterException('Missing required fields', InvalidFilterException::MISSING_AVERAGE_SPENT_FIELDS);
627 }
628 $this->copyDatePeriodData($data, $filterData);
629 $filterData['average_spent_amount'] = $data['average_spent_amount'];
630 $filterData['average_spent_type'] = $data['average_spent_type'];
631 } elseif ($data['action'] === WooCommerceUsedPaymentMethod::ACTION) {
632 if (!isset($data['operator']) || !in_array($data['operator'], WooCommerceUsedPaymentMethod::VALID_OPERATORS, true)) {
633 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
634 }
635 if (!isset($data['payment_methods']) || !is_array($data['payment_methods']) || empty($data['payment_methods'])) {
636 throw new InvalidFilterException('Missing payment gateways', InvalidFilterException::MISSING_VALUE);
637 }
638 $filterData['operator'] = $data['operator'];
639 $filterData['payment_methods'] = $data['payment_methods'];
640 $this->copyDatePeriodData($data, $filterData);
641 } elseif ($data['action'] === WooCommerceUsedShippingMethod::ACTION) {
642 if (!isset($data['operator']) || !in_array($data['operator'], WooCommerceUsedShippingMethod::VALID_OPERATORS, true)) {
643 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
644 }
645 if (!isset($data['shipping_methods']) || !is_array($data['shipping_methods']) || empty($data['shipping_methods'])) {
646 throw new InvalidFilterException('Missing shipping methods', InvalidFilterException::MISSING_VALUE);
647 }
648 $filterData['operator'] = $data['operator'];
649 $filterData['shipping_methods'] = $data['shipping_methods'];
650 $this->copyDatePeriodData($data, $filterData);
651 } elseif (in_array($data['action'], WooCommerceCustomerTextField::ACTIONS)) {
652 if (empty($data['value'])) {
653 throw new InvalidFilterException('Missing value', InvalidFilterException::MISSING_VALUE);
654 }
655 if (empty($data['operator'])) {
656 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
657 }
658 if (!in_array($data['operator'], DynamicSegmentFilterData::TEXT_FIELD_OPERATORS)) {
659 throw new InvalidFilterException('Invalid operator', InvalidFilterException::MISSING_OPERATOR);
660 }
661 $filterData['value'] = $data['value'];
662 $filterData['operator'] = $data['operator'];
663 $filterData['action'] = $data['action'];
664 } elseif ($data['action'] === WooCommerceUsedCouponCode::ACTION) {
665 $this->wooCommerceUsedCouponCode->validateFilterData($data);
666 $filterData['operator'] = $data['operator'];
667 $filterData['coupon_code_ids'] = $data['coupon_code_ids'];
668 $this->copyDatePeriodData($data, $filterData);
669 } elseif ($data['action'] === WooCommercePurchasedWithAttribute::ACTION) {
670 $this->wooCommercePurchasedWithAttribute->validateFilterData($data);
671 $filterData['operator'] = $data['operator'];
672 $filterData['attribute_taxonomy_slug'] = $data['attribute_taxonomy_slug'] ?? null;
673 $filterData['attribute_term_ids'] = $data['attribute_term_ids'] ?? null;
674 $filterData['attribute_type'] = $data['attribute_type'];
675 $filterData['attribute_local_name'] = $data['attribute_local_name'] ?? null;
676 $filterData['attribute_local_values'] = $data['attribute_local_values'] ?? null;
677 $this->copyDatePeriodData($data, $filterData, DynamicSegmentFilterData::TIMEFRAME_ALL_TIME);
678 } elseif ($data['action'] === WooCommerceTag::ACTION) {
679 $this->wooCommerceTag->validateFilterData($data);
680 $filterData['operator'] = $data['operator'];
681 $filterData['tag_ids'] = $data['tag_ids'];
682 $this->copyDatePeriodData($data, $filterData, DynamicSegmentFilterData::TIMEFRAME_ALL_TIME);
683 } else {
684 throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
685 }
686 return new DynamicSegmentFilterData($filterType, $action, $filterData);
687 }
688
689 private function copyDatePeriodData(array $data, array &$filterData, string $defaultTimeframe = DynamicSegmentFilterData::TIMEFRAME_IN_THE_LAST): void {
690 $timeframe = $data['timeframe'] ?? $defaultTimeframe;
691 $datePeriodData = [
692 'timeframe' => $timeframe,
693 'days' => $data['days'] ?? 0,
694 ];
695 if (in_array($timeframe, [DynamicSegmentFilterData::TIMEFRAME_BEFORE, DynamicSegmentFilterData::TIMEFRAME_AFTER, DynamicSegmentFilterData::TIMEFRAME_ON, DynamicSegmentFilterData::TIMEFRAME_BETWEEN], true)) {
696 $datePeriodData['value'] = $data['value'] ?? null;
697 }
698 if ($timeframe === DynamicSegmentFilterData::TIMEFRAME_BETWEEN) {
699 $datePeriodData['value2'] = $data['value2'] ?? null;
700 if (is_string($datePeriodData['value']) && is_string($datePeriodData['value2'])) {
701 [$datePeriodData['value'], $datePeriodData['value2']] = $this->orderDateRange($datePeriodData['value'], $datePeriodData['value2']);
702 }
703 }
704 $this->filterHelper->validateDaysPeriodData($datePeriodData);
705 $filterData['timeframe'] = $datePeriodData['timeframe'];
706 $filterData['days'] = $timeframe === DynamicSegmentFilterData::TIMEFRAME_IN_THE_LAST ? $datePeriodData['days'] : 0;
707 if (isset($datePeriodData['value'])) {
708 $filterData['value'] = $datePeriodData['value'];
709 }
710 if (isset($datePeriodData['value2'])) {
711 $filterData['value2'] = $datePeriodData['value2'];
712 }
713 }
714
715 /**
716 * @return array{0: string, 1: string}
717 */
718 private function orderDateRange(string $value, string $value2): array {
719 return strcmp($value, $value2) <= 0 ? [$value, $value2] : [$value2, $value];
720 }
721
722 /**
723 * @throws InvalidFilterException
724 */
725 private function createWooCommerceMembership(array $data): DynamicSegmentFilterData {
726 if (empty($data['action'])) {
727 throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
728 }
729 $filterData = [
730 'connect' => $data['connect'],
731 ];
732 $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE_MEMBERSHIP;
733 $action = $data['action'];
734 if ($data['action'] === WooCommerceMembership::ACTION_MEMBER_OF) {
735 if (!isset($data['plan_ids']) || !is_array($data['plan_ids'])) {
736 throw new InvalidFilterException('Missing plan', InvalidFilterException::MISSING_PLAN_ID);
737 }
738 if (!isset($data['operator'])) {
739 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
740 }
741 $filterData['operator'] = $data['operator'];
742 $filterData['plan_ids'] = $data['plan_ids'];
743 } else {
744 throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
745 }
746 return new DynamicSegmentFilterData($filterType, $action, $filterData);
747 }
748
749 /**
750 * @throws InvalidFilterException
751 */
752 private function createWooCommerceSubscription(array $data): DynamicSegmentFilterData {
753 if (empty($data['action'])) {
754 throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
755 }
756 $filterData = [
757 'connect' => $data['connect'],
758 ];
759 $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE_SUBSCRIPTION;
760 $action = $data['action'];
761 if ($data['action'] === WooCommerceSubscription::ACTION_HAS_ACTIVE) {
762 if (!isset($data['product_ids']) || !is_array($data['product_ids'])) {
763 throw new InvalidFilterException('Missing product', InvalidFilterException::MISSING_PRODUCT_ID);
764 }
765 if (!isset($data['operator'])) {
766 throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
767 }
768 $filterData['operator'] = $data['operator'];
769 $filterData['product_ids'] = $data['product_ids'];
770 } else {
771 throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
772 }
773 return new DynamicSegmentFilterData($filterType, $action, $filterData);
774 }
775 }
776