Add support for more product ids in filter

[MAILPOET-3954]
This commit is contained in:
Jan Lysý
2021-11-25 17:37:50 +01:00
committed by Veljko V
parent bdd4587fd3
commit d3ee546518
4 changed files with 71 additions and 9 deletions

View File

@ -150,6 +150,8 @@ class DynamicSegments extends APIEndpoint {
return WPFunctions::get()->__('Please select a type for the comparison, an amount and a number of days.', 'mailpoet');
case InvalidFilterException::MISSING_FILTER:
return WPFunctions::get()->__('Please add at least one condition for filtering.', 'mailpoet');
case InvalidFilterException::MISSING_OPERATOR:
return WPFunctions::get()->__('Please select a type for the comparison.', 'mailpoet');
default:
return WPFunctions::get()->__('An error occurred while saving data.', 'mailpoet');
}

View File

@ -19,4 +19,5 @@ class InvalidFilterException extends InvalidStateException {
const INVALID_DATE_VALUE = 12;
const MISSING_COUNTRY = 13;
const MISSING_FILTER = 14;
const MISSING_OPERATOR = 15;
};

View File

@ -171,8 +171,10 @@ class FilterDataMapper {
if (!isset($data['category_id'])) throw new InvalidFilterException('Missing category', InvalidFilterException::MISSING_CATEGORY_ID);
$filterData['category_id'] = $data['category_id'];
} elseif ($data['action'] === WooCommerceProduct::ACTION_PRODUCT) {
if (!isset($data['product_id'])) throw new InvalidFilterException('Missing product', InvalidFilterException::MISSING_PRODUCT_ID);
$filterData['product_id'] = $data['product_id'];
if (!isset($data['product_ids'])) throw new InvalidFilterException('Missing product', InvalidFilterException::MISSING_PRODUCT_ID);
if (!isset($data['operator'])) throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
$filterData['operator'] = $data['operator'];
$filterData['product_ids'] = $data['product_ids'];
} elseif ($data['action'] === WooCommerceCountry::ACTION_CUSTOMER_COUNTRY) {
if (!isset($data['country_code'])) throw new InvalidFilterException('Missing country', InvalidFilterException::MISSING_COUNTRY);
$filterData['country_code'] = $data['country_code'];

View File

@ -1,10 +1,12 @@
<?php
<?php declare(strict_types = 1);
namespace MailPoet\Segments\DynamicSegments\Filters;
use MailPoet\Entities\DynamicSegmentFilterData;
use MailPoet\Entities\DynamicSegmentFilterEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Util\Security;
use MailPoetVendor\Doctrine\DBAL\Connection;
use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
use MailPoetVendor\Doctrine\ORM\EntityManager;
@ -23,24 +25,79 @@ class WooCommerceProduct implements Filter {
public function apply(QueryBuilder $queryBuilder, DynamicSegmentFilterEntity $filter): QueryBuilder {
global $wpdb;
$filterData = $filter->getFilterData();
$productId = (int)$filterData->getParam('product_id');
$operator = $filterData->getOperator();
$productIds = $filterData->getParam('product_ids');
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
$parameterSuffix = $filter->getId() ?? Security::generateRandomString();
$completedOrder = "postmeta.post_id NOT IN ( SELECT id FROM {$wpdb->posts} AS p WHERE p.post_status IN ('wc-cancelled', 'wc-failed'))";
if ($operator === DynamicSegmentFilterData::OPERATOR_ANY) {
$this->applyPostmetaJoin($queryBuilder);
$this->applyOrderItemsJoin($queryBuilder);
$this->applyOrderItemmetaJoin($queryBuilder);
$queryBuilder->where("itemmeta.meta_value IN (:products_{$parameterSuffix})");
} elseif ($operator === DynamicSegmentFilterData::OPERATOR_ALL) {
$this->applyPostmetaJoin($queryBuilder);
$this->applyOrderItemsJoin($queryBuilder);
$this->applyOrderItemmetaJoin($queryBuilder);
$queryBuilder->where("itemmeta.meta_value IN (:products_{$parameterSuffix})")
->groupBy("{$subscribersTable}.id, items.order_id")
->having('COUNT(items.order_id) = :count')
->setParameter('count', count($productIds));
} elseif ($operator === DynamicSegmentFilterData::OPERATOR_NONE) {
$this->applyPostmetaJoin($queryBuilder);
$this->applyOrderItemsJoin($queryBuilder);
// subQuery with subscriber ids that bought products
$subQuery = $this->createQueryBuilder($subscribersTable);
$subQuery->select("DISTINCT $subscribersTable.id");
$subQuery = $this->applyPostmetaJoin($subQuery);
$subQuery = $this->applyOrderItemsJoin($subQuery);
$subQuery = $this->applyOrderItemmetaJoin($subQuery);
$subQuery->where("itemmeta.meta_value IN (:products_{$parameterSuffix})")
->andWhere($completedOrder);
// application subQuery for negation
$queryBuilder->where("{$subscribersTable}.id NOT IN ({$subQuery->getSQL()})");
}
return $queryBuilder
->andWhere($completedOrder)
->setParameter("products_{$parameterSuffix}", $productIds, Connection::PARAM_STR_ARRAY);
}
private function applyPostmetaJoin(QueryBuilder $queryBuilder): QueryBuilder {
global $wpdb;
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
return $queryBuilder->innerJoin(
$subscribersTable,
$wpdb->postmeta,
'postmeta',
"postmeta.meta_key = '_customer_user' AND $subscribersTable.wp_user_id=postmeta.meta_value"
)->join('postmeta',
);
}
private function applyOrderItemsJoin(QueryBuilder $queryBuilder): QueryBuilder {
global $wpdb;
return $queryBuilder->join('postmeta',
$wpdb->prefix . 'woocommerce_order_items',
'items',
'postmeta.post_id = items.order_id'
)->innerJoin(
);
}
private function applyOrderItemmetaJoin(QueryBuilder $queryBuilder): QueryBuilder {
global $wpdb;
return $queryBuilder->innerJoin(
'items',
$wpdb->prefix . 'woocommerce_order_itemmeta',
'itemmeta',
"itemmeta.order_item_id=items.order_item_id AND itemmeta.meta_key='_product_id' AND itemmeta.meta_value=:product" . $parameterSuffix
)->andWhere('postmeta.post_id NOT IN ( SELECT id FROM ' . $wpdb->posts . ' as p WHERE p.post_status IN ("wc-cancelled", "wc-failed"))'
)->setParameter('product' . $parameterSuffix, $productId);
"itemmeta.order_item_id=items.order_item_id AND itemmeta.meta_key='_product_id'"
);
}
private function createQueryBuilder(string $table): QueryBuilder {
return $this->entityManager->getConnection()
->createQueryBuilder()
->from($table);
}
}