contacts_subscriptions-1.x-dev/src/Event/SubscriptionProductVariationsEvent.php
src/Event/SubscriptionProductVariationsEvent.php
<?php
namespace Drupal\contacts_subscriptions\Event;
use Drupal\commerce_product\Entity\ProductVariationInterface;
use Drupal\Component\EventDispatcher\Event;
use Drupal\contacts_subscriptions\Entity\SubscriptionType;
use Drupal\Core\Entity\ContentEntityStorageInterface;
use Drupal\user\UserInterface;
/**
* Event class for determining the product variations to use for subscriptions.
*/
class SubscriptionProductVariationsEvent extends Event {
/**
* The event name.
*/
const NAME = 'contacts_subscription_get_product_variation';
/**
* Indicates that the specified variation suffix is preferred.
*/
const VARIATION_PREFERRED = 'preferred';
/**
* Indicates the specified variation suffix is required.
*/
const VARIATION_REQUIRED = 'required';
/**
* The product entity storage.
*
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
*/
protected ContentEntityStorageInterface $storage;
/**
* The variation SKU suffix.
*
* @var string|null
*/
protected ?string $variationSuffix = NULL;
/**
* The strength of the variation suffix requirement.
*
* @var string
*/
protected string $variationStrength = self::VARIATION_PREFERRED;
/**
* Products that are allowed.
*
* @var int[]
*/
protected array $allowedProductIds = [];
/**
* Products that are dis-allowed.
*
* @var int[]
*/
protected array $disallowedProductIds = [];
/**
* The user entity.
*
* @var \Drupal\user\UserInterface|null
*/
protected ?UserInterface $user;
/**
* SubscriptionProductVariationEvent constructor.
*
* @param \Drupal\user\UserInterface|null $user
* The user entity, if any.
* @param \Drupal\Core\Entity\ContentEntityStorageInterface $storage
* The product entity storage handler.
* @param string|null $variation_suffix
* The variation suffix, if any, that we want. By default this will be a
* preference. To require a variation suffix, use `::setVariationStrength`.
* @param string|null $subscription_type
* (Optional) The type of subscription.
*/
public function __construct(
?UserInterface $user,
ContentEntityStorageInterface $storage,
?string $variation_suffix = NULL,
?SubscriptionType $subscription_type = NULL
) {
$this->storage = $storage;
if ($variation_suffix) {
$this->variationSuffix = "-{$variation_suffix}";
}
$this->user = $user;
}
/**
* Get the organisation, if any.
*
* @return \Drupal\user\UserInterface|null
* The organisation or NULL.
*/
public function getUser(): ?UserInterface {
return $this->user;
}
/**
* Get the variation suffix.
*
* @return string
* The variation suffix.
*/
public function getVariationSuffix(): string {
return $this->variationSuffix;
}
/**
* Set the variation suffix.
*
* @param string|null $variationSuffix
* The variation suffix or NULL for none.
*
* @return $this
*/
public function setVariationSuffix(?string $variationSuffix) {
$this->variationSuffix = $variationSuffix;
return $this;
}
/**
* Get the strength of the variation suffix.
*
* @return string
* One of the self::VARIATION_* constants.
*/
public function getVariationStrength(): string {
return $this->variationStrength;
}
/**
* Set the strength of the variation suffix.
*
* @param string $variationStrength
* One of the self::VARIATION_* constants.
*
* @return $this
*/
public function setVariationStrength(string $variationStrength) {
$this->variationStrength = $variationStrength;
return $this;
}
/**
* Allowed products.
*
* @param int[] $product_ids
* The product IDs to allow.
* @param bool $override_disallowed
* Whether to override a previous dis-allowance. Defaults to FALSE.
*
* @return $this
*/
public function allowProducts(array $product_ids, bool $override_disallowed = FALSE) {
// Clear from dis-allowed if overriding.
if ($override_disallowed) {
$this->disallowedProductIds = array_diff($this->disallowedProductIds, $product_ids);
}
// Otherwise, remove disallowed items.
else {
$product_ids = array_diff($product_ids, $this->disallowedProductIds);
}
if (!empty($product_ids)) {
// Remove anything that would cause a duplicate.
$product_ids = array_diff($product_ids, $this->allowedProductIds);
// Add in the remaining product IDs.
if (!empty($product_ids)) {
$this->allowedProductIds = array_merge($this->allowedProductIds, $product_ids);
}
}
return $this;
}
/**
* Dis-allow products types.
*
* @param int[] $product_ids
* The product IDs to dis-allow.
*
* @return $this
*/
public function disallowProducts(array $product_ids) {
// Remove anything that would cause a duplicate.
$product_ids = array_diff($product_ids, $this->disallowedProductIds);
if (!empty($product_ids)) {
// Add in the remaining product IDs.
$this->disallowedProductIds = array_merge($this->disallowedProductIds, $product_ids);
// Clean up the allowed list.
$this->allowedProductIds = array_diff($this->allowedProductIds, $this->disallowedProductIds);
}
return $this;
}
/**
* Get the available product variations.
*
* @return \Drupal\commerce_product\Entity\ProductVariationInterface[]
* The product variations, keyed by product ID.
*/
public function getProductVariations(): array {
/** @var \Drupal\commerce_product\Entity\ProductInterface[] $products */
$products = $this->storage->loadMultiple($this->allowedProductIds);
$variations = [];
// Loop over the allowed products.
foreach ($products as $product_id => $product) {
// Get all the variations for this product and filter them. We will also
// explicitly track a matching suffix.
$variation_with_suffix = NULL;
// We use an anonymous function as we need to use $variation_with_suffix,
// which can't be done via a method as the callback. However, we do want
// to use a callback so extending classes can override the filter.
$product_variations = array_filter($product->getVariations(), function (ProductVariationInterface $variation) use (&$variation_with_suffix) {
return $this->filterVariation($variation, $variation_with_suffix);
});
// If we have a suffix match, use it.
if ($variation_with_suffix) {
$variations[$product_id] = $variation_with_suffix;
}
// Otherwise, if we have any matches and only prefer the suffix, we use
// the first of our matches.
elseif ($this->variationStrength === self::VARIATION_PREFERRED && $product_variations) {
$variations[$product_id] = reset($product_variations);
}
}
return $variations;
}
/**
* Check whether a variation is suitable.
*
* @param \Drupal\commerce_product\Entity\ProductVariationInterface $variation
* The variation we are checking.
* @param \Drupal\commerce_product\Entity\ProductVariationInterface|null $variation_with_suffix
* Variable by reference to store the first variation that matches the
* specified suffix.
*
* @return bool
* Whether the variation should be included in the possible matches.
*/
protected function filterVariation(ProductVariationInterface $variation, ?ProductVariationInterface &$variation_with_suffix): bool {
if ($variation->getProduct()->bundle() !== 'subscription') {
return FALSE;
}
// Only include published products.
if (!$variation->isPublished()) {
return FALSE;
}
// Ensure we have access.
if (!$variation->access('view')) {
return FALSE;
}
// If we have an exact match on suffix, track it.
if ($this->variationSuffix && substr($variation->getSku(), -strlen($this->variationSuffix)) === $this->variationSuffix) {
$variation_with_suffix = $variation;
}
return TRUE;
}
}
