Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mage-os-ci committed Oct 6, 2023
2 parents 3670cc4 + d75f830 commit 1827f61
Show file tree
Hide file tree
Showing 18 changed files with 922 additions and 11 deletions.
64 changes: 64 additions & 0 deletions BundleProductDataExporter/Model/Provider/Product/Type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\BundleProductDataExporter\Model\Provider\Product;

use Magento\QueryXml\Model\QueryProcessor;
use Magento\Catalog\Model\Product\Type as ProductType;

/**
* Determine bundle product type
*/
class Type
{
private const BUNDLE_FIXED_TYPE = 'bundle_fixed';

private QueryProcessor $queryProcessor;

/**
* @param QueryProcessor $queryProcessor
*/
public function __construct(
QueryProcessor $queryProcessor
) {
$this->queryProcessor = $queryProcessor;
}

/**
* Get provider data
*
* @param array $values
* @return array
* @throws \Zend_Db_Statement_Exception
*/
public function get(array $values) : array
{
$output = [];
$bundleIds = [];
foreach ($values as $value) {
if (!isset($output[$value['sku']])) {
$output[$value['sku']] = [
'type' => $value['type'],
'sku' => $value['sku']
];
// Get list of bundle products
if ($value['type'] === ProductType::TYPE_BUNDLE) {
$bundleIds[] = $value['productId'];
}
}
}
$cursor = $this->queryProcessor->execute('bundleFixedProductType', ['ids' => $bundleIds]);
while ($row = $cursor->fetch()) {
// Set bundle_fixed product type if product has price (only bundle fixed products have it)
if (isset($output[$row['sku']])) {
$output[$row['sku']]['type'] = self::BUNDLE_FIXED_TYPE;
}
}

return $output;
}
}
73 changes: 73 additions & 0 deletions BundleProductDataExporter/Plugin/ExtendProductParentQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* Copyright 2023 Adobe
* All rights reserved
*
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\BundleProductDataExporter\Plugin;

use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Select;
use Magento\ParentProductDataExporter\Model\Query\ProductParentQuery;

