From 187b6ce14617a03a40f70aec2387cd7f9db6af40 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Tue, 14 Nov 2023 19:04:09 +0100 Subject: [PATCH 1/4] Extract attribute generation from combination creator to a dedicated one, also use Faker to generate dynamic random categories of attributes --- composer.json | 39 +++-- config/services.yml | 44 +++--- src/Command/ShopCreatorCommand.php | 82 +++++----- src/Creator/AttributeCreator.php | 140 +++++++++++++++++ src/{ => Creator}/CartCreator.php | 2 +- src/{ => Creator}/CartRuleCreator.php | 2 +- src/{ => Creator}/CustomerCreator.php | 2 +- src/{ => Creator}/OrderCreator.php | 2 +- .../ProductCombinationCreator.php | 47 +----- src/{ => Creator}/ProductCreator.php | 2 +- src/Faker/FakerCategory.php | 142 ++++++++++++++++++ src/Faker/FakerFactory.php | 43 ++++++ 12 files changed, 425 insertions(+), 122 deletions(-) create mode 100644 src/Creator/AttributeCreator.php rename src/{ => Creator}/CartCreator.php (93%) rename src/{ => Creator}/CartRuleCreator.php (96%) rename src/{ => Creator}/CustomerCreator.php (93%) rename src/{ => Creator}/OrderCreator.php (98%) rename src/{ => Creator}/ProductCombinationCreator.php (55%) rename src/{ => Creator}/ProductCreator.php (95%) create mode 100644 src/Faker/FakerCategory.php create mode 100644 src/Faker/FakerFactory.php diff --git a/composer.json b/composer.json index 3d8bbcd..0224539 100644 --- a/composer.json +++ b/composer.json @@ -1,23 +1,32 @@ { - "name": "prestashop/ps_fixtures_creator", + "name": "prestashop/ps_fixtures_creator", "license": "AFL-3.0", "authors": [ - { - "name": "Fabien Papet", - "email": "FabienPapet@users.noreply.github.com" - } - ], - "autoload": { - "psr-4": { - "PrestaShop\\Module\\PsFixturesCreator\\": "src/" - } - }, - "require": { - "fakerphp/faker": "^1.21.0", - "symfony/console": "^4.4" + { + "name": "Fabien Papet", + "email": "FabienPapet@users.noreply.github.com" + } + ], + "autoload": { + "psr-4": { + "PrestaShop\\Module\\PsFixturesCreator\\": "src/" }, + "classmap": [ + "psfixturescreator.php" + ], + "exclude-from-classmap": [] + }, + "require": { + "php": ">=7.4", + "fakerphp/faker": "^1.21.0", + "symfony/console": "^4.4", + "mbezhanov/faker-provider-collection": "^2.0" + }, "config": { - "prepend-autoloader": false + "prepend-autoloader": false, + "platform": { + "php": "7.4.0" + } }, "type": "prestashop-module" } diff --git a/config/services.yml b/config/services.yml index bbf00fa..58e20e1 100644 --- a/config/services.yml +++ b/config/services.yml @@ -1,38 +1,39 @@ services: Faker\Generator: - factory: ['Faker\Factory', 'create'] + factory: ['PrestaShop\Module\PsFixturesCreator\Faker\FakerFactory', 'create'] arguments: - 'en_EN' - PrestaShop\Module\PsFixturesCreator\CustomerCreator: - class: PrestaShop\Module\PsFixturesCreator\CustomerCreator + PrestaShop\Module\PsFixturesCreator\Creator\CustomerCreator: arguments: $faker: '@Faker\Generator' - PrestaShop\Module\PsFixturesCreator\CartCreator: - class: PrestaShop\Module\PsFixturesCreator\CartCreator + PrestaShop\Module\PsFixturesCreator\Creator\CartCreator: arguments: $faker: '@Faker\Generator' - PrestaShop\Module\PsFixturesCreator\CartRuleCreator: - class: PrestaShop\Module\PsFixturesCreator\CartRuleCreator + PrestaShop\Module\PsFixturesCreator\Creator\CartRuleCreator: arguments: $faker: '@Faker\Generator' - PrestaShop\Module\PsFixturesCreator\ProductCreator: - class: PrestaShop\Module\PsFixturesCreator\ProductCreator + PrestaShop\Module\PsFixturesCreator\Creator\ProductCreator: arguments: $faker: '@Faker\Generator' - PrestaShop\Module\PsFixturesCreator\OrderCreator: - class: PrestaShop\Module\PsFixturesCreator\OrderCreator + PrestaShop\Module\PsFixturesCreator\Creator\OrderCreator: arguments: $faker: '@Faker\Generator' - $customerCreator: '@PrestaShop\Module\PsFixturesCreator\CustomerCreator' - $cartCreator: '@PrestaShop\Module\PsFixturesCreator\CartCreator' + $customerCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\CustomerCreator' + $cartCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\CartCreator' - PrestaShop\Module\PsFixturesCreator\ProductCombinationCreator: - class: PrestaShop\Module\PsFixturesCreator\ProductCombinationCreator + PrestaShop\Module\PsFixturesCreator\Creator\AttributeCreator: + arguments: + $entityManager: '@doctrine.orm.entity_manager' + $langRepository: '@prestashop.core.admin.lang.repository' + $shopRepository: '@prestashop.core.admin.shop.repository' + $faker: '@Faker\Generator' + + PrestaShop\Module\PsFixturesCreator\Creator\ProductCombinationCreator: arguments: $entityManager: '@doctrine.orm.entity_manager' $commandBus: '@prestashop.core.command_bus' @@ -40,11 +41,12 @@ services: PrestaShop\Module\PsFixturesCreator\Command\ShopCreatorCommand: class: PrestaShop\Module\PsFixturesCreator\Command\ShopCreatorCommand arguments: - $customerCreator: '@PrestaShop\Module\PsFixturesCreator\CustomerCreator' - $cartCreator: '@PrestaShop\Module\PsFixturesCreator\CartCreator' - $orderCreator: '@PrestaShop\Module\PsFixturesCreator\OrderCreator' - $cartRuleCreator: '@PrestaShop\Module\PsFixturesCreator\CartRuleCreator' - $productCreator: '@PrestaShop\Module\PsFixturesCreator\ProductCreator' - $productCombinationCreator: '@PrestaShop\Module\PsFixturesCreator\ProductCombinationCreator' + $customerCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\CustomerCreator' + $cartCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\CartCreator' + $orderCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\OrderCreator' + $cartRuleCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\CartRuleCreator' + $productCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\ProductCreator' + $attributeCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\AttributeCreator' + $productCombinationCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\ProductCombinationCreator' tags: - { name: 'console.command' } diff --git a/src/Command/ShopCreatorCommand.php b/src/Command/ShopCreatorCommand.php index cd0a350..fbe5772 100644 --- a/src/Command/ShopCreatorCommand.php +++ b/src/Command/ShopCreatorCommand.php @@ -27,12 +27,13 @@ namespace PrestaShop\Module\PsFixturesCreator\Command; use Db; -use PrestaShop\Module\PsFixturesCreator\CartCreator; -use PrestaShop\Module\PsFixturesCreator\CartRuleCreator; -use PrestaShop\Module\PsFixturesCreator\CustomerCreator; -use PrestaShop\Module\PsFixturesCreator\OrderCreator; -use PrestaShop\Module\PsFixturesCreator\ProductCombinationCreator; -use PrestaShop\Module\PsFixturesCreator\ProductCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\AttributeCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\CartCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\CartRuleCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\CustomerCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\OrderCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\ProductCombinationCreator; +use PrestaShop\Module\PsFixturesCreator\Creator\ProductCreator; use Shop; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -54,6 +55,8 @@ class ShopCreatorCommand extends Command private ProductCreator $productCreator; + private AttributeCreator $attributeCreator; + private ProductCombinationCreator $productCombinationCreator; public function __construct( @@ -62,6 +65,7 @@ public function __construct( OrderCreator $orderCreator, CartRuleCreator $cartRuleCreator, ProductCreator $productCreator, + AttributeCreator $attributeCreator, ProductCombinationCreator $productCombinationCreator ) { parent::__construct(null); @@ -71,6 +75,7 @@ public function __construct( $this->orderCreator = $orderCreator; $this->cartRuleCreator = $cartRuleCreator; $this->productCreator = $productCreator; + $this->attributeCreator = $attributeCreator; $this->productCombinationCreator = $productCombinationCreator; } @@ -89,11 +94,8 @@ protected function configure(): void ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, 'The shop identifier', 1) ->addOption('shopGroupId', null, InputOption::VALUE_OPTIONAL, 'The shop group identifier', 1) ->addOption('languageId', null, InputOption::VALUE_OPTIONAL, 'The languageId identifier', 1) - - // values for product combinations, if all three parameters are not above zero, nothing will be done ->addOption('attributeGroups', null, InputOption::VALUE_OPTIONAL, 'Number of attribute groups', 0) - ->addOption('attributes', null, InputOption::VALUE_OPTIONAL, 'Number of attributes per attribute group', 0) - ->addOption('targetProductIdForAttributes', null, InputOption::VALUE_OPTIONAL, 'ID of the product that will receive attributes', 0) + ->addOption('attributes', null, InputOption::VALUE_OPTIONAL, 'Number of attributes per attribute group', 10) ; } @@ -111,38 +113,48 @@ protected function execute(InputInterface $input, OutputInterface $output): int $idShopGroup = (int) $input->getOption('shopGroupId'); $numberOfAttributeGroups = (int) $input->getOption('attributeGroups'); $numberOfAttributes = (int) $input->getOption('attributes'); - $targetProductIdForAttributes = (int) $input->getOption('targetProductIdForAttributes'); - $productIds = $this->getSimpleProducts($idLang); + $productIds = $this->getStandardProducts($idLang); // Create customers (without order) - $this->customerCreator->generate($numberOfCustomerWithoutOrder); - $output->writeln(sprintf('%s customer(s) without orders created.', $numberOfCustomerWithoutOrder)); - - // Create carts - $this->cartCreator->generate($numberOfCarts, $productIds); - $output->writeln(sprintf('%s cart(s) created.', $numberOfCarts)); - - // Create orders - $this->orderCreator->generate($numberOfOrders, $idShopGroup, $idshop, $productIds); - $output->writeln(sprintf('%s order(s) created.', $numberOfOrders)); + if (!empty($numberOfCustomerWithoutOrder)) { + $this->customerCreator->generate($numberOfCustomerWithoutOrder); + $output->writeln(sprintf('%s customer(s) without orders created.', $numberOfCustomerWithoutOrder)); + } // create cart rules - $this->cartRuleCreator->generate($numberOfCartRules, $idLang); - $output->writeln(sprintf('%s cart rule(s) created.', $numberOfCartRules)); + if (!empty($numberOfCartRules)) { + $this->cartRuleCreator->generate($numberOfCartRules, $idLang); + $output->writeln(sprintf('%s cart rule(s) created.', $numberOfCartRules)); + } // create products - $this->productCreator->generate($numberOfProducts, $idLang); - $output->writeln(sprintf('%s product(s) created', $numberOfProducts)); - - // create product combination (attribute groups and attributes which are then attached to a product) - if ($numberOfAttributeGroups > 0 && $numberOfAttributes > 0 && $targetProductIdForAttributes > 0) { - $this->productCombinationCreator->generate($numberOfAttributeGroups, $numberOfAttributes, $targetProductIdForAttributes, $idLang, $idshop); - $output->writeln(sprintf('Created %s attribute group(s) with %s different values each for product with ID %s.', $numberOfAttributeGroups, $numberOfAttributes, $targetProductIdForAttributes)); - } else { - $output->writeln('0 product combination(s) created.'); + if (!empty($numberOfProducts)) { + $this->productCreator->generate($numberOfProducts, $idLang); + $output->writeln(sprintf('%s product(s) created', $numberOfProducts)); + } + + // create product attributes + if ($numberOfAttributeGroups > 0 && $numberOfAttributes > 0) { + $this->attributeCreator->generate($numberOfAttributeGroups, $numberOfAttributes, $idshop); + $output->writeln(sprintf('Created %s attribute group(s) with %s different values each.', $numberOfAttributeGroups, $numberOfAttributes)); + } + + // Carts and orders are created last, so they can use new products randomly + + // Create carts + if (!empty($numberOfCarts)) { + $this->cartCreator->generate($numberOfCarts, $productIds); + $output->writeln(sprintf('%s cart(s) created.', $numberOfCarts)); } + // Create orders + if (!empty($numberOfOrders)) { + $this->orderCreator->generate($numberOfOrders, $idShopGroup, $idshop, $productIds); + $output->writeln(sprintf('%s order(s) created.', $numberOfOrders)); + } + + return 0; } @@ -151,13 +163,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int * * @return array */ - private function getSimpleProducts($id_lang, bool $front = true): bool|array + private function getStandardProducts($id_lang, bool $front = true): bool|array { $sql = 'SELECT p.`id_product`, pl.`name` FROM `' . _DB_PREFIX_ . 'product` p ' . Shop::addSqlAssociation('product', 'p') . ' LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (p.`id_product` = pl.`id_product` ' . Shop::addSqlRestrictionOnLang('pl') . ') - WHERE pl.`id_lang` = ' . (int) $id_lang . ' + WHERE p.`product_type` = "standard" AND pl.`id_lang` = ' . (int) $id_lang . ' ' . ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') . ' ORDER BY pl.`name`'; diff --git a/src/Creator/AttributeCreator.php b/src/Creator/AttributeCreator.php new file mode 100644 index 0000000..925b571 --- /dev/null +++ b/src/Creator/AttributeCreator.php @@ -0,0 +1,140 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + */ + +declare(strict_types=1); + +namespace PrestaShop\Module\PsFixturesCreator\Creator; + +use Doctrine\ORM\EntityManagerInterface; +use Faker\Generator as Faker; +use PrestaShop\Module\PsFixturesCreator\Faker\FakerCategory; +use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface; +use PrestaShopBundle\Entity\Attribute; +use PrestaShopBundle\Entity\AttributeGroup; +use PrestaShopBundle\Entity\AttributeGroupLang; +use PrestaShopBundle\Entity\AttributeLang; +use PrestaShopBundle\Entity\Lang; +use PrestaShopBundle\Entity\Repository\LangRepository; +use PrestaShopBundle\Entity\Repository\ShopRepository; +use PrestaShopBundle\Entity\Shop as ShopEntity; + +class AttributeCreator +{ + private EntityManagerInterface $entityManager; + + private ShopEntity $shop; + + private LangRepository $langRepository; + + private ShopRepository $shopRepository; + + private Faker $faker; + + public function __construct( + EntityManagerInterface $entityManager, + LangRepository $langRepository, + ShopRepository $shopRepository, + Faker $faker + ) { + $this->entityManager = $entityManager; + $this->langRepository = $langRepository; + $this->shopRepository = $shopRepository; + $this->faker = $faker; + } + + public function generate(int $attributeGroupNumber, int $attributeValuePerGroupNumber, int $shopId): void + { + $this->shop = $this->shopRepository->find($shopId); + $languages = $this->langRepository->findAll(); + for ($i = 1; $i <= $attributeGroupNumber; ++$i) { + $fakerCategory = FakerCategory::getCategory(); + $attributeGroup = $this->createAttributeGroup($i, $fakerCategory, $languages); + for ($j = 1; $j <= $attributeValuePerGroupNumber; ++$j) { + $this->createAttribute($j, $attributeGroup, $fakerCategory, $languages); + } + + // Flush created attributes + $this->entityManager->flush(); + } + } + + /** + * @param int $attributeGroupNumber + * @param FakerCategory $fakerCategory + * @param Lang[] $languages + * + * @return AttributeGroup + */ + private function createAttributeGroup(int $attributeGroupNumber, FakerCategory $fakerCategory, array $languages): AttributeGroup + { + $attributeGroup = new AttributeGroup(); + $attributeGroup->setIsColorGroup(false); + $attributeGroup->setGroupType('select'); + $attributeGroup->setPosition($attributeGroupNumber); + $attributeGroup->addShop($this->shop); + + foreach ($languages as $lang) { + $attributeGroupLang = new AttributeGroupLang(); + $attributeGroupLang->setName($fakerCategory->getCategoryName() . ' ' . $lang->getName()); + $attributeGroupLang->setPublicName($fakerCategory->getCategoryName() . ' ' . $lang->getName()); + $attributeGroupLang->setLang($lang); + $attributeGroupLang->setAttributeGroup($attributeGroup); + $attributeGroup->addAttributeGroupLang($attributeGroupLang); + $this->entityManager->persist($attributeGroupLang); + } + + $this->entityManager->persist($attributeGroup); + $this->entityManager->flush(); + + return $attributeGroup; + } + + /** + * @param int $attributeNumber + * @param AttributeGroup $attributeGroup + * @param FakerCategory $fakerCategory + * @param Lang[] $languages + */ + private function createAttribute(int $attributeNumber, AttributeGroup $attributeGroup, FakerCategory $fakerCategory, array $languages): void + { + $attribute = new Attribute(); + $attribute->setAttributeGroup($attributeGroup); + $attribute->setColor(''); + $attribute->setPosition($attributeNumber); + $attribute->addShop($this->shop); + + foreach ($languages as $lang) { + $attributeLang = new AttributeLang(); + $attributeLang->setLang($lang); + $attributeLang->setName($fakerCategory->getCategoryValue($lang->getLocale())); + $attribute->addAttributeLang($attributeLang); + $attributeLang->setAttribute($attribute); + $this->entityManager->persist($attributeLang); + } + + $this->entityManager->persist($attribute); + } +} diff --git a/src/CartCreator.php b/src/Creator/CartCreator.php similarity index 93% rename from src/CartCreator.php rename to src/Creator/CartCreator.php index 1f792e5..2d94239 100644 --- a/src/CartCreator.php +++ b/src/Creator/CartCreator.php @@ -1,6 +1,6 @@ setName('fake_attribute_' . (string) $attributeGroupNumber); - $attributeGroupLang->setPublicName('fake_attribute_' . (string) $attributeGroupNumber); - $attributeGroupLang->setLang($this->lang); - - $attributeGroup = new AttributeGroup(); - $attributeGroup->setIsColorGroup(false); - $attributeGroup->setGroupType('select'); - $attributeGroup->setPosition($attributeGroupNumber); - $attributeGroup->addShop($this->shop); - $attributeGroup->addAttributeGroupLang($attributeGroupLang); - $attributeGroupLang->setAttributeGroup($attributeGroup); - - $this->entityManager->persist($attributeGroup); - $this->entityManager->persist($attributeGroupLang); - - $this->entityManager->flush(); - - return $attributeGroup; - } - - private function createAttribute(int $attributeNumber, AttributeGroup $attributeGroup): Attribute - { - $attributeLang = new AttributeLang(); - $attributeLang->setLang($this->lang); - $attributeLang->setName('fake_attribute_value_' . (string) $attributeNumber); - - $attribute = new Attribute(); - $attribute->setAttributeGroup($attributeGroup); - $attribute->setColor(''); - $attribute->setPosition($attributeNumber); - $attribute->addShop($this->shop); - $attribute->addAttributeLang($attributeLang); - $attributeLang->setAttribute($attribute); - - $this->entityManager->persist($attribute); - $this->entityManager->persist($attributeLang); - - $this->entityManager->flush(); - - return $attribute; - } } diff --git a/src/ProductCreator.php b/src/Creator/ProductCreator.php similarity index 95% rename from src/ProductCreator.php rename to src/Creator/ProductCreator.php index 34c7218..956b078 100644 --- a/src/ProductCreator.php +++ b/src/Creator/ProductCreator.php @@ -1,6 +1,6 @@ + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + */ + +declare(strict_types=1); + +namespace PrestaShop\Module\PsFixturesCreator\Faker; + +use Doctrine\Inflector\Inflector; +use Doctrine\Inflector\InflectorFactory; +use Doctrine\Inflector\NoopWordInflector; +use Faker\Generator; + +class FakerCategory +{ + /** + * All the available categories are actually different providers from the Bezhanov provider collection + */ + public const AVAILABLE_CATEGORIES = [ + 'department', + 'deviceModelName', + 'devicePlatform', + 'university', + 'secondarySchool', + 'course', + 'campus', + 'ingredient', + 'spice', + 'measurement', + 'medicine', + 'chemicalElement', + 'planet', + 'galaxy', + 'constellation', + 'meteorite', + 'bird', + 'creature', + 'plant', + 'team', + ]; + + /** + * Locale static array to avoid generating the same category too often, we perform a cycle through + * all available categories. Then we reinitialize the available categories when they all were used once. + * + * @var array + */ + private static array $randomAvailableCategories; + + /** + * @var Inflector + */ + private static $inflector; + + private string $category; + private string $categoryName; + + /** + * @var Generator[] + */ + private array $localizedGenerators; + + public static function getCategory(string $category = null): self + { + // Choose a random category + if (null === $category) { + // When remaining random categories are empty we fill it up again + if (empty(self::$randomAvailableCategories)) { + self::$randomAvailableCategories = self::AVAILABLE_CATEGORIES; + } + + $category = self::$randomAvailableCategories[rand(0, count(self::$randomAvailableCategories) - 1)]; + + // Remove category from remaining available categories + $randomOffset = array_search($category, self::$randomAvailableCategories); + if (!empty($randomOffset) && $randomOffset < count(self::$randomAvailableCategories)) { + array_splice(self::$randomAvailableCategories, $randomOffset, 1); + } + } + + return new self($category); + } + + private function getInflector(): Inflector + { + if (!self::$inflector) { + self::$inflector = InflectorFactory::create()->build(); + } + + return self::$inflector; + } + + private function __construct(string $category) + { + $this->category = $category; + $this->categoryName = $this->getInflector()->capitalize($this->getInflector()->pluralize($category)); + } + + public function getCategoryName(): string + { + return $this->categoryName; + } + + public function getCategoryValue(string $locale): string + { + if (!isset($this->localizedGenerators[$locale])) { + $this->localizedGenerators[$locale] = FakerFactory::create($locale); + } + $localizedGenerator = $this->localizedGenerators[$locale]; + + switch ($this->category) { + case 'department': + // Special case for department to increase the number of words since it can be specified + return $localizedGenerator->department(6); + default: + // The category matched a faker property that generates random content + return $localizedGenerator->{$this->category}; + } + } +} diff --git a/src/Faker/FakerFactory.php b/src/Faker/FakerFactory.php new file mode 100644 index 0000000..be1d7bb --- /dev/null +++ b/src/Faker/FakerFactory.php @@ -0,0 +1,43 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + */ + +declare(strict_types=1); + +namespace PrestaShop\Module\PsFixturesCreator\Faker; + +use Bezhanov\Faker\ProviderCollectionHelper; +use Faker\Factory; + +class FakerFactory extends Factory +{ + public static function create($locale = self::DEFAULT_LOCALE) + { + $faker = parent::create($locale); + ProviderCollectionHelper::addAllProvidersTo($faker); + + return $faker; + } +} From 1dacaf6e2081f48a7265675a01e952628a0e6275 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Wed, 15 Nov 2023 01:02:26 +0100 Subject: [PATCH 2/4] Improve combination generation, randomly pick adapted attributes or create them if needed --- config/services.yml | 3 + src/Command/ShopCreatorCommand.php | 17 ++- src/Creator/AttributeCreator.php | 12 ++- src/Creator/ProductCombinationCreator.php | 126 ++++++++++++++++++---- 4 files changed, 128 insertions(+), 30 deletions(-) diff --git a/config/services.yml b/config/services.yml index 58e20e1..cf97740 100644 --- a/config/services.yml +++ b/config/services.yml @@ -37,6 +37,9 @@ services: arguments: $entityManager: '@doctrine.orm.entity_manager' $commandBus: '@prestashop.core.command_bus' + $attributeCreator: '@PrestaShop\Module\PsFixturesCreator\Creator\AttributeCreator' + $langRepository: '@prestashop.core.admin.lang.repository' + $faker: '@Faker\Generator' PrestaShop\Module\PsFixturesCreator\Command\ShopCreatorCommand: class: PrestaShop\Module\PsFixturesCreator\Command\ShopCreatorCommand diff --git a/src/Command/ShopCreatorCommand.php b/src/Command/ShopCreatorCommand.php index fbe5772..aebb932 100644 --- a/src/Command/ShopCreatorCommand.php +++ b/src/Command/ShopCreatorCommand.php @@ -91,6 +91,7 @@ protected function configure(): void ->addOption('carts', null, InputOption::VALUE_OPTIONAL, 'Number of carts to create', 0) ->addOption('cart-rules', null, InputOption::VALUE_OPTIONAL, 'Number of cart rules to create', 0) ->addOption('products', null, InputOption::VALUE_OPTIONAL, 'Number of products to create', 0) + ->addOption('productsWithCombinations', null, InputOption::VALUE_OPTIONAL, 'Number of products with combinations to create', 0) ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, 'The shop identifier', 1) ->addOption('shopGroupId', null, InputOption::VALUE_OPTIONAL, 'The shop group identifier', 1) ->addOption('languageId', null, InputOption::VALUE_OPTIONAL, 'The languageId identifier', 1) @@ -113,6 +114,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $idShopGroup = (int) $input->getOption('shopGroupId'); $numberOfAttributeGroups = (int) $input->getOption('attributeGroups'); $numberOfAttributes = (int) $input->getOption('attributes'); + $productsWithCombinations = (int) $input->getOption('productsWithCombinations'); $productIds = $this->getStandardProducts($idLang); @@ -134,14 +136,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(sprintf('%s product(s) created', $numberOfProducts)); } - // create product attributes - if ($numberOfAttributeGroups > 0 && $numberOfAttributes > 0) { - $this->attributeCreator->generate($numberOfAttributeGroups, $numberOfAttributes, $idshop); - $output->writeln(sprintf('Created %s attribute group(s) with %s different values each.', $numberOfAttributeGroups, $numberOfAttributes)); + // create product with combinations, if attributes are needed they will be created dynamically + if (!empty($productsWithCombinations)) { + $this->productCombinationCreator->generate($productsWithCombinations, $numberOfAttributeGroups, $numberOfAttributes, $idshop); + $output->writeln(sprintf('%s product(s) with combinations created', $productsWithCombinations)); + } else { + // If not product with combinations asked, simply create attributes + if ($numberOfAttributeGroups > 0 && $numberOfAttributes > 0) { + $this->attributeCreator->generate($numberOfAttributeGroups, $numberOfAttributes, $idshop); + $output->writeln(sprintf('Created %s attribute group(s) with %s different values each.', $numberOfAttributeGroups, $numberOfAttributes)); + } } // Carts and orders are created last, so they can use new products randomly - // Create carts if (!empty($numberOfCarts)) { $this->cartCreator->generate($numberOfCarts, $productIds); diff --git a/src/Creator/AttributeCreator.php b/src/Creator/AttributeCreator.php index 925b571..bc87ef5 100644 --- a/src/Creator/AttributeCreator.php +++ b/src/Creator/AttributeCreator.php @@ -65,10 +65,11 @@ public function __construct( $this->faker = $faker; } - public function generate(int $attributeGroupNumber, int $attributeValuePerGroupNumber, int $shopId): void + public function generate(int $attributeGroupNumber, int $attributeValuePerGroupNumber, int $shopId): array { $this->shop = $this->shopRepository->find($shopId); $languages = $this->langRepository->findAll(); + $generatedGroups = []; for ($i = 1; $i <= $attributeGroupNumber; ++$i) { $fakerCategory = FakerCategory::getCategory(); $attributeGroup = $this->createAttributeGroup($i, $fakerCategory, $languages); @@ -78,7 +79,10 @@ public function generate(int $attributeGroupNumber, int $attributeValuePerGroupN // Flush created attributes $this->entityManager->flush(); + $generatedGroups[] = $attributeGroup; } + + return $generatedGroups; } /** @@ -98,8 +102,8 @@ private function createAttributeGroup(int $attributeGroupNumber, FakerCategory $ foreach ($languages as $lang) { $attributeGroupLang = new AttributeGroupLang(); - $attributeGroupLang->setName($fakerCategory->getCategoryName() . ' ' . $lang->getName()); - $attributeGroupLang->setPublicName($fakerCategory->getCategoryName() . ' ' . $lang->getName()); + $attributeGroupLang->setName($fakerCategory->getCategoryName() . ' ' . $lang->getLocale()); + $attributeGroupLang->setPublicName($fakerCategory->getCategoryName() . ' ' . $lang->getLocale()); $attributeGroupLang->setLang($lang); $attributeGroupLang->setAttributeGroup($attributeGroup); $attributeGroup->addAttributeGroupLang($attributeGroupLang); @@ -134,7 +138,7 @@ private function createAttribute(int $attributeNumber, AttributeGroup $attribute $attributeLang->setAttribute($attribute); $this->entityManager->persist($attributeLang); } - $this->entityManager->persist($attribute); + $attributeGroup->getAttributes()->add($attribute); } } diff --git a/src/Creator/ProductCombinationCreator.php b/src/Creator/ProductCombinationCreator.php index cc873d1..62ff82b 100644 --- a/src/Creator/ProductCombinationCreator.php +++ b/src/Creator/ProductCombinationCreator.php @@ -2,15 +2,24 @@ namespace PrestaShop\Module\PsFixturesCreator\Creator; +use Doctrine\DBAL\ArrayParameterType; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Query; +use Faker\Generator; use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface; use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Command\GenerateProductCombinationsCommand; +use PrestaShop\PrestaShop\Core\Domain\Product\Command\AddProductCommand; +use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId; +use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductType; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; use PrestaShopBundle\Entity\Attribute; use PrestaShopBundle\Entity\AttributeGroup; use PrestaShopBundle\Entity\AttributeGroupLang; use PrestaShopBundle\Entity\AttributeLang; use PrestaShopBundle\Entity\Lang; +use PrestaShopBundle\Entity\Repository\LangRepository; use PrestaShopBundle\Entity\Shop as ShopEntity; class ProductCombinationCreator @@ -19,40 +28,115 @@ class ProductCombinationCreator private CommandBusInterface $commandBus; - private Lang $lang; + private AttributeCreator $attributeCreator; - private ShopEntity $shop; + private Generator $faker; - public function __construct(EntityManagerInterface $entityManager, CommandBusInterface $commandBus) - { + private LangRepository $langRepository; + + public function __construct( + EntityManagerInterface $entityManager, + CommandBusInterface $commandBus, + AttributeCreator $attributeCreator, + LangRepository $langRepository, + Generator $faker + ) { $this->entityManager = $entityManager; $this->commandBus = $commandBus; + $this->attributeCreator = $attributeCreator; + $this->langRepository = $langRepository; + $this->faker = $faker; } - public function generate(int $attributeGroupNumber, int $attributeValuePerGroupNumber, int $targetProductId, int $langId, int $shopId): void + public function generate(int $productWithCombinations, int $attributeGroupNumber, int $attributeValuePerGroupNumber, int $shopId): void { - $this->lang = $this->entityManager->getRepository(Lang::class)->find($langId); - $this->shop = $this->entityManager->getRepository(ShopEntity::class)->find($shopId); - for ($i = 1; $i <= $attributeGroupNumber; ++$i) { - $attributeGroup = $this->createAttributeGroup($i); - $attributeGroupIdList = []; - for ($j = 1; $j <= $attributeValuePerGroupNumber; ++$j) { - $attribute = $this->createAttribute($j, $attributeGroup); - $attributeGroupIdList[] = $attribute->getId(); - } - $attributes = []; - $requestAttributeGroups = []; - $requestAttributeGroups[$attributeGroup->getId()] = $attributeGroupIdList; + $attributeGroups = $this->getAttributeGroupWithAtLeast($attributeValuePerGroupNumber); + // Create the missing attribute groups if needed + if (count($attributeGroups) < $attributeGroupNumber) { + $attributeGroups = array_merge( + $attributeGroups, + $this->attributeCreator->generate($attributeGroupNumber - count($attributeGroups), $attributeValuePerGroupNumber, $shopId) + ); + } - foreach ($requestAttributeGroups as $attributeGroupId => $requestAttributes) { - $attributes[(int) $attributeGroupId] = array_map('intval', $requestAttributes); + for ($i = 1; $i <= $productWithCombinations; ++$i) { + $combinationAttributes = $this->getAttributesForGeneration($attributeGroups, $attributeGroupNumber, $attributeValuePerGroupNumber); + $productName = $this->faker->productName; + $productNames = []; + /** @var Lang $lang */ + foreach ($this->langRepository->findAll() as $lang) { + $productNames[$lang->getId()] = $productName . ' ' . $lang->getLocale(); } + /** @var ProductId $newProductId */ + $newProductId = $this->commandBus->handle(new AddProductCommand( + ProductType::TYPE_COMBINATIONS, + $shopId, + $productNames + )); + $this->commandBus->handle(new GenerateProductCombinationsCommand( - $targetProductId, - $attributes, + $newProductId->getValue(), + $combinationAttributes, $shopId ? ShopConstraint::shop($shopId) : ShopConstraint::allShops() )); } } + + /** + * @param AttributeGroup[] $attributeGroups + * @param int $attributeGroupNumber + * @param int $attributeValuePerGroupNumber + * + * @return array + */ + private function getAttributesForGeneration(array $attributeGroups, int $attributeGroupNumber, int $attributeValuePerGroupNumber): array + { + $attributeIdsByGroup = []; + $randomAttributeGroupsKeys = array_rand($attributeGroups, $attributeGroupNumber); + foreach ($randomAttributeGroupsKeys as $randomAttributeGroupKey) { + $attributeGroup = $attributeGroups[$randomAttributeGroupKey]; + $attributeIdsByGroup[$attributeGroup->getId()] = []; + + /** @var Attribute[] $attributes */ + $attributes = $attributeGroup->getAttributes()->toArray(); + $randomAttributeKeys = array_rand($attributes, $attributeValuePerGroupNumber); + foreach ($randomAttributeKeys as $randomAttributeKey) { + $attribute = $attributes[$randomAttributeKey]; + $attributeIdsByGroup[$attributeGroup->getId()][] = $attribute->getId(); + } + } + + return $attributeIdsByGroup; + } + + private function getAttributeGroupWithAtLeast(int $minimumValuesNumber): array + { + $qb = $this->entityManager + ->getRepository(AttributeGroup::class) + ->createQueryBuilder('ag') + ; + + $qb + ->select('ag.id, COUNT(a.id) AS attributesNb') + ->leftJoin('ag.attributes', 'a') + ->addGroupBy('ag.id') + ; + $attributeGroups = $qb->getQuery()->getArrayResult(); + $attributeGroupIds = array_map(static function(array $attributeGroup) { + return (int) $attributeGroup['id']; + }, array_filter($attributeGroups, static function(array $attributeGroup) use ($minimumValuesNumber) { + return $attributeGroup['attributesNb'] >= $minimumValuesNumber; + })); + + $qb = $this->entityManager + ->getRepository(AttributeGroup::class) + ->createQueryBuilder('ag') + ; + $qb + ->where('ag.id IN (:attributeGroupIds)') + ->setParameter('attributeGroupIds', array_values($attributeGroupIds), ArrayParameterType::INTEGER) + ; + return $qb->getQuery()->getResult(Query::HYDRATE_OBJECT); + } } From df3fa4e94c2d888d547eab7b3f04bbbd4dd10d48 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Wed, 15 Nov 2023 01:10:37 +0100 Subject: [PATCH 3/4] Add CS fixer workflow --- .github/workflows/php.yml | 77 +++++++++++++++++++++++ .gitignore | 1 + .php-cs-fixer.dist.php | 11 ++++ composer.json | 3 + src/Command/ShopCreatorCommand.php | 1 - src/Creator/AttributeCreator.php | 1 - src/Creator/ProductCombinationCreator.php | 10 +-- src/Faker/FakerCategory.php | 1 - 8 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/php.yml create mode 100644 .php-cs-fixer.dist.php diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..d496330 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,77 @@ +name: PHP tests +on: [push, pull_request] +jobs: + # Check there is no syntax errors in the project + php-linter: + name: PHP Syntax check 7.4 => 8.2 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3.1.0 + + - name: PHP syntax checker 7.4 + uses: prestashop/github-action-php-lint/7.4@master + + - name: PHP syntax checker 8.0 + uses: prestashop/github-action-php-lint/8.0@master + + - name: PHP syntax checker 8.1 + uses: prestashop/github-action-php-lint/8.1@master + + - name: PHP syntax checker 8.2 + uses: prestashop/github-action-php-lint/8.2@master + + # Check the PHP code follow the coding standards + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + + - name: Checkout + uses: actions/checkout@v3.1.0 + + - name: Install dependencies + run: composer install + + - name: Run PHP-CS-Fixer + run: ./vendor/bin/php-cs-fixer fix --dry-run --diff --using-cache=no + + # Run PHPStan against the module and a PrestaShop release + phpstan: + name: PHPStan + runs-on: ubuntu-latest + strategy: + matrix: + presta-versions: ['1.7.7', '1.7.8', '8.0', 'latest'] + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + + - name: Checkout + uses: actions/checkout@v3.1.0 + + # Add vendor folder in cache to make next builds faster + - name: Cache vendor folder + uses: actions/cache@v3 + with: + path: vendor + key: php-${{ hashFiles('composer.lock') }} + + # Add composer local folder in cache to make next builds faster + - name: Cache composer folder + uses: actions/cache@v3 + with: + path: ~/.composer/cache + key: php-composer-cache + + - run: composer install + + # Docker images prestashop/prestashop may be used, even if the shop remains uninstalled + - name: Execute PHPStan on PrestaShop (Tag ${{ matrix.presta-versions }}) + run: ./tests/phpstan.sh ${{ matrix.presta-versions }} diff --git a/.gitignore b/.gitignore index d1502b0..0486d9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor/ composer.lock +.php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..9830d6d --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,11 @@ +setUsingCache(true) + ->getFinder() + ->in(__DIR__) + ->exclude('vendor'); + +return $config; diff --git a/composer.json b/composer.json index 0224539..4262947 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,9 @@ "symfony/console": "^4.4", "mbezhanov/faker-provider-collection": "^2.0" }, + "require-dev": { + "prestashop/php-dev-tools": "^4.3" + }, "config": { "prepend-autoloader": false, "platform": { diff --git a/src/Command/ShopCreatorCommand.php b/src/Command/ShopCreatorCommand.php index aebb932..9a9dee3 100644 --- a/src/Command/ShopCreatorCommand.php +++ b/src/Command/ShopCreatorCommand.php @@ -161,7 +161,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(sprintf('%s order(s) created.', $numberOfOrders)); } - return 0; } diff --git a/src/Creator/AttributeCreator.php b/src/Creator/AttributeCreator.php index bc87ef5..4d7ca8b 100644 --- a/src/Creator/AttributeCreator.php +++ b/src/Creator/AttributeCreator.php @@ -31,7 +31,6 @@ use Doctrine\ORM\EntityManagerInterface; use Faker\Generator as Faker; use PrestaShop\Module\PsFixturesCreator\Faker\FakerCategory; -use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface; use PrestaShopBundle\Entity\Attribute; use PrestaShopBundle\Entity\AttributeGroup; use PrestaShopBundle\Entity\AttributeGroupLang; diff --git a/src/Creator/ProductCombinationCreator.php b/src/Creator/ProductCombinationCreator.php index 62ff82b..b6e88e2 100644 --- a/src/Creator/ProductCombinationCreator.php +++ b/src/Creator/ProductCombinationCreator.php @@ -3,8 +3,6 @@ namespace PrestaShop\Module\PsFixturesCreator\Creator; use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Faker\Generator; @@ -16,11 +14,8 @@ use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; use PrestaShopBundle\Entity\Attribute; use PrestaShopBundle\Entity\AttributeGroup; -use PrestaShopBundle\Entity\AttributeGroupLang; -use PrestaShopBundle\Entity\AttributeLang; use PrestaShopBundle\Entity\Lang; use PrestaShopBundle\Entity\Repository\LangRepository; -use PrestaShopBundle\Entity\Shop as ShopEntity; class ProductCombinationCreator { @@ -123,9 +118,9 @@ private function getAttributeGroupWithAtLeast(int $minimumValuesNumber): array ->addGroupBy('ag.id') ; $attributeGroups = $qb->getQuery()->getArrayResult(); - $attributeGroupIds = array_map(static function(array $attributeGroup) { + $attributeGroupIds = array_map(static function (array $attributeGroup) { return (int) $attributeGroup['id']; - }, array_filter($attributeGroups, static function(array $attributeGroup) use ($minimumValuesNumber) { + }, array_filter($attributeGroups, static function (array $attributeGroup) use ($minimumValuesNumber) { return $attributeGroup['attributesNb'] >= $minimumValuesNumber; })); @@ -137,6 +132,7 @@ private function getAttributeGroupWithAtLeast(int $minimumValuesNumber): array ->where('ag.id IN (:attributeGroupIds)') ->setParameter('attributeGroupIds', array_values($attributeGroupIds), ArrayParameterType::INTEGER) ; + return $qb->getQuery()->getResult(Query::HYDRATE_OBJECT); } } diff --git a/src/Faker/FakerCategory.php b/src/Faker/FakerCategory.php index 41ddbf3..c2d6b80 100644 --- a/src/Faker/FakerCategory.php +++ b/src/Faker/FakerCategory.php @@ -30,7 +30,6 @@ use Doctrine\Inflector\Inflector; use Doctrine\Inflector\InflectorFactory; -use Doctrine\Inflector\NoopWordInflector; use Faker\Generator; class FakerCategory From 440c405e86a020a625720ecb6c12099330d80464 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Wed, 15 Nov 2023 02:15:01 +0100 Subject: [PATCH 4/4] Handle PHPStan valdiation from PrestaShop 8.1 --- .github/workflows/php.yml | 2 +- src/Command/ShopCreatorCommand.php | 6 ++-- src/Creator/AttributeCreator.php | 5 --- src/Creator/ProductCombinationCreator.php | 43 +++++++++++++++-------- src/Faker/FakerCategory.php | 4 +-- tests/index.php | 34 ++++++++++++++++++ tests/phpstan.sh | 28 +++++++++++++++ tests/phpstan/index.php | 34 ++++++++++++++++++ tests/phpstan/phpstan-8.0.neon | 11 ++++++ tests/phpstan/phpstan-8.1.neon | 11 ++++++ tests/phpstan/phpstan-latest.neon | 2 ++ tests/phpstan/phpstan.neon | 14 ++++++++ 12 files changed, 169 insertions(+), 25 deletions(-) create mode 100644 tests/index.php create mode 100755 tests/phpstan.sh create mode 100644 tests/phpstan/index.php create mode 100644 tests/phpstan/phpstan-8.0.neon create mode 100644 tests/phpstan/phpstan-8.1.neon create mode 100644 tests/phpstan/phpstan-latest.neon create mode 100644 tests/phpstan/phpstan.neon diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index d496330..6672106 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - presta-versions: ['1.7.7', '1.7.8', '8.0', 'latest'] + presta-versions: ['8.0', '8.1', 'latest'] steps: - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/src/Command/ShopCreatorCommand.php b/src/Command/ShopCreatorCommand.php index 9a9dee3..26cf1a6 100644 --- a/src/Command/ShopCreatorCommand.php +++ b/src/Command/ShopCreatorCommand.php @@ -167,9 +167,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @param int $id_lang Language identifier * - * @return array + * @return bool|array */ - private function getStandardProducts($id_lang, bool $front = true): bool|array + private function getStandardProducts($id_lang, bool $front = true) { $sql = 'SELECT p.`id_product`, pl.`name` FROM `' . _DB_PREFIX_ . 'product` p @@ -179,6 +179,6 @@ private function getStandardProducts($id_lang, bool $front = true): bool|array ' . ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') . ' ORDER BY pl.`name`'; - return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); + return Db::getInstance((bool) _PS_USE_SQL_SLAVE_)->executeS($sql); } } diff --git a/src/Creator/AttributeCreator.php b/src/Creator/AttributeCreator.php index 4d7ca8b..b3b459a 100644 --- a/src/Creator/AttributeCreator.php +++ b/src/Creator/AttributeCreator.php @@ -29,7 +29,6 @@ namespace PrestaShop\Module\PsFixturesCreator\Creator; use Doctrine\ORM\EntityManagerInterface; -use Faker\Generator as Faker; use PrestaShop\Module\PsFixturesCreator\Faker\FakerCategory; use PrestaShopBundle\Entity\Attribute; use PrestaShopBundle\Entity\AttributeGroup; @@ -50,18 +49,14 @@ class AttributeCreator private ShopRepository $shopRepository; - private Faker $faker; - public function __construct( EntityManagerInterface $entityManager, LangRepository $langRepository, ShopRepository $shopRepository, - Faker $faker ) { $this->entityManager = $entityManager; $this->langRepository = $langRepository; $this->shopRepository = $shopRepository; - $this->faker = $faker; } public function generate(int $attributeGroupNumber, int $attributeValuePerGroupNumber, int $shopId): array diff --git a/src/Creator/ProductCombinationCreator.php b/src/Creator/ProductCombinationCreator.php index b6e88e2..eb19b5a 100644 --- a/src/Creator/ProductCombinationCreator.php +++ b/src/Creator/ProductCombinationCreator.php @@ -2,7 +2,7 @@ namespace PrestaShop\Module\PsFixturesCreator\Creator; -use Doctrine\DBAL\ArrayParameterType; +use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Faker\Generator; @@ -63,18 +63,33 @@ public function generate(int $productWithCombinations, int $attributeGroupNumber $productNames[$lang->getId()] = $productName . ' ' . $lang->getLocale(); } - /** @var ProductId $newProductId */ - $newProductId = $this->commandBus->handle(new AddProductCommand( - ProductType::TYPE_COMBINATIONS, - $shopId, - $productNames - )); - - $this->commandBus->handle(new GenerateProductCombinationsCommand( - $newProductId->getValue(), - $combinationAttributes, - $shopId ? ShopConstraint::shop($shopId) : ShopConstraint::allShops() - )); + if (version_compare(_PS_VERSION_, '8.1', '>=')) { + /** @var ProductId $newProductId */ + $newProductId = $this->commandBus->handle(new AddProductCommand( + ProductType::TYPE_COMBINATIONS, + $shopId, + $productNames + )); + + $this->commandBus->handle(new GenerateProductCombinationsCommand( + $newProductId->getValue(), + $combinationAttributes, + $shopId ? ShopConstraint::shop($shopId) : ShopConstraint::allShops() + )); + } elseif (version_compare(_PS_VERSION_, '8.0', '>=')) { + /** @var ProductId $newProductId */ + $newProductId = $this->commandBus->handle(new AddProductCommand( + ProductType::TYPE_COMBINATIONS, + $productNames + )); + + $this->commandBus->handle(new GenerateProductCombinationsCommand( + $newProductId->getValue(), + $combinationAttributes, + )); + } else { + throw new \RuntimeException(sprintf('Version %s not handled to generate combinations', _PS_VERSION_)); + } } } @@ -130,7 +145,7 @@ private function getAttributeGroupWithAtLeast(int $minimumValuesNumber): array ; $qb ->where('ag.id IN (:attributeGroupIds)') - ->setParameter('attributeGroupIds', array_values($attributeGroupIds), ArrayParameterType::INTEGER) + ->setParameter('attributeGroupIds', array_values($attributeGroupIds), Connection::PARAM_INT_ARRAY) ; return $qb->getQuery()->getResult(Query::HYDRATE_OBJECT); diff --git a/src/Faker/FakerCategory.php b/src/Faker/FakerCategory.php index c2d6b80..fd7baa1 100644 --- a/src/Faker/FakerCategory.php +++ b/src/Faker/FakerCategory.php @@ -69,9 +69,9 @@ class FakerCategory private static array $randomAvailableCategories; /** - * @var Inflector + * @var Inflector|null */ - private static $inflector; + private static $inflector = null; private string $category; private string $categoryName; diff --git a/tests/index.php b/tests/index.php new file mode 100644 index 0000000..45df26c --- /dev/null +++ b/tests/index.php @@ -0,0 +1,34 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/tests/phpstan.sh b/tests/phpstan.sh new file mode 100755 index 0000000..a6126e1 --- /dev/null +++ b/tests/phpstan.sh @@ -0,0 +1,28 @@ +#!/bin/bash +PS_VERSION=$1 + +set -e + +# Docker images prestashop/prestashop may be used, even if the shop remains uninstalled +echo "Pull PrestaShop files (Tag ${PS_VERSION})" + +docker rm -f temp-ps || true +docker volume rm -f ps-volume || true + +docker run -tid --rm -v ps-volume:/var/www/html --name temp-ps prestashop/prestashop:$PS_VERSION + +# Clear previous instance of the module in the PrestaShop volume +echo "Clear previous module" + +docker exec -t temp-ps rm -rf /var/www/html/modules/psfixturescreator + +# Run a container for PHPStan, having access to the module content and PrestaShop sources. +# This tool is outside the composer.json because of the compatibility with PHP 5.6 +echo "Run PHPStan using phpstan-${PS_VERSION}.neon file" + +docker run --rm --volumes-from temp-ps \ + -v $PWD:/var/www/html/modules/psfixturescreator \ + -e _PS_ROOT_DIR_=/var/www/html \ + --workdir=/var/www/html/modules/psfixturescreator ghcr.io/phpstan/phpstan:1 \ + analyse \ + --configuration=/var/www/html/modules/psfixturescreator/tests/phpstan/phpstan-$PS_VERSION.neon diff --git a/tests/phpstan/index.php b/tests/phpstan/index.php new file mode 100644 index 0000000..45df26c --- /dev/null +++ b/tests/phpstan/index.php @@ -0,0 +1,34 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/tests/phpstan/phpstan-8.0.neon b/tests/phpstan/phpstan-8.0.neon new file mode 100644 index 0000000..dbadbf4 --- /dev/null +++ b/tests/phpstan/phpstan-8.0.neon @@ -0,0 +1,11 @@ +includes: + - %currentWorkingDirectory%/tests/phpstan/phpstan.neon + +parameters: + ignoreErrors: + - + message: '#AddProductCommand constructor expects#' + path: ../../src/Creator/ProductCombinationCreator.php + - + message: '#enerateProductCombinationsCommand constructor invoked#' + path: ../../src/Creator/ProductCombinationCreator.php diff --git a/tests/phpstan/phpstan-8.1.neon b/tests/phpstan/phpstan-8.1.neon new file mode 100644 index 0000000..dbadbf4 --- /dev/null +++ b/tests/phpstan/phpstan-8.1.neon @@ -0,0 +1,11 @@ +includes: + - %currentWorkingDirectory%/tests/phpstan/phpstan.neon + +parameters: + ignoreErrors: + - + message: '#AddProductCommand constructor expects#' + path: ../../src/Creator/ProductCombinationCreator.php + - + message: '#enerateProductCombinationsCommand constructor invoked#' + path: ../../src/Creator/ProductCombinationCreator.php diff --git a/tests/phpstan/phpstan-latest.neon b/tests/phpstan/phpstan-latest.neon new file mode 100644 index 0000000..fb8d673 --- /dev/null +++ b/tests/phpstan/phpstan-latest.neon @@ -0,0 +1,2 @@ +includes: + - %currentWorkingDirectory%/tests/phpstan/phpstan-8.1.neon diff --git a/tests/phpstan/phpstan.neon b/tests/phpstan/phpstan.neon new file mode 100644 index 0000000..f2a6c09 --- /dev/null +++ b/tests/phpstan/phpstan.neon @@ -0,0 +1,14 @@ +includes: + - %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/ps-module-extension.neon +parameters: + paths: + - ../../psfixturescreator.php + - ../../src/ + level: 5 + ignoreErrors: + - + message: '#Access to an undefined property Faker\\Generator#' + path: ../../src/Creator/ProductCombinationCreator.php + - + message: '#Call to an undefined method Faker\\Generator#' + path: ../../src/Faker/FakerCategory.php