Move dynamic segments from Premium plugin
[MAILPOET-2382]
This commit is contained in:
committed by
Jack Kitterhing
parent
0af9f09f50
commit
70a89b7939
7
lib/DynamicSegments/Exceptions/ErrorSavingException.php
Normal file
7
lib/DynamicSegments/Exceptions/ErrorSavingException.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Exceptions;
|
||||
|
||||
class ErrorSavingException extends \Exception {
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Exceptions;
|
||||
|
||||
class InvalidSegmentTypeException extends \Exception {
|
||||
|
||||
const MISSING_TYPE = 1;
|
||||
const INVALID_TYPE = 2;
|
||||
const MISSING_ROLE = 3;
|
||||
const MISSING_ACTION = 4;
|
||||
const MISSING_NEWSLETTER_ID = 5;
|
||||
const MISSING_CATEGORY_ID = 6;
|
||||
const MISSING_PRODUCT_ID = 7;
|
||||
|
||||
};
|
109
lib/DynamicSegments/Filters/EmailAction.php
Normal file
109
lib/DynamicSegments/Filters/EmailAction.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\StatisticsClicks;
|
||||
use MailPoet\Models\StatisticsNewsletters;
|
||||
use MailPoet\Models\StatisticsOpens;
|
||||
use MailPoet\Models\Subscriber;
|
||||
|
||||
class EmailAction implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'email';
|
||||
|
||||
const ACTION_OPENED = 'opened';
|
||||
const ACTION_NOT_OPENED = 'notOpened';
|
||||
const ACTION_CLICKED = 'clicked';
|
||||
const ACTION_NOT_CLICKED = 'notClicked';
|
||||
|
||||
private static $allowed_actions = [
|
||||
EmailAction::ACTION_OPENED,
|
||||
EmailAction::ACTION_NOT_OPENED,
|
||||
EmailAction::ACTION_CLICKED,
|
||||
EmailAction::ACTION_NOT_CLICKED,
|
||||
];
|
||||
|
||||
/** @var int */
|
||||
private $newsletter_id;
|
||||
|
||||
/** @var int */
|
||||
private $link_id;
|
||||
|
||||
/** @var string */
|
||||
private $action;
|
||||
|
||||
/** @var string */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param int $newsletter_id
|
||||
* @param int $link_id
|
||||
* @param string $action
|
||||
* @param string $connect
|
||||
*/
|
||||
public function __construct($action, $newsletter_id, $link_id = null, $connect = null) {
|
||||
$this->newsletter_id = (int)$newsletter_id;
|
||||
if ($link_id) {
|
||||
$this->link_id = (int)$link_id;
|
||||
}
|
||||
$this->setAction($action);
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
private function setAction($action) {
|
||||
if (!in_array($action, EmailAction::$allowed_actions)) {
|
||||
throw new \InvalidArgumentException("Unknown action " . $action);
|
||||
}
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
function toSql(\ORM $orm) {
|
||||
if (($this->action === EmailAction::ACTION_CLICKED) || ($this->action === EmailAction::ACTION_NOT_CLICKED)) {
|
||||
$table = StatisticsClicks::$_table;
|
||||
} else {
|
||||
$table = StatisticsOpens::$_table;
|
||||
}
|
||||
|
||||
if (($this->action === EmailAction::ACTION_NOT_CLICKED) || ($this->action === EmailAction::ACTION_NOT_OPENED)) {
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . StatisticsNewsletters::$_table,
|
||||
'statssent.subscriber_id = ' . Subscriber::$_table . '.id AND statssent.newsletter_id = ' . $this->newsletter_id,
|
||||
'statssent'
|
||||
);
|
||||
$orm->rawJoin(
|
||||
'LEFT JOIN ' . $table,
|
||||
$this->createNotStatsJoin(),
|
||||
'stats'
|
||||
);
|
||||
$orm->whereNull('stats.id');
|
||||
} else {
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $table,
|
||||
'stats.subscriber_id = ' . Subscriber::$_table . '.id AND stats.newsletter_id = ' . $this->newsletter_id,
|
||||
'stats'
|
||||
);
|
||||
}
|
||||
if (($this->action === EmailAction::ACTION_CLICKED) && ($this->link_id)) {
|
||||
$orm->where('stats.link_id', $this->link_id);
|
||||
}
|
||||
return $orm;
|
||||
}
|
||||
|
||||
private function createNotStatsJoin() {
|
||||
$clause = 'statssent.subscriber_id = stats.subscriber_id AND stats.newsletter_id = ' . $this->newsletter_id;
|
||||
if (($this->action === EmailAction::ACTION_NOT_CLICKED) && ($this->link_id)) {
|
||||
$clause .= ' AND stats.link_id = ' . $this->link_id;
|
||||
}
|
||||
return $clause;
|
||||
}
|
||||
|
||||
function toArray() {
|
||||
return [
|
||||
'action' => $this->action,
|
||||
'newsletter_id' => $this->newsletter_id,
|
||||
'link_id' => $this->link_id,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => EmailAction::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
11
lib/DynamicSegments/Filters/Filter.php
Normal file
11
lib/DynamicSegments/Filters/Filter.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Filters;
|
||||
|
||||
interface Filter {
|
||||
|
||||
function toSql(\ORM $orm);
|
||||
|
||||
function toArray();
|
||||
|
||||
}
|
44
lib/DynamicSegments/Filters/UserRole.php
Normal file
44
lib/DynamicSegments/Filters/UserRole.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Filters;
|
||||
|
||||
class UserRole implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'userRole';
|
||||
|
||||
/** @var string */
|
||||
private $role;
|
||||
|
||||
/** @var string */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $connect
|
||||
*/
|
||||
public function __construct($role, $connect = null) {
|
||||
$this->role = $role;
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
function toSql(\ORM $orm) {
|
||||
global $wpdb;
|
||||
$orm->join($wpdb->users, ['wpusers.id', '=', MP_SUBSCRIBERS_TABLE . '.wp_user_id'], 'wpusers')
|
||||
->join($wpdb->usermeta, ['wpusers.ID', '=', 'wpusermeta.user_id'], 'wpusermeta')
|
||||
->whereEqual('wpusermeta.meta_key', $wpdb->prefix . 'capabilities')
|
||||
->whereLike('wpusermeta.meta_value', '%"' . $this->role . '"%');
|
||||
return $orm;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'wordpressRole' => $this->role,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => UserRole::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
function getRole() {
|
||||
return $this->role;
|
||||
}
|
||||
}
|
74
lib/DynamicSegments/Filters/WooCommerceCategory.php
Normal file
74
lib/DynamicSegments/Filters/WooCommerceCategory.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class WooCommerceCategory implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'woocommerce';
|
||||
|
||||
const ACTION_CATEGORY = 'purchasedCategory';
|
||||
|
||||
/** @var int */
|
||||
private $category_id;
|
||||
|
||||
/** @var string */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param int $category_id
|
||||
* @param string $connect
|
||||
*/
|
||||
public function __construct($category_id, $connect = null) {
|
||||
$this->category_id = (int)$category_id;
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
function toSql(\ORM $orm) {
|
||||
global $wpdb;
|
||||
$orm->distinct();
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->postmeta,
|
||||
"postmeta.meta_key = '_customer_user' AND " . Subscriber::$_table . '.wp_user_id=postmeta.meta_value',
|
||||
'postmeta'
|
||||
);
|
||||
$orm->join($wpdb->prefix . 'woocommerce_order_items', ['postmeta.post_id', '=', 'items.order_id'], 'items');
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->prefix . 'woocommerce_order_itemmeta',
|
||||
"itemmeta.order_item_id = items.order_item_id AND itemmeta.meta_key = '_product_id'",
|
||||
'itemmeta'
|
||||
);
|
||||
$orm->join($wpdb->term_relationships, ['itemmeta.meta_value', '=', 'term_relationships.object_id'], 'term_relationships');
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->term_taxonomy,
|
||||
'
|
||||
term_taxonomy.term_taxonomy_id=term_relationships.term_taxonomy_id
|
||||
AND
|
||||
term_taxonomy.term_id IN (' . join(',', $this->getAllCategoryIds()) . ')',
|
||||
'term_taxonomy'
|
||||
);
|
||||
$orm->where('status', Subscriber::STATUS_SUBSCRIBED);
|
||||
return $orm;
|
||||
}
|
||||
|
||||
private function getAllCategoryIds() {
|
||||
$subcategories = WPFunctions::get()->getTerms('product_cat', ['child_of' => $this->category_id]);
|
||||
if (!is_array($subcategories)) return [];
|
||||
$ids = array_map(function($category) {
|
||||
return $category->term_id;
|
||||
}, $subcategories);
|
||||
$ids[] = $this->category_id;
|
||||
return $ids;
|
||||
}
|
||||
|
||||
function toArray() {
|
||||
return [
|
||||
'action' => WooCommerceCategory::ACTION_CATEGORY,
|
||||
'category_id' => $this->category_id,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => WooCommerceCategory::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
56
lib/DynamicSegments/Filters/WooCommerceProduct.php
Normal file
56
lib/DynamicSegments/Filters/WooCommerceProduct.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
|
||||
class WooCommerceProduct implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'woocommerce';
|
||||
|
||||
const ACTION_PRODUCT = 'purchasedProduct';
|
||||
|
||||
/** @var int */
|
||||
private $product_id;
|
||||
|
||||
/** @var string */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param int $product_id
|
||||
* @param string $connect
|
||||
*/
|
||||
public function __construct($product_id, $connect = null) {
|
||||
$this->product_id = (int)$product_id;
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
function toSql(\ORM $orm) {
|
||||
global $wpdb;
|
||||
$orm->distinct();
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->postmeta,
|
||||
"postmeta.meta_key = '_customer_user' AND " . Subscriber::$_table . '.wp_user_id=postmeta.meta_value',
|
||||
'postmeta'
|
||||
);
|
||||
$orm->join($wpdb->prefix . 'woocommerce_order_items', ['postmeta.post_id', '=', 'items.order_id'], 'items');
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->prefix . 'woocommerce_order_itemmeta',
|
||||
"itemmeta.order_item_id=items.order_item_id
|
||||
AND itemmeta.meta_key='_product_id'
|
||||
AND itemmeta.meta_value=" . $this->product_id,
|
||||
'itemmeta'
|
||||
);
|
||||
$orm->where('status', Subscriber::STATUS_SUBSCRIBED);
|
||||
return $orm;
|
||||
}
|
||||
|
||||
function toArray() {
|
||||
return [
|
||||
'action' => WooCommerceProduct::ACTION_PRODUCT,
|
||||
'product_id' => $this->product_id,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => WooCommerceProduct::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\FreePluginConnectors;
|
||||
|
||||
use MailPoet\Premium\DynamicSegments\Persistence\Loading\Loader;
|
||||
use MailPoet\Premium\DynamicSegments\Persistence\Loading\SubscribersCount;
|
||||
|
||||
class AddToNewslettersSegments {
|
||||
|
||||
/** @var Loader */
|
||||
private $loader;
|
||||
|
||||
/** @var \MailPoet\Premium\DynamicSegments\Persistence\Loading\SubscribersCount */
|
||||
private $subscribersCountLoader;
|
||||
|
||||
public function __construct(Loader $loader, SubscribersCount $subscribersCountLoader) {
|
||||
$this->loader = $loader;
|
||||
$this->subscribersCountLoader = $subscribersCountLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $initial_segments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function add(array $initial_segments) {
|
||||
$dynamic_segments = $this->getListings();
|
||||
return array_merge($initial_segments, $dynamic_segments);
|
||||
}
|
||||
|
||||
private function getListings() {
|
||||
$dynamic_segments = $this->loader->load();
|
||||
return $this->buildResult($dynamic_segments);
|
||||
}
|
||||
|
||||
private function buildResult($dynamic_segments) {
|
||||
$result = [];
|
||||
foreach ($dynamic_segments as $dynamic_segment) {
|
||||
$result[] = [
|
||||
'id' => $dynamic_segment->id,
|
||||
'name' => $dynamic_segment->name,
|
||||
'subscribers' => $this->subscribersCountLoader->getSubscribersCount($dynamic_segment),
|
||||
'deleted_at' => $dynamic_segment->deleted_at,
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\FreePluginConnectors;
|
||||
|
||||
use MailPoet\Premium\DynamicSegments\Persistence\Loading\Loader;
|
||||
use MailPoet\Premium\DynamicSegments\Persistence\Loading\SubscribersCount;
|
||||
|
||||
class AddToSubscribersFilters {
|
||||
|
||||
/** @var \MailPoet\Premium\DynamicSegments\Persistence\Loading\Loader */
|
||||
private $loader;
|
||||
|
||||
/** @var SubscribersCount */
|
||||
private $subscribersCountLoader;
|
||||
|
||||
public function __construct(Loader $loader, SubscribersCount $subscribersCountLoader) {
|
||||
$this->loader = $loader;
|
||||
$this->subscribersCountLoader = $subscribersCountLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $segment_filters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function add(array $segment_filters) {
|
||||
$dynamic_segments = $this->getListings();
|
||||
return $this->sort(array_merge($segment_filters, $dynamic_segments));
|
||||
}
|
||||
|
||||
private function getListings() {
|
||||
$dynamic_segments = $this->loader->load();
|
||||
return $this->buildResult($dynamic_segments);
|
||||
}
|
||||
|
||||
private function buildResult($dynamic_segments) {
|
||||
$result = [];
|
||||
foreach ($dynamic_segments as $dynamic_segment) {
|
||||
$result[] = [
|
||||
'value' => $dynamic_segment->id,
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
$dynamic_segment->name,
|
||||
number_format($this->subscribersCountLoader->getSubscribersCount($dynamic_segment))
|
||||
),
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function sort($segment_filters) {
|
||||
$special_segment_filters = [];
|
||||
$segments = [];
|
||||
foreach ($segment_filters as $segment_filter) {
|
||||
if (is_numeric($segment_filter['value'])) {
|
||||
$segments[] = $segment_filter;
|
||||
} else {
|
||||
$special_segment_filters[] = $segment_filter;
|
||||
}
|
||||
}
|
||||
usort($segments, function ($a, $b) {
|
||||
return strcasecmp($a["label"], $b["label"]);
|
||||
});
|
||||
return array_merge($special_segment_filters, $segments);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\FreePluginConnectors;
|
||||
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Premium\DynamicSegments\Persistence\Loading\SingleSegmentLoader;
|
||||
use MailPoet\Premium\DynamicSegments\Persistence\Loading\SubscribersIds;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class SendingNewslettersSubscribersFinder {
|
||||
|
||||
/** @var SingleSegmentLoader */
|
||||
private $single_segment_loader;
|
||||
|
||||
/** @var \MailPoet\Premium\DynamicSegments\Persistence\Loading\SubscribersIds */
|
||||
private $subscribers_ids_loader;
|
||||
|
||||
public function __construct(SingleSegmentLoader $single_segment_loader, SubscribersIds $subscribers_ids_loader) {
|
||||
$this->single_segment_loader = $single_segment_loader;
|
||||
$this->subscribers_ids_loader = $subscribers_ids_loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Segment $segment
|
||||
* @param int[] $subscribers_to_process_ids
|
||||
*
|
||||
* @return Subscriber[]
|
||||
*/
|
||||
function findSubscribersInSegment(Segment $segment, array $subscribers_to_process_ids) {
|
||||
if ($segment->type !== DynamicSegment::TYPE_DYNAMIC) return [];
|
||||
$dynamic_segment = $this->single_segment_loader->load($segment->id);
|
||||
return $this->subscribers_ids_loader->load($dynamic_segment, $subscribers_to_process_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Segment $segment
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getSubscriberIdsInSegment(Segment $segment) {
|
||||
if ($segment->type !== DynamicSegment::TYPE_DYNAMIC) return [];
|
||||
$dynamic_segment = $this->single_segment_loader->load($segment->id);
|
||||
$result = $this->subscribers_ids_loader->load($dynamic_segment);
|
||||
return $this->createResultArray($result);
|
||||
}
|
||||
|
||||
private function createResultArray($subscribers) {
|
||||
$result = [];
|
||||
foreach ($subscribers as $subscriber) {
|
||||
$result[] = $subscriber->asArray();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\FreePluginConnectors;
|
||||
|
||||
use MailPoet\Listing\BulkActionController;
|
||||
use MailPoet\Listing\BulkActionFactory;
|
||||
use MailPoet\Listing\Handler;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class SubscribersBulkActionHandler {
|
||||
|
||||
/**
|
||||
* @param array $segment
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
function apply(array $segment, array $data) {
|
||||
if ($segment['type'] === DynamicSegment::TYPE_DYNAMIC) {
|
||||
$bulkAction = new BulkActionController(new BulkActionFactory(), new Handler());
|
||||
return $bulkAction->apply('\MailPoet\Premium\Models\SubscribersInDynamicSegment', $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\FreePluginConnectors;
|
||||
|
||||
use MailPoet\Listing\Handler;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class SubscribersListingsHandlerFactory {
|
||||
|
||||
function get(Segment $segment, $data) {
|
||||
if ($segment->type === DynamicSegment::TYPE_DYNAMIC) {
|
||||
$listing = new Handler();
|
||||
return $listing_data = $listing->get('\MailPoet\Premium\Models\SubscribersInDynamicSegment', $data);
|
||||
}
|
||||
}
|
||||
}
|
86
lib/DynamicSegments/Mappers/DBMapper.php
Normal file
86
lib/DynamicSegments/Mappers/DBMapper.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Mappers;
|
||||
|
||||
use MailPoet\Premium\DynamicSegments\Exceptions\InvalidSegmentTypeException;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\EmailAction;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\UserRole;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\WooCommerceCategory;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\WooCommerceProduct;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
use MailPoet\Premium\Models\DynamicSegmentFilter;
|
||||
|
||||
class DBMapper {
|
||||
|
||||
/**
|
||||
* @param \MailPoet\Premium\Models\DynamicSegment $segment_data
|
||||
* @param DynamicSegmentFilter[] $filters_data
|
||||
*
|
||||
* @return DynamicSegment
|
||||
*/
|
||||
function mapSegment(DynamicSegment $segment_data, array $filters_data) {
|
||||
$filters = $this->getFilters($segment_data->id, $filters_data);
|
||||
$segment_data->setFilters($filters);
|
||||
return $segment_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \MailPoet\Premium\Models\DynamicSegment[] $segments_data
|
||||
* @param DynamicSegmentFilter[] $filters_data
|
||||
*
|
||||
* @return DynamicSegment[]
|
||||
*/
|
||||
function mapSegments(array $segments_data, array $filters_data) {
|
||||
$result = [];
|
||||
foreach ($segments_data as $segment_data) {
|
||||
$result[] = $this->mapSegment($segment_data, $filters_data);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getFilters($segment_id, $all_filters) {
|
||||
$result = [];
|
||||
foreach ($all_filters as $filter) {
|
||||
if ($filter->segment_id === $segment_id) {
|
||||
$result[] = $this->createFilter($filter->filter_data);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return Filter
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function createFilter(array $data) {
|
||||
switch ($this->getSegmentType($data)) {
|
||||
case 'userRole':
|
||||
if (!$data['wordpressRole']) throw new InvalidSegmentTypeException('Missing role');
|
||||
return new UserRole($data['wordpressRole'], 'and');
|
||||
case 'email':
|
||||
return new EmailAction($data['action'], $data['newsletter_id'], $data['link_id']);
|
||||
case 'woocommerce':
|
||||
if ($data['action'] === WooCommerceProduct::ACTION_PRODUCT) {
|
||||
return new WooCommerceProduct($data['product_id']);
|
||||
}
|
||||
return new WooCommerceCategory($data['category_id']);
|
||||
default:
|
||||
throw new InvalidSegmentTypeException('Invalid type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function getSegmentType(array $data) {
|
||||
if (!isset($data['segmentType'])) {
|
||||
throw new InvalidSegmentTypeException('Segment type is not set');
|
||||
}
|
||||
return $data['segmentType'];
|
||||
}
|
||||
|
||||
}
|
120
lib/DynamicSegments/Mappers/FormDataMapper.php
Normal file
120
lib/DynamicSegments/Mappers/FormDataMapper.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Mappers;
|
||||
|
||||
use MailPoet\Premium\DynamicSegments\Exceptions\InvalidSegmentTypeException;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\EmailAction;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\UserRole;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\WooCommerceCategory;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\WooCommerceProduct;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class FormDataMapper {
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return DynamicSegment
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
function mapDataToDB(array $data) {
|
||||
$filters = $this->getFilters($data);
|
||||
$dynamic_segment = $this->createDynamicSegment($data);
|
||||
|
||||
$dynamic_segment->setFilters($filters);
|
||||
|
||||
return $dynamic_segment;
|
||||
}
|
||||
|
||||
private function createDynamicSegment($data) {
|
||||
$dataToSave = [
|
||||
'name' => isset($data['name']) ? $data['name'] : '',
|
||||
'description' => isset($data['description']) ? $data['description'] : '',
|
||||
];
|
||||
$dynamic_segment = null;
|
||||
if (isset($data['id'])) {
|
||||
$dynamic_segment = DynamicSegment::findOne($data['id']);
|
||||
}
|
||||
if ($dynamic_segment instanceof DynamicSegment) {
|
||||
$dynamic_segment->set($dataToSave);
|
||||
} else {
|
||||
$dynamic_segment = DynamicSegment::create();
|
||||
if ($dynamic_segment instanceof DynamicSegment) {
|
||||
$dynamic_segment->hydrate($dataToSave);
|
||||
}
|
||||
}
|
||||
return $dynamic_segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Filter[]
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function getFilters(array $data) {
|
||||
switch ($this->getSegmentType($data)) {
|
||||
case 'userRole':
|
||||
if (!$data['wordpressRole']) throw new InvalidSegmentTypeException('Missing role', InvalidSegmentTypeException::MISSING_ROLE);
|
||||
return [new UserRole($data['wordpressRole'])];
|
||||
case 'email':
|
||||
return $this->createEmail($data);
|
||||
case 'woocommerce':
|
||||
return $this->createWooCommerce($data);
|
||||
default:
|
||||
throw new InvalidSegmentTypeException('Invalid type', InvalidSegmentTypeException::INVALID_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return string
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function getSegmentType(array $data) {
|
||||
if (!isset($data['segmentType'])) {
|
||||
throw new InvalidSegmentTypeException('Segment type is not set', InvalidSegmentTypeException::MISSING_TYPE);
|
||||
}
|
||||
return $data['segmentType'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return EmailAction[]
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function createEmail(array $data) {
|
||||
if (empty($data['action'])) throw new InvalidSegmentTypeException('Missing action', InvalidSegmentTypeException::MISSING_ACTION);
|
||||
if (empty($data['newsletter_id'])) throw new InvalidSegmentTypeException('Missing newsletter id', InvalidSegmentTypeException::MISSING_NEWSLETTER_ID);
|
||||
if (isset($data['link_id'])) {
|
||||
return [new EmailAction($data['action'], $data['newsletter_id'], $data['link_id'])];
|
||||
} else {
|
||||
return [new EmailAction($data['action'], $data['newsletter_id'])];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Filter[]
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function createWooCommerce($data) {
|
||||
if (empty($data['action'])) throw new InvalidSegmentTypeException('Missing action', InvalidSegmentTypeException::MISSING_ACTION);
|
||||
switch ($data['action']) {
|
||||
case WooCommerceCategory::ACTION_CATEGORY:
|
||||
if (!isset($data['category_id'])) throw new InvalidSegmentTypeException('Missing category', InvalidSegmentTypeException::MISSING_CATEGORY_ID);
|
||||
return [new WooCommerceCategory($data['category_id'])];
|
||||
case WooCommerceProduct::ACTION_PRODUCT:
|
||||
if (!isset($data['product_id'])) throw new InvalidSegmentTypeException('Missing product', InvalidSegmentTypeException::MISSING_PRODUCT_ID);
|
||||
return [new WooCommerceProduct($data['product_id'])];
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown action " . $data['action']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
35
lib/DynamicSegments/Persistence/Loading/Loader.php
Normal file
35
lib/DynamicSegments/Persistence/Loading/Loader.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Persistence\Loading;
|
||||
|
||||
use MailPoet\Premium\DynamicSegments\Mappers\DBMapper;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
use MailPoet\Premium\Models\DynamicSegmentFilter;
|
||||
|
||||
class Loader {
|
||||
|
||||
/** @var DBMapper */
|
||||
private $mapper;
|
||||
|
||||
public function __construct(DBMapper $mapper) {
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DynamicSegment[]
|
||||
*/
|
||||
function load() {
|
||||
$segments = DynamicSegment::findAll();
|
||||
return $this->loadFilters($segments);
|
||||
}
|
||||
|
||||
private function loadFilters(array $segments) {
|
||||
$ids = array_map(function($segment) {
|
||||
return $segment->id;
|
||||
}, $segments);
|
||||
$filters = DynamicSegmentFilter::getAllBySegmentIds($ids);
|
||||
|
||||
return $this->mapper->mapSegments($segments, $filters);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Persistence\Loading;
|
||||
|
||||
use MailPoet\Premium\DynamicSegments\Mappers\DBMapper;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class SingleSegmentLoader {
|
||||
|
||||
/** @var DBMapper */
|
||||
private $mapper;
|
||||
|
||||
public function __construct(DBMapper $mapper) {
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $segment_id
|
||||
* @return DynamicSegment
|
||||
*/
|
||||
function load($segment_id) {
|
||||
|
||||
$segment = DynamicSegment::findOne($segment_id);
|
||||
if (!$segment instanceof DynamicSegment) {
|
||||
throw new \InvalidArgumentException('Segment not found');
|
||||
}
|
||||
|
||||
$filters = $segment->dynamicSegmentFilters()->findMany();
|
||||
|
||||
return $this->mapper->mapSegment($segment, $filters);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
23
lib/DynamicSegments/Persistence/Loading/SubscribersCount.php
Normal file
23
lib/DynamicSegments/Persistence/Loading/SubscribersCount.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Persistence\Loading;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class SubscribersCount {
|
||||
|
||||
/**
|
||||
* @param DynamicSegment $dynamic_segment
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function getSubscribersCount(DynamicSegment $dynamic_segment) {
|
||||
$orm = Subscriber::selectExpr('count(distinct ' . Subscriber::$_table . '.id) as cnt');
|
||||
foreach ($dynamic_segment->getFilters() as $filter) {
|
||||
$orm = $filter->toSql($orm);
|
||||
}
|
||||
return $orm->findOne()->cnt;
|
||||
}
|
||||
|
||||
}
|
29
lib/DynamicSegments/Persistence/Loading/SubscribersIds.php
Normal file
29
lib/DynamicSegments/Persistence/Loading/SubscribersIds.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Persistence\Loading;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
|
||||
class SubscribersIds {
|
||||
|
||||
/**
|
||||
* Finds subscribers in a dynamic segment and returns their ids.
|
||||
*
|
||||
* @param DynamicSegment $dynamic_segment
|
||||
* @param array $limit_to_subscribers_ids If passed the result will be limited only to ids within this array
|
||||
*
|
||||
* @return Subscriber[]
|
||||
*/
|
||||
function load(DynamicSegment $dynamic_segment, $limit_to_subscribers_ids = null) {
|
||||
$orm = Subscriber::selectExpr(Subscriber::$_table . '.id');
|
||||
foreach ($dynamic_segment->getFilters() as $filter) {
|
||||
$orm = $filter->toSql($orm);
|
||||
}
|
||||
if ($limit_to_subscribers_ids) {
|
||||
$orm->whereIn(Subscriber::$_table . '.id', $limit_to_subscribers_ids);
|
||||
}
|
||||
return $orm->findMany();
|
||||
}
|
||||
|
||||
}
|
77
lib/DynamicSegments/Persistence/Saver.php
Normal file
77
lib/DynamicSegments/Persistence/Saver.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Premium\DynamicSegments\Persistence;
|
||||
|
||||
use MailPoet\Models\Model;
|
||||
use MailPoet\Premium\DynamicSegments\Exceptions\ErrorSavingException;
|
||||
use MailPoet\Premium\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\Premium\Models\DynamicSegment;
|
||||
use MailPoet\Premium\Models\DynamicSegmentFilter;
|
||||
|
||||
class Saver {
|
||||
|
||||
/**
|
||||
* @param DynamicSegment $segment
|
||||
*
|
||||
* @return integer
|
||||
* @throws ErrorSavingException
|
||||
*/
|
||||
function save(DynamicSegment $segment) {
|
||||
$db = \ORM::get_db();
|
||||
$db->beginTransaction();
|
||||
|
||||
$data_segment = $this->saveSegment($db, $segment);
|
||||
$this->saveFilters($db, $segment, $data_segment->id());
|
||||
$db->commit();
|
||||
return $data_segment->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ErrorSavingException
|
||||
*/
|
||||
private function saveSegment(\PDO $db, DynamicSegment $segment) {
|
||||
$segment->save();
|
||||
$this->checkErrors($db, $segment);
|
||||
return $segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ErrorSavingException
|
||||
*/
|
||||
private function checkErrors(\PDO $db, Model $model) {
|
||||
$errors = $model->getErrors();
|
||||
if ($errors) {
|
||||
$code = null;
|
||||
if (array_key_exists(Model::DUPLICATE_RECORD, $errors)) {
|
||||
$code = Model::DUPLICATE_RECORD;
|
||||
}
|
||||
$db->rollBack();
|
||||
throw new ErrorSavingException(join(", ", $model->getErrors()), $code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ErrorSavingException
|
||||
*/
|
||||
private function saveFilters(\PDO $db, DynamicSegment $segment, $saved_data_id) {
|
||||
$this->deleteFilters($segment);
|
||||
foreach ($segment->getFilters() as $filter) {
|
||||
$data_filter = $this->saveFilter($filter, $saved_data_id);
|
||||
$this->checkErrors($db, $data_filter);
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteFilters(DynamicSegment $segment) {
|
||||
DynamicSegmentFilter::deleteAllBySegmentIds([$segment->id]);
|
||||
}
|
||||
|
||||
private function saveFilter(Filter $filter, $data_segment_id) {
|
||||
$data_filter = DynamicSegmentFilter::create();
|
||||
if ($data_filter instanceof DynamicSegmentFilter) {
|
||||
$data_filter->segment_id = $data_segment_id;
|
||||
$data_filter->filter_data = $filter->toArray();
|
||||
$data_filter->save();
|
||||
}
|
||||
return $data_filter;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user