init
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\AutomaticEmails\WooCommerce\Events;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce as WooCommerceEmail;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Newsletter\Scheduler\AutomaticEmailScheduler;
|
||||
use MailPoet\Statistics\Track\SubscriberActivityTracker;
|
||||
use MailPoet\Statistics\Track\SubscriberCookie;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\WooCommerce\Helper as WooCommerceHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class AbandonedCart {
|
||||
const SLUG = 'woocommerce_abandoned_shopping_cart';
|
||||
const TASK_META_NAME = 'cart_product_ids';
|
||||
|
||||
|
||||
const HOOK_SCHEDULE = 'mailpoet_abandoned_cart_schedule';
|
||||
const HOOK_RE_SCHEDULE = 'mailpoet_abandoned_cart_reschedule';
|
||||
const HOOK_CANCEL = 'mailpoet_abandoned_cart_cancel';
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var WooCommerceHelper */
|
||||
private $wooCommerceHelper;
|
||||
|
||||
/** @var SubscriberCookie */
|
||||
private $subscriberCookie;
|
||||
|
||||
/** @var AutomaticEmailScheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var SubscriberActivityTracker */
|
||||
private $subscriberActivityTracker;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var bool */
|
||||
private $loadSavedCartAfterLogin;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
WooCommerceHelper $wooCommerceHelper,
|
||||
SubscriberCookie $subscriberCookie,
|
||||
SubscriberActivityTracker $subscriberActivityTracker,
|
||||
AutomaticEmailScheduler $scheduler,
|
||||
SubscribersRepository $subscribersRepository
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->wooCommerceHelper = $wooCommerceHelper;
|
||||
$this->subscriberCookie = $subscriberCookie;
|
||||
$this->subscriberActivityTracker = $subscriberActivityTracker;
|
||||
$this->scheduler = $scheduler;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
}
|
||||
|
||||
public function getEventDetails() {
|
||||
return [
|
||||
'slug' => self::SLUG,
|
||||
'title' => _x('Abandoned Shopping Cart', 'This is the name of a type of automatic email for ecommerce. Those emails are sent automatically when a customer adds product to his shopping cart but never complete the checkout process.', 'mailpoet'),
|
||||
'description' => __('Send an email to logged-in visitors who have items in their shopping carts but left your website without checking out. Can convert up to 5% of abandoned carts.', 'mailpoet'),
|
||||
'listingScheduleDisplayText' => _x('Send the email when a customer abandons their cart.', 'Description of Abandoned Shopping Cart email', 'mailpoet'),
|
||||
'afterDelayText' => __('after abandoning the cart', 'mailpoet'),
|
||||
'badge' => [
|
||||
'text' => __('Must-have', 'mailpoet'),
|
||||
'style' => 'red',
|
||||
],
|
||||
'timeDelayValues' => [
|
||||
'minutes' => [
|
||||
'text' => __('minute(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'hours' => [
|
||||
'text' => __('hour(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'days' => [
|
||||
'text' => __('day(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'weeks' => [
|
||||
'text' => __('week(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
],
|
||||
'defaultAfterTimeType' => 'minutes',
|
||||
'schedulingReadMoreLink' => [
|
||||
'link' => 'https://www.mailpoet.com/blog/abandoned-cart-woocommerce',
|
||||
'text' => __('We recommend setting up 3 abandoned cart emails. Here’s why.', 'mailpoet'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function init() {
|
||||
if (!$this->wooCommerceHelper->isWooCommerceActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// item added to cart (not fired on quantity changes)
|
||||
$this->wp->addAction(
|
||||
'woocommerce_add_to_cart',
|
||||
[$this, 'handleCartChange'],
|
||||
10
|
||||
);
|
||||
|
||||
// item removed from cart (not fired on quantity changes, not even change to zero)
|
||||
$this->wp->addAction(
|
||||
'woocommerce_cart_item_removed',
|
||||
[$this, 'handleCartChange'],
|
||||
10
|
||||
);
|
||||
|
||||
// item quantity updated (not fired when quantity updated to zero)
|
||||
$this->wp->addAction(
|
||||
'woocommerce_after_cart_item_quantity_update',
|
||||
[$this, 'handleCartChange'],
|
||||
10
|
||||
);
|
||||
|
||||
// item quantity set to zero
|
||||
$this->wp->addAction(
|
||||
'woocommerce_remove_cart_item',
|
||||
[$this, 'handleCartChange'],
|
||||
10
|
||||
);
|
||||
|
||||
// cart emptied (not called when all items removed)
|
||||
$this->wp->addAction(
|
||||
'woocommerce_cart_emptied',
|
||||
[$this, 'handleCartChange'],
|
||||
10
|
||||
);
|
||||
|
||||
// undo removal of item from cart or cart emptying (does not fire any other cart change hook)
|
||||
$this->wp->addAction(
|
||||
'woocommerce_cart_item_restored',
|
||||
[$this, 'handleCartChange'],
|
||||
10
|
||||
);
|
||||
|
||||
// we should handle loading cart from session if user logs in
|
||||
$this->wp->addAction(
|
||||
'woocommerce_load_cart_from_session',
|
||||
[$this, 'handleUserLogin'],
|
||||
10
|
||||
);
|
||||
|
||||
// cart loaded from session (only processed after login, not on every page load)
|
||||
$this->wp->addAction(
|
||||
'woocommerce_cart_loaded_from_session',
|
||||
[$this, 'handleCartChangeOnLogin'],
|
||||
10
|
||||
);
|
||||
|
||||
$this->subscriberActivityTracker->registerCallback(
|
||||
'mailpoet_abandoned_cart',
|
||||
[$this, 'handleSubscriberActivity']
|
||||
);
|
||||
}
|
||||
|
||||
public function handleCartChange() {
|
||||
$cart = $this->wooCommerceHelper->WC()->cart;
|
||||
|
||||
$currentAction = current_action();
|
||||
if ($currentAction !== 'woocommerce_cart_emptied' && $cart && !$cart->is_empty()) {
|
||||
$this->scheduleAbandonedCartEmail($this->getCartProductIds($cart));
|
||||
} else {
|
||||
$this->cancelAbandonedCartEmail();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleUserLogin() {
|
||||
$wpUserId = $this->wp->getCurrentUserId();
|
||||
if (!$wpUserId) {
|
||||
return false;
|
||||
}
|
||||
$this->loadSavedCartAfterLogin = (bool)$this->wp->getUserMeta($wpUserId, '_woocommerce_load_saved_cart_after_login', true);
|
||||
}
|
||||
|
||||
public function handleCartChangeOnLogin() {
|
||||
if (!$this->loadSavedCartAfterLogin) {
|
||||
return false;
|
||||
}
|
||||
$this->handleCartChange();
|
||||
}
|
||||
|
||||
public function handleSubscriberActivity(SubscriberEntity $subscriber) {
|
||||
// on subscriber activity on site reschedule all currently scheduled (not yet sent) emails for given subscriber
|
||||
// (it tracks at most once per minute to avoid processing many calls at the same time, i.e. AJAX)
|
||||
$this->rescheduleAbandonedCartEmail($subscriber);
|
||||
}
|
||||
|
||||
private function getCartProductIds($cart) {
|
||||
$cartItems = $cart->get_cart() ?: [];
|
||||
return array_column($cartItems, 'product_id');
|
||||
}
|
||||
|
||||
private function scheduleAbandonedCartEmail(array $cartProductIds = []) {
|
||||
$subscriber = $this->getSubscriber();
|
||||
if (!$subscriber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->wp->doAction(self::HOOK_SCHEDULE, $subscriber, $cartProductIds);
|
||||
$meta = [self::TASK_META_NAME => $cartProductIds];
|
||||
$this->scheduler->scheduleOrRescheduleAutomaticEmail(WooCommerceEmail::SLUG, self::SLUG, $subscriber, $meta);
|
||||
}
|
||||
|
||||
private function rescheduleAbandonedCartEmail(SubscriberEntity $subscriber) {
|
||||
$this->wp->doAction(self::HOOK_RE_SCHEDULE, $subscriber);
|
||||
$this->scheduler->rescheduleAutomaticEmail(WooCommerceEmail::SLUG, self::SLUG, $subscriber);
|
||||
}
|
||||
|
||||
private function cancelAbandonedCartEmail() {
|
||||
$subscriber = $this->getSubscriber();
|
||||
if (!$subscriber) {
|
||||
return;
|
||||
}
|
||||
$this->wp->doAction(self::HOOK_CANCEL, $subscriber);
|
||||
$this->scheduler->cancelAutomaticEmail(WooCommerceEmail::SLUG, self::SLUG, $subscriber);
|
||||
}
|
||||
|
||||
private function getSubscriber(): ?SubscriberEntity {
|
||||
$wpUser = $this->wp->wpGetCurrentUser();
|
||||
if ($wpUser->exists()) {
|
||||
return $this->subscribersRepository->findOneBy(['wpUserId' => $wpUser->ID]);
|
||||
}
|
||||
|
||||
// if user not logged in, try to find subscriber by cookie
|
||||
$subscriberId = $this->subscriberCookie->getSubscriberId();
|
||||
if ($subscriberId) {
|
||||
return $this->subscribersRepository->findOneById($subscriberId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\AutomaticEmails\WooCommerce\Events;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce;
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Newsletter\AutomaticEmailsRepository;
|
||||
use MailPoet\Newsletter\Scheduler\AutomaticEmailScheduler;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\WooCommerce\Helper as WCHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class FirstPurchase {
|
||||
const SLUG = 'woocommerce_first_purchase';
|
||||
const ORDER_TOTAL_SHORTCODE = '[woocommerce:order_total]';
|
||||
const ORDER_DATE_SHORTCODE = '[woocommerce:order_date]';
|
||||
/**
|
||||
* @var \MailPoet\WooCommerce\Helper
|
||||
*/
|
||||
private $helper;
|
||||
|
||||
/** @var AutomaticEmailScheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var LoggerFactory */
|
||||
private $loggerFactory;
|
||||
|
||||
/** @var AutomaticEmailsRepository */
|
||||
private $automaticEmailsRepository;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
WCHelper $helper = null
|
||||
) {
|
||||
if ($helper === null) {
|
||||
$helper = ContainerWrapper::getInstance()->get(WCHelper::class);
|
||||
}
|
||||
$this->helper = $helper;
|
||||
$this->scheduler = ContainerWrapper::getInstance()->get(AutomaticEmailScheduler::class);
|
||||
$this->loggerFactory = LoggerFactory::getInstance();
|
||||
$this->automaticEmailsRepository = ContainerWrapper::getInstance()->get(AutomaticEmailsRepository::class);
|
||||
$this->subscribersRepository = ContainerWrapper::getInstance()->get(SubscribersRepository::class);
|
||||
}
|
||||
|
||||
public function init() {
|
||||
WPFunctions::get()->addFilter('mailpoet_newsletter_shortcode', [
|
||||
$this,
|
||||
'handleOrderTotalShortcode',
|
||||
], 10, 4);
|
||||
WPFunctions::get()->addFilter('mailpoet_newsletter_shortcode', [
|
||||
$this,
|
||||
'handleOrderDateShortcode',
|
||||
], 10, 4);
|
||||
|
||||
// We have to use a set of states because an order state after checkout differs for different payment methods
|
||||
$acceptedOrderStates = WPFunctions::get()->applyFilters('mailpoet_first_purchase_order_states', ['completed', 'processing']);
|
||||
|
||||
foreach ($acceptedOrderStates as $state) {
|
||||
WPFunctions::get()->addAction('woocommerce_order_status_' . $state, [
|
||||
$this,
|
||||
'scheduleEmailWhenOrderIsPlaced',
|
||||
], 10, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function getEventDetails() {
|
||||
return [
|
||||
'slug' => self::SLUG,
|
||||
'title' => __('First Purchase', 'mailpoet'),
|
||||
'description' => __('Let MailPoet send an email to customers who make their first purchase.', 'mailpoet'),
|
||||
'listingScheduleDisplayText' => __('Email sent when a customer makes their first purchase.', 'mailpoet'),
|
||||
'afterDelayText' => __('after the first purchase', 'mailpoet'),
|
||||
'badge' => [
|
||||
'text' => __('Must-have', 'mailpoet'),
|
||||
'style' => 'red',
|
||||
],
|
||||
'timeDelayValues' => [
|
||||
'immediate' => [
|
||||
'text' => __('immediately', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => false,
|
||||
],
|
||||
'minutes' => [
|
||||
'text' => __('minute(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'hours' => [
|
||||
'text' => __('hour(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'days' => [
|
||||
'text' => __('day(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'weeks' => [
|
||||
'text' => __('week(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
],
|
||||
'shortcodes' => [
|
||||
[
|
||||
'text' => __('Order amount', 'mailpoet'),
|
||||
'shortcode' => self::ORDER_TOTAL_SHORTCODE,
|
||||
],
|
||||
[
|
||||
'text' => __('Order date', 'mailpoet'),
|
||||
'shortcode' => self::ORDER_DATE_SHORTCODE,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function handleOrderDateShortcode($shortcode, $newsletter, $subscriber, $queue) {
|
||||
$result = $shortcode;
|
||||
if ($shortcode === self::ORDER_DATE_SHORTCODE) {
|
||||
$defaultValue = WPFunctions::get()->dateI18n(get_option('date_format'));
|
||||
if (!$queue) {
|
||||
$result = $defaultValue;
|
||||
} else {
|
||||
$meta = $queue->getMeta();
|
||||
$result = (!empty($meta['order_date'])) ? WPFunctions::get()->dateI18n(get_option('date_format'), $meta['order_date']) : $defaultValue;
|
||||
}
|
||||
}
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'handleOrderDateShortcode called',
|
||||
[
|
||||
'newsletter_id' => ($newsletter instanceof NewsletterEntity) ? $newsletter->getId() : null,
|
||||
'subscriber_id' => ($subscriber instanceof SubscriberEntity) ? $subscriber->getId() : null,
|
||||
'task_id' => ($queue instanceof SendingQueueEntity) ? (($task = $queue->getTask()) ? $task->getId() : null) : null,
|
||||
'shortcode' => $shortcode,
|
||||
'result' => $result,
|
||||
]
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function handleOrderTotalShortcode($shortcode, $newsletter, $subscriber, $queue) {
|
||||
$result = $shortcode;
|
||||
if ($shortcode === self::ORDER_TOTAL_SHORTCODE) {
|
||||
$defaultValue = $this->helper->wcPrice(0);
|
||||
if (!$queue) {
|
||||
$result = $defaultValue;
|
||||
} else {
|
||||
$meta = $queue->getMeta();
|
||||
$result = (!empty($meta['order_amount'])) ? $this->helper->wcPrice($meta['order_amount']) : $defaultValue;
|
||||
}
|
||||
}
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'handleOrderTotalShortcode called',
|
||||
[
|
||||
'newsletter_id' => ($newsletter instanceof NewsletterEntity) ? $newsletter->getId() : null,
|
||||
'subscriber_id' => ($subscriber instanceof SubscriberEntity) ? $subscriber->getId() : null,
|
||||
'task_id' => ($queue instanceof SendingQueueEntity) ? (($task = $queue->getTask()) ? $task->getId() : null) : null,
|
||||
'shortcode' => $shortcode,
|
||||
'result' => $result,
|
||||
]
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function scheduleEmailWhenOrderIsPlaced($orderId) {
|
||||
$orderDetails = $this->helper->wcGetOrder($orderId);
|
||||
if (!$orderDetails || !$orderDetails->get_billing_email()) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because the order customer was not found',
|
||||
['order_id' => $orderId]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$customerEmail = $orderDetails->get_billing_email();
|
||||
$customerOrderCount = $this->getCustomerOrderCount($customerEmail);
|
||||
if ($customerOrderCount > 1) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because this is not the first order of the customer',
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'customer_email' => $customerEmail,
|
||||
'order_count' => $customerOrderCount,
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$meta = [
|
||||
'order_amount' => $orderDetails->get_total(),
|
||||
'order_date' => $orderDetails->get_date_created()->getTimestamp(),
|
||||
'order_id' => $orderDetails->get_id(),
|
||||
];
|
||||
|
||||
$subscriber = $this->subscribersRepository->getWooCommerceSegmentSubscriber($customerEmail);
|
||||
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because the customer was not found as WooCommerce list subscriber',
|
||||
['order_id' => $orderId, 'customer_email' => $customerEmail]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$checkEmailWasNotScheduled = function (NewsletterEntity $newsletter) use ($subscriber) {
|
||||
return !$this->automaticEmailsRepository->wasScheduledForSubscriber((int)$newsletter->getId(), (int)$subscriber->getId());
|
||||
};
|
||||
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email scheduled',
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'customer_email' => $customerEmail,
|
||||
'subscriber_id' => $subscriber->getId(),
|
||||
]
|
||||
);
|
||||
$this->scheduler->scheduleAutomaticEmail(WooCommerce::SLUG, self::SLUG, $checkEmailWasNotScheduled, $subscriber, $meta);
|
||||
}
|
||||
|
||||
public function getCustomerOrderCount($customerEmail) {
|
||||
// registered user
|
||||
$user = WPFunctions::get()->getUserBy('email', $customerEmail);
|
||||
if ($user) {
|
||||
return $this->helper->wcGetCustomerOrderCount($user->ID);
|
||||
}
|
||||
// guest user
|
||||
return $this->getGuestCustomerOrderCountByEmail($customerEmail);
|
||||
}
|
||||
|
||||
private function getGuestCustomerOrderCountByEmail(string $customerEmail): int {
|
||||
$ordersCount = $this->helper->wcGetOrders(
|
||||
[
|
||||
'status' => 'all',
|
||||
'type' => 'shop_order',
|
||||
'billing_email' => $customerEmail,
|
||||
'limit' => 1,
|
||||
'return' => 'ids',
|
||||
'paginate' => true,
|
||||
]
|
||||
)->total;
|
||||
return intval($ordersCount);
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\AutomaticEmails\WooCommerce\Events;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce;
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Newsletter\AutomaticEmailsRepository;
|
||||
use MailPoet\Newsletter\Scheduler\AutomaticEmailScheduler;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WooCommerce\Helper as WCHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class PurchasedInCategory {
|
||||
const SLUG = 'woocommerce_product_purchased_in_category';
|
||||
|
||||
/** @var WCHelper */
|
||||
private $woocommerceHelper;
|
||||
|
||||
/** @var AutomaticEmailScheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var LoggerFactory */
|
||||
private $loggerFactory;
|
||||
|
||||
/** @var AutomaticEmailsRepository */
|
||||
private $repository;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
WCHelper $woocommerceHelper = null
|
||||
) {
|
||||
if ($woocommerceHelper === null) {
|
||||
$woocommerceHelper = ContainerWrapper::getInstance()->get(WCHelper::class);
|
||||
}
|
||||
$this->woocommerceHelper = $woocommerceHelper;
|
||||
$this->scheduler = ContainerWrapper::getInstance()->get(AutomaticEmailScheduler::class);
|
||||
$this->loggerFactory = LoggerFactory::getInstance();
|
||||
$this->repository = ContainerWrapper::getInstance()->get(AutomaticEmailsRepository::class);
|
||||
$this->subscribersRepository = ContainerWrapper::getInstance()->get(SubscribersRepository::class);
|
||||
}
|
||||
|
||||
public function getEventDetails() {
|
||||
return [
|
||||
'slug' => self::SLUG,
|
||||
'title' => _x('Purchased In This Category', 'This is the name of a type for automatic email for ecommerce. Those emails are sent automatically every time a customer buys for the first time a product in a given category', 'mailpoet'),
|
||||
'description' => __('Let MailPoet send an email to customers who purchase a product for the first time in a specific category.', 'mailpoet'),
|
||||
// translators: %s is the name of the category.
|
||||
'listingScheduleDisplayText' => __('Email sent when a customer buys a product in category: %s', 'mailpoet'),
|
||||
// translators: %s is the name of the category.
|
||||
'listingScheduleDisplayTextPlural' => __('Email sent when a customer buys a product in categories: %s', 'mailpoet'),
|
||||
'afterDelayText' => __('after a purchase', 'mailpoet'),
|
||||
'timeDelayValues' => [
|
||||
'immediate' => [
|
||||
'text' => __('immediately', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => false,
|
||||
],
|
||||
'minutes' => [
|
||||
'text' => __('minute(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'hours' => [
|
||||
'text' => __('hour(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'days' => [
|
||||
'text' => __('day(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'weeks' => [
|
||||
'text' => __('week(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
],
|
||||
'options' => [
|
||||
'multiple' => true,
|
||||
'endpoint' => 'product_categories',
|
||||
'placeholder' => _x('Search category', 'Search input for product category (ecommerce)', 'mailpoet'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function init() {
|
||||
WPFunctions::get()->removeAllFilters('woocommerce_product_purchased_get_categories');
|
||||
WPFunctions::get()->addFilter(
|
||||
'woocommerce_product_purchased_get_categories',
|
||||
[$this, 'getCategories']
|
||||
);
|
||||
|
||||
$acceptedOrderStates = WPFunctions::get()->applyFilters('mailpoet_first_purchase_order_states', ['completed', 'processing']);
|
||||
foreach ($acceptedOrderStates as $state) {
|
||||
WPFunctions::get()->addAction(
|
||||
'woocommerce_order_status_' . $state,
|
||||
[$this, 'scheduleEmail'],
|
||||
10,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCategories($searchQuery) {
|
||||
$args = [
|
||||
'taxonomy' => 'product_cat',
|
||||
'search' => $searchQuery,
|
||||
'orderby' => 'name',
|
||||
'hierarchical' => 0,
|
||||
'hide_empty' => 1,
|
||||
'order' => 'ASC',
|
||||
];
|
||||
$allCategories = get_categories($args);
|
||||
|
||||
return array_map(function($category) {
|
||||
return [
|
||||
'id' => $category->term_id, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'name' => $category->name,
|
||||
];
|
||||
}, $allCategories);
|
||||
}
|
||||
|
||||
public function scheduleEmail($orderId) {
|
||||
$orderDetails = $this->woocommerceHelper->wcGetOrder($orderId);
|
||||
if (!$orderDetails || !$orderDetails->get_billing_email()) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because the order customer was not found',
|
||||
['order_id' => $orderId]
|
||||
);
|
||||
return;
|
||||
}
|
||||
$customerEmail = $orderDetails->get_billing_email();
|
||||
|
||||
$subscriber = $this->subscribersRepository->getWooCommerceSegmentSubscriber($customerEmail);
|
||||
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because the customer was not found as WooCommerce list subscriber',
|
||||
['order_id' => $orderId, 'customer_email' => $customerEmail]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$orderedProductCategories = [];
|
||||
foreach ($orderDetails->get_items() as $orderItemProduct) {
|
||||
$product = $orderItemProduct->get_product();
|
||||
if (!$product instanceof \WC_Product) {
|
||||
continue;
|
||||
}
|
||||
if ($product->get_type() === 'variation') {
|
||||
// WooCommerce returns a empty list when get_category_ids() is called for a product variation,
|
||||
// so we need to get the parent product
|
||||
$product = $this->woocommerceHelper->wcGetProduct($product->get_parent_id());
|
||||
}
|
||||
$orderedProductCategories = array_merge($orderedProductCategories, $product->get_category_ids());
|
||||
}
|
||||
|
||||
$schedulingCondition = function(NewsletterEntity $automaticEmail) use ($orderedProductCategories, $subscriber) {
|
||||
$matchedCategories = $this->getProductCategoryIdsMatchingNewsletterTrigger($automaticEmail, $orderedProductCategories);
|
||||
if (empty($matchedCategories)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->repository->wasScheduledForSubscriber((int)$automaticEmail->getId(), (int)$subscriber->getId())) {
|
||||
$sentAllProducts = $this->repository->alreadySentAllProducts((int)$automaticEmail->getId(), (int)$subscriber->getId(), 'orderedProductCategories', $matchedCategories);
|
||||
if ($sentAllProducts) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email scheduled',
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'customer_email' => $customerEmail,
|
||||
'subscriber_id' => $subscriber->getId(),
|
||||
]
|
||||
);
|
||||
$this->scheduler->scheduleAutomaticEmail(
|
||||
WooCommerce::SLUG,
|
||||
self::SLUG,
|
||||
$schedulingCondition,
|
||||
$subscriber,
|
||||
['orderedProductCategories' => $orderedProductCategories],
|
||||
[$this, 'metaModifier']
|
||||
);
|
||||
}
|
||||
|
||||
public function metaModifier(NewsletterEntity $automaticEmail, array $meta): array {
|
||||
$orderedProductCategoryIds = $meta['orderedProductCategories'] ?? null;
|
||||
if (empty($orderedProductCategoryIds)) {
|
||||
return $meta;
|
||||
}
|
||||
$meta['orderedProductCategories'] = $this->getProductCategoryIdsMatchingNewsletterTrigger($automaticEmail, $orderedProductCategoryIds);
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function getProductCategoryIdsMatchingNewsletterTrigger(NewsletterEntity $automaticEmail, array $orderedCategoryIds): array {
|
||||
$automaticEmailMetaValue = $automaticEmail->getOptionValue(NewsletterOptionFieldEntity::NAME_META);
|
||||
$optionValue = Helpers::isJson($automaticEmailMetaValue) ? json_decode($automaticEmailMetaValue, true) : $automaticEmailMetaValue;
|
||||
|
||||
if (!is_array($optionValue) || empty($optionValue['option'])) {
|
||||
return [];
|
||||
}
|
||||
$emailTriggeringCategoryIds = array_column($optionValue['option'], 'id');
|
||||
|
||||
return array_intersect($emailTriggeringCategoryIds, $orderedCategoryIds);
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\AutomaticEmails\WooCommerce\Events;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce;
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Newsletter\AutomaticEmailsRepository;
|
||||
use MailPoet\Newsletter\Scheduler\AutomaticEmailScheduler;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WooCommerce\Helper as WCHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class PurchasedProduct {
|
||||
const SLUG = 'woocommerce_product_purchased';
|
||||
/**
|
||||
* @var \MailPoet\WooCommerce\Helper
|
||||
*/
|
||||
private $helper;
|
||||
|
||||
/** @var AutomaticEmailScheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var LoggerFactory */
|
||||
private $loggerFactory;
|
||||
|
||||
/** @var AutomaticEmailsRepository */
|
||||
private $repository;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
WCHelper $helper = null
|
||||
) {
|
||||
if ($helper === null) {
|
||||
$helper = ContainerWrapper::getInstance()->get(WCHelper::class);
|
||||
}
|
||||
$this->helper = $helper;
|
||||
$this->scheduler = ContainerWrapper::getInstance()->get(AutomaticEmailScheduler::class);
|
||||
$this->loggerFactory = LoggerFactory::getInstance();
|
||||
$this->repository = ContainerWrapper::getInstance()->get(AutomaticEmailsRepository::class);
|
||||
$this->subscribersRepository = ContainerWrapper::getInstance()->get(SubscribersRepository::class);
|
||||
}
|
||||
|
||||
public function init() {
|
||||
WPFunctions::get()->removeAllFilters('woocommerce_product_purchased_get_products');
|
||||
WPFunctions::get()->addFilter(
|
||||
'woocommerce_product_purchased_get_products',
|
||||
[
|
||||
$this,
|
||||
'getProducts',
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
$acceptedOrderStates = WPFunctions::get()->applyFilters('mailpoet_first_purchase_order_states', ['completed', 'processing']);
|
||||
foreach ($acceptedOrderStates as $state) {
|
||||
WPFunctions::get()->addAction('woocommerce_order_status_' . $state, [
|
||||
$this,
|
||||
'scheduleEmailWhenProductIsPurchased',
|
||||
], 10, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function getEventDetails() {
|
||||
return [
|
||||
'slug' => self::SLUG,
|
||||
'title' => __('Purchased This Product', 'mailpoet'),
|
||||
'description' => __('Let MailPoet send an email to customers who purchase a specific product for the first time.', 'mailpoet'),
|
||||
// translators: %s is the name of the product.
|
||||
'listingScheduleDisplayText' => __('Email sent when a customer buys product: %s', 'mailpoet'),
|
||||
// translators: %s is the name of the products.
|
||||
'listingScheduleDisplayTextPlural' => __('Email sent when a customer buys products: %s', 'mailpoet'),
|
||||
'afterDelayText' => __('after a purchase', 'mailpoet'),
|
||||
'timeDelayValues' => [
|
||||
'immediate' => [
|
||||
'text' => __('immediately', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => false,
|
||||
],
|
||||
'minutes' => [
|
||||
'text' => __('minute(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'hours' => [
|
||||
'text' => __('hour(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'days' => [
|
||||
'text' => __('day(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
'weeks' => [
|
||||
'text' => __('week(s)', 'mailpoet'),
|
||||
'displayAfterTimeNumberField' => true,
|
||||
],
|
||||
],
|
||||
'options' => [
|
||||
'multiple' => true,
|
||||
'endpoint' => 'products',
|
||||
'placeholder' => __('Search products', 'mailpoet'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getProducts($productSearchQuery) {
|
||||
$args = [
|
||||
'post_type' => 'product',
|
||||
'post_status' => 'publish',
|
||||
's' => $productSearchQuery,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
];
|
||||
$woocommerceProducts = new \WP_Query($args);
|
||||
$woocommerceProducts = $woocommerceProducts->get_posts();
|
||||
/** @var \WP_Post[] $woocommerceProducts */
|
||||
if (empty($woocommerceProducts)) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'no products found',
|
||||
['search_query' => $productSearchQuery]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$woocommerceProducts = array_map(function($product) {
|
||||
return [
|
||||
'id' => $product->ID,
|
||||
'name' => $product->post_title, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
];
|
||||
}, $woocommerceProducts);
|
||||
return $woocommerceProducts;
|
||||
}
|
||||
|
||||
public function scheduleEmailWhenProductIsPurchased($orderId) {
|
||||
$orderDetails = $this->helper->wcGetOrder($orderId);
|
||||
if (!$orderDetails || !$orderDetails->get_billing_email()) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because the order customer was not found',
|
||||
['order_id' => $orderId]
|
||||
);
|
||||
return;
|
||||
}
|
||||
$customerEmail = $orderDetails->get_billing_email();
|
||||
|
||||
$subscriber = $this->subscribersRepository->getWooCommerceSegmentSubscriber($customerEmail);
|
||||
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email not scheduled because the customer was not found as WooCommerce list subscriber',
|
||||
['order_id' => $orderId, 'customer_email' => $customerEmail]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$orderedProducts = array_map(function($product) {
|
||||
return ($product instanceof \WC_Order_Item_Product) ? $product->get_product_id() : null;
|
||||
}, $orderDetails->get_items());
|
||||
$orderedProducts = array_values(array_filter($orderedProducts));
|
||||
|
||||
$schedulingCondition = function(NewsletterEntity $automaticEmail) use ($orderedProducts, $subscriber) {
|
||||
$matchedProducts = $this->getProductIdsMatchingNewsletterTrigger($automaticEmail, $orderedProducts);
|
||||
if (empty($matchedProducts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->repository->wasScheduledForSubscriber((int)$automaticEmail->getId(), (int)$subscriber->getId())) {
|
||||
$sentAllProducts = $this->repository->alreadySentAllProducts((int)$automaticEmail->getId(), (int)$subscriber->getId(), 'orderedProducts', $matchedProducts);
|
||||
if ($sentAllProducts) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$this->loggerFactory->getLogger(self::SLUG)->info(
|
||||
'Email scheduled',
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'customer_email' => $customerEmail,
|
||||
'subscriber_id' => $subscriber->getId(),
|
||||
]
|
||||
);
|
||||
return $this->scheduler->scheduleAutomaticEmail(
|
||||
WooCommerce::SLUG,
|
||||
self::SLUG,
|
||||
$schedulingCondition,
|
||||
$subscriber,
|
||||
['orderedProducts' => $orderedProducts],
|
||||
[$this, 'metaModifier']
|
||||
);
|
||||
}
|
||||
|
||||
public function metaModifier(NewsletterEntity $newsletter, array $meta): array {
|
||||
$orderedProductIds = $meta['orderedProducts'] ?? null;
|
||||
if (empty($orderedProductIds)) {
|
||||
return $meta;
|
||||
}
|
||||
$meta['orderedProducts'] = $this->getProductIdsMatchingNewsletterTrigger($newsletter, $orderedProductIds);
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function getProductIdsMatchingNewsletterTrigger(NewsletterEntity $automaticEmail, array $orderedProductIds): array {
|
||||
$automaticEmailMetaValue = $automaticEmail->getOptionValue(NewsletterOptionFieldEntity::NAME_META);
|
||||
$optionValue = Helpers::isJson($automaticEmailMetaValue) ? json_decode($automaticEmailMetaValue, true) : $automaticEmailMetaValue;
|
||||
|
||||
if (!is_array($optionValue) || empty($optionValue['option'])) {
|
||||
return [];
|
||||
}
|
||||
$emailTriggeringProductIds = array_column($optionValue['option'], 'id');
|
||||
|
||||
return array_intersect($emailTriggeringProductIds, $orderedProductIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\AutomaticEmails\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\AutomaticEmails;
|
||||
use MailPoet\WooCommerce\Helper as WooCommerceHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoet\WP\Notice;
|
||||
|
||||
class WooCommerce {
|
||||
const SLUG = 'woocommerce';
|
||||
const EVENTS_FILTER = 'mailpoet_woocommerce_events';
|
||||
|
||||
/** @var WooCommerceHelper */
|
||||
private $woocommerceHelper;
|
||||
|
||||
/** @var string[] */
|
||||
public $availableEvents = [
|
||||
'AbandonedCart',
|
||||
'FirstPurchase',
|
||||
'PurchasedInCategory',
|
||||
'PurchasedProduct',
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
private $woocommerceEnabled;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var WooCommerceEventFactory */
|
||||
private $eventFactory;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
WooCommerceHelper $woocommerceHelper,
|
||||
WooCommerceEventFactory $eventFactory
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->woocommerceHelper = $woocommerceHelper;
|
||||
$this->woocommerceEnabled = $this->isWoocommerceEnabled();
|
||||
$this->eventFactory = $eventFactory;
|
||||
}
|
||||
|
||||
public function init() {
|
||||
$this->wp->addFilter(
|
||||
AutomaticEmails::FILTER_PREFIX . self::SLUG,
|
||||
[
|
||||
$this,
|
||||
'setupGroup',
|
||||
]
|
||||
);
|
||||
$this->wp->addFilter(
|
||||
self::EVENTS_FILTER,
|
||||
[
|
||||
$this,
|
||||
'setupEvents',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function setupGroup() {
|
||||
return [
|
||||
'slug' => self::SLUG,
|
||||
'title' => __('WooCommerce', 'mailpoet'),
|
||||
'description' => __('Automatically send an email based on your customers’ purchase behavior. Enhance your customer service and start increasing sales with WooCommerce follow up emails.', 'mailpoet'),
|
||||
'events' => $this->wp->applyFilters(self::EVENTS_FILTER, []),
|
||||
];
|
||||
}
|
||||
|
||||
public function setupEvents($events) {
|
||||
$customEventDetails = (!$this->woocommerceEnabled) ? [
|
||||
'actionButtonTitle' => __('WooCommerce is required', 'mailpoet'),
|
||||
'actionButtonLink' => 'https://wordpress.org/plugins/woocommerce/',
|
||||
] : [];
|
||||
|
||||
foreach ($this->availableEvents as $event) {
|
||||
$eventInstance = in_array($event, $this->availableEvents, true)
|
||||
? $this->eventFactory->createEvent($event)
|
||||
: null;
|
||||
|
||||
if (!$eventInstance) {
|
||||
$this->displayEventWarning($event);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method_exists($eventInstance, 'init')) {
|
||||
$eventInstance->init();
|
||||
} else {
|
||||
$this->displayEventWarning($event);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method_exists($eventInstance, 'getEventDetails')) {
|
||||
$eventDetails = array_merge($eventInstance->getEventDetails(), $customEventDetails);
|
||||
} else {
|
||||
$this->displayEventWarning($event);
|
||||
continue;
|
||||
}
|
||||
$events[] = $eventDetails;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
public function isWoocommerceEnabled() {
|
||||
return $this->woocommerceHelper->isWooCommerceActive();
|
||||
}
|
||||
|
||||
private function displayEventWarning($event) {
|
||||
$notice = sprintf(
|
||||
'%s %s',
|
||||
// translators: %s is the name of the event.
|
||||
sprintf(__('WooCommerce %s event is misconfigured.', 'mailpoet'), $event),
|
||||
__('Please contact our technical support for assistance.', 'mailpoet')
|
||||
);
|
||||
Notice::displayWarning($notice);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\AutomaticEmails\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\Events\AbandonedCart;
|
||||
use MailPoet\AutomaticEmails\WooCommerce\Events\FirstPurchase;
|
||||
use MailPoet\AutomaticEmails\WooCommerce\Events\PurchasedInCategory;
|
||||
use MailPoet\AutomaticEmails\WooCommerce\Events\PurchasedProduct;
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoetVendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
|
||||
class WooCommerceEventFactory {
|
||||
public const EVENTS_MAP = [
|
||||
'AbandonedCart' => AbandonedCart::class,
|
||||
'FirstPurchase' => FirstPurchase::class,
|
||||
'PurchasedInCategory' => PurchasedInCategory::class,
|
||||
'PurchasedProduct' => PurchasedProduct::class,
|
||||
];
|
||||
|
||||
/** @var ContainerWrapper */
|
||||
private $container;
|
||||
|
||||
public function __construct(
|
||||
ContainerWrapper $container
|
||||
) {
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/** @return object|null */
|
||||
public function createEvent(string $eventName) {
|
||||
$eventClass = self::EVENTS_MAP[$eventName] ?? null;
|
||||
|
||||
try {
|
||||
return $eventClass ? $this->container->get($eventClass) : null;
|
||||
} catch (ServiceNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user