OwlCyberSecurity - MANAGER
Edit File: WooCommercePurchasedWithAttribute.php
<?php declare(strict_types = 1); namespace MailPoet\Segments\DynamicSegments\Filters; if (!defined('ABSPATH')) exit; use MailPoet\Entities\DynamicSegmentFilterData; use MailPoet\Entities\DynamicSegmentFilterEntity; use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException; use MailPoet\WP\Functions; use MailPoetVendor\Doctrine\DBAL\ArrayParameterType; use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder; class WooCommercePurchasedWithAttribute implements Filter { const ACTION = 'purchasedWithAttribute'; const TYPE_LOCAL = 'local'; const TYPE_TAXONOMY = 'taxonomy'; private WooFilterHelper $wooFilterHelper; private FilterHelper $filterHelper; private Functions $wp; public function __construct( FilterHelper $filterHelper, WooFilterHelper $wooFilterHelper, Functions $wp ) { $this->wooFilterHelper = $wooFilterHelper; $this->filterHelper = $filterHelper; $this->wp = $wp; } public function apply(QueryBuilder $queryBuilder, DynamicSegmentFilterEntity $filter): QueryBuilder { $filterData = $filter->getFilterData(); $this->validateFilterData((array)$filterData->getData()); $type = $filterData->getStringParam('attribute_type'); if ($type === self::TYPE_LOCAL) { $this->applyForLocalAttribute($queryBuilder, $filterData); } elseif ($type === self::TYPE_TAXONOMY) { $this->applyForTaxonomyAttribute($queryBuilder, $filterData); } return $queryBuilder; } private function applyForTaxonomyAnyOperator(QueryBuilder $queryBuilder, DynamicSegmentFilterData $filterData): void { $attributeTaxonomySlug = $filterData->getStringParam('attribute_taxonomy_slug'); $attributeTermIds = $filterData->getArrayParam('attribute_term_ids'); $termIdsParam = $this->filterHelper->getUniqueParameterName('attribute_term_ids'); $orderStatsAlias = $this->wooFilterHelper->applyOrderStatusFilter($queryBuilder); $productAlias = $this->applyProductJoin($queryBuilder, $orderStatsAlias); $attributeAlias = $this->applyTaxonomyAttributeJoin($queryBuilder, $productAlias, $attributeTaxonomySlug); $queryBuilder->andWhere("$attributeAlias.term_id IN (:$termIdsParam)"); $queryBuilder->setParameter($termIdsParam, $attributeTermIds, ArrayParameterType::STRING); } private function applyProductJoin(QueryBuilder $queryBuilder, string $orderStatsAlias, string $alias = 'product'): string { $queryBuilder->innerJoin( $orderStatsAlias, $this->filterHelper->getPrefixedTable('wc_order_product_lookup'), $alias, "$orderStatsAlias.order_id = product.order_id" ); return $alias; } private function applyTaxonomyAttributeJoin(QueryBuilder $queryBuilder, string $productAlias, $taxonomySlug, string $alias = 'attribute'): string { $queryBuilder->innerJoin( $productAlias, $this->filterHelper->getPrefixedTable('wc_product_attributes_lookup'), $alias, "product.product_id = attribute.product_id AND attribute.taxonomy = '$taxonomySlug'" ); return $alias; } public function getLookupData(DynamicSegmentFilterData $filterData): array { $type = $filterData->getStringParam('attribute_type'); if ($type !== self::TYPE_TAXONOMY) { return []; } $slug = $filterData->getStringParam('attribute_taxonomy_slug'); $lookupData = [ 'attribute' => $slug, ]; $termIds = $filterData->getArrayParam('attribute_term_ids'); $terms = $this->wp->getTerms([ 'taxonomy' => $slug, 'include' => $termIds, 'hide_empty' => false, ]); $lookupData['terms'] = array_map(function($term) { return $term->name; }, $terms); return $lookupData; } public function validateFilterData(array $data): void { $operator = $data['operator'] ?? null; if ( !in_array($operator, [ DynamicSegmentFilterData::OPERATOR_ANY, DynamicSegmentFilterData::OPERATOR_ALL, DynamicSegmentFilterData::OPERATOR_NONE, ]) ) { throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR); } $this->validateAttributeData($data); } public function validateAttributeData(array $data): void { $type = $data['attribute_type']; if (!in_array($type, [self::TYPE_LOCAL, self::TYPE_TAXONOMY], true)) { throw new InvalidFilterException('Invalid attribute type', InvalidFilterException::INVALID_TYPE); } if ($type === self::TYPE_LOCAL) { $name = $data['attribute_local_name'] ?? null; if (!is_string($name) || strlen($name) === 0) { throw new InvalidFilterException('Missing attribute', InvalidFilterException::MISSING_VALUE); } $values = $data['attribute_local_values'] ?? []; if (!is_array($values) || count($values) === 0) { throw new InvalidFilterException('Missing attribute values', InvalidFilterException::MISSING_VALUE); } } if ($type === self::TYPE_TAXONOMY) { $attribute_taxonomy_slug = $data['attribute_taxonomy_slug'] ?? null; if (!is_string($attribute_taxonomy_slug) || strlen($attribute_taxonomy_slug) === 0) { throw new InvalidFilterException('Missing attribute', InvalidFilterException::MISSING_VALUE); } if (!isset($data['attribute_term_ids']) || !is_array($data['attribute_term_ids']) || count($data['attribute_term_ids']) === 0) { throw new InvalidFilterException('Missing attribute terms', InvalidFilterException::MISSING_VALUE); } } } private function applyForTaxonomyAttribute(QueryBuilder $queryBuilder, DynamicSegmentFilterData $filterData) { $operator = $filterData->getOperator(); if ($operator === DynamicSegmentFilterData::OPERATOR_ANY) { $this->applyForTaxonomyAnyOperator($queryBuilder, $filterData); } elseif ($operator === DynamicSegmentFilterData::OPERATOR_ALL) { $this->applyForTaxonomyAnyOperator($queryBuilder, $filterData); $countParam = $this->filterHelper->getUniqueParameterName('count'); $queryBuilder ->groupBy('inner_subscriber_id') ->having("COUNT(DISTINCT attribute.term_id) = :$countParam") ->setParameter($countParam, count($filterData->getArrayParam('attribute_term_ids'))); } elseif ($operator === DynamicSegmentFilterData::OPERATOR_NONE) { $subQuery = $this->filterHelper->getNewSubscribersQueryBuilder(); $this->applyForTaxonomyAnyOperator($subQuery, $filterData); $subscribersTable = $this->filterHelper->getSubscribersTable(); $queryBuilder->where("{$subscribersTable}.id NOT IN ({$this->filterHelper->getInterpolatedSQL($subQuery)})"); } } private function applyForLocalAttribute(QueryBuilder $queryBuilder, DynamicSegmentFilterData $filterData): void { $operator = $filterData->getOperator(); if ($operator === DynamicSegmentFilterData::OPERATOR_ANY) { $this->applyForLocalAnyAttribute($queryBuilder, $filterData); } elseif ($operator === DynamicSegmentFilterData::OPERATOR_ALL) { $this->applyForLocalAnyAttribute($queryBuilder, $filterData); $countParam = $this->filterHelper->getUniqueParameterName('count'); $queryBuilder ->groupBy('inner_subscriber_id') ->having("COUNT(DISTINCT postmeta.meta_value) = :$countParam") ->setParameter($countParam, count($filterData->getArrayParam('attribute_local_values'))); } elseif ($operator === DynamicSegmentFilterData::OPERATOR_NONE) { $subQuery = $this->filterHelper->getNewSubscribersQueryBuilder(); $this->applyForLocalAnyAttribute($subQuery, $filterData); $subscribersTable = $this->filterHelper->getSubscribersTable(); $queryBuilder->where("{$subscribersTable}.id NOT IN ({$this->filterHelper->getInterpolatedSQL($subQuery)})"); } } private function applyForLocalAnyAttribute(QueryBuilder $queryBuilder, DynamicSegmentFilterData $filterData): void { $attributeName = $filterData->getStringParam('attribute_local_name'); $attributeValues = $filterData->getArrayParam('attribute_local_values'); $valuesParam = $this->filterHelper->getUniqueParameterName('attribute_values'); $keyParam = $this->filterHelper->getUniqueParameterName('attribute_name'); $orderStatsAlias = $this->wooFilterHelper->applyOrderStatusFilter($queryBuilder); $productAlias = $this->applyProductJoin($queryBuilder, $orderStatsAlias); $queryBuilder->innerJoin( $productAlias, $this->filterHelper->getPrefixedTable('postmeta'), 'postmeta', "$productAlias.product_id = postmeta.post_id AND postmeta.meta_key = :$keyParam AND postmeta.meta_value IN (:$valuesParam)" ); $queryBuilder->setParameter($keyParam, sprintf("attribute_%s", $attributeName)); $queryBuilder->setParameter($valuesParam, $attributeValues, ArrayParameterType::STRING); } }