/**
* Plugin for get parent products query class
*/
class ExtendProductParentQuery
{
private const BUNDLE_FIXED_TYPE = 'bundle_fixed';

/**
* @var ResourceConnection
*/
private ResourceConnection $resourceConnection;

/**
* MainProductQuery constructor.
*
* @param ResourceConnection $resourceConnection
*/
public function __construct(
ResourceConnection $resourceConnection
) {
$this->resourceConnection = $resourceConnection;
}

/**
* Add bundle product type data to select
*
* @param ProductParentQuery $subject
* @param Select $select
* @param array $arguments
* @return Select
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function afterGetQuery(ProductParentQuery $subject, Select $select, array $arguments): Select
{
$connection = $this->resourceConnection->getConnection();
$joinField = $connection->getAutoIncrementField(
$this->resourceConnection->getTableName('catalog_product_entity')
);

$select->joinLeft(
['eav_attribute' => $this->resourceConnection->getTableName('eav_attribute')],
'eav_attribute.attribute_code = \'price_type\'',
[]
)->joinLeft(
['eavi_parent' => $this->resourceConnection->getTableName('catalog_product_entity_int')],
\sprintf('parent_cpe.%1$s = eavi_parent.%1$s', $joinField) .
' AND eavi_parent.attribute_id = eav_attribute.attribute_id' .
' AND eavi_parent.store_id = 0',
[]
)->columns(
[
'productType' => 'IF(eavi_parent.value = 1, \'' . self::BUNDLE_FIXED_TYPE . '\', cpe.type_id)'
]
);

return $select;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function getBundleFixedProductOptionsDataProvider() : array
'sku' => 'bundle-product',
'storeViewCode' => 'default',
'name' => 'Bundle Product',
'type' => 'bundle',
'type' => 'bundle_fixed',
'optionsV2' => [
[
'type' => 'bundle',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\BundleProductDataExporter\Test\Integration;

use DateTime;
use DateTimeInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\DataExporter\Model\FeedInterface;
use Magento\DataExporter\Model\FeedPool;
use Magento\Indexer\Model\Indexer;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Throwable;
use Zend_Db_Statement_Exception;

/**
* Check parents fields for options bundle products
*
* @magentoDbIsolation disabled
* @magentoAppIsolation enabled
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ExportBundleOptionWithParentTest extends TestCase
{
private const PRODUCT_FEED_INDEXER = 'catalog_data_exporter_products';

/**
* @var Indexer
*/
private Indexer $indexer;

/**
* @var FeedInterface
*/
private FeedInterface $productsFeed;

/**
* @var ProductRepositoryInterface
*/
private ProductRepositoryInterface $productRepository;

/**
* @var ResourceConnection
*/
private ResourceConnection $resourceConnection;

/**
* @param string|null $name
* @param array $data
* @param $dataName
*/
public function __construct(
?string $name = null,
array $data = [],
$dataName = ''
) {
parent::__construct($name, $data, $dataName);
$this->indexer = Bootstrap::getObjectManager()->create(Indexer::class);
$this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
$this->productsFeed = Bootstrap::getObjectManager()->get(FeedPool::class)->getFeed('products');
$this->resourceConnection = Bootstrap::getObjectManager()->create(ResourceConnection::class);
}

/**
* @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options.php
* @magentoDataFixture Magento/Bundle/_files/bundle_product_with_dynamic_price.php
*
* @dataProvider expectedBundleOptionsWithParentData
* @throws NoSuchEntityException
* @throws Zend_Db_Statement_Exception
*/
public function testExportBundleOptionsWithParentData(array $expectedSimpleProduct): void
{
$this->checkExpectedItemsAreExportedInFeed($expectedSimpleProduct);
}

/**
* @return \array[][]
*/
private function expectedBundleOptionsWithParentData(): array
{
return [
[
[
[
'sku' => 'simple1',
'type' => 'SIMPLE',
'parents' => [
0 => ['sku' => 'bundle-product', 'productType' => 'bundle_fixed'],
1 => ['sku' => 'bundle_product_with_dynamic_price', 'productType' => 'bundle'],
],
],
]
]
];
}

/**
* @param array $expectedItems
* @return void
* @throws NoSuchEntityException
* @throws Zend_Db_Statement_Exception
*/
private function checkExpectedItemsAreExportedInFeed(array $expectedItems): void
{
$ids = [];
foreach ($expectedItems as $expectedItem) {
$ids[] = $this->productRepository->get($expectedItem['sku'])->getId();
}
$timestamp = new DateTime('Now - 1 second');
$this->runIndexer($ids);
$actualProductsFeed = $this->productsFeed->getFeedSince($timestamp->format(DateTimeInterface::W3C));

self::assertNotEmpty($actualProductsFeed['feed'], 'Product Feed should not be empty');

foreach ($expectedItems as $index => $product) {
if (!isset($actualProductsFeed['feed'][$index])) {
self::fail("Cannot find product feed");
}

self::assertEquals(
$product['sku'],
$actualProductsFeed['feed'][$index]['sku'],
"Sku is not equal for index {$index}"
);

self::assertEqualsCanonicalizing(
$product['parents'],
$actualProductsFeed['feed'][$index]['parents'],
"Parents is not equal"
);
}
}

/**
* Run the indexer to extract product data
* @param $ids
* @return void
*/
private function runIndexer($ids): void
{
try {
$this->indexer->load(self::PRODUCT_FEED_INDEXER);
$this->indexer->reindexList($ids);
} catch (Throwable) {
throw new RuntimeException('Could not reindex product data');
}
}

/**
* @return void
*/
protected function tearDown(): void
{
parent::tearDown();
$this->truncateIndexTable();
}

/**
* Truncates index table
*/
private function truncateIndexTable(): void
{
$connection = $this->resourceConnection->getConnection();
$feedTable = $this->resourceConnection->getTableName('catalog_data_exporter_products');
$connection->truncateTable($feedTable);
}
}
4 changes: 3 additions & 1 deletion BundleProductDataExporter/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"magento/module-bundle": ">=101.0.4",
"magento/module-catalog": ">=104.0.4",
"magento/module-data-exporter": "self.version",
"magento/module-catalog-data-exporter": "self.version"
"magento/module-catalog-data-exporter": "self.version",
"magento/module-parent-product-data-exporter": "self.version",
"magento/module-query-xml": "self.version"
}
}
4 changes: 4 additions & 0 deletions BundleProductDataExporter/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
</argument>
</arguments>
</type>
<type name="Magento\ParentProductDataExporter\Model\Query\ProductParentQuery">
<plugin name="extend_product_parent_query"
type="Magento\BundleProductDataExporter\Plugin\ExtendProductParentQuery"/>
</type>
</config>
5 changes: 5 additions & 0 deletions BundleProductDataExporter/etc/et_schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@
<record name="ProductOptionValue">
<field name="priceType" type="String" />
</record>
<record name="Product">
<field name="type" type="String"
provider="Magento\BundleProductDataExporter\Model\Provider\Product\Type">
</field>
</record>
</config>
35 changes: 35 additions & 0 deletions BundleProductDataExporter/etc/query.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_QueryXml:etc/query.xsd">
<query name="bundleFixedProductType">
<source name="catalog_product_entity">
<attribute name="sku"/>
<filter glue="and">
<condition attribute="entity_id" operator="in" type="placeholder">ids</condition>
</filter>
<link-source name="eav_entity_type" alias="entity_type">
<using glue="and">
<condition attribute="entity_type_code" operator="eq" type="value">catalog_product</condition>
</using>
</link-source>
<link-source name="eav_attribute">
<using glue="and">
<condition attribute="entity_type_id" operator="eq" type="identifier">entity_type.entity_type_id</condition>
<condition attribute="attribute_code" operator="eq" type="value">price_type</condition>
</using>
</link-source>
<link-source name="catalog_product_entity_int" link-type="inner">
<using glue="and">
<condition attribute="Magento\Catalog\Api\Data\ProductInterface:LinkField" operator="eq" type="identifier">catalog_product_entity.Primary Key</condition>
<condition attribute="attribute_id" operator="eq" type="identifier">eav_attribute.attribute_id</condition>
<condition attribute="value" operator="eq" type="value">1</condition>
</using>
</link-source>
</source>
</query>
</config>
Loading

0 comments on commit 1827f61

Please sign in to comment.