mirror of
https://github.com/bdnugget/magento2.git
synced 2026-11-07 09:17:35 +00:00
Merge pull request #8769 from adobe-commerce-tier-4/L3-PR-2024-02-16
[Support Tier-4 vasyliev] 02-16-2024 Regular delivery of bugfixes and improvements
This commit is contained in:
31 files changed
+732
-561
No files matched your search
@@ -1,173 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Bundle\Model\Product;
|
||||
|
||||
use Magento\Framework\EntityManager\MetadataPool;
|
||||
use Magento\Catalog\Model\Product\Attribute\Source\Status;
|
||||
use Magento\Bundle\Model\ResourceModel\Selection as BundleSelection;
|
||||
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
|
||||
use Magento\Store\Model\StoreManagerInterface;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
|
||||
use Magento\Catalog\Model\Product;
|
||||
use Magento\Catalog\Api\Data\ProductInterface;
|
||||
|
||||
/**
|
||||
* Class to return ids of options and child products when all products in required option are disabled in bundle product
|
||||
*/
|
||||
class SelectionProductsDisabledRequired implements ResetAfterRequestInterface
|
||||
{
|
||||
/**
|
||||
* @var BundleSelection
|
||||
*/
|
||||
private $bundleSelection;
|
||||
|
||||
/**
|
||||
* @var StoreManagerInterface
|
||||
*/
|
||||
private $storeManager;
|
||||
|
||||
/**
|
||||
* @var Status
|
||||
*/
|
||||
private $catalogProductStatus;
|
||||
|
||||
/**
|
||||
* @var ProductCollectionFactory
|
||||
*/
|
||||
private $productCollectionFactory;
|
||||
|
||||
/**
|
||||
* @var MetadataPool
|
||||
*/
|
||||
private $metadataPool;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $hasStockStatusFilter = 'has_stock_status_filter';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $productsDisabledRequired = [];
|
||||
|
||||
/**
|
||||
* @param BundleSelection $bundleSelection
|
||||
* @param StoreManagerInterface $storeManager
|
||||
* @param Status $catalogProductStatus
|
||||
* @param ProductCollectionFactory $productCollectionFactory
|
||||
* @param MetadataPool $metadataPool
|
||||
*/
|
||||
public function __construct(
|
||||
BundleSelection $bundleSelection,
|
||||
StoreManagerInterface $storeManager,
|
||||
Status $catalogProductStatus,
|
||||
ProductCollectionFactory $productCollectionFactory,
|
||||
MetadataPool $metadataPool
|
||||
) {
|
||||
$this->bundleSelection = $bundleSelection;
|
||||
$this->storeManager = $storeManager;
|
||||
$this->catalogProductStatus = $catalogProductStatus;
|
||||
$this->productCollectionFactory = $productCollectionFactory;
|
||||
$this->metadataPool = $metadataPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ids of options and child products when all products in required option are disabled in bundle product
|
||||
*
|
||||
* @param int $bundleId
|
||||
* @param int|null $websiteId
|
||||
* @return array
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*/
|
||||
public function getChildProductIds(int $bundleId, ?int $websiteId = null): array
|
||||
{
|
||||
if (!$websiteId) {
|
||||
$websiteId = (int)$this->storeManager->getStore()->getWebsiteId();
|
||||
}
|
||||
$cacheKey = $this->getCacheKey($bundleId, $websiteId);
|
||||
if (isset($this->productsDisabledRequired[$cacheKey])) {
|
||||
return $this->productsDisabledRequired[$cacheKey];
|
||||
}
|
||||
$selectionProductIds = $this->bundleSelection->getChildrenIds($bundleId);
|
||||
/** for cases when no required products found */
|
||||
if (count($selectionProductIds) === 1 && isset($selectionProductIds[0])) {
|
||||
$this->productsDisabledRequired[$cacheKey] = [];
|
||||
return $this->productsDisabledRequired[$cacheKey];
|
||||
}
|
||||
$products = $this->getProducts($selectionProductIds, $websiteId);
|
||||
if (!$products) {
|
||||
$this->productsDisabledRequired[$cacheKey] = [];
|
||||
return $this->productsDisabledRequired[$cacheKey];
|
||||
}
|
||||
foreach ($selectionProductIds as $optionId => $optionProductIds) {
|
||||
foreach ($optionProductIds as $productId) {
|
||||
if (isset($products[$productId])) {
|
||||
/** @var Product $product */
|
||||
$product = $products[$productId];
|
||||
if (in_array($product->getStatus(), $this->catalogProductStatus->getVisibleStatusIds())) {
|
||||
unset($selectionProductIds[$optionId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->productsDisabledRequired[$cacheKey] = $selectionProductIds;
|
||||
return $this->productsDisabledRequired[$cacheKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products objects
|
||||
*
|
||||
* @param array $selectionProductIds
|
||||
* @param int $websiteId
|
||||
* @return ProductInterface[]
|
||||
*/
|
||||
private function getProducts(array $selectionProductIds, int $websiteId): array
|
||||
{
|
||||
$productIds = [];
|
||||
$defaultStore = $this->storeManager->getWebsite($websiteId)->getDefaultStore();
|
||||
$defaultStoreId = $defaultStore ? $defaultStore->getId() : null;
|
||||
foreach ($selectionProductIds as $optionProductIds) {
|
||||
$productIds[] = $optionProductIds;
|
||||
}
|
||||
$productIds = array_merge([], ...$productIds);
|
||||
$productCollection = $this->productCollectionFactory->create();
|
||||
$productCollection->joinAttribute(
|
||||
ProductInterface::STATUS,
|
||||
Product::ENTITY . '/' . ProductInterface::STATUS,
|
||||
$this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(),
|
||||
null,
|
||||
'inner',
|
||||
$defaultStoreId
|
||||
);
|
||||
$productCollection->addIdFilter($productIds);
|
||||
$productCollection->addStoreFilter($defaultStoreId);
|
||||
$productCollection->setFlag($this->hasStockStatusFilter, true);
|
||||
return $productCollection->getItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key
|
||||
*
|
||||
* @param int $bundleId
|
||||
* @param int $websiteId
|
||||
* @return string
|
||||
*/
|
||||
private function getCacheKey(int $bundleId, int $websiteId): string
|
||||
{
|
||||
return $bundleId . '-' . $websiteId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function _resetState(): void
|
||||
{
|
||||
$this->productsDisabledRequired = [];
|
||||
}
|
||||
}
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Bundle\Model\ResourceModel\Indexer\Price;
|
||||
|
||||
use Magento\Bundle\Model\Product\SelectionProductsDisabledRequired;
|
||||
use Magento\Catalog\Model\Product\Type;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure;
|
||||
use Magento\Framework\App\ResourceConnection;
|
||||
use Magento\Catalog\Model\Config;
|
||||
use Magento\Framework\EntityManager\MetadataPool;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface;
|
||||
use Magento\Bundle\Model\ResourceModel\Selection as BundleSelection;
|
||||
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
|
||||
|
||||
/**
|
||||
* Remove bundle product from price index when all products in required option are disabled
|
||||
*/
|
||||
class DisabledProductOptionPriceModifier implements PriceModifierInterface, ResetAfterRequestInterface
|
||||
{
|
||||
/**
|
||||
* @var ResourceConnection
|
||||
*/
|
||||
private $resourceConnection;
|
||||
|
||||
/**
|
||||
* @var SelectionProductsDisabledRequired
|
||||
*/
|
||||
private $selectionProductsDisabledRequired;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $websiteIdsOfProduct = [];
|
||||
|
||||
/**
|
||||
* @var BundleSelection
|
||||
*/
|
||||
private BundleSelection $bundleSelection;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private Config $config;
|
||||
|
||||
/**
|
||||
* @var MetadataPool
|
||||
*/
|
||||
private MetadataPool $metadataPool;
|
||||
|
||||
/**
|
||||
* @param ResourceConnection $resourceConnection
|
||||
* @param Config $config
|
||||
* @param MetadataPool $metadataPool
|
||||
* @param BundleSelection $bundleSelection
|
||||
* @param SelectionProductsDisabledRequired $selectionProductsDisabledRequired
|
||||
*/
|
||||
public function __construct(
|
||||
ResourceConnection $resourceConnection,
|
||||
Config $config,
|
||||
MetadataPool $metadataPool,
|
||||
BundleSelection $bundleSelection,
|
||||
SelectionProductsDisabledRequired $selectionProductsDisabledRequired
|
||||
) {
|
||||
$this->resourceConnection = $resourceConnection;
|
||||
$this->config = $config;
|
||||
$this->metadataPool = $metadataPool;
|
||||
$this->bundleSelection = $bundleSelection;
|
||||
$this->selectionProductsDisabledRequired = $selectionProductsDisabledRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bundle product from price index when all products in required option are disabled
|
||||
*
|
||||
* @param IndexTableStructure $priceTable
|
||||
* @param array $entityIds
|
||||
* @return void
|
||||
* @throws \Magento\Framework\Exception\LocalizedException
|
||||
*/
|
||||
public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void
|
||||
{
|
||||
foreach ($this->getBundleIds($entityIds) as $entityId) {
|
||||
$entityId = (int) $entityId;
|
||||
foreach ($this->getWebsiteIdsOfProduct($entityId) as $websiteId) {
|
||||
$websiteId = (int) $websiteId;
|
||||
$productIdsDisabledRequired = $this->selectionProductsDisabledRequired
|
||||
->getChildProductIds($entityId, $websiteId);
|
||||
if ($productIdsDisabledRequired) {
|
||||
$connection = $this->resourceConnection->getConnection('indexer');
|
||||
$select = $connection->select();
|
||||
$select->from(['price_index' => $priceTable->getTableName()], []);
|
||||
$priceEntityField = $priceTable->getEntityField();
|
||||
$select->where('price_index.website_id = ?', $websiteId);
|
||||
$select->where("price_index.{$priceEntityField} = ?", $entityId);
|
||||
$query = $select->deleteFromSelect('price_index');
|
||||
$connection->query($query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all website ids of product
|
||||
*
|
||||
* @param int $entityId
|
||||
* @return array
|
||||
*/
|
||||
private function getWebsiteIdsOfProduct(int $entityId): array
|
||||
{
|
||||
if (isset($this->websiteIdsOfProduct[$entityId])) {
|
||||
return $this->websiteIdsOfProduct[$entityId];
|
||||
}
|
||||
$connection = $this->resourceConnection->getConnection('indexer');
|
||||
$select = $connection->select();
|
||||
$select->from(
|
||||
['product_in_websites' => $this->resourceConnection->getTableName('catalog_product_website')],
|
||||
['website_id']
|
||||
)->where('product_in_websites.product_id = ?', $entityId);
|
||||
$this->websiteIdsOfProduct[$entityId] = $connection->fetchCol($select);
|
||||
|
||||
return $this->websiteIdsOfProduct[$entityId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bundle Ids
|
||||
*
|
||||
* @param array $entityIds
|
||||
* @return \Traversable
|
||||
*/
|
||||
private function getBundleIds(array $entityIds): \Traversable
|
||||
{
|
||||
$connection = $this->resourceConnection->getConnection('indexer');
|
||||
$select = $connection->select();
|
||||
$select->from(
|
||||
['cpe' => $this->resourceConnection->getTableName('catalog_product_entity')],
|
||||
['entity_id']
|
||||
)->where('cpe.entity_id in ( ? )', !empty($entityIds) ? $entityIds : [0], \Zend_Db::INT_TYPE)
|
||||
->where('type_id = ?', Type::TYPE_BUNDLE);
|
||||
|
||||
$statement = $select->query();
|
||||
while ($id = $statement->fetchColumn()) {
|
||||
yield $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function _resetState(): void
|
||||
{
|
||||
$this->websiteIdsOfProduct = [];
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Bundle\Plugin\Catalog\Helper;
|
||||
|
||||
use Magento\Catalog\Model\Product as ProductModel;
|
||||
use Magento\Catalog\Model\Product\Type;
|
||||
use Magento\Catalog\Helper\Product as Subject;
|
||||
use Magento\Bundle\Model\Product\SelectionProductsDisabledRequired;
|
||||
use Magento\Framework\App\Config\ScopeConfigInterface;
|
||||
use Magento\CatalogInventory\Model\Configuration;
|
||||
use Magento\Store\Model\ScopeInterface;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Plugin to not show bundle product when all products in required option are disabled
|
||||
*/
|
||||
class Product
|
||||
{
|
||||
/**
|
||||
* @var SelectionProductsDisabledRequired
|
||||
*/
|
||||
private $selectionProductsDisabledRequired;
|
||||
|
||||
/**
|
||||
* @var ScopeConfigInterface
|
||||
*/
|
||||
private $scopeConfig;
|
||||
|
||||
/**
|
||||
* @var ProductRepositoryInterface
|
||||
*/
|
||||
private $productRepository;
|
||||
|
||||
/**
|
||||
* @param SelectionProductsDisabledRequired $selectionProductsDisabledRequired
|
||||
* @param ScopeConfigInterface $scopeConfig
|
||||
* @param ProductRepositoryInterface $productRepository
|
||||
*/
|
||||
public function __construct(
|
||||
SelectionProductsDisabledRequired $selectionProductsDisabledRequired,
|
||||
ScopeConfigInterface $scopeConfig,
|
||||
ProductRepositoryInterface $productRepository
|
||||
) {
|
||||
$this->selectionProductsDisabledRequired = $selectionProductsDisabledRequired;
|
||||
$this->scopeConfig = $scopeConfig;
|
||||
$this->productRepository = $productRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not show bundle product when all products in required option are disabled
|
||||
*
|
||||
* @param Subject $subject
|
||||
* @param bool $result
|
||||
* @param ProductModel|int $product
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function afterCanShow(Subject $subject, $result, $product)
|
||||
{
|
||||
if (is_int($product)) {
|
||||
$product = $this->productRepository->getById($product);
|
||||
}
|
||||
$productId = (int)$product->getEntityId();
|
||||
if ($result == false || $product->getTypeId() !== Type::TYPE_BUNDLE) {
|
||||
return $result;
|
||||
}
|
||||
$isShowOutOfStock = $this->scopeConfig->getValue(
|
||||
Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
|
||||
ScopeInterface::SCOPE_STORE
|
||||
);
|
||||
if ($isShowOutOfStock) {
|
||||
return $result;
|
||||
}
|
||||
$productIdsDisabledRequired = $this->selectionProductsDisabledRequired->getChildProductIds($productId);
|
||||
return $productIdsDisabledRequired ? false : $result;
|
||||
}
|
||||
}
|
||||
@@ -277,13 +277,6 @@
|
||||
</argument>
|
||||
</arguments>
|
||||
</type>
|
||||
<type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier">
|
||||
<arguments>
|
||||
<argument name="priceModifiers" xsi:type="array">
|
||||
<item name="DisabledProductOptionPriceModifier" xsi:type="object">Magento\Bundle\Model\ResourceModel\Indexer\Price\DisabledProductOptionPriceModifier</item>
|
||||
</argument>
|
||||
</arguments>
|
||||
</type>
|
||||
<type name="Magento\CatalogInventory\Observer\SaveInventoryDataObserver">
|
||||
<arguments>
|
||||
<argument name="parentItemProcessorPool" xsi:type="array">
|
||||
|
||||
@@ -22,7 +22,4 @@
|
||||
<type name="Magento\Catalog\ViewModel\Product\OptionsData">
|
||||
<plugin name="add_bundle_options_data" type="Magento\Bundle\Plugin\Catalog\ViewModel\Product\AddBundleOptionsData" />
|
||||
</type>
|
||||
<type name="Magento\Catalog\Helper\Product">
|
||||
<plugin name="dont_show_if_child_products_disabled" type="Magento\Bundle\Plugin\Catalog\Helper\Product"/>
|
||||
</type>
|
||||
</config>
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\BundleGraphQl\Model\Resolver\Products\DataProvider\Product;
|
||||
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Collection;
|
||||
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessorInterface;
|
||||
use Magento\GraphQl\Model\Query\ContextInterface;
|
||||
use Magento\Catalog\Model\Product;
|
||||
use Magento\Bundle\Model\Product\SelectionProductsDisabledRequired;
|
||||
|
||||
/**
|
||||
* Remove bundle product from collection when all products in required option are disabled
|
||||
*/
|
||||
class DisabledProductOptionPostProcessor implements CollectionPostProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @var SelectionProductsDisabledRequired
|
||||
*/
|
||||
private $selectionProductsDisabledRequired;
|
||||
|
||||
/**
|
||||
* @param SelectionProductsDisabledRequired $selectionProductsDisabledRequired
|
||||
*/
|
||||
public function __construct(
|
||||
SelectionProductsDisabledRequired $selectionProductsDisabledRequired
|
||||
) {
|
||||
$this->selectionProductsDisabledRequired = $selectionProductsDisabledRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bundle product from collection when all products in required option are disabled
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param array $attributeNames
|
||||
* @param ContextInterface|null $context
|
||||
* @return Collection
|
||||
* @throws \Magento\Framework\Exception\LocalizedException
|
||||
* @throws \Magento\Framework\Exception\NoSuchEntityException
|
||||
*/
|
||||
public function process(
|
||||
Collection $collection,
|
||||
array $attributeNames,
|
||||
?ContextInterface $context = null
|
||||
): Collection {
|
||||
if (!$collection->isLoaded()) {
|
||||
$collection->load();
|
||||
}
|
||||
/** @var Product $product */
|
||||
foreach ($collection as $key => $product) {
|
||||
if ($product->getTypeId() !== Product\Type::TYPE_BUNDLE || $context === null) {
|
||||
continue;
|
||||
}
|
||||
$productId = (int)$product->getEntityId();
|
||||
$websiteId = (int)$context->getExtensionAttributes()->getStore()->getWebsiteId();
|
||||
$productIdsDisabledRequired = $this->selectionProductsDisabledRequired->getChildProductIds(
|
||||
$productId,
|
||||
$websiteId
|
||||
);
|
||||
if ($productIdsDisabledRequired) {
|
||||
$collection->removeItemByKey($key);
|
||||
}
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
"php": "~8.1.0||~8.2.0||~8.3.0",
|
||||
"magento/module-catalog": "*",
|
||||
"magento/module-bundle": "*",
|
||||
"magento/module-graph-ql": "*",
|
||||
"magento/module-catalog-graph-ql": "*",
|
||||
"magento/module-quote": "*",
|
||||
"magento/module-quote-graph-ql": "*",
|
||||
|
||||
@@ -23,11 +23,4 @@
|
||||
</argument>
|
||||
</arguments>
|
||||
</type>
|
||||
<type name="Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CompositeCollectionPostProcessor">
|
||||
<arguments>
|
||||
<argument name="collectionPostProcessors" xsi:type="array">
|
||||
<item name="disabled_product_option" xsi:type="object">Magento\BundleGraphQl\Model\Resolver\Products\DataProvider\Product\DisabledProductOptionPostProcessor</item>
|
||||
</argument>
|
||||
</arguments>
|
||||
</type>
|
||||
</config>
|
||||
@@ -1645,6 +1645,11 @@
|
||||
<constraint xsi:type="primary" referenceId="PRIMARY">
|
||||
<column name="id"/>
|
||||
</constraint>
|
||||
<index referenceId="CAT_PRD_IDX_PRICE_TMP_ENTT_ID_CSTR_GROUP_ID_WS_ID" indexType="btree">
|
||||
<column name="entity_id"/>
|
||||
<column name="customer_group_id"/>
|
||||
<column name="website_id"/>
|
||||
</index>
|
||||
</table>
|
||||
<table name="catalog_category_product_index_tmp" resource="default" engine="innodb"
|
||||
comment="Catalog Category Product Indexer temporary table">
|
||||
|
||||
@@ -992,7 +992,8 @@
|
||||
"index": {
|
||||
"CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID": true,
|
||||
"CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID": true,
|
||||
"CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true
|
||||
"CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true,
|
||||
"CAT_PRD_IDX_PRICE_TMP_ENTT_ID_CSTR_GROUP_ID_WS_ID": true
|
||||
},
|
||||
"constraint": {
|
||||
"PRIMARY": true
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
*
|
||||
* Copyright 2023 Adobe
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of Adobe and its suppliers, if any. The intellectual
|
||||
* and technical concepts contained herein are proprietary to Adobe
|
||||
* and its suppliers and are protected by all applicable intellectual
|
||||
* property laws, including trade secret and copyright laws.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from Adobe.
|
||||
* ************************************************************************
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Cms\Test\Fixture;
|
||||
|
||||
use Magento\Cms\Api\BlockRepositoryInterface;
|
||||
use Magento\Cms\Api\Data\BlockInterface;
|
||||
use Magento\Framework\DataObject;
|
||||
use Magento\TestFramework\Fixture\Api\ServiceFactory;
|
||||
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
|
||||
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
|
||||
|
||||
class Block implements RevertibleDataFixtureInterface
|
||||
{
|
||||
private const DEFAULT_DATA = [
|
||||
BlockInterface::IDENTIFIER => 'block%uniqid%',
|
||||
BlockInterface::TITLE => 'Block%uniqid%',
|
||||
BlockInterface::CONTENT => 'BlockContent%uniqid%',
|
||||
BlockInterface::CREATION_TIME => null,
|
||||
BlockInterface::UPDATE_TIME => null,
|
||||
'active' => true
|
||||
];
|
||||
|
||||
/**
|
||||
* @param ProcessorInterface $dataProcessor
|
||||
* @param ServiceFactory $serviceFactory
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly ProcessorInterface $dataProcessor,
|
||||
private readonly ServiceFactory $serviceFactory
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param array $data Parameters. Same format as Block::DEFAULT_DATA.
|
||||
*/
|
||||
public function apply(array $data = []): ?DataObject
|
||||
{
|
||||
$data = $this->dataProcessor->process($this, array_merge(self::DEFAULT_DATA, $data));
|
||||
$service = $this->serviceFactory->create(BlockRepositoryInterface::class, 'save');
|
||||
|
||||
return $service->execute(['block' => $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function revert(DataObject $data): void
|
||||
{
|
||||
$service = $this->serviceFactory->create(BlockRepositoryInterface::class, 'deleteById');
|
||||
$service->execute(['blockId' => $data->getId()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
*
|
||||
* Copyright 2023 Adobe
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of Adobe and its suppliers, if any. The intellectual
|
||||
* and technical concepts contained herein are proprietary to Adobe
|
||||
* and its suppliers and are protected by all applicable intellectual
|
||||
* property laws, including trade secret and copyright laws.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from Adobe.
|
||||
* ************************************************************************
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\GraphQlCache\Model\Plugin\View;
|
||||
|
||||
use Magento\Framework\DataObject\IdentityInterface;
|
||||
use Magento\Framework\View\LayoutInterface;
|
||||
use Magento\GraphQlCache\Model\CacheableQuery;
|
||||
|
||||
class Layout
|
||||
{
|
||||
/**
|
||||
* @param CacheableQuery $cacheableQuery
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly CacheableQuery $cacheableQuery
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add block cache tags to cacheable query
|
||||
*
|
||||
* @param LayoutInterface $subject
|
||||
* @param mixed $result
|
||||
* @param mixed $name
|
||||
* @param mixed $block
|
||||
* @return mixed
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function afterSetBlock(
|
||||
LayoutInterface $subject,
|
||||
mixed $result,
|
||||
mixed $name,
|
||||
mixed $block
|
||||
): mixed {
|
||||
if ($block instanceof IdentityInterface) {
|
||||
$this->cacheableQuery->addCacheTags($block->getIdentities());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,7 @@
|
||||
<type name="Magento\Integration\Api\UserTokenRevokerInterface">
|
||||
<plugin name="set-guest-after-revoke" type="Magento\GraphQlCache\Model\Plugin\Auth\TokenRevoker"/>
|
||||
</type>
|
||||
<type name="Magento\Framework\View\LayoutInterface">
|
||||
<plugin name="add_block_cache_tags_to_query_cache" type="Magento\GraphQlCache\Model\Plugin\View\Layout"/>
|
||||
</type>
|
||||
</config>
|
||||
@@ -19,7 +19,8 @@ class PriceMapper implements MapperInterface
|
||||
{
|
||||
$templates[] = [
|
||||
'price_mapping' => [
|
||||
'match' => 'price_*',
|
||||
"match_pattern" => "regex",
|
||||
'match' => 'price_\\d+_\\d+',
|
||||
'match_mapping_type' => 'string',
|
||||
'mapping' => [
|
||||
'type' => 'double',
|
||||
|
||||
@@ -147,7 +147,8 @@ class OpenSearchTest extends TestCase
|
||||
'dynamic_templates' => [
|
||||
[
|
||||
'price_mapping' => [
|
||||
'match' => 'price_*',
|
||||
"match_pattern" => "regex",
|
||||
'match' => 'price_\\d+_\\d+',
|
||||
'match_mapping_type' => 'string',
|
||||
'mapping' => [
|
||||
'type' => 'double',
|
||||
|
||||
@@ -12,6 +12,7 @@ use Magento\Directory\Model\AllowedCountries;
|
||||
use Magento\Framework\Api\AttributeValueFactory;
|
||||
use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
|
||||
use Magento\Framework\App\ObjectManager;
|
||||
use Magento\Framework\DataObject;
|
||||
use Magento\Framework\Exception\NoSuchEntityException;
|
||||
use Magento\Framework\Model\AbstractExtensibleModel;
|
||||
use Magento\Quote\Api\Data\PaymentInterface;
|
||||
@@ -1619,7 +1620,7 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
* Add product. Returns error message if product type instance can't prepare product.
|
||||
*
|
||||
* @param mixed $product
|
||||
* @param null|float|\Magento\Framework\DataObject $request
|
||||
* @param null|float|DataObject $request
|
||||
* @param null|string $processMode
|
||||
* @return \Magento\Quote\Model\Quote\Item|string
|
||||
* @throws \Magento\Framework\Exception\LocalizedException
|
||||
@@ -1637,11 +1638,12 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
if (is_numeric($request)) {
|
||||
$request = $this->objectFactory->create(['qty' => $request]);
|
||||
}
|
||||
if (!$request instanceof \Magento\Framework\DataObject) {
|
||||
if (!$request instanceof DataObject) {
|
||||
throw new \Magento\Framework\Exception\LocalizedException(
|
||||
__('We found an invalid request for adding product to quote.')
|
||||
);
|
||||
}
|
||||
$invalidProductAddFlag = $this->checkForInvalidProductAdd($request);
|
||||
|
||||
if (!$product->isSalable()) {
|
||||
throw new \Magento\Framework\Exception\LocalizedException(
|
||||
@@ -1699,7 +1701,9 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
|
||||
// collect errors instead of throwing first one
|
||||
if ($item->getHasError()) {
|
||||
$this->deleteItem($item);
|
||||
if (!$invalidProductAddFlag) {
|
||||
$this->deleteItem($item);
|
||||
}
|
||||
foreach ($item->getMessage(false) as $message) {
|
||||
if (!in_array($message, $errors)) {
|
||||
// filter duplicate messages
|
||||
@@ -1717,6 +1721,20 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
return $parentItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if invalid products should be added to quote
|
||||
*
|
||||
* @param DataObject $request
|
||||
* @return bool
|
||||
*/
|
||||
private function checkForInvalidProductAdd(DataObject $request): bool
|
||||
{
|
||||
$forceAdd = $request->getAddToCartInvalidProduct();
|
||||
$request->unsetData('add_to_cart_invalid_product');
|
||||
|
||||
return (bool) $forceAdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding catalog product object data to quote
|
||||
*
|
||||
@@ -1772,8 +1790,8 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
* For more options see \Magento\Catalog\Helper\Product->addParamsToBuyRequest()
|
||||
*
|
||||
* @param int $itemId
|
||||
* @param \Magento\Framework\DataObject $buyRequest
|
||||
* @param null|array|\Magento\Framework\DataObject $params
|
||||
* @param DataObject $buyRequest
|
||||
* @param null|array|DataObject $params
|
||||
* @return \Magento\Quote\Model\Quote\Item
|
||||
* @throws \Magento\Framework\Exception\LocalizedException
|
||||
*
|
||||
@@ -1795,9 +1813,9 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
$product = clone $this->productRepository->getById($productId, false, $this->getStore()->getId());
|
||||
|
||||
if (!$params) {
|
||||
$params = new \Magento\Framework\DataObject();
|
||||
$params = new DataObject();
|
||||
} elseif (is_array($params)) {
|
||||
$params = new \Magento\Framework\DataObject($params);
|
||||
$params = new DataObject($params);
|
||||
}
|
||||
$params->setCurrentConfig($item->getBuyRequest());
|
||||
$buyRequest = $this->_catalogProduct->addParamsToBuyRequest($buyRequest, $params);
|
||||
@@ -2146,7 +2164,7 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
|
||||
* @param string|null $origin Usually a name of module, that embeds error
|
||||
* @param int|null $code Error code, unique for origin, that sets it
|
||||
* @param string|null $message Error message
|
||||
* @param \Magento\Framework\DataObject|null $additionalData Any additional data, that caller would like to store
|
||||
* @param DataObject|null $additionalData Any additional data, that caller would like to store
|
||||
* @return $this
|
||||
*/
|
||||
public function addErrorInfo(
|
||||
|
||||
@@ -30,6 +30,7 @@ use Magento\Framework\DataObject;
|
||||
use Magento\Framework\DataObject\Copy;
|
||||
use Magento\Framework\DataObject\Factory;
|
||||
use Magento\Framework\Event\Manager;
|
||||
use Magento\Framework\Exception\LocalizedException;
|
||||
use Magento\Framework\Exception\NoSuchEntityException;
|
||||
use Magento\Framework\Model\Context;
|
||||
use Magento\Framework\Phrase;
|
||||
@@ -1072,17 +1073,28 @@ class QuoteTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $request
|
||||
* @param $hasError
|
||||
* @return void
|
||||
* @throws LocalizedException
|
||||
* @dataProvider dataProviderForTestAddProductItem
|
||||
*/
|
||||
public function testAddProductItemNew(): void
|
||||
public function testAddProductItemNew($request, $hasError): void
|
||||
{
|
||||
$itemMock = $this->createMock(Item::class);
|
||||
$itemMock = $this->getMockBuilder(Item::class)
|
||||
->disableOriginalConstructor()
|
||||
->addMethods(['getHasError'])
|
||||
->onlyMethods(['representProduct', 'setProduct', 'setOptions', 'setQuote', 'getProduct'])
|
||||
->getMock();
|
||||
$itemMock->expects($this->once())->method('getHasError')->willReturn($hasError);
|
||||
$product = $this->createMock(Product::class);
|
||||
$itemMock->expects($this->any())->method('getProduct')->willReturn($product);
|
||||
|
||||
$expectedResult = $itemMock;
|
||||
$requestMock = $this->createMock(
|
||||
DataObject::class
|
||||
);
|
||||
$this->objectFactoryMock->expects($this->once())
|
||||
$this->objectFactoryMock->expects($this->any())
|
||||
->method('create')
|
||||
->with(['qty' => 1])
|
||||
->willReturn($requestMock);
|
||||
@@ -1145,10 +1157,29 @@ class QuoteTest extends TestCase
|
||||
->method('getTypeInstance')
|
||||
->willReturn($typeInstanceMock);
|
||||
|
||||
$result = $this->quote->addProduct($this->productMock, null);
|
||||
$result = $this->quote->addProduct($this->productMock, $request);
|
||||
$this->assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function dataProviderForTestAddProductItem(): array
|
||||
{
|
||||
return [
|
||||
'not_invalid_product_add' => [null, false],
|
||||
'invalid_product_add' => [
|
||||
new DataObject(
|
||||
[
|
||||
'add_to_cart_invalid_product' => true,
|
||||
'qty' => 1
|
||||
]
|
||||
),
|
||||
true
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -94,16 +94,6 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface
|
||||
// to session for guest customer, as it does \Magento\Checkout\Model\Cart::save which is deprecated.
|
||||
$this->checkoutSession->setQuoteId($reorderOutput->getCart()->getId());
|
||||
|
||||
$errors = $reorderOutput->getErrors();
|
||||
if (!empty($errors)) {
|
||||
$useNotice = $this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true);
|
||||
foreach ($errors as $error) {
|
||||
$useNotice
|
||||
? $this->messageManager->addNoticeMessage($error->getMessage())
|
||||
: $this->messageManager->addErrorMessage($error->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $resultRedirect->setPath('checkout/cart');
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,11 @@ class Reorder
|
||||
*/
|
||||
private $storeManager;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $addToCartInvalidProduct;
|
||||
|
||||
/**
|
||||
* @param OrderFactory $orderFactory
|
||||
* @param CustomerCartResolver $customerCartProvider
|
||||
@@ -121,6 +126,9 @@ class Reorder
|
||||
* @param ProductCollectionFactory $productCollectionFactory
|
||||
* @param OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter
|
||||
* @param StoreManagerInterface|null $storeManager
|
||||
* @param bool $addToCartInvalidProduct
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
|
||||
*/
|
||||
public function __construct(
|
||||
OrderFactory $orderFactory,
|
||||
@@ -131,7 +139,8 @@ class Reorder
|
||||
LoggerInterface $logger,
|
||||
ProductCollectionFactory $productCollectionFactory,
|
||||
OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter,
|
||||
?StoreManagerInterface $storeManager = null
|
||||
?StoreManagerInterface $storeManager = null,
|
||||
bool $addToCartInvalidProduct = false
|
||||
) {
|
||||
$this->orderFactory = $orderFactory;
|
||||
$this->cartRepository = $cartRepository;
|
||||
@@ -143,6 +152,7 @@ class Reorder
|
||||
$this->orderInfoBuyRequestGetter = $orderInfoBuyRequestGetter;
|
||||
$this->storeManager = $storeManager
|
||||
?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
|
||||
$this->addToCartInvalidProduct = $addToCartInvalidProduct;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,6 +286,7 @@ class Reorder
|
||||
|
||||
$addProductResult = null;
|
||||
try {
|
||||
$infoBuyRequest->setAddToCartInvalidProduct($this->addToCartInvalidProduct);
|
||||
$addProductResult = $cart->addProduct($product, $infoBuyRequest);
|
||||
} catch (\Magento\Framework\Exception\LocalizedException $e) {
|
||||
$this->addError($this->getCartItemErrorMessage($orderItem, $product, $e->getMessage()));
|
||||
|
||||
@@ -22,4 +22,9 @@
|
||||
</argument>
|
||||
</arguments>
|
||||
</type>
|
||||
<type name="Magento\Sales\Model\Reorder\Reorder">
|
||||
<arguments>
|
||||
<argument name="addToCartInvalidProduct" xsi:type="boolean">true</argument>
|
||||
</arguments>
|
||||
</type>
|
||||
</config>
|
||||
@@ -305,18 +305,19 @@ class Rule extends AbstractResource
|
||||
public function getActiveAttributes()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$subSelect = $connection->select();
|
||||
$subSelect->reset();
|
||||
$subSelect->from($this->getTable('salesrule_product_attribute'))
|
||||
->group('attribute_id');
|
||||
$select = $connection->select()->from(
|
||||
['a' => $this->getTable('salesrule_product_attribute')],
|
||||
new \Zend_Db_Expr('DISTINCT ea.attribute_code')
|
||||
['a' => $subSelect],
|
||||
new \Zend_Db_Expr('ea.attribute_code')
|
||||
)->joinInner(
|
||||
['ea' => $this->getTable('eav_attribute')],
|
||||
'ea.attribute_id = a.attribute_id',
|
||||
[]
|
||||
)->joinInner(
|
||||
['sr' => $this->getTable('salesrule')],
|
||||
'a.' . $this->getLinkField() . ' = sr.' . $this->getLinkField() . ' AND sr.is_active = 1',
|
||||
[]
|
||||
);
|
||||
|
||||
return $connection->fetchAll($select);
|
||||
}
|
||||
|
||||
|
||||
+24
-8
@@ -14,13 +14,15 @@ use Magento\Catalog\Api\Data\ProductInterface;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
|
||||
use Magento\Framework\App\Config\ScopeConfigInterface;
|
||||
use Magento\Store\Test\Fixture\Group as GroupFixture;
|
||||
use Magento\Store\Test\Fixture\Store as StoreFixture;
|
||||
use Magento\Store\Test\Fixture\Website as WebsiteFixture;
|
||||
use Magento\TestFramework\Fixture\DataFixture;
|
||||
use Magento\TestFramework\Fixture\DbIsolation;
|
||||
use Magento\TestFramework\ObjectManager;
|
||||
use Magento\TestFramework\TestCase\GraphQlAbstract;
|
||||
use Magento\Store\Model\StoreManagerInterface;
|
||||
use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
|
||||
use Magento\TestFramework\Fixture\DataFixtureStorage;
|
||||
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
|
||||
|
||||
/**
|
||||
* Test querying Bundle products
|
||||
@@ -278,7 +280,7 @@ QUERY;
|
||||
'quantity' => (int)$bundleProductLink->getQty(),
|
||||
'position' => $bundleProductLink->getPosition(),
|
||||
'is_default' => (bool)$bundleProductLink->getIsDefault(),
|
||||
'price_type' => self::KEY_PRICE_TYPE_FIXED,
|
||||
'price_type' => self::KEY_PRICE_TYPE_FIXED,
|
||||
'can_change_quantity' => $bundleProductLink->getCanChangeQuantity()
|
||||
]
|
||||
);
|
||||
@@ -443,9 +445,20 @@ QUERY;
|
||||
$this->graphQlQuery($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @magentoApiDataFixture Magento/Bundle/_files/product_1.php
|
||||
*/
|
||||
#[
|
||||
DbIsolation(false),
|
||||
DataFixture(WebsiteFixture::class, as: 'website2'),
|
||||
DataFixture(GroupFixture::class, ['website_id' => '$website2.id$'], 'group2'),
|
||||
DataFixture(StoreFixture::class, ['store_group_id' => '$group2.id$', 'code' => 'store2'], 'store2'),
|
||||
DataFixture(ProductFixture::class, ['sku' => 'p1', 'website_ids' => [1, '$website2.id$']], 'p1'),
|
||||
DataFixture(ProductFixture::class, ['sku' => 'p2', 'website_ids' => [1, '$website2.id$']], 'p2'),
|
||||
DataFixture(BundleOptionFixture::class, ['product_links' => ['$p1$']], 'opt1'),
|
||||
DataFixture(BundleOptionFixture::class, ['product_links' => ['$p2$']], 'opt2'),
|
||||
DataFixture(
|
||||
BundleProductFixture::class,
|
||||
['sku' => 'bundle-product', '_options' => ['$opt1$', '$opt2$'], 'website_ids' => [1, '$website2.id$']]
|
||||
),
|
||||
]
|
||||
public function testBundleProductWithDisabledProductOption()
|
||||
{
|
||||
/** @var StoreManagerInterface $storeManager */
|
||||
@@ -453,7 +466,7 @@ QUERY;
|
||||
$storeIdDefault = $storeManager->getDefaultStoreView()->getId();
|
||||
/** @var ProductRepositoryInterface $productRepository */
|
||||
$productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
|
||||
$simpleProduct = $productRepository->get('simple', false, $storeIdDefault, true);
|
||||
$simpleProduct = $productRepository->get('p1', true, $storeIdDefault, true);
|
||||
$simpleProduct->setStatus(ProductStatus::STATUS_DISABLED);
|
||||
$simpleProduct->setStoreIds([$storeIdDefault]);
|
||||
$productRepository->save($simpleProduct);
|
||||
@@ -508,8 +521,11 @@ QUERY;
|
||||
}
|
||||
QUERY;
|
||||
|
||||
$response = $this->graphQlQuery($query);
|
||||
$response = $this->graphQlQuery($query, [], '', ['Store' => 'default']);
|
||||
$this->assertEmpty($response['products']['items']);
|
||||
$response = $this->graphQlQuery($query, [], '', ['Store' => 'store2']);
|
||||
$this->assertNotEmpty($response['products']['items']);
|
||||
$this->assertEquals($productSku, $response['products']['items'][0]['sku']);
|
||||
}
|
||||
|
||||
#[
|
||||
|
||||
@@ -7,9 +7,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace Magento\GraphQl\PageCache;
|
||||
|
||||
use Magento\Catalog\Api\CategoryRepositoryInterface;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
use Magento\Catalog\Model\Product;
|
||||
use Magento\Catalog\Test\Fixture\Category as CategoryFixture;
|
||||
use Magento\Cms\Model\BlockRepository;
|
||||
use Magento\Cms\Test\Fixture\Block as BlockFixture;
|
||||
use Magento\GraphQlCache\Model\CacheId\CacheIdCalculator;
|
||||
use Magento\PageCache\Model\Config;
|
||||
use Magento\Store\Model\Store;
|
||||
use Magento\TestFramework\Fixture\Config as ConfigFixture;
|
||||
use Magento\TestFramework\Fixture\DataFixture;
|
||||
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\TestFramework\ObjectManager;
|
||||
|
||||
/**
|
||||
@@ -133,6 +143,140 @@ QUERY;
|
||||
);
|
||||
}
|
||||
|
||||
#[
|
||||
ConfigFixture(Config::XML_PAGECACHE_TYPE, Config::VARNISH),
|
||||
DataFixture(BlockFixture::class, ['content' => 'Original Block content'], as: 'block'),
|
||||
DataFixture(CategoryFixture::class, as: 'category1'),
|
||||
DataFixture(CategoryFixture::class, ['description' => 'Original Category description'], as: 'category2'),
|
||||
]
|
||||
public function testCacheInvalidationForCategoriesWithWidget(): void
|
||||
{
|
||||
$fixtures = DataFixtureStorageManager::getStorage();
|
||||
$block = $fixtures->get('block');
|
||||
$category1 = $fixtures->get('category1');
|
||||
$category2 = $fixtures->get('category2');
|
||||
$queryForCategory1 = $this->getCategoriesQuery((int) $category1->getId());
|
||||
$queryForCategory2 = $this->getCategoriesQuery((int) $category2->getId());
|
||||
|
||||
$this->updateCategoryDescription((int) $category1->getId(), $this->getBlockWidget((int) $block->getId()));
|
||||
|
||||
$responseCacheIdForCategory1 = $this->getQueryResponseCacheKey($queryForCategory1);
|
||||
// Verify we get MISS for category1 query in the first request
|
||||
$responseMissForCategory1 = $this->assertCacheMissAndReturnResponse(
|
||||
$queryForCategory1,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1]
|
||||
);
|
||||
|
||||
// Verify we get HIT for category 1 query in the second request
|
||||
$responseHitForCategory1 = $this->assertCacheHitAndReturnResponse(
|
||||
$queryForCategory1,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1]
|
||||
);
|
||||
|
||||
$this->assertEquals($responseMissForCategory1['body'], $responseHitForCategory1['body']);
|
||||
|
||||
// Verify category1 description contains block content
|
||||
$this->assertCategoryDescription('Original Block content', $responseHitForCategory1);
|
||||
|
||||
$responseCacheIdForCategory2 = $this->getQueryResponseCacheKey($queryForCategory2);
|
||||
// Verify we get MISS for category2 query in the first request
|
||||
$responseMissForCategory2 = $this->assertCacheMissAndReturnResponse(
|
||||
$queryForCategory2,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory2]
|
||||
);
|
||||
|
||||
// Verify we get HIT for category 2 query in the second request
|
||||
$responseHitForCategory2 = $this->assertCacheHitAndReturnResponse(
|
||||
$queryForCategory2,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory2]
|
||||
);
|
||||
|
||||
$this->assertEquals($responseMissForCategory2['body'], $responseHitForCategory2['body']);
|
||||
|
||||
// Verify category2 description is the same as created
|
||||
$this->assertCategoryDescription('Original Category description', $responseHitForCategory2);
|
||||
|
||||
// Update block content
|
||||
$newBlockContent = 'New block content!!!';
|
||||
$this->updateBlockContent((int) $block->getId(), $newBlockContent);
|
||||
|
||||
// Verify we get MISS for category1 query after block is updated
|
||||
$this->assertCacheMissAndReturnResponse(
|
||||
$queryForCategory1,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1]
|
||||
);
|
||||
|
||||
// Verify we get HIT for category1 query in the second request after block is updated
|
||||
$responseHitForCategory1 = $this->assertCacheHitAndReturnResponse(
|
||||
$queryForCategory1,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1]
|
||||
);
|
||||
|
||||
// Verify we get HIT for category2 query after block is updated
|
||||
$responseHitForCategory2 = $this->assertCacheHitAndReturnResponse(
|
||||
$queryForCategory2,
|
||||
[CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory2]
|
||||
);
|
||||
|
||||
// Verify the updated block data is returned in category1 query response
|
||||
$this->assertCategoryDescription($newBlockContent, $responseHitForCategory1);
|
||||
|
||||
// Verify category2 description is the same as created
|
||||
$this->assertCategoryDescription('Original Category description', $responseHitForCategory2);
|
||||
}
|
||||
|
||||
private function assertCategoryDescription(string $expected, array $response): void
|
||||
{
|
||||
$responseBody = $response['body'];
|
||||
$this->assertIsArray($responseBody);
|
||||
$this->assertArrayNotHasKey('errors', $responseBody);
|
||||
$this->assertStringContainsString($expected, $responseBody['categories']['items'][0]['description']);
|
||||
}
|
||||
|
||||
private function getQueryResponseCacheKey(string $query): string
|
||||
{
|
||||
$response = $this->graphQlQueryWithResponseHeaders($query);
|
||||
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $response['headers']);
|
||||
return $response['headers'][CacheIdCalculator::CACHE_ID_HEADER];
|
||||
}
|
||||
|
||||
private function updateBlockContent(int $id, string $content): void
|
||||
{
|
||||
$blockRepository = Bootstrap::getObjectManager()->get(BlockRepository::class);
|
||||
$block = $blockRepository->getById($id);
|
||||
$block->setContent($content);
|
||||
$blockRepository->save($block);
|
||||
}
|
||||
|
||||
private function updateCategoryDescription(int $id, string $description): void
|
||||
{
|
||||
$categoryRepository = Bootstrap::getObjectManager()->get(CategoryRepositoryInterface::class);
|
||||
$category = $categoryRepository->get($id, Store::DEFAULT_STORE_ID);
|
||||
$category->setCustomAttribute('description', $description);
|
||||
$categoryRepository->save($category);
|
||||
}
|
||||
|
||||
private function getBlockWidget(int $blockId): string
|
||||
{
|
||||
return "{{widget type=\"Magento\\Cms\\Block\\Widget\\Block\" " .
|
||||
"template=\"widget/static_block/default.phtml\" " .
|
||||
"block_id=\"$blockId\" " .
|
||||
"type_name=\"CMS Static Block\"}}";
|
||||
}
|
||||
|
||||
private function getCategoriesQuery(int $categoryId): string
|
||||
{
|
||||
return <<<QUERY
|
||||
{
|
||||
categories(filters: {ids: {in: ["$categoryId"]}}) {
|
||||
items{
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
QUERY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Product query
|
||||
*
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
*
|
||||
* Copyright 2023 Adobe
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of Adobe and its suppliers, if any. The intellectual
|
||||
* and technical concepts contained herein are proprietary to Adobe
|
||||
* and its suppliers and are protected by all applicable intellectual
|
||||
* property laws, including trade secret and copyright laws.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from Adobe.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Catalog;
|
||||
|
||||
use Magento\Framework\App\ObjectManager;
|
||||
use Magento\Framework\App\ResourceConnection;
|
||||
use Magento\Framework\DB\Adapter\AdapterInterface;
|
||||
use Monolog\Test\TestCase;
|
||||
|
||||
class DbSchemaTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param string $tableName
|
||||
* @param string $indexName
|
||||
* @param array $columns
|
||||
* @param string $indexType
|
||||
* @return void
|
||||
* @dataProvider indexDataProvider
|
||||
*/
|
||||
public function testIndex(
|
||||
string $tableName,
|
||||
string $indexName,
|
||||
array $columns,
|
||||
string $indexType = AdapterInterface::INDEX_TYPE_INDEX,
|
||||
): void {
|
||||
$connection = ObjectManager::getInstance()->get(ResourceConnection::class)->getConnection();
|
||||
$indexes = $connection->getIndexList($tableName);
|
||||
$this->assertArrayHasKey($indexName, $indexes);
|
||||
$this->assertSame($columns, $indexes[$indexName]['COLUMNS_LIST']);
|
||||
$this->assertSame($indexType, $indexes[$indexName]['INDEX_TYPE']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function indexDataProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'catalog_product_index_price_tmp',
|
||||
'CAT_PRD_IDX_PRICE_TMP_ENTT_ID_CSTR_GROUP_ID_WS_ID',
|
||||
['entity_id', 'customer_group_id', 'website_id']
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -108,13 +108,7 @@ class ReorderTest extends AbstractController
|
||||
$order = $this->orderFactory->create()->loadByIncrementId('55555555');
|
||||
$this->customerSession->setCustomerId($order->getCustomerId());
|
||||
$this->dispatchReorderRequest((int)$order->getId());
|
||||
$origMessage = (string)__('The requested qty is not available');
|
||||
$message = $this->escaper->escapeHtml(
|
||||
__('Could not add the product with SKU "%1" to the shopping cart: %2', 'simple-1', $origMessage)
|
||||
);
|
||||
$constraint = $this->logicalOr($this->containsEqual($origMessage), $this->containsEqual($message));
|
||||
$this->assertThat($this->getMessages(MessageInterface::TYPE_ERROR), $constraint);
|
||||
$this->quote = $this->checkoutSession->getQuote();
|
||||
$this->assertRedirect($this->stringContains('checkout/cart'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -117,7 +117,7 @@ class ReorderWithDifferentStoreTest extends AbstractController
|
||||
$this->dispatch('sales/order/reorder/');
|
||||
$this->assertRedirect($this->stringContains('checkout/cart'));
|
||||
$this->quote = $this->checkoutSession->getQuote();
|
||||
$quoteItemsCollection = $this->quote->getAllItems();
|
||||
$this->assertCount(0, $quoteItemsCollection);
|
||||
|
||||
$this->assertCount(1, $this->quote->getErrors());
|
||||
}
|
||||
}
|
||||
@@ -72,8 +72,6 @@ class RuleTest extends \PHPUnit\Framework\TestCase
|
||||
public function testGetActiveAttributes()
|
||||
{
|
||||
$rule = $this->fixtures->get('rule1');
|
||||
$items = $this->resource->getActiveAttributes();
|
||||
$this->assertEquals([], $items);
|
||||
$rule->setIsActive(1);
|
||||
$rule->save();
|
||||
$items = $this->resource->getActiveAttributes();
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
*
|
||||
* Copyright 2023 Adobe
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of Adobe and its suppliers, if any. The intellectual
|
||||
* and technical concepts contained herein are proprietary to Adobe
|
||||
* and its suppliers and are protected by all applicable intellectual
|
||||
* property laws, including trade secret and copyright laws.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from Adobe.
|
||||
* ************************************************************************
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Framework\Mview\Test\Unit\View;
|
||||
|
||||
use Magento\Framework\App\ResourceConnection;
|
||||
use Magento\Framework\DB\Adapter\AdapterInterface;
|
||||
use Magento\Framework\DB\Ddl\Table;
|
||||
use Magento\Framework\DB\Query\Generator;
|
||||
use Magento\Framework\DB\Select;
|
||||
use Magento\Framework\Mview\View\ChangelogInterface;
|
||||
use Magento\Framework\Mview\View\ChangelogBatchWalker;
|
||||
use Magento\Framework\Mview\View\ChangelogBatchWalker\IdsContext;
|
||||
use Magento\Framework\Mview\View\ChangelogBatchWalker\IdsSelectBuilderInterface;
|
||||
use Magento\Framework\Mview\View\ChangelogBatchWalker\IdsTableBuilderInterface;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test Coverage for Changelog View.
|
||||
*
|
||||
* @see \Magento\Framework\Mview\View\Changelog
|
||||
*/
|
||||
class ChangelogBatchWalkerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ChangelogBatchWalker
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @var ResourceConnection|MockObject
|
||||
*/
|
||||
private $resourceConnection;
|
||||
|
||||
/**
|
||||
* @var Generator|MockObject
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* @var IdsTableBuilderInterface|MockObject
|
||||
*/
|
||||
private $idsTableBuilder;
|
||||
|
||||
/**
|
||||
* @var IdsSelectBuilderInterface|MockObject
|
||||
*/
|
||||
private $idsSelectBuilder;
|
||||
|
||||
/**
|
||||
* @var IdsContext|MockObject
|
||||
*/
|
||||
private $idsContext;
|
||||
|
||||
/**
|
||||
* @var ChangelogInterface
|
||||
*/
|
||||
private $changeLog;
|
||||
|
||||
/**
|
||||
* @var AdapterInterface|MockObject
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var Table|MockObject
|
||||
*/
|
||||
private $table;
|
||||
|
||||
/**
|
||||
* @var Select|MockObject
|
||||
*/
|
||||
private $select;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->resourceConnection = $this->getMockBuilder(ResourceConnection::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->generator = $this->getMockBuilder(Generator::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->idsTableBuilder = $this->getMockBuilder(IdsTableBuilderInterface::class)
|
||||
->getMockForAbstractClass();
|
||||
$this->idsSelectBuilder = $this->getMockBuilder(IdsSelectBuilderInterface::class)
|
||||
->getMockForAbstractClass();
|
||||
$this->idsContext = $this->getMockBuilder(IdsContext::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->idsContext->expects($this->any())
|
||||
->method('getSelectBuilder')
|
||||
->willReturn($this->idsSelectBuilder);
|
||||
$this->idsContext->expects($this->any())
|
||||
->method('getTableBuilder')
|
||||
->willReturn($this->idsTableBuilder);
|
||||
|
||||
$this->changeLog = $this->getMockBuilder(ChangelogInterface::class)
|
||||
->getMockForAbstractClass();
|
||||
$this->connection = $this->getMockBuilder(AdapterInterface::class)
|
||||
->getMockForAbstractClass();
|
||||
$this->table = $this->getMockBuilder(Table::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->resourceConnection->expects($this->any())
|
||||
->method('getConnection')
|
||||
->willReturn($this->connection);
|
||||
|
||||
$this->select = $this->getMockBuilder(Select::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->select->expects($this->any())
|
||||
->method('from')
|
||||
->willReturnSelf();
|
||||
$this->select->expects($this->any())
|
||||
->method('where')
|
||||
->willReturnSelf();
|
||||
$this->select->expects($this->any())
|
||||
->method('distinct')
|
||||
->willReturnSelf();
|
||||
$this->connection->expects($this->any())
|
||||
->method('select')
|
||||
->willReturn($this->select);
|
||||
|
||||
$this->model = new ChangelogBatchWalker(
|
||||
$this->resourceConnection,
|
||||
$this->generator,
|
||||
$this->idsContext
|
||||
);
|
||||
}
|
||||
|
||||
public function testNoTemporaryTablesUsed()
|
||||
{
|
||||
$this->connection->expects($this->once())
|
||||
->method('isTableExists')
|
||||
->willReturn(true);
|
||||
$this->table->expects($this->any())
|
||||
->method('getColumns')
|
||||
->willReturn([]);
|
||||
$this->idsTableBuilder->expects($this->any())
|
||||
->method('build')
|
||||
->willReturn($this->table);
|
||||
$this->idsSelectBuilder->expects($this->any())
|
||||
->method('build')
|
||||
->willReturn($this->select);
|
||||
$this->generator->expects($this->any())
|
||||
->method('generate')
|
||||
->willReturn([]);
|
||||
|
||||
foreach ($this->model->walk($this->changeLog, 1, 2, 1) as $iteration) {
|
||||
$this->assertEmpty($iteration);
|
||||
$this->connection->expects($this->once())
|
||||
->method('createTable');
|
||||
$this->connection->expects($this->never())
|
||||
->method('createTemporaryTableTable');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,30 +24,34 @@ use Magento\Framework\Phrase;
|
||||
class ChangelogBatchWalker implements ChangelogBatchWalkerInterface
|
||||
{
|
||||
/**
|
||||
* @var \Magento\Framework\App\ResourceConnection
|
||||
* @var ResourceConnection
|
||||
*/
|
||||
private ResourceConnection $resourceConnection;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\DB\Query\Generator
|
||||
* @var Generator
|
||||
*/
|
||||
private Generator $generator;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsTableBuilderInterface
|
||||
* @var IdsTableBuilderInterface
|
||||
*/
|
||||
private IdsTableBuilderInterface $idsTableBuilder;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsSelectBuilderInterface
|
||||
* @var IdsSelectBuilderInterface
|
||||
*/
|
||||
private IdsSelectBuilderInterface $idsSelectBuilder;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsFetcherInterface
|
||||
* @var IdsFetcherInterface
|
||||
*/
|
||||
private IdsFetcherInterface $idsFetcher;
|
||||
|
||||
/**
|
||||
* @param ResourceConnection $resourceConnection
|
||||
* @param \Magento\Framework\DB\Query\Generator $generator
|
||||
* @param \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsContext $idsContext
|
||||
* @param Generator $generator
|
||||
* @param IdsContext $idsContext
|
||||
*/
|
||||
public function __construct(
|
||||
ResourceConnection $resourceConnection,
|
||||
@@ -80,7 +84,7 @@ class ChangelogBatchWalker implements ChangelogBatchWalkerInterface
|
||||
$idsTable = $this->idsTableBuilder->build($changelog);
|
||||
|
||||
try {
|
||||
$connection->createTemporaryTable($idsTable);
|
||||
$connection->createTable($idsTable);
|
||||
|
||||
$columns = $this->getIdsColumns($idsTable);
|
||||
|
||||
@@ -122,7 +126,7 @@ class ChangelogBatchWalker implements ChangelogBatchWalkerInterface
|
||||
yield $ids;
|
||||
}
|
||||
} finally {
|
||||
$connection->dropTemporaryTable($idsTable->getName());
|
||||
$connection->dropTable($idsTable->getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ class IdsTableBuilder implements IdsTableBuilderInterface
|
||||
['unsigned' => true, 'nullable' => false],
|
||||
'Entity ID'
|
||||
);
|
||||
$table->setOption('type', 'memory');
|
||||
$table->addIndex(
|
||||
self::INDEX_NAME_UNIQUE,
|
||||
[
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
*
|
||||
* Copyright 2023 Adobe
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of Adobe and its suppliers, if any. The intellectual
|
||||
* and technical concepts contained herein are proprietary to Adobe
|
||||
* and its suppliers and are protected by all applicable intellectual
|
||||
* property laws, including trade secret and copyright laws.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from Adobe.
|
||||
* ************************************************************************
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\Framework\Test\Unit\Mview\View\ChangelogBatchWalker;
|
||||
|
||||
use Magento\Framework\App\ResourceConnection;
|
||||
use Magento\Framework\DB\Adapter\AdapterInterface;
|
||||
use Magento\Framework\DB\Ddl\Table;
|
||||
use Magento\Framework\Mview\View\ChangelogBatchWalker\IdsTableBuilder;
|
||||
use Magento\Framework\Mview\View\ChangelogInterface;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class IdsTableBuilderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ResourceConnection|MockObject
|
||||
*/
|
||||
private $resourceConnection;
|
||||
|
||||
/**
|
||||
* @var ChangelogInterface|MockObject
|
||||
*/
|
||||
private $changeLog;
|
||||
|
||||
/**
|
||||
* @var AdapterInterface|MockObject
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var Table|MockObject
|
||||
*/
|
||||
private $table;
|
||||
|
||||
/**
|
||||
* @var IdsTableBuilder
|
||||
*/
|
||||
private $model;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->changeLog = $this->getMockBuilder(ChangelogInterface::class)
|
||||
->getMockForAbstractClass();
|
||||
$this->connection = $this->getMockBuilder(AdapterInterface::class)
|
||||
->getMockForAbstractClass();
|
||||
$this->table = $this->getMockBuilder(Table::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->resourceConnection = $this->getMockBuilder(ResourceConnection::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->resourceConnection->expects($this->any())
|
||||
->method('getConnection')
|
||||
->willReturn($this->connection);
|
||||
$this->connection->expects($this->any())
|
||||
->method('newTable')
|
||||
->willReturn($this->table);
|
||||
|
||||
$this->model = new IdsTableBuilder($this->resourceConnection);
|
||||
}
|
||||
|
||||
public function testBuildDoNotCreateMemoryTable() : void
|
||||
{
|
||||
$this->table->expects($this->never())
|
||||
->method('setOption')
|
||||
->with('type', 'memory');
|
||||
|
||||
$this->model->build($this->changeLog);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user