/wp-content/plugins/mailpoet/lib/Segments/DynamicSegments/FilterDataMapper.php

https://gitlab.com/remyvianne/krowkaramel · PHP · 292 lines · 251 code · 18 blank · 23 comment · 66 complexity · 59d20d0ff3bda8784aaf677c593c632e MD5 · raw file

  1. <?php
  2. namespace MailPoet\Segments\DynamicSegments;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\Entities\DynamicSegmentFilterData;
  5. use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException;
  6. use MailPoet\Segments\DynamicSegments\Filters\EmailAction;
  7. use MailPoet\Segments\DynamicSegments\Filters\EmailActionClickAny;
  8. use MailPoet\Segments\DynamicSegments\Filters\EmailOpensAbsoluteCountAction;
  9. use MailPoet\Segments\DynamicSegments\Filters\MailPoetCustomFields;
  10. use MailPoet\Segments\DynamicSegments\Filters\SubscriberScore;
  11. use MailPoet\Segments\DynamicSegments\Filters\SubscriberSegment;
  12. use MailPoet\Segments\DynamicSegments\Filters\SubscriberSubscribedDate;
  13. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceCategory;
  14. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceCountry;
  15. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceMembership;
  16. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceNumberOfOrders;
  17. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceProduct;
  18. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
  19. use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
  20. use MailPoet\WP\Functions as WPFunctions;
  21. class FilterDataMapper {
  22. /** @var WPFunctions */
  23. private $wp;
  24. public function __construct(
  25. WPFunctions $wp = null
  26. ) {
  27. if (!$wp) {
  28. $wp = WPFunctions::get();
  29. }
  30. $this->wp = $wp;
  31. }
  32. /**
  33. * @param array $data
  34. * @return DynamicSegmentFilterData[]
  35. */
  36. public function map(array $data = []): array {
  37. $filters = [];
  38. if (!isset($data['filters']) || count($data['filters'] ?? []) < 1) {
  39. throw new InvalidFilterException('Filters are missing', InvalidFilterException::MISSING_FILTER);
  40. }
  41. $processFilter = function ($filter, $data) {
  42. $filter['connect'] = $data['filters_connect'] ?? DynamicSegmentFilterData::CONNECT_TYPE_AND;
  43. return $this->createFilter($filter);
  44. };
  45. $wpFilterName = 'mailpoet_dynamic_segments_filters_map';
  46. if ($this->wp->hasFilter($wpFilterName)) {
  47. return $this->wp->applyFilters($wpFilterName, $data, $processFilter);
  48. }
  49. $filter = reset($data['filters']);
  50. return [$processFilter($filter, $data)];
  51. }
  52. private function createFilter(array $filterData): DynamicSegmentFilterData {
  53. switch ($this->getSegmentType($filterData)) {
  54. case DynamicSegmentFilterData::TYPE_USER_ROLE:
  55. return $this->createSubscriber($filterData);
  56. case DynamicSegmentFilterData::TYPE_EMAIL:
  57. return $this->createEmail($filterData);
  58. case DynamicSegmentFilterData::TYPE_WOOCOMMERCE:
  59. return $this->createWooCommerce($filterData);
  60. case DynamicSegmentFilterData::TYPE_WOOCOMMERCE_MEMBERSHIP:
  61. return $this->createWooCommerceMembership($filterData);
  62. case DynamicSegmentFilterData::TYPE_WOOCOMMERCE_SUBSCRIPTION:
  63. return $this->createWooCommerceSubscription($filterData);
  64. default:
  65. throw new InvalidFilterException('Invalid type', InvalidFilterException::INVALID_TYPE);
  66. }
  67. }
  68. /**
  69. * @throws InvalidFilterException
  70. */
  71. private function getSegmentType(array $data): string {
  72. if (!isset($data['segmentType'])) {
  73. throw new InvalidFilterException('Segment type is not set', InvalidFilterException::MISSING_TYPE);
  74. }
  75. return $data['segmentType'];
  76. }
  77. private function createSubscriber(array $data): DynamicSegmentFilterData {
  78. if (empty($data['action'])) {
  79. $data['action'] = DynamicSegmentFilterData::TYPE_USER_ROLE;
  80. }
  81. if ($data['action'] === SubscriberSubscribedDate::TYPE) {
  82. if (empty($data['value'])) throw new InvalidFilterException('Missing number of days', InvalidFilterException::MISSING_VALUE);
  83. return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
  84. 'value' => $data['value'],
  85. 'operator' => $data['operator'] ?? SubscriberSubscribedDate::BEFORE,
  86. 'connect' => $data['connect'],
  87. ]);
  88. }
  89. if ($data['action'] === SubscriberScore::TYPE) {
  90. if (!isset($data['value'])) throw new InvalidFilterException('Missing engagement score value', InvalidFilterException::MISSING_VALUE);
  91. return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
  92. 'value' => $data['value'],
  93. 'operator' => $data['operator'] ?? SubscriberScore::HIGHER_THAN,
  94. 'connect' => $data['connect'],
  95. ]);
  96. }
  97. if ($data['action'] === SubscriberSegment::TYPE) {
  98. if (empty($data['segments'])) throw new InvalidFilterException('Missing segments', InvalidFilterException::MISSING_VALUE);
  99. return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
  100. 'segments' => array_map(function ($segmentId) {
  101. return intval($segmentId);
  102. }, $data['segments']),
  103. 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
  104. 'connect' => $data['connect'],
  105. ]);
  106. }
  107. if ($data['action'] === MailPoetCustomFields::TYPE) {
  108. if (empty($data['custom_field_id'])) throw new InvalidFilterException('Missing custom field id', InvalidFilterException::MISSING_VALUE);
  109. if (empty($data['custom_field_type'])) throw new InvalidFilterException('Missing custom field type', InvalidFilterException::MISSING_VALUE);
  110. if (!isset($data['value'])) throw new InvalidFilterException('Missing value', InvalidFilterException::MISSING_VALUE);
  111. $filterData = [
  112. 'value' => $data['value'],
  113. 'custom_field_id' => $data['custom_field_id'],
  114. 'custom_field_type' => $data['custom_field_type'],
  115. 'connect' => $data['connect'],
  116. ];
  117. if (!empty($data['date_type'])) $filterData['date_type'] = $data['date_type'];
  118. if (!empty($data['operator'])) $filterData['operator'] = $data['operator'];
  119. return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], $filterData);
  120. }
  121. if (empty($data['wordpressRole'])) throw new InvalidFilterException('Missing role', InvalidFilterException::MISSING_ROLE);
  122. return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_USER_ROLE, $data['action'], [
  123. 'wordpressRole' => $data['wordpressRole'],
  124. 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
  125. 'connect' => $data['connect'],
  126. ]);
  127. }
  128. /**
  129. * @throws InvalidFilterException
  130. */
  131. private function createEmail(array $data): DynamicSegmentFilterData {
  132. if (empty($data['action'])) throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
  133. if (!in_array($data['action'], EmailAction::ALLOWED_ACTIONS)) throw new InvalidFilterException('Invalid email action', InvalidFilterException::INVALID_EMAIL_ACTION);
  134. if (
  135. ($data['action'] === EmailOpensAbsoluteCountAction::TYPE)
  136. || ($data['action'] === EmailOpensAbsoluteCountAction::MACHINE_TYPE)
  137. ) {
  138. return $this->createEmailOpensAbsoluteCount($data);
  139. }
  140. if ($data['action'] === EmailActionClickAny::TYPE) {
  141. return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_EMAIL, $data['action'], [
  142. 'connect' => $data['connect'],
  143. ]);
  144. }
  145. $filterData = [
  146. 'connect' => $data['connect'],
  147. 'operator' => $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY,
  148. ];
  149. if (($data['action'] === EmailAction::ACTION_CLICKED)) {
  150. if (empty($data['newsletter_id'])) throw new InvalidFilterException('Missing newsletter id', InvalidFilterException::MISSING_NEWSLETTER_ID);
  151. $filterData['newsletter_id'] = $data['newsletter_id'];
  152. } else {
  153. if (empty($data['newsletters']) || !is_array($data['newsletters'])) throw new InvalidFilterException('Missing newsletter', InvalidFilterException::MISSING_NEWSLETTER_ID);
  154. $filterData['newsletters'] = array_map(function ($segmentId) {
  155. return intval($segmentId);
  156. }, $data['newsletters']);
  157. }
  158. $filterType = DynamicSegmentFilterData::TYPE_EMAIL;
  159. $action = $data['action'];
  160. if (isset($data['link_ids']) && is_array($data['link_ids'])) {
  161. $filterData['link_ids'] = array_map('intval', $data['link_ids']);
  162. if (!isset($data['operator'])) throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
  163. $filterData['operator'] = $data['operator'];
  164. }
  165. return new DynamicSegmentFilterData($filterType, $action, $filterData);
  166. }
  167. /**
  168. * @throws InvalidFilterException
  169. */
  170. private function createEmailOpensAbsoluteCount(array $data): DynamicSegmentFilterData {
  171. if (!isset($data['opens'])) throw new InvalidFilterException('Missing number of opens', InvalidFilterException::MISSING_VALUE);
  172. if (empty($data['days'])) throw new InvalidFilterException('Missing number of days', InvalidFilterException::MISSING_VALUE);
  173. $filterData = [
  174. 'opens' => $data['opens'],
  175. 'days' => $data['days'],
  176. 'operator' => $data['operator'] ?? 'more',
  177. 'connect' => $data['connect'],
  178. ];
  179. $filterType = DynamicSegmentFilterData::TYPE_EMAIL;
  180. $action = $data['action'];
  181. return new DynamicSegmentFilterData($filterType, $action, $filterData);
  182. }
  183. /**
  184. * @throws InvalidFilterException
  185. */
  186. private function createWooCommerce(array $data): DynamicSegmentFilterData {
  187. if (empty($data['action'])) throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
  188. $filterData = [
  189. 'connect' => $data['connect'],
  190. ];
  191. $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE;
  192. $action = $data['action'];
  193. if ($data['action'] === WooCommerceCategory::ACTION_CATEGORY) {
  194. if (!isset($data['category_ids'])) throw new InvalidFilterException('Missing category', InvalidFilterException::MISSING_CATEGORY_ID);
  195. if (!isset($data['operator'])) throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
  196. $filterData['operator'] = $data['operator'];
  197. $filterData['category_ids'] = $data['category_ids'];
  198. } elseif ($data['action'] === WooCommerceProduct::ACTION_PRODUCT) {
  199. if (!isset($data['product_ids'])) throw new InvalidFilterException('Missing product', InvalidFilterException::MISSING_PRODUCT_ID);
  200. if (!isset($data['operator'])) throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
  201. $filterData['operator'] = $data['operator'];
  202. $filterData['product_ids'] = $data['product_ids'];
  203. } elseif ($data['action'] === WooCommerceCountry::ACTION_CUSTOMER_COUNTRY) {
  204. if (!isset($data['country_code'])) throw new InvalidFilterException('Missing country', InvalidFilterException::MISSING_COUNTRY);
  205. $filterData['country_code'] = $data['country_code'];
  206. $filterData['operator'] = $data['operator'] ?? DynamicSegmentFilterData::OPERATOR_ANY;
  207. } elseif ($data['action'] === WooCommerceNumberOfOrders::ACTION_NUMBER_OF_ORDERS) {
  208. if (
  209. !isset($data['number_of_orders_type'])
  210. || !isset($data['number_of_orders_count']) || $data['number_of_orders_count'] < 0
  211. || !isset($data['number_of_orders_days']) || $data['number_of_orders_days'] < 1
  212. ) {
  213. throw new InvalidFilterException('Missing required fields', InvalidFilterException::MISSING_NUMBER_OF_ORDERS_FIELDS);
  214. }
  215. $filterData['number_of_orders_type'] = $data['number_of_orders_type'];
  216. $filterData['number_of_orders_count'] = $data['number_of_orders_count'];
  217. $filterData['number_of_orders_days'] = $data['number_of_orders_days'];
  218. } elseif ($data['action'] === WooCommerceTotalSpent::ACTION_TOTAL_SPENT) {
  219. if (
  220. !isset($data['total_spent_type'])
  221. || !isset($data['total_spent_amount']) || $data['total_spent_amount'] < 0
  222. || !isset($data['total_spent_days']) || $data['total_spent_days'] < 1
  223. ) {
  224. throw new InvalidFilterException('Missing required fields', InvalidFilterException::MISSING_TOTAL_SPENT_FIELDS);
  225. }
  226. $filterData['total_spent_type'] = $data['total_spent_type'];
  227. $filterData['total_spent_amount'] = $data['total_spent_amount'];
  228. $filterData['total_spent_days'] = $data['total_spent_days'];
  229. } else {
  230. throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
  231. }
  232. return new DynamicSegmentFilterData($filterType, $action, $filterData);
  233. }
  234. /**
  235. * @throws InvalidFilterException
  236. */
  237. private function createWooCommerceMembership(array $data): DynamicSegmentFilterData {
  238. if (empty($data['action'])) throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
  239. $filterData = [
  240. 'connect' => $data['connect'],
  241. ];
  242. $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE_MEMBERSHIP;
  243. $action = $data['action'];
  244. if ($data['action'] === WooCommerceMembership::ACTION_MEMBER_OF) {
  245. if (!isset($data['plan_ids']) || !is_array($data['plan_ids'])) throw new InvalidFilterException('Missing plan', InvalidFilterException::MISSING_PLAN_ID);
  246. if (!isset($data['operator'])) throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
  247. $filterData['operator'] = $data['operator'];
  248. $filterData['plan_ids'] = $data['plan_ids'];
  249. } else {
  250. throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
  251. }
  252. return new DynamicSegmentFilterData($filterType, $action, $filterData);
  253. }
  254. /**
  255. * @throws InvalidFilterException
  256. */
  257. private function createWooCommerceSubscription(array $data): DynamicSegmentFilterData {
  258. if (empty($data['action'])) throw new InvalidFilterException('Missing action', InvalidFilterException::MISSING_ACTION);
  259. $filterData = [
  260. 'connect' => $data['connect'],
  261. ];
  262. $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE_SUBSCRIPTION;
  263. $action = $data['action'];
  264. if ($data['action'] === WooCommerceSubscription::ACTION_HAS_ACTIVE) {
  265. if (!isset($data['product_ids']) || !is_array($data['product_ids'])) throw new InvalidFilterException('Missing product', InvalidFilterException::MISSING_PRODUCT_ID);
  266. if (!isset($data['operator'])) throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
  267. $filterData['operator'] = $data['operator'];
  268. $filterData['product_ids'] = $data['product_ids'];
  269. } else {
  270. throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
  271. }
  272. return new DynamicSegmentFilterData($filterType, $action, $filterData);
  273. }
  274. }