Skip to content

Instantly share code, notes, and snippets.

@igormukhingmailcom
Last active July 8, 2020 12:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igormukhingmailcom/31c5beae4c98aacead603bd827a1d3e4 to your computer and use it in GitHub Desktop.
Save igormukhingmailcom/31c5beae4c98aacead603bd827a1d3e4 to your computer and use it in GitHub Desktop.
Enable ability to specify Product's price/originalPrice at Sylius fixtures
<?php
declare(strict_types=1);
namespace App\Fixture;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
trait PriceAwareProductFixtureTrait
{
protected function configurePricesResourceNode(ArrayNodeDefinition $resourceNode): void
{
$childrenNode = $resourceNode->children();
$childrenNode->variableNode('prices');
}
}
<?php
declare(strict_types=1);
namespace App\Fixture\Factory;
use Sylius\Component\Core\Model\ChannelPricingInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @see https://github.com/Sylius/Sylius/pull/10794/files
*
* sylius_fixtures:
* suites:
* default:
* fixtures:
* app_product:
* options:
* custom:
* - name: Common prices product
* prices: 10.00
*
* - name: Random common prices product
* prices: [10.00, 11.00, 12.00] # Random one from this list will be applied
*
* - name: Common prices discounted product
* prices:
* price: 10.00
* originalPrice: 15.00
*
* - name: Common random prices discounted product
* prices:
* price: [10.00, 11.00, 12.00] # Random one from this list will be applied
* originalPrice: 15.00
*
* - name: Different channel prices product
* prices:
* FASHION_WEB: 10.00
*
* - name: Different channel prices discounted product
* prices:
* FASHION_WEB:
* price: 10.00
* originalPrice: 15.00
*
* - name: Random channel prices product
* prices:
* FASHION_WEB:
* price: [10.00, 11.00, 12.00] # Random one from this list will be applied
* originalPrice: 15.00
*
*/
trait PricesAwareProductExampleFactoryTrait
{
public function updatePrices(ProductInterface $product, array $resolvedOptions = []): void
{
if (!array_key_exists('prices', $resolvedOptions)) {
return;
}
$prices = $resolvedOptions['prices'];
if (null === $prices) {
return;
}
/** @var ProductVariantInterface $variant */
foreach ($product->getVariants() as $variant) {
foreach ($variant->getChannelPricings() as $channelPricing) {
$pricesForChannel = $this->getPricesForChannel($channelPricing->getChannelCode(), $prices);
$this->setChannelPricingsPrices($channelPricing, $pricesForChannel);
}
}
}
protected function configurePricesOptions(OptionsResolver $resolver): void
{
$resolver
->setDefault('prices', null)
->setAllowedTypes('prices', ['null', 'int', 'double', 'array'])
->setNormalizer('prices', \Closure::fromCallable([$this, 'normalizePrices']))
;
}
/**
* @param array|int|float|null $value
* @return array|int|null
*/
protected function normalizePrices(Options $options, $value)
{
if (null === $value) {
return null;
}
if (is_array($value)) {
foreach ($value as $key => $v) {
$value[$key] = $this->normalizePrices($options, $v);
}
return $value;
}
return (int) (100 * $value);
}
/**
* Check whether prices is common for all channels
*
* @param array|int|null $prices
*/
protected function isCommonPrices($prices): bool
{
if (is_int($prices)) {
return true;
}
if (is_array($prices)) {
return $this->hasSubprices($prices);
}
return false;
}
/**
* @param array|int|null $prices
*/
protected function hasSubprices($prices): bool
{
return array_key_exists('price', $prices) || array_key_exists('originalPrice', $prices);
}
/**
* @param array|int|null $prices
* @return array|int|null
*/
protected function getPricesForChannel(string $channelCode, $prices)
{
if ($this->isCommonPrices($prices)) {
return $prices;
}
return $prices[$channelCode];
}
/**
* @param array|int|null $prices
*/
protected function setChannelPricingsPrices(ChannelPricingInterface $channelPricing, $prices): void
{
if (null === $prices) {
return;
}
if (!is_array($prices)) {
$channelPricing->setPrice($prices);
return;
}
if (!$this->hasSubprices($prices)) {
// Random item from list
$channelPricing->setPrice($prices[random_int(0, count($prices) - 1)]);
}
if (array_key_exists('price', $prices)) {
if (is_array($prices['price'])) {
// Random item from list
$channelPricing->setPrice($prices['price'][random_int(0, count($prices['price']) - 1)]);
} else {
$channelPricing->setPrice($prices['price']);
}
}
if (array_key_exists('originalPrice', $prices)) {
if (is_array($prices['originalPrice'])) {
// Random item from list
$channelPricing->setPrice($prices['originalPrice'][random_int(0, count($prices['originalPrice']) - 1)]);
} else {
$channelPricing->setOriginalPrice($prices['originalPrice']);
}
}
}
}
<?php
declare(strict_types=1);
namespace App\Fixture\Factory;
use Sylius\Bundle\CoreBundle\Fixture\Factory\ProductExampleFactory as BaseProductExampleFactory;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
use Sylius\Component\Product\Generator\ProductVariantGeneratorInterface;
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductExampleFactory extends BaseProductExampleFactory
{
use PricesAwareProductExampleFactoryTrait;
/** @var OptionsResolver */
protected $optionsResolver;
public function __construct(FactoryInterface $productFactory, FactoryInterface $productVariantFactory, FactoryInterface $channelPricing, ProductVariantGeneratorInterface $variantGenerator, FactoryInterface $productAttributeValueFactory, FactoryInterface $productImageFactory, FactoryInterface $productProductFactory, ImageUploaderInterface $imageUploader, SlugGeneratorInterface $slugGenerator, RepositoryInterface $productRepository, RepositoryInterface $productAttributeRepository, RepositoryInterface $productOptionRepository, RepositoryInterface $channelRepository, RepositoryInterface $localeRepository, ?RepositoryInterface $taxCategoryRepository = null, ?FileLocatorInterface $fileLocator = null)
{
parent::__construct($productFactory, $productVariantFactory, $channelPricing, $variantGenerator, $productAttributeValueFactory, $productImageFactory, $productProductFactory, $imageUploader, $slugGenerator, $productRepository, $productAttributeRepository, $productOptionRepository, $channelRepository, $localeRepository, $taxCategoryRepository, $fileLocator);
$this->optionsResolver = new OptionsResolver();
$this->configureOptions($this->optionsResolver);
}
public function create(array $options = []): ProductInterface
{
/** @var ProductInterface $product */
$product = parent::create($options);
$options = $this->optionsResolver->resolve($options);
$this->updatePrices($product, $options);
return $product;
}
protected function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$this->configurePricesOptions($resolver);
}
}
<?php
declare(strict_types=1);
namespace App\Fixture;
use Sylius\Bundle\CoreBundle\Fixture\ProductFixture as BaseProductFixture;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
class ProductFixture extends BaseProductFixture
{
use PriceAwareProductFixtureTrait;
public function getName(): string
{
return 'app_product';
}
protected function configureResourceNode(ArrayNodeDefinition $resourceNode): void
{
parent::configureResourceNode($resourceNode);
$this->configurePricesResourceNode($resourceNode);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment