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; } }