diff --git a/app/code/Magento/Backend/Helper/Dashboard/Order.php b/app/code/Magento/Backend/Helper/Dashboard/Order.php index e3c5dfbb4b9450e718600e191c3536b48ad7be9e..d8ece48d182e409baaed64ffe98bca31ce71360c 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/Order.php +++ b/app/code/Magento/Backend/Helper/Dashboard/Order.php @@ -5,6 +5,8 @@ */ namespace Magento\Backend\Helper\Dashboard; +use Magento\Framework\App\ObjectManager; + /** * Adminhtml dashboard helper for orders */ @@ -23,18 +25,24 @@ class Order extends \Magento\Backend\Helper\Dashboard\AbstractDashboard /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection - * @param \Magento\Store\Model\StoreManagerInterface $storeManager */ public function __construct( \Magento\Framework\App\Helper\Context $context, - \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection, - \Magento\Store\Model\StoreManagerInterface $storeManager + \Magento\Reports\Model\ResourceModel\Order\Collection $orderCollection ) { $this->_orderCollection = $orderCollection; - $this->_storeManager = $storeManager; - parent::__construct( - $context - ); + parent::__construct($context); + } + + /** + * @return \Magento\SalesRule\Model\RuleFactory + * @deprecated + */ + public function getStoreManager() + { + if ($this->_storeManager instanceof \Magento\Store\Model\StoreManagerInterface) { + $this->_storeManager = ObjectManager::getInstance()->get('\Magento\Store\Model\StoreManagerInterface'); + } } /** diff --git a/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml b/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml index 534b7e88192010cb170946a7c31009fbd16d92cc..378e72e42d8ab582d5b97a9af035c9198b46a863 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml @@ -27,7 +27,19 @@ </form> <script data-template="search-suggest" type="text/x-magento-template"> <ul class="search-global-menu"> - <% if (data.items.length) { %> + <li class="item"> + <a href="<?php /* @escapeNotVerified */ echo $block->getURL('catalog/product/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Products</a> + </li> + <li class="item"> + <a href="<?php /* @escapeNotVerified */ echo $block->getURL('sales/order/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Orders</a> + </li> + <li class="item"> + <a href="<?php /* @escapeNotVerified */ echo $block->getURL('customer/index/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Customers</a> + </li> + <li class="item"> + <a href="<?php /* @escapeNotVerified */ echo $block->getURL('cms/page/index/'); ?>?search=<%- data.term%>" class="title">"<%- data.term%>" in Pages</a> + </li> + <% if (data.items.length) { %> <% _.each(data.items, function(value){ %> <li class="item" <%- data.optionData(value) %> diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index 9ba1ad0b6436f3b4a921c6e212f6acd2890ef188..daa08141549d77de4300d00e320e4b3c13e6057b 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -100,7 +100,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category $data['general'] = $this->getRequest()->getPostValue(); $isNewCategory = !isset($data['general']['entity_id']); - $data = $this->stringToBoolConverting($this->stringToBoolInputs, $data); + $data = $this->stringToBoolConverting($data); $data = $this->imagePreprocessing($data); $storeId = isset($data['general']['store_id']) ? $data['general']['store_id'] : null; $parentId = isset($data['general']['parent']) ? $data['general']['parent'] : null; @@ -121,11 +121,9 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category foreach ($generalPost['use_config'] as $attributeCode => $attributeValue) { if ($attributeValue) { $useConfig[] = $attributeCode; + $category->setData($attributeCode, null); } } - foreach ($useConfig as $attributeCode) { - $category->setData($attributeCode, null); - } } $category->setAttributeSetId($category->getDefaultAttributeSetId()); @@ -148,7 +146,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category if (isset($generalPost['use_default']) && !empty($generalPost['use_default'])) { foreach ($generalPost['use_default'] as $attributeCode => $attributeValue) { if ($attributeValue) { - $category->setData($attributeCode, false); + $category->setData($attributeCode, null); } } } @@ -234,8 +232,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category { if (!isset($_FILES) || (isset($_FILES['image']) && $_FILES['image']['name'] === '' )) { unset($data['general']['image']); - if ( - isset($data['general']['savedImage']['delete']) && + if (isset($data['general']['savedImage']['delete']) && $data['general']['savedImage']['delete'] ) { $data['general']['image']['delete'] = $data['general']['savedImage']['delete']; @@ -247,17 +244,20 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category /** * Converting inputs from string to boolean * - * @param array $stringToBoolInputs * @param array $data + * @param array $stringToBoolInputs * * @return array */ - public function stringToBoolConverting($stringToBoolInputs, $data) + public function stringToBoolConverting($data, $stringToBoolInputs = null) { + if (null === $stringToBoolInputs) { + $stringToBoolInputs = $this->stringToBoolInputs; + } foreach ($stringToBoolInputs as $key => $value) { if (is_array($value)) { if (isset($data[$key])) { - $data[$key] = $this->stringToBoolConverting($value, $data[$key]); + $data[$key] = $this->stringToBoolConverting($data[$key], $value); } } else { if (isset($data[$value])) { diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 5e42c3aabccc036df1e61ba5ab63b61a8c04bf17..387c83f312b59266413902f37d1e6e156634b4c1 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -160,7 +160,7 @@ class Helper foreach ($useDefaults as $attributeCode => $useDefaultState) { if ($useDefaultState) { - $product->setData($attributeCode, false); + $product->setData($attributeCode, null); } } diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index a47dd846a6d92805c6faf211d59e1921b49a4b02..e98e449d12bfada7624d94b5a2f8b2710f52ce2d 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -571,9 +571,9 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements public function getStoreId() { if ($this->hasData('store_id')) { - return $this->_getData('store_id'); + return (int)$this->_getData('store_id'); } - return $this->_storeManager->getStore()->getId(); + return (int)$this->_storeManager->getStore()->getId(); } /** @@ -1261,6 +1261,7 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements } //@codeCoverageIgnoreStart + /** * Set parent category ID * diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php index 924f96ded1adf4a480746a8661a3b72fc5f80a03..668803b3482bd10313f584be8a09930c4a585b60 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php @@ -96,10 +96,10 @@ class Sortby extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend if (!is_array($data)) { $data = []; } - $object->setData($attributeCode, join(',', $data)); + $object->setData($attributeCode, implode(',', $data) ?: null); } if (!$object->hasData($attributeCode)) { - $object->setData($attributeCode, false); + $object->setData($attributeCode, null); } return $this; } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index 5345072c4a5a679092c63a0d94ca958b9dc5b30b..6cebe3e1177a5be24294359a1008e1a8b4320f41 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -11,9 +11,12 @@ use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Form\Field; use Magento\Ui\DataProvider\EavValidationRules; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Framework\Exception\NoSuchEntityException; /** * Class DataProvider @@ -22,6 +25,11 @@ use Magento\Ui\DataProvider\EavValidationRules; */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { + /** + * @var string + */ + protected $requestScopeFieldName = 'store'; + /** * @var array */ @@ -86,8 +94,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider /** * @var \Magento\Framework\App\RequestInterface */ - private $request; - + protected $request; /** * @var Config @@ -100,7 +107,12 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider private $storeManager; /** - * Constructor + * @var CategoryFactory + */ + private $categoryFactory; + + /** + * DataProvider constructor * * @param string $name * @param string $primaryFieldName @@ -111,9 +123,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param \Magento\Framework\Registry $registry * @param Config $eavConfig * @param \Magento\Framework\App\RequestInterface $request + * @param CategoryFactory $categoryFactory * @param array $meta * @param array $data - * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -126,6 +138,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider \Magento\Framework\Registry $registry, Config $eavConfig, \Magento\Framework\App\RequestInterface $request, + CategoryFactory $categoryFactory, array $meta = [], array $data = [] ) { @@ -136,6 +149,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider $this->registry = $registry; $this->storeManager = $storeManager; $this->request = $request; + $this->categoryFactory = $categoryFactory; parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->meta = $this->prepareMeta($this->meta); } @@ -282,7 +296,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider protected function addUseDefaultSettings($category, $categoryData) { if ($category->getExistsStoreValueFlag('url_key') || - $category->getStoreId() === \Magento\Store\Model\Store::DEFAULT_STORE_ID + $category->getStoreId() === Store::DEFAULT_STORE_ID ) { $categoryData['use_default']['url_key'] = false; } else { @@ -296,10 +310,25 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * Get current category * * @return Category + * @throws NoSuchEntityException */ public function getCurrentCategory() { - return $this->registry->registry('category'); + $category = $this->registry->registry('category'); + if ($category) { + return $category; + } + $requestId = $this->request->getParam($this->requestFieldName); + $requestScope = $this->request->getParam($this->requestScopeFieldName, Store::DEFAULT_STORE_ID); + if ($requestId) { + $category = $this->categoryFactory->create(); + $category->setStoreId($requestScope); + $category->load($requestId); + if (!$category->getId()) { + throw NoSuchEntityException::singleField('id', $requestId); + } + } + return $category; } /** @@ -349,7 +378,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider public function getDefaultMetaData($result) { $result['parent']['default'] = (int)$this->request->getParam('parent'); - $result['is_anchor']['default'] = false; $result['use_config.available_sort_by']['default'] = true; $result['use_config.default_sort_by']['default'] = true; $result['use_config.filter_price_range']['default'] = true; @@ -418,6 +446,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider [ 'custom_use_parent_settings', 'custom_apply_to_products', + 'custom_design', 'page_layout', 'custom_layout_update', ], @@ -425,7 +454,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider [ 'custom_design_from', 'custom_design_to', - 'custom_design', ], 'category_view_optimization' => [ diff --git a/app/code/Magento/Catalog/Model/CategoryRepository.php b/app/code/Magento/Catalog/Model/CategoryRepository.php index d95c444d9f55cee3bad57614f080df0c9dd57362..ac693599985bce90b251eec0fcf74292976a62de 100644 --- a/app/code/Magento/Catalog/Model/CategoryRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryRepository.php @@ -12,6 +12,9 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; use Magento\Catalog\Api\Data\CategoryInterface; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInterface { /** @@ -50,18 +53,21 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter * @param \Magento\Catalog\Model\CategoryFactory $categoryFactory * @param \Magento\Catalog\Model\ResourceModel\Category $categoryResource * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool */ public function __construct( \Magento\Catalog\Model\CategoryFactory $categoryFactory, \Magento\Catalog\Model\ResourceModel\Category $categoryResource, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Model\Entity\MetadataPool $metadataPool + \Magento\Framework\Model\Entity\MetadataPool $metadataPool, + \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter ) { $this->categoryFactory = $categoryFactory; $this->categoryResource = $categoryResource; $this->storeManager = $storeManager; $this->metadataPool = $metadataPool; + $this->extensibleDataObjectConverter = $extensibleDataObjectConverter; } /** @@ -69,19 +75,19 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter */ public function save(\Magento\Catalog\Api\Data\CategoryInterface $category) { - $existingData = $category->toFlatArray(); - - /** 'available_sort_by' should be set separately because fields of array type are destroyed by toFlatArray() */ - $existingData['available_sort_by'] = $category->getAvailableSortBy(); + $storeId = (int)$this->storeManager->getStore()->getId(); + $existingData = $this->extensibleDataObjectConverter + ->toNestedArray($category, [], 'Magento\Catalog\Api\Data\CategoryInterface'); + $existingData = array_diff_key($existingData, array_flip(['path', 'level', 'parent_id'])); + $existingData['store_id'] = $storeId; if ($category->getId()) { $metadata = $this->metadataPool->getMetadata( CategoryInterface::class ); - $existingCategory = $this->get($category->getId()); - - $existingData[$metadata->getLinkField()] = $existingCategory->getData( + $category = $this->get($category->getId(), $storeId); + $existingData[$metadata->getLinkField()] = $category->getData( $metadata->getLinkField() ); @@ -89,22 +95,13 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter $existingData['image_additional_data'] = $existingData['image']; unset($existingData['image']); } - $existingData['id'] = $existingCategory->getId(); - $existingData['parent_id'] = $existingCategory->getParentId(); - $existingData['path'] = $existingCategory->getPath(); - $existingData['is_active'] = $existingCategory->getIsActive(); - $existingData['include_in_menu'] = - isset($existingData['include_in_menu']) ? (bool)$existingData['include_in_menu'] : false; - $category->setData($existingData); } else { $parentId = $category->getParentId() ?: $this->storeManager->getStore()->getRootCategoryId(); - $parentCategory = $this->get($parentId); - $existingData['include_in_menu'] = - isset($existingData['include_in_menu']) ? (bool)$existingData['include_in_menu'] : false; - /** @var $category Category */ - $category->setData($existingData); - $category->setPath($parentCategory->getPath()); + $parentCategory = $this->get($parentId, $storeId); + $existingData['path'] = $parentCategory->getPath(); + $existingData['parent_id'] = $parentId; } + $category->addData($existingData); try { $this->validateCategory($category); $this->categoryResource->save($category); @@ -118,7 +115,7 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter ); } unset($this->instances[$category->getId()]); - return $category; + return $this->get($category->getId(), $storeId); } /** diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php index d59a209b84607d398ed1f3394e387fdab93ae41c..ebb3ce35c03775f551a2002eb0b3f40b783e890d 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php @@ -5,11 +5,13 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Flat; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Model\Entity\MetadataPool; /** * Abstract action reindex class - * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractAction { @@ -72,6 +74,12 @@ abstract class AbstractAction protected $_flatTableBuilder; /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @param MetadataPool $metadataPool * @param \Magento\Framework\App\ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper @@ -80,6 +88,7 @@ abstract class AbstractAction * @param FlatTableBuilder $flatTableBuilder */ public function __construct( + MetadataPool $metadataPool, \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper, @@ -93,6 +102,7 @@ abstract class AbstractAction $this->_connection = $resource->getConnection(); $this->_tableBuilder = $tableBuilder; $this->_flatTableBuilder = $flatTableBuilder; + $this->metadataPool = $metadataPool; } /** @@ -194,6 +204,8 @@ abstract class AbstractAction return $this; } + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + foreach ($this->_getProductTypeInstances() as $typeInstance) { /** @var $typeInstance \Magento\Catalog\Model\Product\Type\AbstractType */ if (!$typeInstance->isComposite(null)) { @@ -210,7 +222,11 @@ abstract class AbstractAction /** @var $select \Magento\Framework\DB\Select */ $select = $this->_connection->select()->from( ['t' => $this->_productIndexerHelper->getTable($relation->getTable())], - [$relation->getParentFieldName(), $relation->getChildFieldName(), new \Zend_Db_Expr('1')] + [$relation->getChildFieldName(), new \Zend_Db_Expr('1')] + )->join( + ['entity_table' => $this->_connection->getTableName('catalog_product_entity')], + 'entity_table.' . $metadata->getLinkField() . 't.' . $relation->getParentFieldName(), + [$relation->getParentFieldName() => 'entity_table.entity_id'] )->join( ['e' => $this->_productIndexerHelper->getFlatTableName($storeId)], "e.entity_id = t.{$relation->getChildFieldName()}", @@ -222,7 +238,7 @@ abstract class AbstractAction if ($productIds !== null) { $cond = [ $this->_connection->quoteInto("{$relation->getChildFieldName()} IN(?)", $productIds), - $this->_connection->quoteInto("{$relation->getParentFieldName()} IN(?)", $productIds), + $this->_connection->quoteInto("entity_table.entity_id IN(?)", $productIds), ]; $select->where(implode(' OR ', $cond)); @@ -247,6 +263,8 @@ abstract class AbstractAction return $this; } + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + foreach ($this->_getProductTypeInstances() as $typeInstance) { /** @var $typeInstance \Magento\Catalog\Model\Product\Type\AbstractType */ if (!$typeInstance->isComposite(null)) { @@ -258,11 +276,15 @@ abstract class AbstractAction $select = $this->_connection->select()->distinct( true )->from( - $this->_productIndexerHelper->getTable($relation->getTable()), - "{$relation->getParentFieldName()}" + ['t' => $this->_productIndexerHelper->getTable($relation->getTable())], + [] + )->join( + ['entity_table' => $this->_connection->getTableName('catalog_product_entity')], + 'entity_table.' . $metadata->getLinkField() . 't.' . $relation->getParentFieldName(), + [$relation->getParentFieldName() => 'entity_table.entity_id'] ); $joinLeftCond = [ - "e.entity_id = t.{$relation->getParentFieldName()}", + "e.entity_id = entity_table.entity_id", "e.child_id = t.{$relation->getChildFieldName()}", ]; if ($relation->getWhere() !== null) { diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index e0e784f8799de90c9eb7e1e18a50f7e9161f7841..6f4091b1cc7568b4918cba0a60c2f3e66ef3af4a 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Model\Indexer\Product\Flat\Action; use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder; use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder; +use Magento\Framework\Model\Entity\MetadataPool; /** * Class Row reindex action @@ -24,6 +25,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction protected $flatItemEraser; /** + * @param MetadataPool $metadataPool * @param \Magento\Framework\App\ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper @@ -34,6 +36,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction * @param Eraser $flatItemEraser */ public function __construct( + MetadataPool $metadataPool, \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper, @@ -44,6 +47,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction Eraser $flatItemEraser ) { parent::__construct( + $metadataPool, $resource, $storeManager, $productHelper, diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php index 36a5dbf013f571bf239a3fa50fe36e2fa4e34c3e..3153e2bfd54e62f8704c82d273e8de7f477ae7bd 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Model\Indexer\Product\Flat\Action; use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder; use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder; +use Magento\Framework\Model\Entity\MetadataPool; /** * Class Rows reindex action for mass actions @@ -20,6 +21,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction protected $flatItemEraser; /** + * @param MetadataPool $metadataPool * @param \Magento\Framework\App\ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper @@ -29,6 +31,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction * @param Eraser $flatItemEraser */ public function __construct( + MetadataPool $metadataPool, \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper, @@ -38,6 +41,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction Eraser $flatItemEraser ) { parent::__construct( + $metadataPool, $resource, $storeManager, $productHelper, diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php index b42fc677f718e3fa112be20b85447e6b88795e32..3a91b567861d72e1549f6ea285a5c0354b03d765 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php @@ -99,7 +99,7 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity protected function _isCallableAttributeInstance($instance, $method, $args) { if ($instance instanceof \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend - && ($method == 'beforeSave' || $method = 'afterSave') + && ($method == 'beforeSave' || $method == 'afterSave') ) { $attributeCode = $instance->getAttribute()->getAttributeCode(); if (isset($args[0]) && $args[0] instanceof \Magento\Framework\DataObject && $args[0]->getData($attributeCode) === false) { @@ -467,19 +467,6 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity return $origObject; } - /** - * Check is attribute value empty - * - * @param AbstractAttribute $attribute - * @param mixed $value - * @return bool - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - protected function _isAttributeValueEmpty(AbstractAttribute $attribute, $value) - { - return $value === false; - } - /** * Return if attribute exists in original data array. * Checks also attribute's store scope: diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 57492bf78b705c1a7539bdba898eff168a6ad8a1..d3d72ae38273dfe55e2d3f3254713ef2965b79bb 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -73,6 +73,11 @@ class Category extends AbstractResource */ protected $entityManager; + /** + * @var Category\AggregateCount + */ + protected $aggregateCount; + /** * Category constructor. * @param \Magento\Eav\Model\Entity\Context $context @@ -82,6 +87,7 @@ class Category extends AbstractResource * @param Category\TreeFactory $categoryTreeFactory * @param Category\CollectionFactory $categoryCollectionFactory * @param EntityManager $entityManager + * @param Category\AggregateCount $aggregateCount * @param array $data */ public function __construct( @@ -92,6 +98,7 @@ class Category extends AbstractResource \Magento\Catalog\Model\ResourceModel\Category\TreeFactory $categoryTreeFactory, \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory, EntityManager $entityManager, + Category\AggregateCount $aggregateCount, $data = [] ) { parent::__construct( @@ -104,8 +111,8 @@ class Category extends AbstractResource $this->_categoryCollectionFactory = $categoryCollectionFactory; $this->_eventManager = $eventManager; $this->entityManager = $entityManager; - $this->connectionName = 'catalog'; + $this->aggregateCount = $aggregateCount; } /** @@ -184,20 +191,8 @@ class Category extends AbstractResource protected function _beforeDelete(\Magento\Framework\DataObject $object) { parent::_beforeDelete($object); - - /** - * Update children count for all parent categories - */ - $parentIds = $object->getParentIds(); - if ($parentIds) { - $childDecrease = $object->getChildrenCount() + 1; - // +1 is itself - $data = ['children_count' => new \Zend_Db_Expr('children_count - ' . $childDecrease)]; - $where = ['entity_id IN(?)' => $parentIds]; - $this->getConnection()->update($this->getEntityTable(), $data, $where); - } + $this->aggregateCount->processDelete($object); $this->deleteChildren($object); - return $this; } /** @@ -1009,7 +1004,6 @@ class Category extends AbstractResource */ $this->entityManager->load(CategoryInterface::class, $object, $entityId); - if (!$this->entityManager->has(\Magento\Catalog\Api\Data\CategoryInterface::class, $entityId)) { $object->isObjectNew(true); } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php new file mode 100644 index 0000000000000000000000000000000000000000..4599fc4a8428ac934c5989699b091f7094fbacc0 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\ResourceModel\Category; + +use Magento\Catalog\Model\Category; + +/** + * Class AggregateCount + */ +class AggregateCount +{ + /** + * @param Category $category + * @return void + */ + public function processDelete(Category $category) + { + /** @var \Magento\Catalog\Model\ResourceModel\Category $resourceModel */ + $resourceModel = $category->getResource(); + /** + * Update children count for all parent categories + */ + $parentIds = $category->getParentIds(); + if ($parentIds) { + $childDecrease = $category->getChildrenCount() + 1; + // +1 is itself + $data = ['children_count' => new \Zend_Db_Expr('children_count - ' . $childDecrease)]; + $where = ['entity_id IN(?)' => $parentIds]; + $resourceModel->getConnection()->update($resourceModel->getEntityTable(), $data, $where); + } + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index 5a72330349dc5566726266b8aa7ec4420d70230b..ccc04da10abae97853971973d397b0b1d7642211 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -66,6 +66,11 @@ class Product extends AbstractResource */ protected $defaultAttributes; + /** + * @var array + */ + protected $availableCategoryIdsCache = []; + /** * @param \Magento\Eav\Model\Entity\Context $context * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -465,18 +470,22 @@ class Product extends AbstractResource { // is_parent=1 ensures that we'll get only category IDs those are direct parents of the product, instead of // fetching all parent IDs, including those are higher on the tree - $select = $this->getConnection()->select()->distinct()->from( - $this->getTable('catalog_category_product_index'), - ['category_id'] - )->where( - 'product_id = ? AND is_parent = 1', - (int)$object->getEntityId() - )->where( - 'visibility != ?', - \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE - ); - - return $this->getConnection()->fetchCol($select); + $entityId = (int)$object->getEntityId(); + if (!isset($this->availableCategoryIdsCache[$entityId])) { + $this->availableCategoryIdsCache[$entityId] = $this->getConnection()->fetchCol( + $this->getConnection()->select()->distinct()->from( + $this->getTable('catalog_category_product_index'), + ['category_id'] + )->where( + 'product_id = ? AND is_parent = 1', + $entityId + )->where( + 'visibility != ?', + \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE + ) + ); + } + return $this->availableCategoryIdsCache[$entityId]; } /** diff --git a/app/code/Magento/Catalog/Plugin/Model/Attribute/Backend/AttributeValidation.php b/app/code/Magento/Catalog/Plugin/Model/Attribute/Backend/AttributeValidation.php new file mode 100644 index 0000000000000000000000000000000000000000..0b1ca4228abd4ed2fac5f2fc2636b6faf6691c81 --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/Model/Attribute/Backend/AttributeValidation.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Plugin\Model\Attribute\Backend; + +class AttributeValidation +{ + /** + * @param \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend $subject + * @param \Closure $proceed + * @param \Magento\Framework\DataObject $attribute + * @return bool + */ + public function aroundValidate( + \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend $subject, + \Closure $proceed, + \Magento\Framework\DataObject $attribute + ) { + $useDefault = $attribute->getUseDefault(); + $attrCode = $subject->getAttribute()->getAttributeCode(); + if ($useDefault && isset($useDefault[$attrCode])) { + return true; + } + return $proceed($attribute); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php index fe26ac2a4481aedda0daf58ce240b1be20557401..33d441d3b8115c827671d7c80e1ef002ec7e1c69 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php @@ -91,12 +91,12 @@ class SortbyTest extends \PHPUnit_Framework_TestCase 'attribute does not exist' => [ self::DEFAULT_ATTRIBUTE_CODE, [], - false, + null, ], 'attribute sort by empty' => [ 'available_sort_by', ['available_sort_by' => null], - '', + null, ], 'attribute sort by' => [ 'available_sort_by', diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php index a774bab4793026208f40a28c067f4edd436ef50f..3e6f8ddf40b23381f5d89113bfa2e2ee28a9adbb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php @@ -22,6 +22,16 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase */ protected $categoryResourceMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $extensibleDataObjectConverterMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $storeMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -44,6 +54,16 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase $this->categoryResourceMock = $this->getMock('\Magento\Catalog\Model\ResourceModel\Category', [], [], '', false); $this->storeManagerMock = $this->getMock('\Magento\Store\Model\StoreManagerInterface'); + $this->storeMock = $this->getMockBuilder('Magento\Store\Api\Data\StoreInterface') + ->disableOriginalConstructor() + ->setMethods(['getId']) + ->getMockForAbstractClass(); + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($this->storeMock); + $this->extensibleDataObjectConverterMock = $this + ->getMockBuilder('\Magento\Framework\Api\ExtensibleDataObjectConverter') + ->setMethods(['toNestedArray']) + ->disableOriginalConstructor() + ->getMock(); $metadataMock = $this->getMock( 'Magento\Framework\Model\Entity\EntityMetadata', @@ -72,7 +92,8 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase $this->categoryFactoryMock, $this->categoryResourceMock, $this->storeManagerMock, - $this->metadataPoolMock + $this->metadataPoolMock, + $this->extensibleDataObjectConverterMock ); } @@ -124,23 +145,58 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase $this->assertEquals($categoryMock, $this->model->get($categoryId)); } - public function testUpdateExistingCategory() + /** + * @return array + */ + public function filterExtraFieldsOnUpdateCategoryDataProvider() { - $categoryId = 5; + return [ + [ + 3, + ['level' => '1', 'path' => '1/2', 'parent_id' => 1, 'name' => 'category'], + [ + 'store_id' => 1, + 'name' => 'category', + 'entity_id' => null + ] + ], + [ + 4, + ['level' => '1', 'path' => '1/2', 'image' => ['categoryImage'], 'name' => 'category'], + [ + 'store_id' => 1, + 'image_additional_data' => ['categoryImage'], + 'name' => 'category', + 'entity_id' => null + ] + ] + ]; + } + + /** + * @param $categoryId + * @param $categoryData + * @param $dataForSave + * @dataProvider filterExtraFieldsOnUpdateCategoryDataProvider + */ + public function testFilterExtraFieldsOnUpdateCategory($categoryId, $categoryData, $dataForSave) + { + $this->storeMock->expects($this->any())->method('getId')->willReturn(1); $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true); $categoryMock->expects( $this->atLeastOnce() )->method('getId')->willReturn($categoryId); $this->categoryFactoryMock->expects( - $this->once() + $this->exactly(2) )->method('create')->willReturn( $categoryMock ); - $categoryMock->expects($this->atLeastOnce())->method('toFlatArray')->willReturn(['image' => []]); + $this->extensibleDataObjectConverterMock + ->expects($this->once()) + ->method('toNestedArray') + ->will($this->returnValue($categoryData)); $categoryMock->expects($this->once())->method('validate')->willReturn(true); - $categoryMock->expects($this->once())->method('getParentId')->willReturn(3); - $categoryMock->expects($this->once())->method('getPath')->willReturn('path'); - $categoryMock->expects($this->once())->method('getIsActive')->willReturn(true); + $categoryMock->expects($this->once())->method('addData')->with($dataForSave); $this->categoryResourceMock->expects($this->once()) ->method('save') ->willReturn('\Magento\Framework\DataObject'); @@ -149,21 +205,27 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase public function testCreateNewCategory() { + $this->storeMock->expects($this->any())->method('getId')->willReturn(1); $categoryId = null; $parentCategoryId = 15; $newCategoryId = 25; + $categoryData = ['level' => '1', 'path' => '1/2', 'parent_id' => 1, 'name' => 'category']; + $dataForSave = ['store_id' => 1, 'name' => 'category', 'path' => 'path', 'parent_id' => 15,]; + $this->extensibleDataObjectConverterMock + ->expects($this->once()) + ->method('toNestedArray') + ->will($this->returnValue($categoryData)); $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true); $parentCategoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true); $categoryMock->expects($this->any())->method('getId') ->will($this->onConsecutiveCalls($categoryId, $newCategoryId)); - $categoryMock->expects($this->never())->method('getIsActive'); - $this->categoryFactoryMock->expects($this->once())->method('create')->willReturn($parentCategoryMock); + $this->categoryFactoryMock->expects($this->exactly(2))->method('create')->willReturn($parentCategoryMock); $parentCategoryMock->expects($this->atLeastOnce())->method('getId')->willReturn($parentCategoryId); $categoryMock->expects($this->once())->method('getParentId')->willReturn($parentCategoryId); $parentCategoryMock->expects($this->once())->method('getPath')->willReturn('path'); + $categoryMock->expects($this->once())->method('addData')->with($dataForSave); $categoryMock->expects($this->once())->method('validate')->willReturn(true); - $categoryMock->expects($this->once())->method('getParentId')->willReturn(3); $this->categoryResourceMock->expects($this->once()) ->method('save') ->willReturn('\Magento\Framework\DataObject'); @@ -178,6 +240,10 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase { $categoryId = 5; $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false, true, true); + $this->extensibleDataObjectConverterMock + ->expects($this->once()) + ->method('toNestedArray') + ->will($this->returnValue([])); $categoryMock->expects( $this->atLeastOnce() )->method('getId')->willReturn($categoryId); @@ -187,7 +253,6 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase $categoryMock ); $categoryMock->expects($this->once())->method('validate')->willReturn(false); - $categoryMock->expects($this->once())->method('getParentId')->willReturn(3); $this->model->save($categoryMock); } @@ -199,6 +264,10 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase $this->setExpectedException($expectedException, $expectedExceptionMessage); $categoryId = 5; $categoryMock = $this->getMock('\Magento\Catalog\Model\Category', [], [], '', false); + $this->extensibleDataObjectConverterMock + ->expects($this->once()) + ->method('toNestedArray') + ->will($this->returnValue([])); $objectMock = $this->getMock('\Magento\Framework\DataObject', ['getFrontend', 'getLabel'], [], '', false); $categoryMock->expects( $this->atLeastOnce() @@ -210,7 +279,6 @@ class CategoryRepositoryTest extends \PHPUnit_Framework_TestCase ); $objectMock->expects($this->any())->method('getFrontend')->willReturn($objectMock); $objectMock->expects($this->any())->method('getLabel')->willReturn('ValidateCategoryTest'); - $categoryMock->expects($this->once())->method('getParentId')->willReturn(3); $categoryMock->expects($this->once())->method('validate')->willReturn([42 => $error]); $this->categoryResourceMock->expects($this->any())->method('getAttribute')->with(42)->willReturn($objectMock); $categoryMock->expects($this->never())->method('unsetData'); diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index ffeed2a2d25f15ce6907d4816771fa3236c1d4ca..d25285265394847ca85fe04f58b02737a28ab166 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -43,6 +43,7 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column /** * {@inheritdoc} + * @deprecated */ public function prepareDataSource(array $dataSource) { @@ -63,15 +64,16 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column return $dataSource; } - + /** * Prepare component configuration * @return void */ public function prepare() { - if (!$this->storeManager->isSingleStoreMode()) { - parent::prepare(); + parent::prepare(); + if ($this->storeManager->isSingleStoreMode()) { + $this->_data['config']['componentDisabled'] = true; } } } diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 2227f27b94a3ea3f85dadf88e9da345a686501ef..0cb04569e0ed455a94b2b759d44b9bb1c6305bae 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -477,6 +477,9 @@ <type name="Magento\Catalog\Api\ProductRepositoryInterface"> <plugin name="transactionWrapper" type="Magento\Catalog\Model\Plugin\ProductRepository\TransactionWrapper" sortOrder="-1"/> </type> + <type name="Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend"> + <plugin name="attributeValidation" type="Magento\Catalog\Plugin\Model\Attribute\Backend\AttributeValidation"/> + </type> <type name="Magento\Catalog\Model\CategoryRepository"> <arguments> <argument name="categoryResource" xsi:type="object">Magento\Catalog\Model\ResourceModel\Category\Proxy</argument> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index f0c892107d4659f4745cb8bab972e2171bf807f6..0bbd48f56e0620f25ca6b152ba165790c123c882 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -252,9 +252,10 @@ <item name="label" xsi:type="string" translate="true">Anchor</item> <item name="prefer" xsi:type="string">toggle</item> <item name="valueMap" xsi:type="array"> - <item name="true" xsi:type="boolean">true</item> - <item name="false" xsi:type="boolean">false</item> + <item name="true" xsi:type="string">1</item> + <item name="false" xsi:type="string">0</item> </item> + <item name="default" xsi:type="number">1</item> </item> </argument> </field> @@ -487,12 +488,25 @@ </item> </argument> </field> - <field name="page_layout"> + <field name="custom_design"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="sortOrder" xsi:type="number">180</item> <item name="dataType" xsi:type="string">string</item> <item name="formElement" xsi:type="string">select</item> + <item name="label" xsi:type="string" translate="true">Theme</item> + <item name="imports" xsi:type="array"> + <item name="disabled" xsi:type="string">${ $.parentName }.custom_use_parent_settings:checked</item> + </item> + </item> + </argument> + </field> + <field name="page_layout"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="sortOrder" xsi:type="number">190</item> + <item name="dataType" xsi:type="string">string</item> + <item name="formElement" xsi:type="string">select</item> <item name="label" xsi:type="string" translate="true">Layout</item> <item name="imports" xsi:type="array"> <item name="disabled" xsi:type="string">${ $.parentName }.custom_use_parent_settings:checked</item> @@ -503,7 +517,7 @@ <field name="custom_layout_update"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="sortOrder" xsi:type="number">190</item> + <item name="sortOrder" xsi:type="number">200</item> <item name="dataType" xsi:type="string">string</item> <item name="formElement" xsi:type="string">textarea</item> <item name="label" xsi:type="string" translate="true">Layout Update XML</item> @@ -517,7 +531,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="additionalClasses" xsi:type="string">admin__field-no-label</item> - <item name="sortOrder" xsi:type="number">200</item> + <item name="sortOrder" xsi:type="number">210</item> <item name="label" xsi:type="string"/> <item name="description" xsi:type="string" translate="true">Apply Design to Products</item> <item name="dataType" xsi:type="string">boolean</item> @@ -549,7 +563,8 @@ <item name="additionalClasses" xsi:type="string">admin__control-grouped-date</item> <item name="component" xsi:type="string">Magento_Ui/js/form/components/group</item> <item name="label" xsi:type="string" translate="true">Schedule Update From</item> - <item name="sortOrder" xsi:type="number">210</item> + <item name="required" xsi:type="boolean">false</item> + <item name="sortOrder" xsi:type="number">220</item> <item name="breakLine" xsi:type="boolean">false</item> <item name="imports" xsi:type="array"> <item name="disabled" xsi:type="string">ns = ${ $.ns }, index = custom_use_parent_settings :checked</item> @@ -560,7 +575,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="additionalClasses" xsi:type="string">admin__field-date</item> - <item name="sortOrder" xsi:type="number">220</item> + <item name="sortOrder" xsi:type="number">230</item> <item name="dataType" xsi:type="string">string</item> <item name="formElement" xsi:type="string">date</item> <item name="imports" xsi:type="array"> @@ -573,7 +588,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="additionalClasses" xsi:type="string">admin__field-date</item> - <item name="sortOrder" xsi:type="number">230</item> + <item name="sortOrder" xsi:type="number">240</item> <item name="dataType" xsi:type="string">string</item> <item name="label" xsi:type="string" translate="true">To</item> <item name="formElement" xsi:type="string">date</item> @@ -584,18 +599,5 @@ </argument> </field> </container> - <field name="custom_design"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="sortOrder" xsi:type="number">240</item> - <item name="dataType" xsi:type="string">string</item> - <item name="formElement" xsi:type="string">select</item> - <item name="label" xsi:type="string" translate="true">New Theme</item> - <item name="imports" xsi:type="array"> - <item name="disabled" xsi:type="string">ns = ${ $.ns }, index = custom_use_parent_settings :checked</item> - </item> - </item> - </argument> - </field> </fieldset> </form> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index 346b96024c8b245f2e6ae1cb6bb3404e4008044e..8f6792b862d2ce93c9e14e0086977ff83a3dd16f 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -230,7 +230,7 @@ </item> </argument> </column> - <column name="websites"> + <column name="websites" class="Magento\Catalog\Ui\Component\Listing\Columns\Websites"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Magento\Store\Model\ResourceModel\Website\Collection</item> <item name="config" xsi:type="array"> diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index f13343a80e1c239f2e7a809c230d8078f416702d..6ce20ca28a736080c6442f5eb938a6ad080108fc 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2163,7 +2163,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity if (!$this->validator->isValid($rowData)) { foreach ($this->validator->getMessages() as $message) { - $this->addRowError($message, $rowNum); + $this->addRowError($message, $rowNum, $this->validator->getInvalidAttribute()); } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 961e5c2d3a1cacc7ecd52f852a2ec89b25d45c51..17164013b87b75409010b1e6f7067c604e367a64 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -35,6 +35,11 @@ class Validator extends AbstractValidator implements RowValidatorInterface */ protected $_rowData; + /* + * @var string|null + */ + protected $invalidAttribute; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param RowValidatorInterface[] $validators @@ -200,10 +205,32 @@ class Validator extends AbstractValidator implements RowValidatorInterface } $this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] = $rowData[Product::COL_SKU]; } + + if (!$valid) { + $this->setInvalidAttribute($attrCode); + } + return (bool)$valid; } + /** + * @param string|null $attribute + * @return void + */ + protected function setInvalidAttribute($attribute) + { + $this->invalidAttribute = $attribute; + } + + /** + * @return string + */ + public function getInvalidAttribute() + { + return $this->invalidAttribute; + } + /** * @return bool * @SuppressWarnings(PHPMD.UnusedLocalVariable) @@ -211,6 +238,7 @@ class Validator extends AbstractValidator implements RowValidatorInterface protected function isValidAttributes() { $this->_clearMessages(); + $this->setInvalidAttribute(null); if (!isset($this->_rowData['product_type'])) { return false; } diff --git a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php index 3953bb9d6b79eb1432cb8fcda76f2f03059d41d1..50b60a5434aaf0bc3874aca6ba555ec86e589e91 100644 --- a/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php +++ b/app/code/Magento/CatalogRule/Block/Adminhtml/Promo/Catalog/Edit/Tab/Conditions.php @@ -153,7 +153,7 @@ class Conditions extends Generic implements TabInterface $renderer = $this->_rendererFieldset->setTemplate('Magento_CatalogRule::promo/fieldset.phtml') ->setNewChildUrl($newChildUrl) - ->setConditionsFieldSetId($model->getConditionsFieldSetId($formName)); + ->setFieldSetId($model->getConditionsFieldSetId($formName)); $fieldset = $form->addFieldset( $fieldsetId, diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index bb94d52397bbe73fdc8bd6b62d1cb8bbffa6cf3e..aadacc9074f8e22765c21ae1d7dd32cd4d0bdbdc 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -337,7 +337,7 @@ class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\Catalog protected function _getWebsitesMap() { $map = []; - $websites = $this->_storeManager->getWebsites(true); + $websites = $this->_storeManager->getWebsites(); foreach ($websites as $website) { // Continue if website has no store to be able to create catalog rule for website without store if ($website->getDefaultStore() === null) { @@ -582,6 +582,7 @@ class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\Catalog } //@codeCoverageIgnoreStart + /** * {@inheritdoc} */ diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php index bb547ca2859adf4e3438e0528cdd520b51d1266c..66bf230a43391b12d36859f020b2b9d9edd7d5f7 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php @@ -163,7 +163,7 @@ class RuleTest extends \PHPUnit_Framework_TestCase 'created_at' => '2014-06-25 13:14:30', 'updated_at' => '2014-06-25 14:37:15' ]; - $this->storeManager->expects($this->any())->method('getWebsites')->with(true) + $this->storeManager->expects($this->any())->method('getWebsites')->with(false) ->will($this->returnValue([$this->websiteModel, $this->websiteModel])); $this->websiteModel->expects($this->at(0))->method('getId') ->will($this->returnValue('1')); diff --git a/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml b/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml index 714f80fd47e8acef6476318fa150a1228bd8bebb..3cd22ae688f37ad1e5f39e683688240ca5f75b9f 100644 --- a/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml +++ b/app/code/Magento/CatalogRule/view/adminhtml/templates/promo/fieldset.phtml @@ -9,7 +9,7 @@ /**@var \Magento\Backend\Block\Widget\Form\Renderer\Fieldset $block */ ?> <?php $_element = $block->getElement() ?> -<?php $_jsObjectName = $block->getConditionsFieldSetId() != null ? $block->getConditionsFieldSetId() : $_element->getHtmlId() ?> +<?php $_jsObjectName = $block->getFieldSetId() != null ? $block->getFieldSetId() : $_element->getHtmlId() ?> <div class="rule-tree"> <fieldset id="<?php /* @escapeNotVerified */ echo $_jsObjectName ?>" <?php /* @escapeNotVerified */ echo $_element->serialize(['class']) ?> class="fieldset"> <legend class="legend"><span><?php /* @escapeNotVerified */ echo $_element->getLegend() ?></span></legend> diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php index 7899c1bcbd33117479af255361e5ebfa1a9c4fc3..648a849a81a4edfa1fca5cb4d4bbdb551b83b3b1 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php +++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/ConfigurableProductsProvider.php @@ -11,6 +11,11 @@ class ConfigurableProductsProvider /** @var \Magento\Framework\App\ResourceConnection */ private $resource; + /** + * @var array + */ + private $productIds = []; + /** * @param \Magento\Framework\App\ResourceConnection $resource */ @@ -25,13 +30,17 @@ class ConfigurableProductsProvider */ public function getIds(array $ids) { - $connection = $this->resource->getConnection(); - return $connection->fetchCol( - $connection - ->select() - ->from(['e' => $this->resource->getTableName('catalog_product_entity')], ['e.entity_id']) - ->where('e.type_id = ?', \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) - ->where('e.entity_id IN (?)', $ids) - ); + $key = md5(json_encode($ids)); + if (!isset($this->productIds[$key])) { + $connection = $this->resource->getConnection(); + $this->productIds[$key] = $connection->fetchCol( + $connection + ->select() + ->from(['e' => $this->resource->getTableName('catalog_product_entity')], ['e.entity_id']) + ->where('e.type_id = ?', \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) + ->where('e.entity_id IN (?)', $ids) + ); + } + return $this->productIds[$key]; } } diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php index 8c7eff09a889c70f5b50c21b2eb0154ff0e2aa15..855acac70751d6a406e894baf71dea1271bf7791 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php +++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php @@ -20,6 +20,11 @@ class ConfigurableProductHandler /** @var ConfigurableProductsProvider */ private $configurableProductsProvider; + /** + * @var array + */ + private $childrenProducts = []; + /** * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurable * @param ConfigurableProductsProvider $configurableProductsProvider @@ -42,7 +47,10 @@ class ConfigurableProductHandler { $configurableProductIds = $this->configurableProductsProvider->getIds(array_keys($productIds)); foreach ($configurableProductIds as $productId) { - $subProductIds = $this->configurable->getChildrenIds($productId)[0]; + if (!isset($this->childrenProducts[$productId])) { + $this->childrenProducts[$productId] = $this->configurable->getChildrenIds($productId)[0]; + } + $subProductIds = $this->childrenProducts[$productId]; $parentValidationResult = isset($productIds[$productId]) ? array_filter($productIds[$productId]) : []; diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 577e0aa871420f3fa5a5135a10ca05632b128939..7d6370a2ca341a20241eb713ed35f0067f088e3a 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -372,21 +372,13 @@ class DataProvider ['main' => $this->getTable($relation->getTable())], [$relation->getChildFieldName()] ); - //TODO: Will be removed in MAGETWO-47395 - if ($typeId === 'configurable') { - $select->where( - $relation->getParentFieldName() . ' = ?', - $productId - ); - } else { - $select->join( - ['e' => $this->resource->getTableName('catalog_product_entity')], - 'e.' . $this->metadata->getLinkField() . ' = main.' . $relation->getParentFieldName() - )->where( - 'e.entity_id = ?', - $productId - ); - } + $select->join( + ['e' => $this->resource->getTableName('catalog_product_entity')], + 'e.' . $this->metadata->getLinkField() . ' = main.' . $relation->getParentFieldName() + )->where( + 'e.entity_id = ?', + $productId + ); if ($relation->getWhere() !== null) { $select->where($relation->getWhere()); diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php index b57768ea9d283e8dcec2572c46d9baa998114d1d..6b54c8cbc3118976e31eae672d0bc81f8c85e6dd 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php @@ -6,6 +6,8 @@ namespace Magento\CatalogSearch\Model\ResourceModel\Advanced; use Magento\Catalog\Model\Product; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Search\SearchCriteriaBuilder; use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage; /** @@ -23,19 +25,24 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection private $filters = []; /** - * @var \Magento\CatalogSearch\Model\Advanced\Request\Builder + * @var \Magento\Search\Api\SearchInterface */ - private $requestBuilder; + private $search; /** - * @var \Magento\Search\Model\SearchEngine + * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory */ - private $searchEngine; + private $temporaryStorageFactory; /** - * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory + * @var SearchCriteriaBuilder */ - private $temporaryStorageFactory; + private $searchCriteriaBuilder; + + /** + * @var FilterBuilder + */ + private $filterBuilder; /** * Collection constructor. @@ -59,10 +66,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement * @param \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation - * @param \Magento\CatalogSearch\Model\Advanced\Request\Builder $requestBuilder - * @param \Magento\Search\Model\SearchEngine $searchEngine + * @param \Magento\Search\Api\SearchInterface $search * @param \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory - * @param \Zend_Db_Adapter_Abstract $connection + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param FilterBuilder $filterBuilder + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -86,14 +94,16 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Customer\Api\GroupManagementInterface $groupManagement, \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation, - \Magento\CatalogSearch\Model\Advanced\Request\Builder $requestBuilder, - \Magento\Search\Model\SearchEngine $searchEngine, + \Magento\Search\Api\SearchInterface $search, \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory, - $connection = null + SearchCriteriaBuilder $searchCriteriaBuilder, + FilterBuilder $filterBuilder, + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { - $this->requestBuilder = $requestBuilder; - $this->searchEngine = $searchEngine; + $this->search = $search; $this->temporaryStorageFactory = $temporaryStorageFactory; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; parent::__construct( $entityFactory, $logger, @@ -140,19 +150,18 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection protected function _renderFiltersBefore() { if ($this->filters) { - $this->requestBuilder->bindDimension('scope', $this->getStoreId()); - $this->requestBuilder->setRequestName('advanced_search_container'); foreach ($this->filters as $attributes) { foreach ($attributes as $attributeCode => $attributeValue) { $attributeCode = $this->getAttributeCode($attributeCode); - $this->requestBuilder->bindRequestValue($attributeCode, $attributeValue); + $this->addAttributeToSearch($attributeCode, $attributeValue); } } - $queryRequest = $this->requestBuilder->create(); - $queryResponse = $this->searchEngine->search($queryRequest); + $searchCriteria = $this->searchCriteriaBuilder->create(); + $searchCriteria->setRequestName('advanced_search_container'); + $searchResult = $this->search->search($searchCriteria); $temporaryStorage = $this->temporaryStorageFactory->create(); - $table = $temporaryStorage->storeDocuments($queryResponse->getIterator()); + $table = $temporaryStorage->storeApiDocuments($searchResult->getItems()); $this->getSelect()->joinInner( [ @@ -162,7 +171,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection [] ); } - return parent::_renderFiltersBefore(); + parent::_renderFiltersBefore(); } /** @@ -178,4 +187,49 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection return $attributeCode; } + + /** + * Create a filter and add it to the SearchCriteriaBuilder. + * + * @param string $attributeCode + * @param array|string $attributeValue + * @return void + */ + private function addAttributeToSearch($attributeCode, $attributeValue) + { + if (isset($attributeValue['from']) || isset($attributeValue['to'])) { + $this->addRangeAttributeToSearch($attributeCode, $attributeValue); + } elseif (!is_array($attributeValue)) { + $this->filterBuilder->setField($attributeCode)->setValue($attributeValue); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); + } elseif (isset($attributeValue['like'])) { + $this->filterBuilder->setField($attributeCode)->setValue($attributeValue['like']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); + } elseif (isset($attributeValue['in'])) { + $this->filterBuilder->setField($attributeCode)->setValue($attributeValue['in']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); + } elseif (isset($attributeValue['in_set'])) { + $this->filterBuilder->setField($attributeCode)->setValue($attributeValue['in_set']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); + } + } + + /** + * Add attributes that have a range (from,to) to the SearchCriteriaBuilder. + * + * @param string $attributeCode + * @param array|string $attributeValue + * @return void + */ + private function addRangeAttributeToSearch($attributeCode, $attributeValue) + { + if (isset($attributeValue['from']) && '' !== $attributeValue['from']) { + $this->filterBuilder->setField("{$attributeCode}.from")->setValue($attributeValue['from']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); + } + if (isset($attributeValue['to']) && '' !== $attributeValue['to']) { + $this->filterBuilder->setField("{$attributeCode}.to")->setValue($attributeValue['to']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); + } + } } diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 9b2eb2876b403e1b7cd8ec2837af8cf3a93a6912..740786c7cbe3dc541206e76b4dc680e36f2d403c 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -9,6 +9,7 @@ use Magento\Framework\DB\Select; use Magento\Framework\Exception\StateException; use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage; use Magento\Framework\Search\Response\QueryResponse; +use Magento\Framework\App\ObjectManager; /** * Fulltext Collection @@ -16,23 +17,29 @@ use Magento\Framework\Search\Response\QueryResponse; */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { - /** @var QueryResponse */ + /** + * @var QueryResponse + * @deprecated + */ protected $queryResponse; /** * Catalog search data * * @var \Magento\Search\Model\QueryFactory + * @deprecated */ protected $queryFactory = null; /** * @var \Magento\Framework\Search\Request\Builder + * @deprecated */ private $requestBuilder; /** * @var \Magento\Search\Model\SearchEngine + * @deprecated */ private $searchEngine; @@ -56,6 +63,26 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ private $temporaryStorageFactory; + /** + * @var \Magento\Search\Api\SearchInterface + */ + private $search; + + /** + * @var \Magento\Framework\Api\Search\SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var \Magento\Framework\Api\Search\SearchResultInterface + */ + private $searchResult; + + /** + * @var \Magento\Framework\Api\FilterBuilder + */ + private $filterBuilder; + /** * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger @@ -143,6 +170,73 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection $this->searchRequestName = $searchRequestName; } + /** + * @deprecated + * @return \Magento\Search\Api\SearchInterface + */ + private function getSearch() + { + if ($this->search === null) { + $this->search = ObjectManager::getInstance()->get('\Magento\Search\Api\SearchInterface'); + } + return $this->search; + } + + /** + * @deprecated + * @param \Magento\Search\Api\SearchInterface $object + * @return void + */ + public function setSearch(\Magento\Search\Api\SearchInterface $object) + { + $this->search = $object; + } + + /** + * @deprecated + * @return \Magento\Framework\Api\Search\SearchCriteriaBuilder + */ + private function getSearchCriteriaBuilder() + { + if ($this->searchCriteriaBuilder === null) { + $this->searchCriteriaBuilder = ObjectManager::getInstance() + ->get('\Magento\Framework\Api\Search\SearchCriteriaBuilder'); + } + return $this->searchCriteriaBuilder; + } + + /** + * @deprecated + * @param \Magento\Framework\Api\Search\SearchCriteriaBuilder $object + * @return void + */ + public function setSearchCriteriaBuilder(\Magento\Framework\Api\Search\SearchCriteriaBuilder $object) + { + $this->searchCriteriaBuilder = $object; + } + + /** + * @deprecated + * @return \Magento\Framework\Api\FilterBuilder + */ + private function getFilterBuilder() + { + if ($this->filterBuilder === null) { + $this->filterBuilder = ObjectManager::getInstance()->get('\Magento\Framework\Api\FilterBuilder'); + } + return $this->filterBuilder; + } + + /** + * @deprecated + * @param \Magento\Framework\Api\FilterBuilder $object + * @return void + */ + public function setFilterBuilder(\Magento\Framework\Api\FilterBuilder $object) + { + $this->filterBuilder = $object; + } + /** * Apply attribute filter to facet collection * @@ -152,17 +246,26 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ public function addFieldToFilter($field, $condition = null) { - if ($this->queryResponse !== null) { + if ($this->searchResult !== null) { throw new \RuntimeException('Illegal state'); } + + $this->getSearchCriteriaBuilder(); + $this->getFilterBuilder(); if (!is_array($condition) || !in_array(key($condition), ['from', 'to'])) { - $this->requestBuilder->bind($field, $condition); + $this->filterBuilder->setField($field); + $this->filterBuilder->setValue($condition); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); } else { if (!empty($condition['from'])) { - $this->requestBuilder->bind("{$field}.from", $condition['from']); + $this->filterBuilder->setField("{$field}.from"); + $this->filterBuilder->setValue($condition['from']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); } if (!empty($condition['to'])) { - $this->requestBuilder->bind("{$field}.to", $condition['to']); + $this->filterBuilder->setField("{$field}.to"); + $this->filterBuilder->setValue($condition['to']); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); } } return $this; @@ -185,9 +288,14 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ protected function _renderFiltersBefore() { - $this->requestBuilder->bindDimension('scope', $this->getStoreId()); + $this->getSearchCriteriaBuilder(); + $this->getFilterBuilder(); + $this->getSearch(); + if ($this->queryText) { - $this->requestBuilder->bind('search_term', $this->queryText); + $this->filterBuilder->setField('search_term'); + $this->filterBuilder->setValue($this->queryText); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); } $priceRangeCalculation = $this->_scopeConfig->getValue( @@ -195,16 +303,17 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); if ($priceRangeCalculation) { - $this->requestBuilder->bind('price_dynamic_algorithm', $priceRangeCalculation); + $this->filterBuilder->setField('price_dynamic_algorithm'); + $this->filterBuilder->setValue($priceRangeCalculation); + $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); } - $this->requestBuilder->setRequestName($this->searchRequestName); - $queryRequest = $this->requestBuilder->create(); - - $this->queryResponse = $this->searchEngine->search($queryRequest); + $searchCriteria = $this->searchCriteriaBuilder->create(); + $searchCriteria->setRequestName($this->searchRequestName); + $this->searchResult = $this->search->search($searchCriteria); $temporaryStorage = $this->temporaryStorageFactory->create(); - $table = $temporaryStorage->storeDocuments($this->queryResponse->getIterator()); + $table = $temporaryStorage->storeApiDocuments($this->searchResult->getItems()); $this->getSelect()->joinInner( [ @@ -214,7 +323,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection [] ); - $this->_totalRecords = $this->queryResponse->count(); + $this->_totalRecords = $this->searchResult->getTotalCount(); if ($this->order && 'relevance' === $this->order['field']) { $this->getSelect()->order('search_result.'. TemporaryStorage::FIELD_SCORE . ' ' . $this->order['dir']); @@ -268,7 +377,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { $this->_renderFilters(); $result = []; - $aggregations = $this->queryResponse->getAggregations(); + $aggregations = $this->searchResult->getAggregations(); $bucket = $aggregations->getBucket($field . '_bucket'); if ($bucket) { foreach ($bucket->getValues() as $value) { diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..353533b9c2e981ef717c3a5a7c6dfbb371c55b02 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel\Advanced; + +use Magento\Catalog\Model\Product; +use Magento\CatalogSearch\Test\Unit\Model\ResourceModel\BaseCollectionTest; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Tests Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection + */ +class CollectionTest extends BaseCollectionTest +{ + /** + * @var \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection + */ + private $advancedCollection; + + /** + * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterBuilder; + + /** + * @var \Magento\Framework\Api\Search\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $criteriaBuilder; + + /** + * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $temporaryStorageFactory; + + /** + * @var \Magento\Search\Api\SearchInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $search; + + /** + * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavConfig; + + /** + * setUp method for CollectionTest + */ + protected function setUp() + { + $helper = new ObjectManagerHelper($this); + + $this->eavConfig = $this->getMock('Magento\Eav\Model\Config', [], [], '', false); + $storeManager = $this->getStoreManager(); + $universalFactory = $this->getUniversalFactory(); + $this->criteriaBuilder = $this->getCriteriaBuilder(); + $this->filterBuilder = $this->getMock('Magento\Framework\Api\FilterBuilder', [], [], '', false); + $this->temporaryStorageFactory = $this->getMock( + 'Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory', + [], + [], + '', + false + ); + $this->search = $this->getMock('Magento\Search\Api\SearchInterface', [], [], '', false); + + $this->advancedCollection = $helper->getObject( + 'Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection', + [ + 'eavConfig' => $this->eavConfig, + 'storeManager' => $storeManager, + 'universalFactory' => $universalFactory, + 'searchCriteriaBuilder' => $this->criteriaBuilder, + 'filterBuilder' => $this->filterBuilder, + 'temporaryStorageFactory' => $this->temporaryStorageFactory, + 'search' => $this->search, + ] + ); + } + + public function testLoadWithFilterNoFilters() + { + $this->advancedCollection->loadWithFilter(); + } + + /** + * Test a search using 'like' condition + */ + public function testLike() + { + $attributeCode = 'description'; + $attributeCodeId = 42; + $attribute = $this->getMock('Magento\Catalog\Model\ResourceModel\Eav\Attribute', [], [], '', false); + $attribute->expects($this->once())->method('getAttributeCode')->willReturn($attributeCode); + $this->eavConfig->expects($this->once())->method('getAttribute')->with(Product::ENTITY, $attributeCodeId) + ->willReturn($attribute); + $filtersData = ['catalog_product_entity_text' => [$attributeCodeId => ['like' => 'search text']]]; + $this->filterBuilder->expects($this->once())->method('setField')->with($attributeCode) + ->willReturn($this->filterBuilder); + $this->filterBuilder->expects($this->once())->method('setValue')->with('search text') + ->willReturn($this->filterBuilder); + + $filter = $this->getMock('Magento\Framework\Api\Filter'); + $this->filterBuilder->expects($this->once())->method('create')->willReturn($filter); + + $criteria = $this->getMock('Magento\Framework\Api\Search\SearchCriteria', [], [], '', false); + $this->criteriaBuilder->expects($this->once())->method('create')->willReturn($criteria); + $criteria->expects($this->once()) + ->method('setRequestName') + ->with('advanced_search_container'); + + $tempTable = $this->getMock('Magento\Framework\DB\Ddl\Table', [], [], '', false); + $temporaryStorage = $this->getMock( + 'Magento\Framework\Search\Adapter\Mysql\TemporaryStorage', + [], + [], + '', + false + ); + $temporaryStorage->expects($this->once())->method('storeApiDocuments')->willReturn($tempTable); + $this->temporaryStorageFactory->expects($this->once())->method('create')->willReturn($temporaryStorage); + $searchResult = $this->getMock('Magento\Framework\Api\Search\SearchResultInterface', [], [], '', false); + $this->search->expects($this->once())->method('search')->willReturn($searchResult); + + // addFieldsToFilter will load filters, + // then loadWithFilter will trigger _renderFiltersBefore code in Advanced/Collection + $this->assertSame( + $this->advancedCollection, + $this->advancedCollection->addFieldsToFilter($filtersData)->loadWithFilter() + ); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getCriteriaBuilder() + { + $criteriaBuilder = $this->getMockBuilder('Magento\Framework\Api\Search\SearchCriteriaBuilder') + ->setMethods(['addFilter', 'create', 'setRequestName']) + ->disableOriginalConstructor() + ->getMock(); + return $criteriaBuilder; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollectionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5d4ba4eef15f9a302ee547f31859fc0f2a90a8ff --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollectionTest.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel; + +/** + * Base class for Collection tests. + * + * Contains helper methods to get commonly used mocks used for collection tests. + **/ +class BaseCollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * Get Mocks for StoreManager so Collection can be used. + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getStoreManager() + { + $store = $this->getMockBuilder('Magento\Store\Model\Store') + ->setMethods(['getId']) + ->disableOriginalConstructor() + ->getMock(); + $store->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $storeManager = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface') + ->setMethods(['getStore']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $storeManager->expects($this->once()) + ->method('getStore') + ->willReturn($store); + + return $storeManager; + } + + /** + * Get mock for UniversalFactory so Collection can be used. + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getUniversalFactory() + { + $connection = $this->getMockBuilder('Magento\Framework\DB\Adapter\Pdo\Mysql') + ->disableOriginalConstructor() + ->setMethods(['select']) + ->getMockForAbstractClass(); + $select = $this->getMockBuilder('Magento\Framework\DB\Select') + ->disableOriginalConstructor() + ->getMock(); + $connection->expects($this->any())->method('select')->willReturn($select); + + $entity = $this->getMockBuilder('Magento\Eav\Model\Entity\AbstractEntity') + ->setMethods(['getConnection', 'getTable', 'getDefaultAttributes', 'getEntityTable']) + ->disableOriginalConstructor() + ->getMock(); + $entity->expects($this->once()) + ->method('getConnection') + ->willReturn($connection); + $entity->expects($this->exactly(2)) + ->method('getTable') + ->willReturnArgument(0); + $entity->expects($this->once()) + ->method('getDefaultAttributes') + ->willReturn(['attr1', 'attr2']); + $entity->expects($this->once()) + ->method('getEntityTable') + ->willReturn('table'); + + $universalFactory = $this->getMockBuilder('Magento\Framework\Validator\UniversalFactory') + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $universalFactory->expects($this->once()) + ->method('create') + ->willReturn($entity); + + return $universalFactory; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php index 616fa2c937b637a9d07f1c62710ef89b3c92260d..800b3a2ca311b2559c382a49cfe09b2e96a1775a 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php @@ -5,16 +5,20 @@ */ namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel\Fulltext; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use PHPUnit_Framework_TestCase; +use Magento\CatalogSearch\Test\Unit\Model\ResourceModel\BaseCollectionTest; -class CollectionTest extends PHPUnit_Framework_TestCase +class CollectionTest extends BaseCollectionTest { /** * @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection */ private $model; + /** + * @var \Magento\Framework\Api\Filter + */ + private $filter; + /** * setUp method for CollectionTest */ @@ -25,7 +29,8 @@ class CollectionTest extends PHPUnit_Framework_TestCase $storeManager = $this->getStoreManager(); $universalFactory = $this->getUniversalFactory(); $scopeConfig = $this->getScopeConfig(); - $requestBuilder = $this->getRequestBuilder(); + $criteriaBuilder = $this->getCriteriaBuilder(); + $filterBuilder = $this->getFilterBuilder(); $this->model = $helper->getObject( 'Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection', @@ -33,9 +38,14 @@ class CollectionTest extends PHPUnit_Framework_TestCase 'storeManager' => $storeManager, 'universalFactory' => $universalFactory, 'scopeConfig' => $scopeConfig, - 'requestBuilder' => $requestBuilder ] ); + + $search = $this->getMockForAbstractClass('\Magento\Search\Api\SearchInterface'); + $this->model->setSearchCriteriaBuilder($criteriaBuilder); + $this->model->setSearch($search); + $this->model->setFilterBuilder($filterBuilder); + } /** @@ -48,72 +58,6 @@ class CollectionTest extends PHPUnit_Framework_TestCase $this->model->getFacetedData('field'); } - /** - * @return \PHPUnit_Framework_MockObject_MockObject - */ - protected function getStoreManager() - { - $store = $this->getMockBuilder('Magento\Store\Model\Store') - ->setMethods(['getId']) - ->disableOriginalConstructor() - ->getMock(); - $store->expects($this->once()) - ->method('getId') - ->willReturn(1); - - $storeManager = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface') - ->setMethods(['getStore']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $storeManager->expects($this->once()) - ->method('getStore') - ->willReturn($store); - - return $storeManager; - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject - */ - protected function getUniversalFactory() - { - $connection = $this->getMockBuilder('Magento\Framework\DB\Adapter\Pdo\Mysql') - ->disableOriginalConstructor() - ->setMethods(['select']) - ->getMockForAbstractClass(); - $select = $this->getMockBuilder('Magento\Framework\DB\Select') - ->disableOriginalConstructor() - ->getMock(); - $connection->expects($this->any())->method('select')->willReturn($select); - - $entity = $this->getMockBuilder('Magento\Eav\Model\Entity\AbstractEntity') - ->setMethods(['getConnection', 'getTable', 'getDefaultAttributes', 'getEntityTable']) - ->disableOriginalConstructor() - ->getMock(); - $entity->expects($this->once()) - ->method('getConnection') - ->willReturn($connection); - $entity->expects($this->exactly(2)) - ->method('getTable') - ->willReturnArgument(0); - $entity->expects($this->once()) - ->method('getDefaultAttributes') - ->willReturn(['attr1', 'attr2']); - $entity->expects($this->once()) - ->method('getEntityTable') - ->willReturn('table'); - - $universalFactory = $this->getMockBuilder('Magento\Framework\Validator\UniversalFactory') - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $universalFactory->expects($this->once()) - ->method('create') - ->willReturn($entity); - - return $universalFactory; - } - /** * @return \PHPUnit_Framework_MockObject_MockObject */ @@ -133,20 +77,37 @@ class CollectionTest extends PHPUnit_Framework_TestCase /** * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getRequestBuilder() + protected function getCriteriaBuilder() { - $requestBuilder = $this->getMockBuilder('Magento\Framework\Search\Request\Builder') - ->setMethods(['bind', 'setRequestName']) + $criteriaBuilder = $this->getMockBuilder('Magento\Framework\Api\Search\SearchCriteriaBuilder') + ->setMethods(['addFilter', 'create', 'setRequestName']) ->disableOriginalConstructor() ->getMock(); - $requestBuilder->expects($this->once()) - ->method('bind') - ->withConsecutive(['price_dynamic_algorithm', 1]); - $requestBuilder->expects($this->once()) + $this->filter = new \Magento\Framework\Api\Filter(); + $this->filter->setField('price_dynamic_algorithm'); + $this->filter->setValue(1); + $criteriaBuilder->expects($this->once()) + ->method('addFilter') + ->with($this->filter); + $criteria = $this->getMock('Magento\Framework\Api\Search\SearchCriteria', [], [], '', false); + $criteriaBuilder->expects($this->once())->method('create')->willReturn($criteria); + $criteria->expects($this->once()) ->method('setRequestName') ->withConsecutive(['catalog_view_container']) ->willThrowException(new \Exception('setRequestName', 333)); - return $requestBuilder; + return $criteriaBuilder; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getFilterBuilder() + { + $filterBuilder = $this->getMock('Magento\Framework\Api\FilterBuilder', [], [], '', false); + $filterBuilder->expects($this->once())->method('setField')->with('price_dynamic_algorithm'); + $filterBuilder->expects($this->once())->method('setValue')->with(1); + $filterBuilder->expects($this->once())->method('create')->willReturn($this->filter); + return $filterBuilder; } } diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml index 9d5a35b0cc3318bf9fceaccc594e2fb35079bc53..46973ff5cfcc743a36f1a43f4d838f148d4d2e15 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml @@ -171,7 +171,7 @@ </argument> </field> </fieldset> - <fieldset name="websites"> + <fieldset name="websites" class="Magento\Store\Ui\Component\Form\Fieldset\Websites"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="collapsible" xsi:type="boolean">true</item> diff --git a/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php b/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php index 40e6653aab6381f562f24d3c1a53d8dde2c0a81c..d8d8211951292c8494ada0d1f1adb35d9c3bc95d 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php +++ b/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php @@ -303,7 +303,7 @@ class Matrix extends \Magento\Backend\Block\Template ]; foreach ($attribute->getOptions() as $option) { if (!empty($option->getValue())) { - $attributes[$attribute->getAttributeId()]['options'][$option->getValue()] = [ + $attributes[$attribute->getAttributeId()]['options'][] = [ 'attribute_code' => $attribute->getAttributeCode(), 'attribute_label' => $attribute->getStoreLabel(0), 'id' => $option->getValue(), @@ -322,7 +322,7 @@ class Matrix extends \Magento\Backend\Block\Template 'value' => $optionId, ]; $variationOptions[] = $variationOption; - $attributes[$attribute->getAttributeId()]['chosen'][$optionId] = $variationOption; + $attributes[$attribute->getAttributeId()]['chosen'][] = $variationOption; } $productMatrix[] = [ diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php index f62703cc75d2763171ac030891d37ab49f77f79b..a052a70c1e9104a416641c5c5ad4d4541f249358 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php @@ -51,7 +51,7 @@ class Plugin $attributes = $request->getParam('attributes'); if (!empty($attributes)) { $product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->configurableType->setUsedProductAttributeIds($attributes, $product); + $this->configurableType->setUsedProductAttributes($product, $attributes); } else { $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE); } @@ -73,7 +73,9 @@ class Plugin && $request->getParam('id', false) === false ) { $configProduct = $this->productFactory->create(); - $configProduct->setStoreId(0)->load($request->getParam('product'))->setTypeId($request->getParam('type')); + $configProduct->setStoreId(0) + ->load($request->getParam('product')) + ->setTypeId($request->getParam('type')); $data = []; foreach ($configProduct->getTypeInstance()->getEditableAttributes($configProduct) as $attribute) { diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php index ee09e6ae6a221471286b25e6974ca1054f1455c9..1d0ee47fea955d048e940b455819c6d20fa76453 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php @@ -1,75 +1,121 @@ <?php /** - * Product initialzation helper + * Product initialization helper * * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin; +use Magento\Catalog\Api\Data\ProductExtensionInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableProduct; +use Magento\ConfigurableProduct\Model\Product\VariationHandler; +use Magento\Framework\App\RequestInterface; +/** + * Class Configurable + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class Configurable { - /** @var \Magento\ConfigurableProduct\Model\Product\VariationHandler */ - protected $variationHandler; + /** + * @var VariationHandler + */ + private $variationHandler; - /** @var \Magento\Framework\App\RequestInterface */ - protected $request; + /** + * @var RequestInterface + */ + private $request; - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable */ - protected $productType; + /** + * @var Factory + */ + private $optionsFactory; /** - * @param \Magento\ConfigurableProduct\Model\Product\VariationHandler $variationHandler - * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $productType - * @param \Magento\Framework\App\RequestInterface $request + * Constructor + * + * @param VariationHandler $variationHandler + * @param RequestInterface $request + * @param Factory $optionsFactory */ public function __construct( - \Magento\ConfigurableProduct\Model\Product\VariationHandler $variationHandler, - \Magento\ConfigurableProduct\Model\Product\Type\Configurable $productType, - \Magento\Framework\App\RequestInterface $request + VariationHandler $variationHandler, + RequestInterface $request, + Factory $optionsFactory ) { $this->variationHandler = $variationHandler; - $this->productType = $productType; $this->request = $request; + $this->optionsFactory = $optionsFactory; } /** * Initialize data for configurable product * - * @param \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper $subject - * @param \Magento\Catalog\Model\Product $product + * @param Helper $subject + * @param ProductInterface $product + * @return ProductInterface + * @throws \InvalidArgumentException * - * @return \Magento\Catalog\Model\Product * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterInitialize( - \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper $subject, - \Magento\Catalog\Model\Product $product - ) { + public function afterInitialize(Helper $subject, ProductInterface $product) + { $attributes = $this->request->getParam('attributes'); - if ($product->getTypeId() == ConfigurableProduct::TYPE_CODE && !empty($attributes)) { - $setId = $this->request->getPost('new-variations-attribute-set-id'); - if ($setId) { - $product->setAttributeSetId($setId); - } - $this->productType->setUsedProductAttributeIds($attributes, $product); + $productData = $this->request->getPost('product', []); + + if ($product->getTypeId() !== ConfigurableProduct::TYPE_CODE || empty($attributes)) { + return $product; + } + + $setId = $this->request->getPost('new-variations-attribute-set-id'); + if ($setId) { + $product->setAttributeSetId($setId); + } + $extensionAttributes = $product->getExtensionAttributes(); - $product->setNewVariationsAttributeSetId($setId); - $associatedProductIds = $this->request->getPost('associated_product_ids', []); - $variationsMatrix = $this->request->getParam('variations-matrix', []); - if (!empty($variationsMatrix)) { - $generatedProductIds = $this->variationHandler->generateSimpleProducts($product, $variationsMatrix); - $associatedProductIds = array_merge($associatedProductIds, $generatedProductIds); - } - $product->setAssociatedProductIds(array_filter($associatedProductIds)); + $product->setNewVariationsAttributeSetId($setId); - $product->setCanSaveConfigurableAttributes( - (bool)$this->request->getPost('affect_configurable_product_attributes') + $configurableOptions = []; + if (!empty($productData['configurable_attributes_data'])) { + $configurableOptions = $this->optionsFactory->create( + (array) $productData['configurable_attributes_data'] ); } + $extensionAttributes->setConfigurableProductOptions($configurableOptions); + + $this->setLinkedProducts($product, $extensionAttributes); + $product->setCanSaveConfigurableAttributes( + (bool) $this->request->getPost('affect_configurable_product_attributes') + ); + + $product->setExtensionAttributes($extensionAttributes); + return $product; } + + /** + * Relate simple products to configurable + * + * @param ProductInterface $product + * @param ProductExtensionInterface $extensionAttributes + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function setLinkedProducts(ProductInterface $product, ProductExtensionInterface $extensionAttributes) + { + $associatedProductIds = $this->request->getPost('associated_product_ids', []); + $variationsMatrix = $this->request->getParam('variations-matrix', []); + if (!empty($variationsMatrix)) { + $generatedProductIds = $this->variationHandler->generateSimpleProducts($product, $variationsMatrix); + $associatedProductIds = array_merge($associatedProductIds, $generatedProductIds); + } + $extensionAttributes->setConfigurableProductLinks(array_filter($associatedProductIds)); + } } diff --git a/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Factory.php b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Factory.php new file mode 100644 index 0000000000000000000000000000000000000000..4ecdf85d2634627d8d49a6d0c1865573f3249beb --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Factory.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Helper\Product\Options; + +use Magento\ConfigurableProduct\Api\Data\OptionInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory; + +/** + * Class Factory + */ +class Factory +{ + /** + * @var AttributeFactory + */ + private $attributeFactory; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var Configurable + */ + private $productType; + + /** + * @var OptionValueInterfaceFactory + */ + private $optionValueFactory; + + /** + * Constructor + * + * @param Configurable $productType + * @param AttributeFactory $attributeFactory + * @param OptionValueInterfaceFactory $optionValueFactory + * @param ProductAttributeRepositoryInterface $productAttributeRepository + */ + public function __construct( + Configurable $productType, + AttributeFactory $attributeFactory, + OptionValueInterfaceFactory $optionValueFactory, + ProductAttributeRepositoryInterface $productAttributeRepository + ) { + $this->productType = $productType; + $this->attributeFactory = $attributeFactory; + $this->optionValueFactory = $optionValueFactory; + $this->productAttributeRepository = $productAttributeRepository; + } + + /** + * Create configurable product options + * + * @param array $attributesData + * @return OptionInterface[] + * @throws \InvalidArgumentException + */ + public function create(array $attributesData) + { + $options = []; + + foreach ($attributesData as $item) { + $attribute = $this->attributeFactory->create(); + $eavAttribute = $this->productAttributeRepository->get($item[Attribute::KEY_ATTRIBUTE_ID]); + + if (!$this->productType->canUseAttribute($eavAttribute)) { + throw new \InvalidArgumentException('Provided attribute can not be used with configurable product.'); + } + + $this->updateAttributeData($attribute, $item); + $options[] = $attribute; + } + + return $options; + } + + /** + * Update attribute data + * + * @param OptionInterface $attribute + * @param array $item + * @return void + */ + private function updateAttributeData(OptionInterface $attribute, array $item) + { + $values = []; + foreach ($item['values'] as $value) { + $option = $this->optionValueFactory->create(); + $option->setValueIndex($value['value_index']); + $values[] = $option; + } + $attribute->setData( + array_replace_recursive( + (array)$attribute->getData(), + $item + ) + ); + $attribute->setValues($values); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php new file mode 100644 index 0000000000000000000000000000000000000000..e71e7ccf2f377261b46efc22b974db0d25d22d39 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Helper/Product/Options/Loader.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Helper\Product\Options; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\ConfigurableProduct\Api\Data\OptionInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory; + +/** + * Class Loader + */ +class Loader +{ + /** + * @var OptionValueInterfaceFactory + */ + private $optionValueFactory; + + /** + * ReadHandler constructor + * + * @param OptionValueInterfaceFactory $optionValueFactory + */ + public function __construct(OptionValueInterfaceFactory $optionValueFactory) + { + $this->optionValueFactory = $optionValueFactory; + } + + /** + * @param ProductInterface $product + * @return OptionInterface[] + */ + public function load(ProductInterface $product) + { + $options = []; + /** @var Configurable $typeInstance */ + $typeInstance = $product->getTypeInstance(); + $attributeCollection = $typeInstance->getConfigurableAttributes($product); + + foreach ($attributeCollection as $attribute) { + $values = []; + $attributeOptions = $attribute->getOptions(); + if (is_array($attributeOptions)) { + foreach ($attributeOptions as $option) { + /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */ + $value = $this->optionValueFactory->create(); + $value->setValueIndex($option['value_index']); + $values[] = $value; + } + } + $attribute->setValues($values); + $options[] = $attribute; + } + + return $options; + } +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php b/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php index 059b7e1f9bfd18ced6418d331bc703f8a43c51e5..4b086658d70f9af8bf9cd7b84417c76eec65478f 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php +++ b/app/code/Magento/ConfigurableProduct/Model/Attribute/LockValidator.php @@ -5,9 +5,14 @@ */ namespace Magento\ConfigurableProduct\Model\Attribute; -use Magento\Catalog\Model\Attribute\LockValidatorInterface; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Catalog\Model\Attribute\LockValidatorInterface; +/** + * Class LockValidator + */ class LockValidator implements LockValidatorInterface { /** @@ -16,11 +21,22 @@ class LockValidator implements LockValidatorInterface protected $resource; /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * Constructor + * * @param ResourceConnection $resource + * @param MetadataPool $metadataPool */ - public function __construct(ResourceConnection $resource) - { + public function __construct( + ResourceConnection $resource, + MetadataPool $metadataPool + ) { $this->resource = $resource; + $this->metadataPool = $metadataPool; } /** @@ -33,25 +49,22 @@ class LockValidator implements LockValidatorInterface */ public function validate(\Magento\Framework\Model\AbstractModel $object, $attributeSet = null) { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $connection = $this->resource->getConnection(); - $attrTable = $this->resource->getTableName('catalog_product_super_attribute'); - $productTable = $this->resource->getTableName('catalog_product_entity'); $bind = ['attribute_id' => $object->getAttributeId()]; + $select = clone $connection->select(); - $select->reset()->from( - ['main_table' => $attrTable], - ['psa_count' => 'COUNT(product_super_attribute_id)'] - )->join( - ['entity' => $productTable], - 'main_table.product_id = entity.entity_id' - )->where( - 'main_table.attribute_id = :attribute_id' - )->group( - 'main_table.attribute_id' - )->limit( - 1 - ); + $select->reset() + ->from( + ['main_table' => $this->resource->getTableName('catalog_product_super_attribute')], + ['psa_count' => 'COUNT(product_super_attribute_id)'] + )->join( + ['entity' => $this->resource->getTableName('catalog_product_entity')], + 'main_table.product_id = entity.' . $metadata->getLinkField() + )->where('main_table.attribute_id = :attribute_id') + ->group('main_table.attribute_id') + ->limit(1); if ($attributeSet !== null) { $bind['attribute_set_id'] = $attributeSet; diff --git a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php index d1e08f2367c3518e83a6a6fb92c46edc15af6f67..27470ce03797b16d5bc20b35b2da892195fb321c 100644 --- a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php +++ b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php @@ -9,6 +9,7 @@ namespace Magento\ConfigurableProduct\Model; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\ConfigurableProduct\Api\Data\OptionInterface; +use Magento\ConfigurableProduct\Helper\Product\Options\Loader; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; use Magento\Framework\Exception\InputException; @@ -16,6 +17,7 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableType; use Magento\Catalog\Model\Product\Type as ProductType; use Magento\Store\Model\Store; +use Magento\Framework\Model\Entity\MetadataPool; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -62,6 +64,16 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit */ private $configurableTypeResource; + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var Loader + */ + private $optionLoader; + /** * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory @@ -71,6 +83,10 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository * @param ConfigurableType\AttributeFactory $configurableAttributeFactory * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurableTypeResource + * @param MetadataPool $metadataPool + * @param Loader $optionLoader + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, @@ -80,7 +96,9 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository, \Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory $configurableAttributeFactory, - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurableTypeResource + \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $configurableTypeResource, + MetadataPool $metadataPool, + Loader $optionLoader ) { $this->productRepository = $productRepository; $this->optionValueFactory = $optionValueFactory; @@ -90,6 +108,8 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit $this->productAttributeRepository = $productAttributeRepository; $this->configurableAttributeFactory = $configurableAttributeFactory; $this->configurableTypeResource = $configurableTypeResource; + $this->metadataPool = $metadataPool; + $this->optionLoader = $optionLoader; } /** @@ -99,15 +119,13 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit { $product = $this->getProduct($sku); - $extensionAttribute = $product->getExtensionAttributes(); - if ($extensionAttribute !== null) { - $options = $extensionAttribute->getConfigurableProductOptions(); - foreach ($options as $option) { - if ($option->getId() == $id) { - return $option; - } + $options = $this->optionLoader->load($product); + foreach ($options as $option) { + if ($option->getId() == $id) { + return $option; } } + throw new NoSuchEntityException(__('Requested option doesn\'t exist: %1', $id)); } @@ -116,14 +134,9 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit */ public function getList($sku) { - $options = []; $product = $this->getProduct($sku); - $extensionAttribute = $product->getExtensionAttributes(); - if ($extensionAttribute !== null) { - $options = $extensionAttribute->getConfigurableProductOptions(); - } - return $options; + return (array) $this->optionLoader->load($product); } /** @@ -131,12 +144,14 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit */ public function delete(OptionInterface $option) { - $product = $this->getProductById($option->getProductId()); + $entityId = $this->configurableTypeResource->getEntityIdByAttribute($option); + $product = $this->getProductById($entityId); + try { $this->configurableTypeResource->saveProducts($product, []); } catch (\Exception $exception) { throw new StateException( - __('Cannot delete variations from product: %1', $option->getProductId()) + __('Cannot delete variations from product: %1', $entityId) ); } try { @@ -170,13 +185,14 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit */ public function save($sku, OptionInterface $option) { - /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */ - $configurableAttribute = $this->configurableAttributeFactory->create(); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); if ($option->getId()) { /** @var Product $product */ $product = $this->getProduct($sku); - $configurableAttribute->load($option->getId()); - if (!$configurableAttribute->getId() || $configurableAttribute->getProductId() != $product->getId()) { + $data = $option->getData(); + $option->load($option->getId()); + $option->setData(array_replace_recursive($option->getData(), $data)); + if (!$option->getId() || $option->getProductId() != $product->getData($metadata->getLinkField())) { throw new NoSuchEntityException( __( 'Option with id "%1" not found', @@ -184,60 +200,27 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit ) ); } - $configurableAttribute->addData($option->getData()); - $configurableAttribute->setValues( - $option->getValues() !== null ? $option->getValues() : $configurableAttribute->getOptions() - ); - - try { - $configurableAttribute->save(); - } catch (\Exception $e) { - throw new CouldNotSaveException( - __( - 'Could not update option with id "%1"', - $option->getId() - ) - ); - } } else { - $this->validateNewOptionData($option); /** @var Product $product */ $product = $this->productRepository->get($sku); + $this->validateNewOptionData($option); $allowedTypes = [ProductType::TYPE_SIMPLE, ProductType::TYPE_VIRTUAL, ConfigurableType::TYPE_CODE]; if (!in_array($product->getTypeId(), $allowedTypes)) { throw new \InvalidArgumentException('Incompatible product type'); } + $option->setProductId($product->getData($metadata->getLinkField())); + } - $eavAttribute = $this->productAttributeRepository->get($option->getAttributeId()); - $configurableAttribute->loadByProductAndAttribute($product, $eavAttribute); - if ($configurableAttribute->getId()) { - throw new CouldNotSaveException(__('Product already has this option')); - } - - $configurableAttributesData = [ - 'attribute_id' => $option->getAttributeId(), - 'position' => $option->getPosition(), - 'use_default' => $option->getIsUseDefault(), - 'label' => $option->getLabel(), - 'values' => $option->getValues() - ]; - - try { - $product->setTypeId(ConfigurableType::TYPE_CODE); - $product->setConfigurableAttributesData([$configurableAttributesData]); - $product->setStoreId($this->storeManager->getStore(Store::ADMIN_CODE)->getId()); - $product->save(); - } catch (\Exception $e) { - throw new CouldNotSaveException(__('Something went wrong while saving option.')); - } - - $configurableAttribute = $this->configurableAttributeFactory->create(); - $configurableAttribute->loadByProductAndAttribute($product, $eavAttribute); + try { + $option->save(); + } catch (\Exception $e) { + throw new CouldNotSaveException(__('Something went wrong while saving option.')); } - if (!$configurableAttribute->getId()) { + + if (!$option->getId()) { throw new CouldNotSaveException(__('Something went wrong while saving option.')); } - return $configurableAttribute->getId(); + return $option->getId(); } /** diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php deleted file mode 100644 index 2d04a2cda28b87c7baf2de3d78f1a5c4c3b4939d..0000000000000000000000000000000000000000 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\ConfigurableProduct\Model\Plugin; - -class AfterProductLoad -{ - /** - * @var \Magento\Catalog\Api\Data\ProductExtensionFactory - */ - protected $productExtensionFactory; - - /** - * @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory - */ - protected $optionValueFactory; - - /** - * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory - * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory - */ - public function __construct( - \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory, - \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory - ) { - $this->productExtensionFactory = $productExtensionFactory; - $this->optionValueFactory = $optionValueFactory; - } - - /** - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Catalog\Model\Product - */ - public function afterLoad(\Magento\Catalog\Model\Product $product) - { - if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) { - return $product; - } - - $productExtension = $product->getExtensionAttributes(); - if ($productExtension === null) { - $productExtension = $this->productExtensionFactory->create(); - } - - $productExtension->setConfigurableProductOptions($this->getOptions($product)); - $productExtension->setConfigurableProductLinks($this->getLinkedProducts($product)); - - $product->setExtensionAttributes($productExtension); - - return $product; - } - - /** - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[] - */ - protected function getOptions(\Magento\Catalog\Model\Product $product) - { - $options = []; - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */ - $typeInstance = $product->getTypeInstance(); - $attributeCollection = $typeInstance->getConfigurableAttributes($product); - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $option */ - foreach ($attributeCollection as $attribute) { - $values = []; - $attributeOptions = $attribute->getOptions(); - if (is_array($attributeOptions)) { - foreach ($attributeOptions as $option) { - /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */ - $value = $this->optionValueFactory->create(); - $value->setValueIndex($option['value_index']); - $values[] = $value; - } - } - $attribute->setValues($values); - $options[] = $attribute; - } - return $options; - } - - /** - * @param \Magento\Catalog\Model\Product $product - * @return int[] - */ - protected function getLinkedProducts(\Magento\Catalog\Model\Product $product) - { - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */ - $typeInstance = $product->getTypeInstance(); - $childrenIds = $typeInstance->getChildrenIds($product->getId()); - - if (isset($childrenIds[0])) { - return $childrenIds[0]; - } else { - return []; - } - } -} diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php index de7898af9a19c70c1f4c0025b031e1bff8a79f74..266a8eb172adeec3b1466f00a9e4e81bc2fcdd7e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php @@ -1,172 +1,105 @@ <?php /** - * * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\ConfigurableProduct\Model\Plugin; +use Magento\Catalog\Model\ProductFactory; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\Exception\InputException; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\ConfigurableProduct\Api\Data\OptionInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +/** + * Class AroundProductRepositorySave + */ class AroundProductRepositorySave { /** - * @var \Magento\ConfigurableProduct\Api\OptionRepositoryInterface - */ - protected $optionRepository; - - /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductAttributeRepositoryInterface */ - protected $productFactory; + private $productAttributeRepository; /** - * Type configurable factory - * - * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory + * @var ProductFactory */ - protected $typeConfigurableFactory; + private $productFactory; /** - * @param \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository - * @param \Magento\Catalog\Model\ProductFactory $productFactory - * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory $typeConfigurableFactory + * @param ProductAttributeRepositoryInterface $productAttributeRepository + * @param ProductFactory $productFactory */ public function __construct( - \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository, - \Magento\Catalog\Model\ProductFactory $productFactory, - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory $typeConfigurableFactory + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductFactory $productFactory ) { - $this->optionRepository = $optionRepository; + $this->productAttributeRepository = $productAttributeRepository; $this->productFactory = $productFactory; - $this->typeConfigurableFactory = $typeConfigurableFactory; } /** - * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject + * @param ProductRepositoryInterface $subject * @param callable $proceed - * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param ProductInterface $product * @param bool $saveOptions - * @return \Magento\Catalog\Api\Data\ProductInterface - * @throws \Magento\Framework\Exception\CouldNotSaveException + * @return ProductInterface + * @throws CouldNotSaveException + * @throws InputException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function aroundSave( - \Magento\Catalog\Api\ProductRepositoryInterface $subject, + ProductRepositoryInterface $subject, \Closure $proceed, - \Magento\Catalog\Api\Data\ProductInterface $product, + ProductInterface $product, $saveOptions = false ) { - /** @var \Magento\Catalog\Api\Data\ProductInterface $result */ + /** @var ProductInterface $result */ $result = $proceed($product, $saveOptions); - - if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) { + if ($product->getTypeId() !== Configurable::TYPE_CODE) { return $result; } - $extendedAttributes = $product->getExtensionAttributes(); - if ($extendedAttributes === null) { + $extensionAttributes = $result->getExtensionAttributes(); + if ($extensionAttributes === null) { return $result; } - $configurableProductOptions = $extendedAttributes->getConfigurableProductOptions(); - $configurableProductLinks = $extendedAttributes->getConfigurableProductLinks(); - if ($configurableProductOptions === null && $configurableProductLinks === null) { - return $result; - } - if ($configurableProductOptions !== null) { - $this->saveConfigurableProductOptions($result, $configurableProductOptions); - $result->getTypeInstance()->resetConfigurableAttributes($result); - } - if ($configurableProductLinks !== null) { - $this->saveConfigurableProductLinks($result, $configurableProductLinks); - } - return $subject->get($result->getSku(), false, $result->getStoreId(), true); - } - - /** - * @param \Magento\Catalog\Api\Data\ProductInterface $product - * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $options - * @return $this - */ - protected function saveConfigurableProductOptions( - \Magento\Catalog\Api\Data\ProductInterface $product, - array $options - ) { - $existingOptionIds = []; - if ($product->getExtensionAttributes() !== null) { - $extensionAttributes = $product->getExtensionAttributes(); - if ($extensionAttributes->getConfigurableProductOptions() !== null) { - $existingOptions = $extensionAttributes->getConfigurableProductOptions(); - foreach ($existingOptions as $option) { - $existingOptionIds[] = $option->getId(); - } - } - } - $updatedOptionIds = []; - foreach ($options as $option) { - if ($option->getId()) { - $updatedOptionIds[] = $option->getId(); - } - $this->optionRepository->save($product->getSku(), $option); - } + $configurableLinks = (array) $extensionAttributes->getConfigurableProductLinks(); + $configurableOptions = (array) $extensionAttributes->getConfigurableProductOptions(); - $optionIdsToDelete = array_diff($existingOptionIds, $updatedOptionIds); - foreach ($optionIdsToDelete as $optionId) { - $this->optionRepository->deleteById($product->getSku(), $optionId); + if (empty($configurableLinks) && empty($configurableOptions)) { + return $result; } - return $this; - } - /** - * @param \Magento\Catalog\Api\Data\ProductInterface $product - * @param int[] $linkIds - * @return $this - */ - protected function saveConfigurableProductLinks( - \Magento\Catalog\Api\Data\ProductInterface $product, - array $linkIds - ) { - $configurableProductTypeResource = $this->typeConfigurableFactory->create(); - if (!empty($linkIds)) { - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableProductType */ - $configurableProductType = $product->getTypeInstance(); - $configurableAttributes = $configurableProductType->getConfigurableAttributes($product); - $attributeCodes = []; - foreach ($configurableAttributes as $configurableAttribute) { - /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $productAttribute */ - $productAttribute = $configurableAttribute->getProductAttribute(); - $attributeCode = $productAttribute->getAttributeCode(); - $attributeCodes[] = $attributeCode; - } - $this->validateProductLinks($attributeCodes, $linkIds); + $attributeCodes = []; + /** @var OptionInterface $configurableOption */ + foreach ($configurableOptions as $configurableOption) { + $eavAttribute = $this->productAttributeRepository->get($configurableOption->getAttributeId()); + $attributeCode = $eavAttribute->getAttributeCode(); + $attributeCodes[] = $attributeCode; } + $this->validateProductLinks($attributeCodes, $configurableLinks); - $configurableProductTypeResource->saveProducts($product, $linkIds); - return $this; + return $subject->get($result->getSku(), false, $result->getStoreId(), true); } /** * @param array $attributeCodes * @param array $linkIds - * @throws InputException * @return $this + * @throws InputException */ - protected function validateProductLinks(array $attributeCodes, array $linkIds) + private function validateProductLinks(array $attributeCodes, array $linkIds) { $valueMap = []; - if (empty($attributeCodes) && !empty($linkIds)) { - throw new InputException( - __('The configurable product does not have any variation attribute.') - ); - } foreach ($linkIds as $productId) { $variation = $this->productFactory->create()->load($productId); - if (!$variation->getId()) { - throw new InputException(__('Product with id "%1" does not exist.', $productId)); - } $valueKey = ''; foreach ($attributeCodes as $attributeCode) { if (!$variation->getData($attributeCode)) { @@ -178,11 +111,14 @@ class AroundProductRepositorySave } if (isset($valueMap[$valueKey])) { throw new InputException( - __('Products "%1" and %2 have the same set of attribute values.', $productId, $valueMap[$valueKey]) + __( + 'Products "%1" and "%2" have the same set of attribute values.', + $productId, + $valueMap[$valueKey] + ) ); } $valueMap[$valueKey] = $productId; } - return $this; } } diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/ReadHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/ReadHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..87d67b7ca334f3979c27a885490d70bde50a6ac9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Product/ReadHandler.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Model\Product; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\ConfigurableProduct\Helper\Product\Options\Loader; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; + +/** + * Class ReadHandler + */ +class ReadHandler +{ + /** + * @var Loader + */ + private $optionLoader; + + /** + * ReadHandler constructor + * + * @param Loader $optionLoader + */ + public function __construct(Loader $optionLoader) + { + $this->optionLoader = $optionLoader; + } + + /** + * @param string $entityType + * @param ProductInterface $entity + * @return ProductInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute($entityType, $entity) + { + if ($entity->getTypeId() !== Configurable::TYPE_CODE) { + return $entity; + } + + $extensionAttributes = $entity->getExtensionAttributes(); + + $extensionAttributes->setConfigurableProductLinks($this->getLinkedProducts($entity)); + $extensionAttributes->setConfigurableProductOptions($this->optionLoader->load($entity)); + + $entity->setExtensionAttributes($extensionAttributes); + + return $entity; + } + + /** + * Get linked to configurable simple products + * + * @param ProductInterface $product + * @return int[] + */ + private function getLinkedProducts(ProductInterface $product) + { + /** @var Configurable $typeInstance */ + $typeInstance = $product->getTypeInstance(); + $childrenIds = $typeInstance->getChildrenIds($product->getId()); + + if (isset($childrenIds[0])) { + return $childrenIds[0]; + } else { + return []; + } + } +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/SaveHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/SaveHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..8a1c0c96b4c57f8234c62b7389e52d6ff57fb1cc --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Product/SaveHandler.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Model\Product; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\ConfigurableProduct\Api\OptionRepositoryInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable as ResourceModelConfigurable; + +/** + * Class SaveHandler + */ +class SaveHandler +{ + /** + * @var OptionRepositoryInterface + */ + private $optionRepository; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var ResourceModelConfigurable + */ + private $resourceModel; + + /** + * SaveHandler constructor + * + * @param ResourceModelConfigurable $resourceModel + * @param OptionRepositoryInterface $optionRepository + * @param ProductAttributeRepositoryInterface $productAttributeRepository + */ + public function __construct( + ResourceModelConfigurable $resourceModel, + OptionRepositoryInterface $optionRepository, + ProductAttributeRepositoryInterface $productAttributeRepository + ) { + $this->resourceModel = $resourceModel; + $this->optionRepository = $optionRepository; + $this->productAttributeRepository = $productAttributeRepository; + } + + /** + * @param string $entityType + * @param ProductInterface $entity + * @return ProductInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute($entityType, ProductInterface $entity) + { + if ($entity->getTypeId() !== Configurable::TYPE_CODE) { + return $entity; + } + + $extensionAttributes = $entity->getExtensionAttributes(); + if ($extensionAttributes === null) { + return $entity; + } + + $ids = []; + $configurableOptions = (array) $extensionAttributes->getConfigurableProductOptions(); + if (!empty($configurableOptions)) { + $ids = $this->saveConfigurableProductAttributes($entity, $configurableOptions); + } + + $configurableLinks = (array) $extensionAttributes->getConfigurableProductLinks(); + $this->resourceModel->saveProducts($entity, $configurableLinks); + if (empty($configurableLinks) || !empty($ids)) { + $this->deleteConfigurableProductAttributes($entity, $ids); + } + + return $entity; + } + + /** + * Save attributes for configurable product + * + * @param ProductInterface $product + * @param array $attributes + * @return array + */ + private function saveConfigurableProductAttributes(ProductInterface $product, array $attributes) + { + $ids = []; + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $attribute */ + foreach ($attributes as $attribute) { + $eavAttribute = $this->productAttributeRepository->get($attribute->getAttributeId()); + + $data = $attribute->getData(); + $attribute->loadByProductAndAttribute($product, $eavAttribute); + $attribute->setData(array_replace_recursive($attribute->getData(), $data)); + + $ids[] = $this->optionRepository->save($product->getSku(), $attribute); + } + + return $ids; + } + + /** + * Remove product attributes + * + * @param ProductInterface $product + * @param array $attributesIds + * @return void + */ + private function deleteConfigurableProductAttributes(ProductInterface $product, array $attributesIds) + { + $list = $this->optionRepository->getList($product->getSku()); + foreach ($list as $item) { + if (!in_array($item->getId(), $attributesIds)) { + $this->optionRepository->deleteById($product->getSku(), $item->getId()); + } + } + } +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 93f2588d214597ef54278c672963794b66d5d00b..727b090c73e5b1139436a539943954578ed22f35 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -5,6 +5,8 @@ */ namespace Magento\ConfigurableProduct\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Model\Entity\MetadataPool; use Magento\Catalog\Api\ProductRepositoryInterface; /** @@ -28,14 +30,14 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * * @var string */ - protected $_usedProductAttributeIds = '_cache_instance_used_product_attribute_ids'; + protected $usedProductAttributeIds = '_cache_instance_used_product_attribute_ids'; /** * Cache key for Used Product Attributes * * @var string */ - protected $_usedProductAttributes = '_cache_instance_used_product_attributes'; + protected $usedProductAttributes = '_cache_instance_used_product_attributes'; /** * Cache key for Used Attributes @@ -111,7 +113,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * * @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory */ - protected $_configurableAttributeFactory; + protected $configurableAttributeFactory; /** * Eav attribute factory @@ -125,13 +127,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory */ - protected $_typeConfigurableFactory; + protected $typeConfigurableFactory; /** * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface */ protected $extensionAttributesJoinProcessor; + /** + * @var MetadataPool + */ + private $metadataPool; + /** * @codingStandardsIgnoreStart/End * @@ -152,7 +159,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor - * + * @param MetadataPool $metadataPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -172,16 +179,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\CollectionFactory $attributeCollectionFactory, \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor + \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, + MetadataPool $metadataPool ) { - $this->_typeConfigurableFactory = $typeConfigurableFactory; + $this->typeConfigurableFactory = $typeConfigurableFactory; $this->_eavAttributeFactory = $eavAttributeFactory; - $this->_configurableAttributeFactory = $configurableAttributeFactory; + $this->configurableAttributeFactory = $configurableAttributeFactory; $this->_productCollectionFactory = $productCollectionFactory; $this->_attributeCollectionFactory = $attributeCollectionFactory; $this->_catalogProductTypeConfigurable = $catalogProductTypeConfigurable; $this->_scopeConfig = $scopeConfig; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; + $this->metadataPool = $metadataPool; parent::__construct( $catalogProductOption, @@ -260,6 +269,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * @param array $ids * @param \Magento\Catalog\Model\Product $product * @return \Magento\ConfigurableProduct\Model\Product\Type\Configurable + * @deprecated use \Magento\ConfigurableProduct\Model\Product\Type\Configurable::setUsedProductAttributes instead */ public function setUsedProductAttributeIds($ids, $product) { @@ -268,17 +278,37 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType foreach ($ids as $attributeId) { $usedProductAttributes[] = $this->getAttributeById($attributeId, $product); - $configurableAttributes[] = $this->_configurableAttributeFactory->create()->setProductAttribute( + $configurableAttributes[] = $this->configurableAttributeFactory->create()->setProductAttribute( $this->getAttributeById($attributeId, $product) ); } - $product->setData($this->_usedProductAttributes, $usedProductAttributes); - $product->setData($this->_usedProductAttributeIds, $ids); + $product->setData($this->usedProductAttributes, $usedProductAttributes); + $product->setData($this->usedProductAttributeIds, $ids); $product->setData($this->_configurableAttributes, $configurableAttributes); return $this; } + /** + * Set list of used attributes to product + * + * @param ProductInterface $product + * @param array $ids + * @return $this + */ + public function setUsedProductAttributes(ProductInterface $product, array $ids) + { + $usedProductAttributes = []; + + foreach ($ids as $attributeId) { + $usedProductAttributes[] = $this->getAttributeById($attributeId, $product); + } + $product->setData($this->usedProductAttributes, $usedProductAttributes); + $product->setData($this->usedProductAttributeIds, $ids); + + return $this; + } + /** * Retrieve identifiers of used product attributes * @@ -287,14 +317,14 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ public function getUsedProductAttributeIds($product) { - if (!$product->hasData($this->_usedProductAttributeIds)) { + if (!$product->hasData($this->usedProductAttributeIds)) { $usedProductAttributeIds = []; foreach ($this->getUsedProductAttributes($product) as $attribute) { $usedProductAttributeIds[] = $attribute->getId(); } - $product->setData($this->_usedProductAttributeIds, $usedProductAttributeIds); + $product->setData($this->usedProductAttributeIds, $usedProductAttributeIds); } - return $product->getData($this->_usedProductAttributeIds); + return $product->getData($this->usedProductAttributeIds); } /** @@ -305,7 +335,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ public function getUsedProductAttributes($product) { - if (!$product->hasData($this->_usedProductAttributes)) { + if (!$product->hasData($this->usedProductAttributes)) { $usedProductAttributes = []; $usedAttributes = []; foreach ($this->getConfigurableAttributes($product) as $attribute) { @@ -316,9 +346,9 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType } } $product->setData($this->_usedAttributes, $usedAttributes); - $product->setData($this->_usedProductAttributes, $usedProductAttributes); + $product->setData($this->usedProductAttributes, $usedProductAttributes); } - return $product->getData($this->_usedProductAttributes); + return $product->getData($this->usedProductAttributes); } /** @@ -529,57 +559,92 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * @param \Magento\Catalog\Model\Product $product * @return $this * @throws \InvalidArgumentException + * @deprecated the \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute should be used instead */ public function save($product) { parent::save($product); - /* Save attributes information */ + $extensionAttributes = $product->getExtensionAttributes(); + + // this approach is needed for 3rd-party extensions which are not using extension attributes + if (empty($extensionAttributes->getConfigurableProductOptions())) { + $this->saveConfigurableOptions($product); + } + + if (empty($extensionAttributes->getConfigurableProductLinks())) { + $this->saveRelatedProducts($product); + } + return $this; + } + + /** + * Save configurable product attributes + * + * @param ProductInterface $product + * @return void + * @throws \Exception + * @deprecated + */ + private function saveConfigurableOptions(ProductInterface $product) + { $data = $product->getConfigurableAttributesData(); - if ($data) { - foreach ($data as $attributeData) { - /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */ - $configurableAttribute = $this->_configurableAttributeFactory->create(); - if (!$product->getIsDuplicate()) { - if (!empty($attributeData['id'])) { - $configurableAttribute->load($attributeData['id']); - $attributeData['attribute_id'] = $configurableAttribute->getAttributeId(); - } elseif (!empty($attributeData['attribute_id'])) { - $attribute = $this->_eavConfig->getAttribute( - \Magento\Catalog\Model\Product::ENTITY, $attributeData['attribute_id'] + if (!$data) { + return; + } + + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + foreach ($data as $attributeData) { + /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */ + $configurableAttribute = $this->configurableAttributeFactory->create(); + if (!$product->getIsDuplicate()) { + if (!empty($attributeData['id'])) { + $configurableAttribute->load($attributeData['id']); + $attributeData['attribute_id'] = $configurableAttribute->getAttributeId(); + } elseif (!empty($attributeData['attribute_id'])) { + $attribute = $this->_eavConfig->getAttribute( + \Magento\Catalog\Model\Product::ENTITY, $attributeData['attribute_id'] + ); + $attributeData['attribute_id'] = $attribute->getId(); + if (!$this->canUseAttribute($attribute)) { + throw new \InvalidArgumentException( + 'Provided attribute can not be used with configurable product' ); - $attributeData['attribute_id'] = $attribute->getId(); - if (!$this->canUseAttribute($attribute)) { - throw new \InvalidArgumentException( - 'Provided attribute can not be used with configurable product' - ); - } - $configurableAttribute->loadByProductAndAttribute($product, $attribute); } + $configurableAttribute->loadByProductAndAttribute($product, $attribute); } - unset($attributeData['id']); - $configurableAttribute - ->addData($attributeData) - ->setStoreId($product->getStoreId()) - ->setProductId($product->getId()) - ->save(); } - /** @var $configurableAttributesCollection \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection */ - $configurableAttributesCollection = $this->_attributeCollectionFactory->create(); - $configurableAttributesCollection->setProductFilter($product); - $configurableAttributesCollection->addFieldToFilter( - 'attribute_id', - ['nin' => $this->getUsedProductAttributeIds($product)] - ); - $configurableAttributesCollection->walk('delete'); + unset($attributeData['id']); + $configurableAttribute + ->addData($attributeData) + ->setStoreId($product->getStoreId()) + ->setProductId($product->getData($metadata->getLinkField())) + ->save(); } + /** @var $configurableAttributesCollection \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection */ + $configurableAttributesCollection = $this->_attributeCollectionFactory->create(); + $configurableAttributesCollection->setProductFilter($product); + $configurableAttributesCollection->addFieldToFilter( + 'attribute_id', + ['nin' => $this->getUsedProductAttributeIds($product)] + ); + $configurableAttributesCollection->walk('delete'); + } - /* Save product relations */ + /** + * Save related products + * + * @param ProductInterface $product + * @return void + * @deprecated + */ + private function saveRelatedProducts(ProductInterface $product) + { $productIds = $product->getAssociatedProductIds(); if (is_array($productIds)) { - $this->_typeConfigurableFactory->create()->saveProducts($product, $productIds); + $this->typeConfigurableFactory->create()->saveProducts($product, $productIds); } - return $this; } /** @@ -639,9 +704,11 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType foreach ($attributesInfo as $attributeId => $attributeValue) { $productCollection->addAttributeToFilter($attributeId, $attributeValue); } + /** @var \Magento\Catalog\Model\Product $productObject */ $productObject = $productCollection->getFirstItem(); - if ($productObject->getId()) { - return $this->productRepository->getById($productObject->getId()); + $productLinkFieldId = $productObject->getId(); + if ($productLinkFieldId) { + return $this->productRepository->getById($productLinkFieldId); } foreach ($this->getUsedProducts($product) as $productObject) { @@ -749,9 +816,10 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType } if ($subProduct) { + $subProductLinkFieldId = $subProduct->getId(); $product->addCustomOption('attributes', serialize($attributes)); - $product->addCustomOption('product_qty_' . $subProduct->getId(), 1, $subProduct); - $product->addCustomOption('simple_product', $subProduct->getId(), $subProduct); + $product->addCustomOption('product_qty_' . $subProductLinkFieldId, 1, $subProduct); + $product->addCustomOption('simple_product', $subProductLinkFieldId, $subProduct); $_result = $subProduct->getTypeInstance()->processConfiguration( $buyRequest, @@ -779,12 +847,9 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType } } - $_result[0]->setParentProductId( - $product->getId() - )->addCustomOption( - 'parent_product_id', - $product->getId() - ); + $productLinkFieldId = $product->getId(); + $_result[0]->setParentProductId($productLinkFieldId) + ->addCustomOption('parent_product_id', $productLinkFieldId); if ($this->_isStrictProcessMode($processMode)) { $_result[0]->setCartQty(1); } @@ -1016,9 +1081,9 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product) { - $this->_typeConfigurableFactory->create()->saveProducts($product, []); + $this->typeConfigurableFactory->create()->saveProducts($product, []); /** @var $configurableAttribute \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute */ - $configurableAttribute = $this->_configurableAttributeFactory->create(); + $configurableAttribute = $this->configurableAttributeFactory->create(); $configurableAttribute->deleteByProduct($product); } diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php index 29b866880c2906fbe25d49b284251bd7454b18e8..46579e63d12c104e662cf4da26afb3429906325b 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php @@ -7,6 +7,9 @@ */ namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Catalog\Api\Data\ProductInterface; /** * @method Attribute _getResource() @@ -28,6 +31,43 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme const KEY_PRODUCT_ID = 'product_id'; /**#@-*/ + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @param \Magento\Framework\Model\Context $context + * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + * @param AttributeValueFactory $customAttributeFactory + * @param MetadataPool $metadataPool + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param array $data + */ + public function __construct( + \Magento\Framework\Model\Context $context, + \Magento\Framework\Registry $registry, + \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, + AttributeValueFactory $customAttributeFactory, + MetadataPool $metadataPool, + \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, + \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $data = [] + ) { + $this->metadataPool = $metadataPool; + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $resource, + $resourceCollection, + $data + ); + } + /** * Initialize resource model * @@ -47,6 +87,7 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme { return $this->getData('options'); } + /** * {@inheritdoc} */ @@ -82,7 +123,12 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme */ public function loadByProductAndAttribute($product, $attribute) { - $id = $this->_getResource()->getIdByProductIdAndAttributeId($this, $product->getId(), $attribute->getId()); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $id = $this->_getResource()->getIdByProductIdAndAttributeId( + $this, + $product->getData($metadata->getLinkField()), + $attribute->getId() + ); if ($id) { $this->load($id); } @@ -96,7 +142,8 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme */ public function deleteByProduct($product) { - $this->_getResource()->deleteAttributesByProductId($product->getId()); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $this->_getResource()->deleteAttributesByProductId($product->getData($metadata->getLinkField())); } /** @@ -136,6 +183,7 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme } //@codeCoverageIgnoreStart + /** * @param string $attributeId * @return $this diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php index 1bab6a2fb8d299a9d566151c19c63e4580353686..82fb0c2f227bfb93a1f7ca4a68501fd0d2ee87a6 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php @@ -5,8 +5,8 @@ */ namespace Magento\ConfigurableProduct\Model\Product; -use Magento\Framework\Exception\LocalizedException; use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Framework\Exception\LocalizedException; /** * Variation Handler diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php index 1df170963270445015e77f43fa01add88f24feec..4a2233b76a891ab4b9593258c7ef60bce00da763 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php @@ -25,6 +25,7 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer */ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false) { + $metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $connection = $this->getConnection(); $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable(); $select = $connection->select()->from(['e' => $this->getTable('catalog_product_entity')], ['entity_id']); @@ -42,7 +43,7 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer [] )->joinLeft( ['l' => $this->getTable('catalog_product_super_link')], - 'l.parent_id = e.entity_id', + 'l.parent_id = e.' . $metadata->getLinkField(), [] )->join( ['le' => $this->getTable('catalog_product_entity')], @@ -62,7 +63,6 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer )->group( ['e.entity_id', 'cw.website_id', 'cis.stock_id'] ); - $metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $psExpr = $this->_addAttributeToSelect($select, 'status', 'e.' . $metadata->getLinkField(), 'cs.store_id'); $psCond = $connection->quoteInto($psExpr . '=?', ProductStatus::STATUS_ENABLED); diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php index 84395f7f5b138fd8ff1feb7832a6adf14994c0e2..97b2a0bbb75290dd3ea9dd87d569cbf14dabba01 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php @@ -7,6 +7,8 @@ */ namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price; +use Magento\Catalog\Api\Data\ProductInterface; + class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice { /** @@ -69,23 +71,40 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ */ private function getRelatedProducts($entityIds) { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $select = $this->getConnection()->select()->union( [ $this->getConnection()->select() - ->from($this->getTable('catalog_product_super_link'), 'parent_id') - ->where('parent_id in (?)', $entityIds), + ->from( + ['e' => $this->getTable('catalog_product_entity')], + 'e.entity_id' + )->join( + ['cpsl' => $this->getTable('catalog_product_super_link')], + 'cpsl.parent_id = e.' . $metadata->getLinkField(), + [] + )->where( + 'e.entity_id IN (?)', + $entityIds + ), $this->getConnection()->select() - ->from($this->getTable('catalog_product_super_link'), 'product_id') - ->where('parent_id in (?)', $entityIds), + ->from( + ['cpsl' => $this->getTable('catalog_product_super_link')], + 'cpsl.product_id' + )->join( + ['e' => $this->getTable('catalog_product_entity')], + 'cpsl.parent_id = e.' . $metadata->getLinkField(), + [] + )->where( + 'e.entity_id IN (?)', + $entityIds + ), $this->getConnection()->select() ->from($this->getTable('catalog_product_super_link'), 'product_id') ->where('product_id in (?)', $entityIds), ] ); - return array_map( - 'intval', - $this->getConnection()->fetchCol($select) - ); + + return array_map('intval', $this->getConnection()->fetchCol($select)); } /** @@ -139,6 +158,7 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ */ protected function _applyConfigurableOption() { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $connection = $this->getConnection(); $coaTable = $this->_getConfigurableOptionAggregateTable(); $copTable = $this->_getConfigurableOptionPriceTable(); @@ -149,10 +169,14 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ $select = $connection->select()->from( ['i' => $this->_getDefaultFinalPriceTable()], [] + )->join( + ['e' => $this->getTable('catalog_product_entity')], + 'e.entity_id = i.entity_id', + ['parent_id' => 'e.entity_id'] )->join( ['l' => $this->getTable('catalog_product_super_link')], - 'l.parent_id = i.entity_id', - ['parent_id', 'product_id'] + 'l.parent_id = e.' . $metadata->getLinkField(), + ['product_id'] )->columns( ['customer_group_id', 'website_id'], 'i' @@ -163,7 +187,7 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ )->where( 'le.required_options=0' )->group( - ['l.parent_id', 'i.customer_group_id', 'i.website_id', 'l.product_id'] + ['parent_id', 'i.customer_group_id', 'i.website_id', 'l.product_id'] ); $priceColumn = $this->_addAttributeToSelect($select, 'price', 'l.product_id', 0, null, true); $tierPriceColumn = $connection->getCheckSql("MIN(i.tier_price) IS NOT NULL", "i.tier_price", 'NULL'); diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php index 8dda060c4f75ff4428bb4ebede6985265c61def2..5d7d9acce0c9b37aac3a5895a3122ab8c1654ce8 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php @@ -8,6 +8,7 @@ namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\ConfigurableProduct\Api\Data\OptionInterface; use Magento\Framework\Model\Entity\MetadataPool; class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb @@ -17,12 +18,12 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * * @var \Magento\Catalog\Model\ResourceModel\Product\Relation */ - protected $_catalogProductRelation; + protected $catalogProductRelation; /** * @var MetadataPool */ - protected $metadataPool; + private $metadataPool; /** * @param \Magento\Framework\Model\ResourceModel\Db\Context $context @@ -36,7 +37,7 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb MetadataPool $metadataPool, $connectionName = null ) { - $this->_catalogProductRelation = $catalogProductRelation; + $this->catalogProductRelation = $catalogProductRelation; $this->metadataPool = $metadataPool; parent::__construct($context, $connectionName); } @@ -51,6 +52,27 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb $this->_init('catalog_product_super_link', 'link_id'); } + /** + * Get product entity id by product attribute + * + * @param OptionInterface $option + * @return int + */ + public function getEntityIdByAttribute(OptionInterface $option) + { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $this->getConnection()->select()->from( + ['e' => $this->getTable('catalog_product_entity')], + ['e.entity_id'] + )->where( + 'e.' . $metadata->getLinkField() . '=?', + $option->getProductId() + )->limit(1); + + return (int) $this->getConnection()->fetchOne($select); + } + /** * Save configurable product relations * @@ -58,39 +80,37 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * @param array $productIds the children id array * @return $this */ - public function saveProducts($mainProduct, $productIds) + public function saveProducts($mainProduct, array $productIds) { - $isProductInstance = false; - if ($mainProduct instanceof \Magento\Catalog\Model\Product) { - $mainProductId = $mainProduct->getId(); - $isProductInstance = true; - } - $old = []; - if (!$mainProduct->getIsDuplicate()) { - $old = $mainProduct->getTypeInstance()->getUsedProductIds($mainProduct); + if (!$mainProduct instanceof ProductInterface) { + return $this; } - $insert = array_diff($productIds, $old); - $delete = array_diff($old, $productIds); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $productId = $mainProduct->getData($metadata->getLinkField()); - if ((!empty($insert) || !empty($delete)) && $isProductInstance) { - $mainProduct->setIsRelationsChanged(true); + $data = []; + foreach ($productIds as $id) { + $data[] = ['product_id' => (int) $id, 'parent_id' => (int) $productId]; } - if (!empty($delete)) { - $where = ['parent_id = ?' => $mainProductId, 'product_id IN(?)' => $delete]; - $this->getConnection()->delete($this->getMainTable(), $where); + if (!empty($data)) { + $this->getConnection()->insertOnDuplicate( + $this->getMainTable(), + $data, + ['product_id', 'parent_id'] + ); } - if (!empty($insert)) { - $data = []; - foreach ($insert as $childId) { - $data[] = ['product_id' => (int)$childId, 'parent_id' => (int)$mainProductId]; - } - $this->getConnection()->insertMultiple($this->getMainTable(), $data); + + $where = ['parent_id = ?' => $productId]; + if (!empty($productIds)) { + $where['product_id NOT IN(?)'] = $productIds; } - $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + + $this->getConnection()->delete($this->getMainTable(), $where); + // configurable product relations should be added to relation table - $this->_catalogProductRelation->processRelations($mainProduct->getData($linkField), $productIds); + $this->catalogProductRelation->processRelations($productId, $productIds); return $this; } @@ -108,15 +128,16 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ public function getChildrenIds($parentId, $required = true) { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $select = $this->getConnection()->select()->from( - ['l' => $this->getMainTable()], - ['product_id', 'parent_id'] - )->join( ['e' => $this->getTable('catalog_product_entity')], - 'e.entity_id = l.product_id AND e.required_options = 0', + ['l.product_id'] + )->join( + ['l' => $this->getMainTable()], + 'l.parent_id = e.' . $metadata->getLinkField(), [] )->where( - 'parent_id IN (?)', + 'e.entity_id IN (?) AND e.required_options = 0', $parentId ); @@ -129,7 +150,7 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb } /** - * Retrieve parent ids array by requered child + * Retrieve parent ids array by required child * * @param int|array $childId * @return string[] @@ -138,15 +159,19 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { $parentIds = []; - $select = $this->getConnection()->select()->from( - $this->getMainTable(), - ['product_id', 'parent_id'] - )->where( - 'product_id IN(?)', - $childId - ); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $this->getConnection() + ->select() + ->from(['l' => $this->getMainTable()], []) + ->join( + ['e' => $this->getTable('catalog_product_entity')], + 'e.' . $metadata->getLinkField() . ' = l.parent_id', + ['e.entity_id'] + )->where('l.product_id IN(?)', $childId); + foreach ($this->getConnection()->fetchAll($select) as $row) { - $parentIds[] = $row['parent_id']; + $parentIds[] = $row['entity_id']; } return $parentIds; @@ -162,16 +187,22 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb public function getConfigurableOptions($product, $attributes) { $attributesOptionsData = []; + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $productLinkFieldId = $product->getData($metadata->getLinkField()); foreach ($attributes as $superAttribute) { $select = $this->getConnection()->select()->from( ['super_attribute' => $this->getTable('catalog_product_super_attribute')], [ 'sku' => 'entity.sku', - 'product_id' => 'super_attribute.product_id', + 'product_id' => 'product_entity.entity_id', 'attribute_code' => 'attribute.attribute_code', 'option_title' => 'option_value.value', 'super_attribute_label' => 'attribute_label.value', ] + )->joinInner( + ['product_entity' => $this->getTable('catalog_product_entity')], + 'product_entity.' . $metadata->getLinkField() . ' = super_attribute.product_id', + [] )->joinInner( ['product_link' => $this->getTable('catalog_product_super_link')], 'product_link.parent_id = super_attribute.product_id', @@ -217,7 +248,7 @@ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb [] )->where( 'super_attribute.product_id = ?', - $product->getId() + $productLinkFieldId ); $attributesOptionsData[$superAttribute->getAttributeId()] = $this->getConnection()->fetchAll($select); diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php index dbca1777f450b4c810baa4bc2181fbee1665ac96..0a11e18b822e0007eefed9205f6f04b27adb70dd 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php @@ -8,6 +8,8 @@ namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Catalog\Api\Data\ProductInterface; /** * @SuppressWarnings(PHPMD.LongVariable) @@ -50,6 +52,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab */ protected $_storeManager; + /** + * @var MetadataPool + */ + private $metadataPool; + /** * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger @@ -59,6 +66,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $catalogProductTypeConfigurable * @param \Magento\Catalog\Helper\Data $catalogData * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute $resource + * @param MetadataPool $metadataPool * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -71,11 +79,13 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab \Magento\ConfigurableProduct\Model\Product\Type\Configurable $catalogProductTypeConfigurable, \Magento\Catalog\Helper\Data $catalogData, \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute $resource, + MetadataPool $metadataPool, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->_storeManager = $storeManager; $this->_productTypeConfigurable = $catalogProductTypeConfigurable; $this->_catalogData = $catalogData; + $this->metadataPool = $metadataPool; parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource); } @@ -101,8 +111,9 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab */ public function setProductFilter($product) { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $this->_product = $product; - return $this->addFieldToFilter('product_id', $product->getId()); + return $this->addFieldToFilter('product_id', $product->getData($metadata->getLinkField())); } /** diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php index 7b73e1eabc5debe5e23f072378c9d139075cdf84..a88f34db5fe07dad06bb2503807ae27d9c9289ce 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php @@ -7,6 +7,15 @@ */ namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Class Collection + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { /** @@ -16,6 +25,87 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ protected $_linkTable; + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Eav\Model\Config $eavConfig + * @param \Magento\Framework\App\ResourceConnection $resource + * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory + * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper + * @param \Magento\Framework\Validator\UniversalFactory $universalFactory + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Module\Manager $moduleManager + * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory + * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Framework\Stdlib\DateTime $dateTime + * @param GroupManagementInterface $groupManagement + * @param \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation + * @param MetadataPool $metadataPool + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + \Magento\Framework\Data\Collection\EntityFactory $entityFactory, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Eav\Model\Config $eavConfig, + \Magento\Framework\App\ResourceConnection $resource, + \Magento\Eav\Model\EntityFactory $eavEntityFactory, + \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, + \Magento\Framework\Validator\UniversalFactory $universalFactory, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Module\Manager $moduleManager, + \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, + \Magento\Catalog\Model\ResourceModel\Url $catalogUrl, + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, + \Magento\Customer\Model\Session $customerSession, + \Magento\Framework\Stdlib\DateTime $dateTime, + GroupManagementInterface $groupManagement, + \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation $productLimitation, + MetadataPool $metadataPool, + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null + ) { + $this->metadataPool = $metadataPool; + parent::__construct( + $entityFactory, + $logger, + $fetchStrategy, + $eventManager, + $eavConfig, + $resource, + $eavEntityFactory, + $resourceHelper, + $universalFactory, + $storeManager, + $moduleManager, + $catalogProductFlatState, + $scopeConfig, + $productOptionFactory, + $catalogUrl, + $localeDate, + $customerSession, + $dateTime, + $groupManagement, + $productLimitation, + $connection + ); + } + /** * Assign link table name * @@ -34,6 +124,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection protected function _initSelect() { parent::_initSelect(); + $this->getSelect()->join( ['link_table' => $this->_linkTable], 'link_table.product_id = e.entity_id', @@ -51,7 +142,9 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ public function setProductFilter($product) { - $this->getSelect()->where('link_table.parent_id = ?', (int)$product->getId()); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $this->getSelect()->where('link_table.parent_id = ?', $product->getData($metadata->getLinkField())); return $this; } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php index 7537e2b7fcb2a4e33453f0a596ee020996e5cb61..b30b7d24e8f19f3000ab9f55dda252775e79ee0a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php @@ -158,15 +158,6 @@ class PluginTest extends \PHPUnit_Framework_TestCase )->will( $this->returnSelf() ); - $this->configurableTypeMock->expects( - $this->once() - )->method( - 'setUsedProductAttributeIds' - )->with( - ['attributes'] - )->will( - $this->returnSelf() - ); $this->productMock->expects( $this->once() )->method( diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php index 5f71035b2a93609572514060626535915ff2ddb8..96c648bb4df989878eebdefd98a31042c97a8c48 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php @@ -5,176 +5,254 @@ */ namespace Magento\ConfigurableProduct\Test\Unit\Controller\Adminhtml\Product\Initialization\Helper\Plugin; -use \Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Configurable; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product; +use Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Configurable; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableProduct; +use Magento\ConfigurableProduct\Model\Product\VariationHandler; +use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes; +use Magento\Framework\App\Request\Http; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Class ConfigurableTest + */ class ConfigurableTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Configurable + * @var VariationHandler|MockObject */ - protected $plugin; + private $variationHandler; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ - protected $productTypeMock; + private $request; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Factory|MockObject */ - protected $requestMock; + private $optionFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $productMock; + private $product; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Helper|MockObject */ - protected $variationHandler; + private $subject; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Configurable */ - protected $subjectMock; + private $plugin; + /** + * @inheritdoc + */ protected function setUp() { - $this->productTypeMock = $this->getMock( - 'Magento\ConfigurableProduct\Model\Product\Type\Configurable', - [], - [], - '', - false - ); - $this->variationHandler = $this->getMock( - 'Magento\ConfigurableProduct\Model\Product\VariationHandler', - [], - [], - '', - false - ); - $this->requestMock = $this->getMock('\Magento\Framework\App\Request\Http', [], [], '', false); - $methods = [ - 'setNewVariationsAttributeSetId', - 'setAssociatedProductIds', - 'setCanSaveConfigurableAttributes', - 'getTypeId', - '__wakeup', - ]; - $this->productMock = $this->getMock('Magento\Catalog\Model\Product', $methods, [], '', false); - $this->subjectMock = $this->getMock( - 'Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper', - [], - [], - '', - false + $this->variationHandler = $this->getMockBuilder(VariationHandler::class) + ->disableOriginalConstructor() + ->setMethods(['generateSimpleProducts']) + ->getMock(); + + $this->request = $this->getMockBuilder(Http::class) + ->disableOriginalConstructor() + ->setMethods(['getParam', 'getPost']) + ->getMock(); + + $this->optionFactory = $this->getMockBuilder(Factory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getTypeId', 'setAttributeSetId', 'getExtensionAttributes', 'setNewVariationsAttributeSetId', + 'setCanSaveConfigurableAttributes', 'setExtensionAttributes' + ]) + ->getMock(); + + $this->subject = $this->getMockBuilder(Helper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->plugin = new Configurable( + $this->variationHandler, + $this->request, + $this->optionFactory ); - $this->plugin = new Configurable($this->variationHandler, $this->productTypeMock, $this->requestMock); } - public function testAfterInitializeIfAttributesNotEmptyAndActionNameNotGenerateVariations() + /** + * @covers Configurable::afterInitialize + */ + public function testAfterInitializeWithAttributesAndVariations() { - $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE); - $associatedProductIds = ['key' => 'value']; - $generatedProductIds = ['key_one' => 'value_one']; - $expectedArray = ['key' => 'value', 'key_one' => 'value_one']; - $attributes = ['key' => 'value']; - $postValue = 'postValue'; - $postValueMap = [ - ['new-variations-attribute-set-id', null, $postValue], - ['associated_product_ids', [], $associatedProductIds], - ['affect_configurable_product_attributes', null, $postValue], + $attributes = [ + ['attribute_id' => 90, 'values' => [ + ['value_index' => 12], ['value_index' => 13] + ]] ]; - $this->requestMock->expects($this->any())->method('getPost')->will($this->returnValueMap($postValueMap)); - + $valueMap = [ + ['new-variations-attribute-set-id', null, 24], + ['associated_product_ids', [], []], + ['product', [], ['configurable_attributes_data' => $attributes]], + ]; + $simpleProductsIds = [1, 2, 3]; + $simpleProducts = ['simple1', 'simple2', 'simple3']; $paramValueMap = [ - ['variations-matrix', [], $postValue], + ['variations-matrix', [], $simpleProducts], ['attributes', null, $attributes], ]; - $this->requestMock->expects($this->any())->method('getParam')->will($this->returnValueMap($paramValueMap)); - $this->productTypeMock->expects( - $this->once() - )->method( - 'setUsedProductAttributeIds' - )->with( - $attributes, - $this->productMock - ); - $this->productMock->expects($this->once())->method('setNewVariationsAttributeSetId')->with($postValue); - $this->variationHandler->expects( - $this->once() - )->method( - 'generateSimpleProducts' - )->with( - $this->productMock, - $postValue - )->will( - $this->returnValue($generatedProductIds) - ); - $this->productMock->expects($this->once())->method('setAssociatedProductIds')->with($expectedArray); - $this->productMock->expects($this->once())->method('setCanSaveConfigurableAttributes')->with(true); - $this->plugin->afterInitialize($this->subjectMock, $this->productMock); + + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(ConfigurableProduct::TYPE_CODE); + + $this->request->expects(static::any()) + ->method('getPost') + ->willReturnMap($valueMap); + + $this->request->expects(static::any()) + ->method('getParam') + ->willReturnMap($paramValueMap); + + $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + ->disableOriginalConstructor() + ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) + ->getMockForAbstractClass(); + $this->product->expects(static::once()) + ->method('getExtensionAttributes') + ->willReturn($extensionAttributes); + + $this->optionFactory->expects(static::once()) + ->method('create') + ->with($attributes) + ->willReturn($attributes); + + $extensionAttributes->expects(static::once()) + ->method('setConfigurableProductOptions') + ->with($attributes); + + $this->variationHandler->expects(static::once()) + ->method('generateSimpleProducts') + ->with($this->product, $simpleProducts) + ->willReturn($simpleProductsIds); + + $extensionAttributes->expects(static::once()) + ->method('setConfigurableProductLinks') + ->with($simpleProductsIds); + + $this->product->expects(static::once()) + ->method('setExtensionAttributes') + ->with($extensionAttributes); + + $this->plugin->afterInitialize($this->subject, $this->product); } - public function testAfterInitializeIfAttributesNotEmptyAndActionNameGenerateVariations() + /** + * @covers Configurable::afterInitialize + */ + public function testAfterInitializeWithAttributesAndWithoutVariations() { - $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE); - $associatedProductIds = ['key' => 'value']; - $attributes = ['key' => 'value']; - $postValue = 'postValue'; + $attributes = [ + ['attribute_id' => 90, 'values' => [ + ['value_index' => 12], ['value_index' => 13] + ]] + ]; $valueMap = [ - ['new-variations-attribute-set-id', null, $postValue], - ['associated_product_ids', [], $associatedProductIds], - ['affect_configurable_product_attributes', null, $postValue], + ['new-variations-attribute-set-id', null, 24], + ['associated_product_ids', [], []], + ['product', [], ['configurable_attributes_data' => $attributes]], ]; - $this->requestMock->expects($this->any())->method('getPost')->will($this->returnValueMap($valueMap)); $paramValueMap = [ ['variations-matrix', [], []], ['attributes', null, $attributes], ]; - $this->requestMock->expects($this->any())->method('getParam')->will($this->returnValueMap($paramValueMap)); - $this->productTypeMock->expects( - $this->once() - )->method( - 'setUsedProductAttributeIds' - )->with( - $attributes, - $this->productMock - ); - $this->productMock->expects($this->once())->method('setNewVariationsAttributeSetId')->with($postValue); - $this->productTypeMock->expects($this->never())->method('generateSimpleProducts'); - $this->productMock->expects($this->once())->method('setAssociatedProductIds')->with($associatedProductIds); - $this->productMock->expects($this->once())->method('setCanSaveConfigurableAttributes')->with(true); - $this->plugin->afterInitialize($this->subjectMock, $this->productMock); + + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(ConfigurableProduct::TYPE_CODE); + + $this->request->expects(static::any()) + ->method('getPost') + ->willReturnMap($valueMap); + + $this->request->expects(static::any()) + ->method('getParam') + ->willReturnMap($paramValueMap); + + $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + ->disableOriginalConstructor() + ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) + ->getMockForAbstractClass(); + $this->product->expects(static::once()) + ->method('getExtensionAttributes') + ->willReturn($extensionAttributes); + + $this->optionFactory->expects(static::once()) + ->method('create') + ->with($attributes) + ->willReturn($attributes); + + $extensionAttributes->expects(static::once()) + ->method('setConfigurableProductOptions') + ->with($attributes); + + $this->variationHandler->expects(static::never()) + ->method('generateSimpleProducts'); + + $extensionAttributes->expects(static::once()) + ->method('setConfigurableProductLinks'); + + $this->product->expects(static::once()) + ->method('setExtensionAttributes') + ->with($extensionAttributes); + + $this->plugin->afterInitialize($this->subject, $this->product); } + /** + * @covers Configurable::afterInitialize + */ public function testAfterInitializeIfAttributesEmpty() { - $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE); - $this->requestMock->expects( - $this->once() - )->method( - 'getParam' - )->with( - 'attributes' - )->will( - $this->returnValue([]) - ); - $this->productTypeMock->expects($this->never())->method('setUsedProductAttributeIds'); - $this->requestMock->expects($this->never())->method('getPost'); - $this->productTypeMock->expects($this->never())->method('generateSimpleProducts'); - $this->plugin->afterInitialize($this->subjectMock, $this->productMock); + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(ConfigurableProduct::TYPE_CODE); + $this->request->expects(static::once()) + ->method('getParam') + ->with('attributes') + ->willReturn([]); + $this->product->expects(static::never()) + ->method('getExtensionAttributes'); + $this->request->expects(static::once()) + ->method('getPost'); + $this->variationHandler->expects(static::never()) + ->method('generateSimpleProducts'); + $this->plugin->afterInitialize($this->subject, $this->product); } + /** + * @covers Configurable::afterInitialize + */ public function testAfterInitializeForNotConfigurableProduct() { - $this->productMock->expects($this->once())->method('getTypeId')->willReturn('non-configurable'); - $this->productTypeMock->expects($this->never())->method('setUsedProductAttributeIds'); - $this->requestMock->expects($this->never())->method('getPost'); - $this->productTypeMock->expects($this->never())->method('generateSimpleProducts'); - $this->plugin->afterInitialize($this->subjectMock, $this->productMock); + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn('non-configurable'); + $this->product->expects(static::never()) + ->method('getExtensionAttributes'); + $this->request->expects(static::once()) + ->method('getPost'); + $this->variationHandler->expects(static::never()) + ->method('generateSimpleProducts'); + $this->plugin->afterInitialize($this->subject, $this->product); } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/FactoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/FactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..499f992aab0af76a7628f768bf7d2b7c56b140b7 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/FactoryTest.php @@ -0,0 +1,179 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Helper\Product\Options; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; +use Magento\ConfigurableProduct\Api\Data\OptionValueInterface; +use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class FactoryTest + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Configurable|MockObject + */ + private $configurable; + + /** + * @var AttributeFactory|MockObject + */ + private $attributeFactory; + + /** + * @var OptionValueInterfaceFactory|MockObject + */ + private $optionValueFactory; + + /** + * @var ProductAttributeRepositoryInterface|MockObject + */ + private $productAttributeRepository; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Factory + */ + private $factory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->configurable = $this->getMockBuilder(Configurable::class) + ->disableOriginalConstructor() + ->setMethods(['canUseAttribute']) + ->getMock(); + + $this->attributeFactory = $this->getMockBuilder(AttributeFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->optionValueFactory = $this->getMockBuilder(OptionValueInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->productAttributeRepository = $this->getMock(ProductAttributeRepositoryInterface::class); + + $this->factory = new Factory( + $this->configurable, + $this->attributeFactory, + $this->optionValueFactory, + $this->productAttributeRepository + ); + } + + /** + * @covers \Magento\ConfigurableProduct\Helper\Product\Options\Factory::create + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Provided attribute can not be used with configurable product. + */ + public function testCreateWithException() + { + $attributeId = 90; + $data = [ + ['attribute_id' => $attributeId, 'values' => [ + ['value_index' => 12], ['value_index' => 13] + ]] + ]; + + $attribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->setMethods(['setValues', 'getData']) + ->getMock(); + + $this->attributeFactory->expects(static::once()) + ->method('create') + ->willReturn($attribute); + + $eavAttribute = $this->getMockBuilder(EavAttribute::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productAttributeRepository->expects(static::once()) + ->method('get') + ->with($attributeId) + ->willReturn($eavAttribute); + + $this->configurable->expects(static::once()) + ->method('canUseAttribute') + ->with($eavAttribute) + ->willReturn(false); + + $this->factory->create($data); + } + + /** + * @covers \Magento\ConfigurableProduct\Helper\Product\Options\Factory::create + */ + public function testCreate() + { + $attributeId = 90; + $valueIndex = 12; + $item = ['attribute_id' => $attributeId, 'values' => [['value_index' => $valueIndex]]]; + $data = [$item]; + + $attribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->setMethods(['setValues', 'setData', '__wakeup']) + ->getMock(); + + $this->attributeFactory->expects(static::once()) + ->method('create') + ->willReturn($attribute); + + $eavAttribute = $this->getMockBuilder(EavAttribute::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productAttributeRepository->expects(static::once()) + ->method('get') + ->with($attributeId) + ->willReturn($eavAttribute); + + $this->configurable->expects(static::once()) + ->method('canUseAttribute') + ->with($eavAttribute) + ->willReturn(true); + + $option = $this->getMock(OptionValueInterface::class); + $option->expects(static::once()) + ->method('setValueIndex') + ->with($valueIndex) + ->willReturnSelf(); + $this->optionValueFactory->expects(static::once()) + ->method('create') + ->willReturn($option); + + $attribute->expects(static::once()) + ->method('setData') + ->with($item); + + $attribute->expects(static::once()) + ->method('setValues') + ->with([$option]); + + $result = $this->factory->create($data); + static::assertSame([$attribute], $result); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b0f840ac224c24d600e328aa459213e847ee5365 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/Product/Options/LoaderTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Helper\Product\Options; + +use Magento\Catalog\Model\Product; +use Magento\ConfigurableProduct\Api\Data\OptionValueInterface; +use Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Loader; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class LoaderTest + */ +class LoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var OptionValueInterfaceFactory|MockObject + */ + private $optionValueFactory; + + /** + * @var Product|MockObject + */ + private $product; + + /** + * @var Configurable|MockObject + */ + private $configurable; + + /** + * @var Loader + */ + private $loader; + + protected function setUp() + { + $this->optionValueFactory = $this->getMockBuilder(OptionValueInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getTypeInstance']) + ->getMock(); + + $this->configurable = $this->getMockBuilder(Configurable::class) + ->disableOriginalConstructor() + ->setMethods(['getConfigurableAttributes']) + ->getMock(); + + $this->loader = new Loader($this->optionValueFactory); + } + + /** + * @covers \Magento\ConfigurableProduct\Helper\Product\Options\Loader::load + */ + public function testLoad() + { + $option = [ + 'value_index' => 23 + ]; + + $this->product->expects(static::once()) + ->method('getTypeInstance') + ->willReturn($this->configurable); + + $attribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->setMethods(['getOptions', 'setValues']) + ->getMock(); + $attributes = [$attribute]; + + $this->configurable->expects(static::once()) + ->method('getConfigurableAttributes') + ->with($this->product) + ->willReturn($attributes); + + $attribute->expects(static::once()) + ->method('getOptions') + ->willReturn([$option]); + + $optionValue = $this->getMockForAbstractClass(OptionValueInterface::class); + $this->optionValueFactory->expects(static::once()) + ->method('create') + ->willReturn($optionValue); + $optionValue->expects(static::once()) + ->method('setValueIndex') + ->with($option['value_index']); + + $attribute->expects(static::once()) + ->method('setValues') + ->with([$optionValue]); + + $options = $this->loader->load($this->product); + static::assertSame([$attribute], $options); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php index 71cac64a95d3782ad845d41bc00c3337bef42638..c5b30adcb606b49d8364b60b7963027d069568ce 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Attribute/LockValidatorTest.php @@ -5,7 +5,10 @@ */ namespace Magento\ConfigurableProduct\Test\Unit\Model\Attribute; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Model\Entity\EntityMetadata; +use Magento\Framework\Model\Entity\MetadataPool; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class LockValidatorTest extends \PHPUnit_Framework_TestCase @@ -30,6 +33,11 @@ class LockValidatorTest extends \PHPUnit_Framework_TestCase */ private $select; + /** + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPoolMock; + protected function setUp() { $helper = new ObjectManager($this); @@ -48,9 +56,21 @@ class LockValidatorTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->metadataPoolMock = $this->getMockBuilder(MetadataPool::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->metadataPoolMock->expects(self::once()) + ->method('getMetadata') + ->with(ProductInterface::class) + ->willReturn($this->getMetaDataMock()); + $this->model = $helper->getObject( 'Magento\ConfigurableProduct\Model\Attribute\LockValidator', - ['resource' => $this->resource] + [ + 'resource' => $this->resource, + 'metadataPool' => $this->metadataPoolMock + ] ); } @@ -59,6 +79,22 @@ class LockValidatorTest extends \PHPUnit_Framework_TestCase $this->validate(false); } + /** + * @return EntityMetadata|\PHPUnit_Framework_MockObject_MockObject + */ + private function getMetaDataMock() + { + $metadata = $this->getMockBuilder(EntityMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $metadata->expects(self::once()) + ->method('getLinkField') + ->willReturn('entity_id'); + + return $metadata; + } + /** * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage This attribute is used in configurable products. diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php index db131d2bb5cb6ca1519c97447c3840304b85093b..01d1f6adc386e1139e1454461ab50af1acaba6f0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php @@ -3,11 +3,17 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\ConfigurableProduct\Test\Unit\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\ConfigurableProduct\Api\Data\OptionInterface; +use Magento\ConfigurableProduct\Helper\Product\Options\Loader; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute; +/** + * Class OptionRepositoryTest + */ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase { /** @@ -35,20 +41,32 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase */ protected $optionResource; + /** + * @var Loader|\PHPUnit_Framework_MockObject_MockObject + */ + private $optionLoader; + protected function setUp() { - $this->productRepositoryMock = $this->getMock('\Magento\Catalog\Api\ProductRepositoryInterface'); + $this->productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class) + ->getMockForAbstractClass(); + $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $this->configurableTypeResource = $this->getMockBuilder( 'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable' - ) - ->disableOriginalConstructor() + )->disableOriginalConstructor() ->getMock(); + $this->optionResource = $this->getMockBuilder( 'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute' - ) + )->disableOriginalConstructor() + ->getMock(); + + $this->optionLoader = $this->getMockBuilder(Loader::class) ->disableOriginalConstructor() ->getMock(); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->model = $objectManager->getObject( '\Magento\ConfigurableProduct\Model\OptionRepository', @@ -56,38 +74,39 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase 'productRepository' => $this->productRepositoryMock, 'configurableTypeResource' => $this->configurableTypeResource, 'optionResource' => $this->optionResource, + 'optionLoader' => $this->optionLoader ] ); } public function testGet() { - $productSku = "configurable"; $optionId = 3; + $productSku = "configurable"; - $this->productRepositoryMock->expects($this->once()) + $this->productMock->expects(self::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); + + $this->productRepositoryMock->expects(self::once()) ->method('get') ->with($productSku) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn(Configurable::TYPE_CODE); - $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); - $optionMock->expects($this->once()) + $optionMock = $this->getMock(OptionInterface::class); + $optionMock->expects(self::once()) ->method('getId') ->willReturn($optionId); - $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') - ->setMethods(['getConfigurableProductOptions']) - ->getMock(); - $productExtensionMock->expects($this->once()) - ->method('getConfigurableProductOptions') + + $this->optionLoader->expects(self::once()) + ->method('load') + ->with($this->productMock) ->willReturn([$optionMock]); - $this->productMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($productExtensionMock); - $this->assertEquals($optionMock, $this->model->get($productSku, $optionId)); + self::assertEquals( + $optionMock, + $this->model->get($productSku, $optionId) + ); } /** @@ -99,13 +118,21 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase $productSku = "configurable"; $optionId = 3; - $this->productRepositoryMock->expects($this->once()) + $this->productMock->expects(self::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE . '-not'); + + $this->productRepositoryMock->expects(self::once()) ->method('get') ->with($productSku) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn('simple'); + + $optionMock = $this->getMock(OptionInterface::class); + $optionMock->expects(self::never()) + ->method('getId'); + + $this->optionLoader->expects(self::never()) + ->method('load'); $this->model->get($productSku, $optionId); } @@ -116,25 +143,25 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase */ public function testGetNotProductById() { - $productId = 3; + $entityId = 3; + /** @var OptionInterface $optionMock */ + $optionMock = $this->getMock(OptionInterface::class); - $option = $this->getMockBuilder('Magento\ConfigurableProduct\Api\Data\OptionInterface') - ->disableOriginalConstructor() - ->setMethods(['getProductId']) - ->getMockForAbstractClass(); - $option->expects($this->once()) - ->method('getProductId') - ->willReturn($productId); + $this->configurableTypeResource->expects(self::once()) + ->method('getEntityIdByAttribute') + ->with($optionMock) + ->willReturn($entityId); - $this->productRepositoryMock->expects($this->once()) + $this->productRepositoryMock->expects(self::once()) ->method('getById') - ->with($productId) + ->with($entityId) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) + + $this->productMock->expects(self::once()) ->method('getTypeId') - ->willReturn('simple'); + ->willReturn(Configurable::TYPE_CODE . '-not'); - $this->model->delete($option); + $this->model->delete($optionMock); } /** @@ -143,29 +170,30 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase */ public function testDeleteCantSaveProducts() { - $productId = 3; + $entityId = 3; + /** @var OptionInterface $optionMock */ + $optionMock = $this->getMock(OptionInterface::class); - $option = $this->getMockBuilder('Magento\ConfigurableProduct\Api\Data\OptionInterface') - ->disableOriginalConstructor() - ->setMethods(['getProductId']) - ->getMockForAbstractClass(); - $option->expects($this->any()) - ->method('getProductId') - ->willReturn($productId); + $this->configurableTypeResource->expects(self::once()) + ->method('getEntityIdByAttribute') + ->with($optionMock) + ->willReturn($entityId); - $this->productRepositoryMock->expects($this->once()) + $this->productRepositoryMock->expects(self::once()) ->method('getById') - ->with($productId) + ->with($entityId) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) + + $this->productMock->expects(self::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->configurableTypeResource->expects($this->once()) + $this->configurableTypeResource->expects(self::once()) ->method('saveProducts') ->with($this->productMock) ->willThrowException(new \Exception()); - $this->model->delete($option); + + $this->model->delete($optionMock); } /** @@ -174,69 +202,73 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase */ public function testDeleteCantDeleteOption() { - $productId = 3; - $optionId = 33; - - $option = $this->getMockBuilder('Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') + $entityId = 3; + $optionMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() - ->setMethods(['getProductId', 'getId']) - ->getMockForAbstractClass(); - $option->expects($this->any()) - ->method('getProductId') - ->willReturn($productId); - $option->expects($this->once()) + ->getMock(); + + $optionMock->expects(self::once()) ->method('getId') - ->willReturn($optionId); + ->willReturn(33); - $this->productRepositoryMock->expects($this->once()) + $this->configurableTypeResource->expects(self::once()) + ->method('getEntityIdByAttribute') + ->with($optionMock) + ->willReturn($entityId); + + $this->productRepositoryMock->expects(self::once()) ->method('getById') - ->with($productId) + ->with($entityId) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) + + $this->productMock->expects(self::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->configurableTypeResource->expects($this->once()) + $this->configurableTypeResource->expects(self::once()) ->method('saveProducts') ->with($this->productMock); - $this->optionResource->expects($this->once()) + + $this->optionResource->expects(self::once()) ->method('delete') - ->with($option) + ->with($optionMock) ->willThrowException(new \Exception()); - $this->model->delete($option); + + $this->model->delete($optionMock); } public function testDelete() { - $productId = 3; - $optionId = 33; - - $option = $this->getMockBuilder('Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') + $entityId = 3; + $optionMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() - ->setMethods(['getProductId', 'getId']) - ->getMockForAbstractClass(); - $option->expects($this->any()) - ->method('getProductId') - ->willReturn($productId); - $option->expects($this->any()) - ->method('getId') - ->willReturn($optionId); + ->getMock(); - $this->productRepositoryMock->expects($this->once()) + $this->configurableTypeResource->expects(self::once()) + ->method('getEntityIdByAttribute') + ->with($optionMock) + ->willReturn($entityId); + + $this->productRepositoryMock->expects(self::once()) ->method('getById') - ->with($productId) + ->with($entityId) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) + + $this->productMock->expects(self::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->configurableTypeResource->expects($this->once()) + $this->configurableTypeResource->expects(self::once()) ->method('saveProducts') ->with($this->productMock); - $this->optionResource->expects($this->once()) + + $this->optionResource->expects(self::once()) ->method('delete') - ->with($option); - $result = $this->model->delete($option); + ->with($optionMock) + ->willReturn(true); + + $result = $this->model->delete($optionMock); + $this->assertTrue($result); } @@ -246,54 +278,26 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase */ public function testGetEmptyExtensionAttribute() { - $productSku = "configurable"; $optionId = 3; + $productSku = "configurable"; - $this->productRepositoryMock->expects($this->once()) - ->method('get') - ->with($productSku) - ->willReturn($this->productMock); - $this->productMock->expects($this->once()) + $this->productMock->expects(self::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn(null); - - $this->model->get($productSku, $optionId); - } - - /** - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Requested option doesn't exist: 3 - */ - public function testGetOptionIdNotFound() - { - $productSku = "configurable"; - $optionId = 3; - - $this->productRepositoryMock->expects($this->once()) + $this->productRepositoryMock->expects(self::once()) ->method('get') ->with($productSku) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn(Configurable::TYPE_CODE); - $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); - $optionMock->expects($this->once()) - ->method('getId') - ->willReturn(6); - $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') - ->setMethods(['getConfigurableProductOptions']) - ->getMock(); - $productExtensionMock->expects($this->once()) - ->method('getConfigurableProductOptions') - ->willReturn([$optionMock]); - $this->productMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($productExtensionMock); + $optionMock = $this->getMock(OptionInterface::class); + $optionMock->expects(self::never()) + ->method('getId'); + + $this->optionLoader->expects(self::once()) + ->method('load') + ->with($this->productMock) + ->willReturn([]); $this->model->get($productSku, $optionId); } @@ -302,45 +306,23 @@ class OptionRepositoryTest extends \PHPUnit_Framework_TestCase { $productSku = "configurable"; - $this->productRepositoryMock->expects($this->once()) - ->method('get') - ->with($productSku) - ->willReturn($this->productMock); - $this->productMock->expects($this->once()) + $this->productMock->expects(self::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); - $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') - ->setMethods(['getConfigurableProductOptions']) - ->getMock(); - $productExtensionMock->expects($this->once()) - ->method('getConfigurableProductOptions') - ->willReturn([$optionMock]); - $this->productMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($productExtensionMock); - - $this->assertEquals([$optionMock], $this->model->getList($productSku)); - } - - public function testGetListNullExtensionAttribute() - { - $productSku = "configurable"; - - $this->productRepositoryMock->expects($this->once()) + $this->productRepositoryMock->expects(self::once()) ->method('get') ->with($productSku) ->willReturn($this->productMock); - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn(Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn(null); + $optionMock = $this->getMock(OptionInterface::class); - $this->assertEquals([], $this->model->getList($productSku)); + $this->optionLoader->expects(self::once()) + ->method('load') + ->with($this->productMock) + ->willReturn([$optionMock]); + + $this->assertEquals([$optionMock], $this->model->getList($productSku)); } /** diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php deleted file mode 100644 index 858a696a572a797bfbedd5da47b52ecff52d90f5..0000000000000000000000000000000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php +++ /dev/null @@ -1,224 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin; - -use Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad; - -class AfterProductLoadTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var AfterProductLoad - */ - protected $model; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $optionValueFactory; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $productExtensionFactory; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|Magento\Catalog\Model\Product - */ - protected $productMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $configurableProductTypeInstanceMock; - - protected function setUp() - { - $this->optionValueFactory = $this->getMock( - '\Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory', - ['create'], - [], - '', - false - ); - $this->productMock = $this->getMockBuilder('Magento\Catalog\Model\Product') - ->disableOriginalConstructor() - ->getMock(); - $this->configurableProductTypeInstanceMock = $this->getMock( - '\Magento\ConfigurableProduct\Model\Product\Type\Configurable', - [], - [], - '', - false - ); - $this->productMock->expects($this->any()) - ->method('getTypeInstance') - ->willReturn($this->configurableProductTypeInstanceMock); - $this->productExtensionFactory = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory') - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = new \Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad( - $this->productExtensionFactory, - $this->optionValueFactory - ); - } - - protected function setupOptions() - { - $optionValues = [ - [ - 'value_index' => 5, - ], - [ - 'value_index' => 6, - ], - ]; - $optionMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') - ->disableOriginalConstructor() - ->setMethods(['getOptions', 'setValues']) - ->getMock(); - $optionMock1->expects($this->once()) - ->method('getOptions') - ->willReturn($optionValues); - - $optionValueMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface') - ->disableOriginalConstructor() - ->getMock(); - $optionValueMock1->expects($this->once()) - ->method('setValueIndex') - ->with($optionValues[0]['value_index']) - ->willReturnSelf(); - - $optionValueMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface') - ->disableOriginalConstructor() - ->getMock(); - $optionValueMock2->expects($this->once()) - ->method('setValueIndex') - ->with($optionValues[1]['value_index']) - ->willReturnSelf(); - - $this->optionValueFactory->expects($this->at(0)) - ->method('create') - ->willReturn($optionValueMock1); - $optionMock1->expects($this->once()) - ->method('setValues') - ->with([$optionValueMock1, $optionValueMock2]); - - $optionMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') - ->disableOriginalConstructor() - ->setMethods(['getOptions', 'setValues']) - ->getMock(); - $optionMock2->expects($this->once()) - ->method('getOptions') - ->willReturn([]); - $optionMock2->expects($this->once()) - ->method('setValues') - ->with([]); - $this->optionValueFactory->expects($this->at(1)) - ->method('create') - ->willReturn($optionValueMock2); - - $options = [$optionMock1, $optionMock2]; - - $this->configurableProductTypeInstanceMock->expects($this->once()) - ->method('getConfigurableAttributes') - ->with($this->productMock) - ->willReturn($options); - return $options; - } - - protected function setupLinks() - { - $id = 5; - $links = [6, 7]; - $this->productMock->expects($this->once()) - ->method('getId') - ->willReturn($id); - $this->configurableProductTypeInstanceMock->expects($this->once()) - ->method('getChildrenIds') - ->with($id) - ->willReturn([$links]); - return $links; - } - - public function testAfterLoad() - { - $options = $this->setupOptions(); - $links = $this->setupLinks(); - - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - - $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') - ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) - ->getMock(); - - $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions') - ->with($options) - ->willReturnSelf(); - $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks') - ->with($links) - ->willReturnSelf(); - - $this->productExtensionFactory->expects($this->once()) - ->method('create') - ->willReturn($extensionAttributeMock); - - $this->productMock->expects($this->once()) - ->method('setExtensionAttributes') - ->with($extensionAttributeMock) - ->willReturnSelf(); - - $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock)); - } - - public function testAfterLoadNotConfigurableProduct() - { - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn('simple'); - - $this->productMock->expects($this->never()) - ->method('getExtensionAttributes'); - $this->productMock->expects($this->never()) - ->method('setExtensionAttributes'); - $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock)); - } - - public function testAfterLoadNoLinks() - { - $options = $this->setupOptions(); - - $this->productMock->expects($this->once()) - ->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - - $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') - ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) - ->getMock(); - - $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions') - ->with($options) - ->willReturnSelf(); - $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks') - ->with([]) - ->willReturnSelf(); - - $this->productExtensionFactory->expects($this->once()) - ->method('create') - ->willReturn($extensionAttributeMock); - - $this->productMock->expects($this->once()) - ->method('setExtensionAttributes') - ->with($extensionAttributeMock) - ->willReturnSelf(); - - $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock)); - } -} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php index 4e812f684634f6e963d56324f2df1e5170b8a0d9..f58f04849ca96fcbf5ffb0282de42d15d2def72f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php @@ -3,528 +3,306 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Api\Data\OptionInterface; use Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Class AroundProductRepositorySaveTest + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase { /** - * @var AroundProductRepositorySave + * @var ProductAttributeRepositoryInterface|MockObject */ - protected $plugin; + private $productAttributeRepository; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductFactory|MockObject */ - protected $productRepositoryMock; + private $productFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Closure */ - protected $productOptionRepositoryMock; + private $closure; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $productFactoryMock; + private $product; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $productMock; + private $result; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductRepositoryInterface|MockObject */ - protected $productExtensionMock; + private $productRepository; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductExtensionAttributes|MockObject */ - protected $configurableTypeFactoryMock; + private $extensionAttributes; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeInterface|MockObject */ - protected $productInterfaceMock; + private $eavAttribute; /** - * @var \Closure + * @var OptionInterface|MockObject */ - protected $closureMock; + private $option; + + /** + * @var AroundProductRepositorySave + */ + private $plugin; protected function setUp() { - $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface'); - $this->productOptionRepositoryMock = $this->getMock( - 'Magento\ConfigurableProduct\Api\OptionRepositoryInterface' - ); - $this->configurableTypeFactoryMock = $this->getMock( - '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory', - ['create'], - [], - '', - false - ); - $this->productInterfaceMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); - $this->productMock = $this->getMock( - 'Magento\Catalog\Model\Product', - ['getExtensionAttributes', 'getTypeId', 'getSku', 'getStoreId', 'getId', 'getTypeInstance'], - [], - '', - false - ); - $this->closureMock = function () { - return $this->productMock; - }; + $this->productAttributeRepository = $this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class); - $this->productFactoryMock = $this->getMockBuilder('\Magento\Catalog\Model\ProductFactory') + $this->productFactory = $this->getMockBuilder(ProductFactory::class) + ->disableOriginalConstructor() ->setMethods(['create']) + ->getMock(); + + $this->product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() + ->setMethods(['getTypeId']) ->getMock(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->plugin = $objectManager->getObject( - 'Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave', - [ - 'optionRepository' => $this->productOptionRepositoryMock, - 'productFactory' => $this->productFactoryMock, - 'typeConfigurableFactory' => $this->configurableTypeFactoryMock - ] - ); - $this->productExtensionMock = $this->getMock( - 'Magento\Catalog\Api\Data\ProductExtension', - [ - 'getConfigurableProductOptions', - 'getConfigurableProductLinks', - 'setConfigurableProductOptions', - 'setConfigurableProductLinks', - ], - [], - '', - false - ); - } + $this->result = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getExtensionAttributes']) + ->getMock(); - public function testAroundSaveWhenProductIsSimple() - { - $this->productMock->expects($this->once())->method('getTypeId')->willReturn('simple'); - $this->productMock->expects($this->never())->method('getExtensionAttributes'); + $this->closure = function () { + return $this->result; + }; - $this->assertEquals( - $this->productMock, - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class); + + $this->extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + ->disableOriginalConstructor() + ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks']) + ->getMockForAbstractClass(); + + $this->eavAttribute = $this->getMockForAbstractClass(ProductAttributeInterface::class); + + $this->option = $this->getMockForAbstractClass(OptionInterface::class); + + $this->plugin = new AroundProductRepositorySave( + $this->productAttributeRepository, + $this->productFactory ); } - public function testAroundSaveWithoutOptions() + public function testAroundSaveWhenProductIsSimple() { - $this->productInterfaceMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->productInterfaceMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtensionMock); - $this->productExtensionMock->expects($this->once()) - ->method('getConfigurableProductOptions') - ->willReturn(null); - $this->productExtensionMock->expects($this->once()) - ->method('getConfigurableProductLinks') - ->willReturn(null); + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn('simple'); + $this->product->expects(static::never()) + ->method('getExtensionAttributes'); $this->assertEquals( - $this->productMock, - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock) + $this->result, + $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product) ); } - protected function setupProducts($productIds, $attributeCode, $additionalProductId = null) - { - $count = 0; - $products = []; - foreach ($productIds as $productId) { - $productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') - ->disableOriginalConstructor() - ->getMock(); - $productMock->expects($this->once()) - ->method('load') - ->with($productId) - ->willReturnSelf(); - $productMock->expects($this->once()) - ->method('getId') - ->willReturn($productId); - $productMock->expects($this->any()) - ->method('getData') - ->with($attributeCode) - ->willReturn($productId); - $this->productFactoryMock->expects($this->at($count)) - ->method('create') - ->willReturn($productMock); - $products[] = $productMock; - $count++; - } - - if ($additionalProductId) { - $nonExistingProductMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') - ->disableOriginalConstructor() - ->getMock(); - $nonExistingProductMock->expects($this->once()) - ->method('load') - ->with($additionalProductId) - ->willReturnSelf(); - $this->productFactoryMock->expects($this->at($count)) - ->method('create') - ->willReturn($nonExistingProductMock); - $products[] = $nonExistingProductMock; - } - return $products; - } - - protected function setupConfigurableProductAttributes($attributeCodes) + public function testAroundSaveWithoutOptions() { - $configurableProductTypeMock = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\Product\Type\Configurable' - )->disableOriginalConstructor()->getMock(); - - $this->productMock->expects($this->once()) - ->method('getTypeInstance') - ->willReturn($configurableProductTypeMock); - - $configurableAttributes = []; - foreach ($attributeCodes as $attributeCode) { - $configurableAttribute = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute' - )->setMethods(['getProductAttribute']) - ->disableOriginalConstructor() - ->getMock(); - $productAttributeMock = $this->getMockBuilder('\Magento\Catalog\Model\ResourceModel\Eav\Attribute') - ->disableOriginalConstructor() - ->getMock(); - $productAttributeMock->expects($this->once()) - ->method('getAttributeCode') - ->willReturn($attributeCode); - $configurableAttribute->expects($this->once()) - ->method('getProductAttribute') - ->willReturn($productAttributeMock); - $configurableAttributes[] = $configurableAttribute; - } - - $configurableProductTypeMock->expects($this->once()) - ->method('getConfigurableAttributes') - ->with($this->productMock) - ->willReturn($configurableAttributes); - - return $this; - } + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); - public function testAroundSaveWithLinks() - { - $links = [4, 5]; - $configurableAttributeCode = 'color'; - $this->productMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) + $this->result->expects(static::once()) ->method('getExtensionAttributes') - ->willReturn($this->productExtensionMock); - $this->productExtensionMock->expects($this->once()) + ->willReturn($this->extensionAttributes); + + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductOptions') - ->willReturn(null); - $this->productExtensionMock->expects($this->once()) + ->willReturn([]); + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductLinks') - ->willReturn($links); - - $this->setupConfigurableProductAttributes([$configurableAttributeCode]); - $this->setupProducts($links, $configurableAttributeCode); - - $configurableTypeMock = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable' - )->disableOriginalConstructor()->getMock(); - $this->configurableTypeFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($configurableTypeMock); - $configurableTypeMock->expects($this->once()) - ->method('saveProducts') - ->with($this->productMock, $links); + ->willReturn([]); - $productSku = 'configurable-sku'; - $this->productMock->expects($this->any()) - ->method('getSku') - ->willReturn($productSku); - $newProductMock = $this->setupReload($productSku); + $this->productAttributeRepository->expects(static::never()) + ->method('get'); $this->assertEquals( - $newProductMock, - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + $this->result, + $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product) ); } /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Product with id "6" does not exist. + * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values. */ - public function testAroundSaveWithNonExistingLinks() + public function testAroundSaveWithLinks() { $links = [4, 5]; - $nonExistingId = 6; - $configurableAttributeCode = 'color'; - - $this->setupConfigurableProductAttributes([$configurableAttributeCode]); - $productMocks = $this->setupProducts($links, $configurableAttributeCode, $nonExistingId); - $nonExistingProductMock = $productMocks[2]; - $nonExistingProductMock->expects($this->once()) - ->method('getId') - ->willReturn(null); - $links[] = $nonExistingId; + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); - $this->productMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) + $this->result->expects(static::once()) ->method('getExtensionAttributes') - ->willReturn($this->productExtensionMock); - $this->productExtensionMock->expects($this->once()) + ->willReturn($this->extensionAttributes); + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductOptions') ->willReturn(null); - $this->productExtensionMock->expects($this->once()) + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductLinks') ->willReturn($links); - $configurableTypeMock = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable' - )->disableOriginalConstructor()->getMock(); - $this->configurableTypeFactoryMock->expects($this->once()) + $this->productAttributeRepository->expects(static::never()) + ->method('get'); + + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['load', 'getData', '__wakeup']) + ->getMock(); + + $this->productFactory->expects(static::exactly(2)) ->method('create') - ->willReturn($configurableTypeMock); - $configurableTypeMock->expects($this->never()) - ->method('saveProducts') - ->with($this->productMock, $links); + ->willReturn($product); - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); + $product->expects(static::exactly(2)) + ->method('load') + ->willReturnSelf(); + $product->expects(static::never()) + ->method('getData'); + + $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product); } /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Product with id "6" does not contain required attribute "color". + * @expectedExceptionMessage Product with id "4" does not contain required attribute "color". */ public function testAroundSaveWithLinksWithMissingAttribute() { - $links = [4, 5]; - $simpleProductId = 6; - $configurableAttributeCode = 'color'; - - $this->setupConfigurableProductAttributes([$configurableAttributeCode]); - $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId); - $simpleProductMock = $productMocks[2]; - $simpleProductMock->expects($this->once()) - ->method('getId') - ->willReturn($simpleProductId); - $simpleProductMock->expects($this->any()) - ->method('getData') - ->with($configurableAttributeCode) - ->willReturn(null); + $simpleProductId = 4; + $links = [$simpleProductId, 5]; + $attributeCode = 'color'; + $attributeId = 23; - $links[] = $simpleProductId; + $this->option->expects(static::once()) + ->method('getAttributeId') + ->willReturn($attributeId); - $this->productMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); + + $this->result->expects(static::once()) ->method('getExtensionAttributes') - ->willReturn($this->productExtensionMock); - $this->productExtensionMock->expects($this->once()) + ->willReturn($this->extensionAttributes); + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductOptions') - ->willReturn(null); - $this->productExtensionMock->expects($this->once()) + ->willReturn([$this->option]); + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductLinks') ->willReturn($links); - $configurableTypeMock = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable' - )->disableOriginalConstructor()->getMock(); - $this->configurableTypeFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($configurableTypeMock); - $configurableTypeMock->expects($this->never()) - ->method('saveProducts') - ->with($this->productMock, $links); - - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); - } - - /** - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Products "6" and 4 have the same set of attribute values. - */ - public function testAroundSaveWithLinksWithDuplicateAttributes() - { - $links = [4, 5]; - $simpleProductId = 6; - $configurableAttributeCode = 'color'; - - $this->setupConfigurableProductAttributes([$configurableAttributeCode]); - $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId); - $simpleProductMock = $productMocks[2]; - $simpleProductMock->expects($this->once()) - ->method('getId') - ->willReturn($simpleProductId); - $simpleProductMock->expects($this->any()) - ->method('getData') - ->with($configurableAttributeCode) - ->willReturn(4); + $this->productAttributeRepository->expects(static::once()) + ->method('get') + ->willReturn($this->eavAttribute); - $links[] = $simpleProductId; + $this->eavAttribute->expects(static::once()) + ->method('getAttributeCode') + ->willReturn($attributeCode); - $this->productMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtensionMock); - $this->productExtensionMock->expects($this->once()) - ->method('getConfigurableProductOptions') - ->willReturn(null); - $this->productExtensionMock->expects($this->once()) - ->method('getConfigurableProductLinks') - ->willReturn($links); + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['load', 'getData', '__wakeup']) + ->getMock(); - $configurableTypeMock = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable' - )->disableOriginalConstructor()->getMock(); - $this->configurableTypeFactoryMock->expects($this->once()) + $this->productFactory->expects(static::once()) ->method('create') - ->willReturn($configurableTypeMock); - $configurableTypeMock->expects($this->never()) - ->method('saveProducts') - ->with($this->productMock, $links); + ->willReturn($product); + $product->expects(static::once()) + ->method('load') + ->with($simpleProductId) + ->willReturnSelf(); + $product->expects(static::once()) + ->method('getData') + ->with($attributeCode) + ->willReturn(false); - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); + $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product); } /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The configurable product does not have any variation attribute. + * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values. */ - public function testAroundSaveWithLinksWithoutVariationAttributes() + public function testAroundSaveWithLinksWithDuplicateAttributes() { $links = [4, 5]; + $attributeCode = 'color'; + $attributeId = 23; + + $this->option->expects(static::once()) + ->method('getAttributeId') + ->willReturn($attributeId); - $this->setupConfigurableProductAttributes([]); + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); - $this->productMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - $this->productMock->expects($this->once()) + $this->result->expects(static::once()) ->method('getExtensionAttributes') - ->willReturn($this->productExtensionMock); - $this->productExtensionMock->expects($this->once()) + ->willReturn($this->extensionAttributes); + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductOptions') - ->willReturn(null); - $this->productExtensionMock->expects($this->once()) + ->willReturn([$this->option]); + $this->extensionAttributes->expects(static::once()) ->method('getConfigurableProductLinks') ->willReturn($links); - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); - } - - public function testAroundSaveWithOptions() - { - $productSku = "configurable_sku"; - $this->productInterfaceMock->expects($this->once())->method('getTypeId') - ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); - //two options with id 5 and 6 - $options = $this->setupOptions([5, 6]); - //two existing options with id 4 and 5 - $this->setupExistingOptions([4, 5]); - - $this->productMock->expects($this->any())->method('getSku') - ->will($this->returnValue($productSku)); - - $this->productOptionRepositoryMock->expects($this->at(0)) - ->method('save') - ->with($productSku, $options[0]); - $this->productOptionRepositoryMock->expects($this->at(1)) - ->method('save') - ->with($productSku, $options[1]); - $this->productOptionRepositoryMock->expects($this->at(2)) - ->method('deleteById') - ->with($productSku, 4); - - $configurableProductTypeMock = $this->getMockBuilder( - '\Magento\ConfigurableProduct\Model\Product\Type\Configurable' - )->disableOriginalConstructor()->getMock(); - $configurableProductTypeMock->expects($this->once()) - ->method('resetConfigurableAttributes') - ->with($this->productMock) - ->willReturnSelf(); - $this->productMock->expects($this->any()) - ->method('getTypeInstance') - ->willReturn($configurableProductTypeMock); - - $newProductMock = $this->setupReload($productSku); - - $this->assertEquals( - $newProductMock, - $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock) - ); - } - - protected function setupReload($productSku) - { - $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') - ->disableOriginalConstructor()->getMock(); - $this->productRepositoryMock->expects($this->once()) + $this->productAttributeRepository->expects(static::once()) ->method('get') - ->with($productSku, false, null, true) - ->willReturn($newProductMock); - return $newProductMock; - } + ->willReturn($this->eavAttribute); - protected function setupExistingOptions(array $existingOptionIds) - { - $options = []; - foreach ($existingOptionIds as $existingOptionId) { - $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); - $optionMock->expects($this->any()) - ->method('getId') - ->willReturn($existingOptionId); - $options[] = $optionMock; - } - - $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension') - ->disableOriginalConstructor() - ->setMethods(['getConfigurableProductOptions']) - ->getMock(); - $productExtensionMock->expects($this->any()) - ->method('getConfigurableProductOptions') - ->willReturn($options); - - $this->productMock->expects($this->any()) - ->method('getExtensionAttributes') - ->willReturn($productExtensionMock); - } + $this->eavAttribute->expects(static::once()) + ->method('getAttributeCode') + ->willReturn($attributeCode); - protected function setupOptions(array $optionIds) - { - $options = []; - foreach ($optionIds as $optionId) { - $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); - $optionMock->expects($this->any()) - ->method('getId') - ->willReturn($optionId); - $options[] = $optionMock; - } - - $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension') + $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks']) + ->setMethods(['load', 'getData', '__wakeup']) ->getMock(); - $productExtensionMock->expects($this->any()) - ->method('getConfigurableProductOptions') - ->willReturn($options); - $productExtensionMock->expects($this->any()) - ->method('getConfigurableProductLinks') - ->willReturn(null); - $this->productInterfaceMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($productExtensionMock); - return $options; + $this->productFactory->expects(static::exactly(2)) + ->method('create') + ->willReturn($product); + $product->expects(static::exactly(2)) + ->method('load') + ->willReturnSelf(); + $product->expects(static::exactly(4)) + ->method('getData') + ->with($attributeCode) + ->willReturn($attributeId); + + $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product); } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php index f004391098ddd30ba76e5816922858d5ee450d61..f1b203aee546f828ed79bc5229f22d80970872dc 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/PriceBackendTest.php @@ -6,35 +6,53 @@ namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Backend\Price; +use Magento\Catalog\Model\Product\Type; use Magento\ConfigurableProduct\Model\Plugin\PriceBackend; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; -use Magento\Catalog\Model\Product\Type; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Class PriceBackendTest + */ class PriceBackendTest extends \PHPUnit_Framework_TestCase { const CLOSURE_VALUE = 'CLOSURE'; - /** @var PriceBackend */ + + /** + * @var PriceBackend + */ private $priceBackendPlugin; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $priceAttributeMock; - /** @var \Closure */ + + /** + * @var Price|MockObject + */ + private $priceAttribute; + + /** + * @var \Closure + */ private $closure; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - private $productMock; + + /** + * @var Product|MockObject + */ + private $product; protected function setUp() { $objectManager = new ObjectManager($this); - $this->priceBackendPlugin = $objectManager->getObject('Magento\ConfigurableProduct\Model\Plugin\PriceBackend'); + $this->priceBackendPlugin = $objectManager->getObject(PriceBackend::class); $this->closure = function () { return static::CLOSURE_VALUE; }; - $this->priceAttributeMock = $this->getMockBuilder('Magento\Catalog\Model\Product\Attribute\Backend\Price') + $this->priceAttribute = $this->getMockBuilder(Price::class) ->disableOriginalConstructor() ->getMock(); - $this->productMock = $this->getMockBuilder('Magento\Catalog\Model\Product') + $this->product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getTypeId', 'getPriceType', '__wakeUp']) ->getMock(); @@ -48,11 +66,13 @@ class PriceBackendTest extends \PHPUnit_Framework_TestCase */ public function testAroundValidate($typeId, $expectedResult) { - $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue($typeId)); + $this->product->expects(static::once()) + ->method('getTypeId') + ->willReturn($typeId); $result = $this->priceBackendPlugin->aroundValidate( - $this->priceAttributeMock, + $this->priceAttribute, $this->closure, - $this->productMock + $this->product ); $this->assertEquals($expectedResult, $result); } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductExtensionAttributes.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductExtensionAttributes.php new file mode 100644 index 0000000000000000000000000000000000000000..f5f121784801e35fc6b65ce3e96bd78e7d883573 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductExtensionAttributes.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Model\Product; + +use Magento\Catalog\Api\Data\ProductExtensionInterface; + +/** + * Class ProductExtensionAttributes + */ +abstract class ProductExtensionAttributes implements ProductExtensionInterface +{ + +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductOptionExtensionAttributes.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductOptionExtensionAttributes.php new file mode 100644 index 0000000000000000000000000000000000000000..c61ad824835c5b234b0408890334184ebb572b76 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ProductOptionExtensionAttributes.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Model\Product; + +use Magento\Quote\Api\Data\ProductOptionExtensionInterface; + +/** + * Class ProductOptionExtensionAttributes + */ +abstract class ProductOptionExtensionAttributes implements ProductOptionExtensionInterface +{ + +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ReadHandlerTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ReadHandlerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..62d14806d85b0b5975c05f11c8d1df896a7126d5 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/ReadHandlerTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Model\Product; + +use Magento\Catalog\Model\Product; +use Magento\ConfigurableProduct\Helper\Product\Options\Loader; +use Magento\ConfigurableProduct\Model\Product\ReadHandler; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class ReadHandlerTest + */ +class ReadHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ReadHandler + */ + private $readHandler; + + /** + * @var Loader|MockObject + */ + private $optionLoader; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->optionLoader = $this->getMockBuilder(Loader::class) + ->disableOriginalConstructor() + ->setMethods(['load']) + ->getMock(); + + $this->readHandler = new ReadHandler($this->optionLoader); + } + + /** + * @covers \Magento\ConfigurableProduct\Model\Product\ReadHandler::execute + */ + public function testExecuteWithInvalidProductType() + { + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getTypeId', 'getExtensionAttributes']) + ->getMock(); + + $product->expects(static::once()) + ->method('getTypeId') + ->willReturn('simple'); + + $product->expects(static::never()) + ->method('getExtensionAttributes'); + + $entity = $this->readHandler->execute('Entity', $product); + static::assertSame($product, $entity); + } + + /** + * @covers \Magento\ConfigurableProduct\Model\Product\ReadHandler::execute + */ + public function testExecute() + { + $options = [ + ['value_index' => 12], + ['value_index' => 13] + ]; + $entityId = 1; + $ids = [1, 2, 3]; + + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getTypeId', 'getId', 'getExtensionAttributes', 'setExtensionAttributes', 'getTypeInstance' + ]) + ->getMock(); + + $product->expects(static::once()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); + + $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + ->disableOriginalConstructor() + ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) + ->getMockForAbstractClass(); + + $product->expects(static::once()) + ->method('getExtensionAttributes') + ->willReturn($extensionAttributes); + + $this->optionLoader->expects(static::once()) + ->method('load') + ->with($product) + ->willReturn($options); + + $typeInstance = $this->getMockBuilder(Configurable::class) + ->disableOriginalConstructor() + ->setMethods(['getChildrenIds']) + ->getMock(); + + $product->expects(static::once()) + ->method('getTypeInstance') + ->willReturn($typeInstance); + + $product->expects(static::once()) + ->method('getId') + ->willReturn($entityId); + + $typeInstance->expects(static::once()) + ->method('getChildrenIds') + ->willReturn($ids); + + $product->expects(static::once()) + ->method('setExtensionAttributes') + ->with($extensionAttributes); + + $entity = $this->readHandler->execute('Entity', $product); + static::assertSame($product, $entity); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/SaveHandlerTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/SaveHandlerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2dd2d432669886504f64ee4053d318a542fad95e --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/SaveHandlerTest.php @@ -0,0 +1,264 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Model\Product; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductRepository; +use Magento\ConfigurableProduct\Api\Data\OptionInterface; +use Magento\ConfigurableProduct\Model\OptionRepository; +use Magento\ConfigurableProduct\Model\Product\SaveHandler; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableModel; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class SaveHandlerTest + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SaveHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var OptionRepository|MockObject + */ + private $optionRepository; + + /** + * @var ConfigurableFactory|MockObject + */ + private $configurableFactory; + + /** + * @var Configurable|MockObject + */ + private $configurable; + + /** + * @var ProductAttributeRepositoryInterface|MockObject + */ + private $productAttributeRepository; + + /** + * @var SaveHandler + */ + private $saveHandler; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->optionRepository = $this->getMockBuilder(OptionRepository::class) + ->disableOriginalConstructor() + ->setMethods(['save', 'getList', 'deleteById']) + ->getMock(); + + $this->initConfigurableFactoryMock(); + + $this->productAttributeRepository = $this->getMock(ProductAttributeRepositoryInterface::class); + + $this->saveHandler = new SaveHandler( + $this->configurable, + $this->optionRepository, + $this->productAttributeRepository + ); + } + + /** + * @covers \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute + */ + public function testExecuteWithInvalidProductType() + { + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getTypeId', 'getExtensionAttributes']) + ->getMock(); + + $product->expects(static::once()) + ->method('getTypeId') + ->willReturn('simple'); + + $product->expects(static::never()) + ->method('getExtensionAttributes'); + + $entity = $this->saveHandler->execute('Entity', $product); + static::assertSame($product, $entity); + } + + /** + * @covers \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute + */ + public function testExecuteWithEmptyExtensionAttributes() + { + $sku = 'test'; + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getTypeId', 'getExtensionAttributes', 'getSku']) + ->getMock(); + + $product->expects(static::once()) + ->method('getTypeId') + ->willReturn(ConfigurableModel::TYPE_CODE); + $product->expects(static::exactly(1)) + ->method('getSku') + ->willReturn($sku); + + $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $product->expects(static::once()) + ->method('getExtensionAttributes') + ->willReturn($extensionAttributes); + + $extensionAttributes->expects(static::once()) + ->method('getConfigurableProductOptions') + ->willReturn([]); + $extensionAttributes->expects(static::once()) + ->method('getConfigurableProductLinks') + ->willReturn([]); + + $this->optionRepository->expects(static::once()) + ->method('getList') + ->with($sku) + ->willReturn([]); + $this->optionRepository->expects(static::never()) + ->method('deleteById'); + + $this->productAttributeRepository->expects(static::never()) + ->method('get'); + + $entity = $this->saveHandler->execute('Entity', $product); + static::assertSame($product, $entity); + } + + /** + * @covers \Magento\ConfigurableProduct\Model\Product\SaveHandler::execute + */ + public function testExecute() + { + $attributeId = 90; + $sku = 'config-1'; + $id = 25; + $configurableProductLinks = [1, 2, 3]; + + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getTypeId', 'getSku', 'getData', 'getExtensionAttributes']) + ->getMock(); + $product->expects(static::once()) + ->method('getTypeId') + ->willReturn(ConfigurableModel::TYPE_CODE); + $product->expects(static::exactly(2)) + ->method('getSku') + ->willReturn($sku); + + $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $product->expects(static::once()) + ->method('getExtensionAttributes') + ->willReturn($extensionAttributes); + + $attribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributeId', 'loadByProductAndAttribute']) + ->getMock(); + $this->processSaveOptions($attribute, $product, $attributeId, $sku, $id); + + $option = $this->getMockForAbstractClass(OptionInterface::class); + $option->expects(static::once()) + ->method('getId') + ->willReturn($id); + + $list = [$option]; + $this->optionRepository->expects(static::once()) + ->method('getList') + ->with($sku) + ->willReturn($list); + $this->optionRepository->expects(static::never()) + ->method('deleteById'); + + $configurableAttributes = [ + $attribute + ]; + $extensionAttributes->expects(static::once()) + ->method('getConfigurableProductOptions') + ->willReturn($configurableAttributes); + + $extensionAttributes->expects(static::once()) + ->method('getConfigurableProductLinks') + ->willReturn($configurableProductLinks); + + $this->configurable->expects(static::once()) + ->method('saveProducts') + ->with($product, $configurableProductLinks); + + $entity = $this->saveHandler->execute('Entity', $product); + static::assertSame($product, $entity); + } + + /** + * Init mock object for configurable factory + * + * @return void + */ + private function initConfigurableFactoryMock() + { + $this->configurable = $this->getMockBuilder(Configurable::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->configurableFactory = $this->getMockBuilder(ConfigurableFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->configurableFactory->expects(static::any()) + ->method('create') + ->willReturn($this->configurable); + } + + /** + * Mock for options save + * + * @param MockObject $attribute + * @param MockObject $product + * @param $attributeId + * @param $sku + * @param $id + * @return void + */ + private function processSaveOptions(MockObject $attribute, MockObject $product, $attributeId, $sku, $id) + { + $attribute->expects(static::once()) + ->method('getAttributeId') + ->willReturn($attributeId); + + $eavAttribute = $this->getMock(ProductAttributeInterface::class); + $this->productAttributeRepository->expects(static::once()) + ->method('get') + ->with($attributeId) + ->willReturn($eavAttribute); + $attribute->expects(static::once()) + ->method('loadByProductAndAttribute') + ->with($product, $eavAttribute) + ->willReturnSelf(); + + $this->optionRepository->expects(static::once()) + ->method('save') + ->with($sku, $attribute) + ->willReturn($id); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php index 4b88a4cef8894f34c075160330fac17c9277d6e7..ef858285e1b8035e30d9867a909ea61b5892fc18 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php @@ -8,7 +8,11 @@ namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductExtensionInterface; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\Model\Entity\EntityMetadata; +use Magento\Framework\Model\Entity\MetadataPool; /** * Class \Magento\ConfigurableProduct\Test\Unit\Model\Product\Type\ConfigurableTest @@ -68,6 +72,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase */ protected $extensionAttributesJoinProcessorMock; + /** + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + protected $metadataPool; + + /** + * @var EntityMetadata|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityMetadata; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -120,6 +134,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase '', false ); + $this->entityMetadata = $this->getMockBuilder(EntityMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + $this->metadataPool = $this->getMockBuilder(MetadataPool::class) + ->disableOriginalConstructor() + ->getMock(); + $this->metadataPool->expects($this->any()) + ->method('getMetadata') + ->with(ProductInterface::class) + ->willReturn($this->entityMetadata); $this->_model = $this->_objectHelper->getObject( 'Magento\ConfigurableProduct\Model\Product\Type\Configurable', @@ -135,6 +159,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase 'logger' => $logger, 'productRepository' => $this->productRepository, 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock, + 'metadataPool' => $this->metadataPool, ] ); } @@ -170,13 +195,26 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($this->attributeData)); $product->expects($this->once())->method('getIsDuplicate')->will($this->returnValue(true)); $product->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); - $product->expects($this->any())->method('getId')->will($this->returnValue(1)); $product->expects($this->any())->method('getAssociatedProductIds')->will($this->returnValue([2])); $product->expects($this->any())->method('hasData')->with('_cache_instance_used_product_attribute_ids') ->will($this->returnValue(true)); - $product->expects($this->any())->method('getData')->with('_cache_instance_used_product_attribute_ids') - ->will($this->returnValue([1])); - + $extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class) + ->setMethods([ + 'getConfigurableProductOptions', + 'getConfigurableProductLinks' + ]) + ->getMockForAbstractClass(); + $this->entityMetadata->expects(static::once()) + ->method('getLinkField') + ->willReturn('link'); + $dataMap = [ + ['extension_attributes', null, $extensionAttributes], + ['_cache_instance_used_product_attribute_ids', null, 1], + ['link', null, 1], + ]; + $product->expects($this->any()) + ->method('getData') + ->willReturnMap($dataMap); $attribute = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') ->disableOriginalConstructor() ->setMethods(['addData', 'setStoreId', 'setProductId', 'save', '__wakeup', '__sleep']) @@ -644,18 +682,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf()); $productCollection->expects($this->any())->method('addAttributeToFilter')->will($this->returnSelf()); $productCollection->expects($this->once())->method('getFirstItem')->willReturn($firstItemMock); - $firstItemMock->expects($this->once())->method('getId')->willReturn(false); - $productMock->expects($this->at(0)) - ->method('getData') - ->with('_cache_instance_store_filter') - ->willReturn('some_filter'); + $firstItemMock->expects(static::once()) + ->method('getId') + ->willReturn(false); + $dataMap = [ + ['_cache_instance_store_filter', null, 'some_filter'], + ['_cache_instance_products', null, [$usedProductMock]], + ['_cache_instance_product_set_attributes', null, [$eavAttributeMock]], + ]; + $productMock->expects($this->any())->method('getData')->willReturnMap($dataMap); $productMock->expects($this->any())->method('hasData')->willReturn(true); - $productMock->expects($this->at(3))->method('getData') - ->with('_cache_instance_products') - ->willReturn([$usedProductMock]); - $productMock->expects($this->at(5))->method('getData') - ->with('_cache_instance_product_set_attributes') - ->willReturn([$eavAttributeMock]); $eavAttributeMock->expects($this->once())->method('getId')->willReturn(1); $eavAttributeMock->expects($this->once())->method('getAttributeCode')->willReturn('attr_code'); $usedProductMock->expects($this->once()) @@ -701,9 +737,12 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf()); $productCollection->expects($this->any())->method('addAttributeToFilter')->will($this->returnSelf()); $productCollection->expects($this->once())->method('getFirstItem')->willReturn($firstItemMock); - $firstItemMock->expects($this->any())->method('getId')->willReturn(3); + $firstItemMock->expects(static::once()) + ->method('getId') + ->willReturn(3); $this->productRepository->expects($this->once())->method('getById')->with(3)->willReturn($firstItemMock); + $this->assertEquals( $firstItemMock, $this->_model->getProductByAttributes($this->attributeData, $productMock) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php index 6c1eff26cb135e2c1f815ac6e9634073a3b5390b..a84c625f82fdc24fe91f2995937bbe88cc069d0e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php @@ -5,6 +5,9 @@ */ namespace Magento\ConfigurableProduct\Test\Unit\Model\Quote\Item; +use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductOptionExtensionAttributes; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + class CartItemProcessorTest extends \PHPUnit_Framework_TestCase { /** @@ -13,25 +16,30 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $objectFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $optionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $optionExtensionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $optionValueFactoryMock; + /** + * @var ProductOptionExtensionAttributes|MockObject + */ + private $productOptionExtensionAttributes; + protected function setUp() { $this->objectFactoryMock = $this->getMock('\Magento\Framework\DataObject\Factory', ['create'], [], '', false); @@ -57,6 +65,11 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase false ); + $this->productOptionExtensionAttributes = $this->getMockBuilder(ProductOptionExtensionAttributes::class) + ->disableOriginalConstructor() + ->setMethods(['setConfigurableItemOptions']) + ->getMock(); + $this->model = new \Magento\ConfigurableProduct\Model\Quote\Item\CartItemProcessor( $this->objectFactoryMock, $this->optionFactoryMock, @@ -156,21 +169,16 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase $this->optionFactoryMock->expects($this->once())->method('create')->willReturn($productOptionMock); $productOptionMock->expects($this->once())->method('getExtensionAttributes')->willReturn(null); - $extAttributeMock = $this->getMock( - '\Magento\Quote\Api\Data\ProductOptionExtension', - ['setConfigurableItemOptions'], - [], - '', - false - ); - $this->optionExtensionFactoryMock->expects($this->once())->method('create')->willReturn($extAttributeMock); - $extAttributeMock->expects($this->once()) + $this->optionExtensionFactoryMock->expects(static::once()) + ->method('create') + ->willReturn($this->productOptionExtensionAttributes); + $this->productOptionExtensionAttributes->expects($this->once()) ->method('setConfigurableItemOptions') ->with([$optionValueMock]) ->willReturnSelf(); $productOptionMock->expects($this->once()) ->method('setExtensionAttributes') - ->with($extAttributeMock) + ->with($this->productOptionExtensionAttributes) ->willReturnSelf(); $cartItemMock->expects($this->once())->method('setProductOption')->with($productOptionMock)->willReturnSelf(); @@ -200,27 +208,22 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase $optionValueMock->expects($this->once())->method('setOptionId')->with($optionId)->willReturnSelf(); $optionValueMock->expects($this->once())->method('setOptionValue')->with($optionValue)->willReturnSelf(); - $extAttributeMock = $this->getMock( - '\Magento\Quote\Api\Data\ProductOptionExtension', - ['setConfigurableItemOptions'], - [], - '', - false - ); - $extAttributeMock->expects($this->once()) + $this->productOptionExtensionAttributes->expects($this->once()) ->method('setConfigurableItemOptions') ->with([$optionValueMock]) ->willReturnSelf(); $productOptionMock = $this->getMock('\Magento\Quote\Api\Data\ProductOptionInterface'); - $productOptionMock->expects($this->exactly(2))->method('getExtensionAttributes')->willReturn($extAttributeMock); + $productOptionMock->expects(static::exactly(2)) + ->method('getExtensionAttributes') + ->willReturn($this->productOptionExtensionAttributes); $cartItemMock->expects($this->once())->method('getProduct')->willReturn($productMock); $cartItemMock->expects($this->exactly(2))->method('getProductOption')->willReturn($productOptionMock); $productOptionMock->expects($this->once()) ->method('setExtensionAttributes') - ->with($extAttributeMock) + ->with($this->productOptionExtensionAttributes) ->willReturnSelf(); $cartItemMock->expects($this->once())->method('setProductOption')->with($productOptionMock)->willReturnSelf(); diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php index 833ca223a7b9b7ebf1ccc0cde42de3b63e5a166a..92603ce718c92b8e266de24e7285744e5ef98f6d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/Type/ConfigurableTest.php @@ -6,12 +6,15 @@ namespace Magento\ConfigurableProduct\Test\Unit\Model\ResourceModel\Product\Type; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable; +use Magento\Framework\Model\ResourceModel\Db\Context; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; class ConfigurableTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable + * @var Configurable */ protected $configurable; @@ -35,6 +38,11 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase */ protected $metadataPool; + /** + * @var \Magento\Framework\Model\Entity\EntityMetadata|\PHPUnit_Framework_MockObject_MockObject + */ + protected $metadata; + protected function setUp() { $connectionMock = $this->getMockBuilder('\Magento\Framework\DB\Adapter\AdapterInterface')->getMock(); @@ -44,13 +52,13 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $this->relation = $this->getMock('Magento\Catalog\Model\ResourceModel\Product\Relation', [], [], '', false); - $metadata = $this->getMock('Magento\Framework\Model\Entity\EntityMetadata', [], [], '', false); + $this->metadata = $this->getMock('Magento\Framework\Model\Entity\EntityMetadata', [], [], '', false); $this->metadataPool = $this->getMock('Magento\Framework\Model\Entity\MetadataPool', [], [], '', false); $this->metadataPool->expects($this->any()) ->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) - ->willReturn($metadata); + ->with(ProductInterface::class) + ->willReturn($this->metadata); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->configurable = $this->objectManagerHelper->getObject( @@ -65,30 +73,19 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testSaveProducts() { + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $mainProduct */ $mainProduct = $this->getMockBuilder('Magento\Catalog\Model\Product') - ->setMethods(['getIsDuplicate', '__sleep', '__wakeup', 'getTypeInstance', 'getConnection']) + ->setMethods(['__sleep', '__wakeup', 'getData']) ->disableOriginalConstructor() ->getMock(); - $mainProduct->expects($this->once())->method('getIsDuplicate')->will($this->returnValue(false)); - - $typeInstance = $this->getMockBuilder('Magento\ConfigurableProduct\Model\Product\Type\Configurable') - ->disableOriginalConstructor()->getMock(); - $typeInstance->expects($this->once())->method('getUsedProductIds')->will($this->returnValue([1])); - $mainProduct->expects($this->once())->method('getTypeInstance')->will($this->returnValue($typeInstance)); - - $this->configurable->saveProducts($mainProduct, [1, 2, 3]); - } - - public function testSaveProductsForDuplicate() - { - $mainProduct = $this->getMockBuilder('Magento\Catalog\Model\Product') - ->setMethods(['getIsDuplicate', '__sleep', '__wakeup', 'getTypeInstance', 'getConnection']) - ->disableOriginalConstructor() - ->getMock(); - - $mainProduct->expects($this->once())->method('getIsDuplicate')->will($this->returnValue(true)); - $mainProduct->expects($this->never())->method('getTypeInstance')->will($this->returnSelf()); + $this->metadata->expects($this->once()) + ->method('getLinkField') + ->willReturn('link'); + $mainProduct->expects($this->once()) + ->method('getData') + ->with('link') + ->willReturn(3); $this->configurable->saveProducts($mainProduct, [1, 2, 3]); } @@ -98,40 +95,42 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase */ public function testGetConfigurableOptions() { - $configurable = $this->getMock( - 'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable', - [ - 'getTable', - 'getConnection', - ], - [ - $this->resource, - $this->relation, - ], - '', - false - ); + $context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var Configurable|\PHPUnit_Framework_MockObject_MockObject $configurable */ + $configurable = $this->getMockBuilder(Configurable::class) + ->setMethods(['getTable', 'getConnection']) + ->setConstructorArgs([$context, $this->relation, $this->metadataPool]) + ->getMock(); + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder('Magento\Catalog\Model\Product') ->setMethods( [ '__sleep', '__wakeup', - 'getId', + 'getData', ] ) ->disableOriginalConstructor() ->getMock(); + + $this->metadata->expects(self::exactly(2)) + ->method('getLinkField') + ->willReturn('link'); $product->expects($this->once()) - ->method('getId') + ->method('getData') + ->with('link') ->willReturn('getId value'); - $configurable->expects($this->exactly(6)) + $configurable->expects($this->exactly(7)) ->method('getTable') ->will( $this->returnValueMap( [ ['catalog_product_super_attribute', 'catalog_product_super_attribute value'], + ['catalog_product_entity', 'catalog_product_entity value'], ['catalog_product_super_link', 'catalog_product_super_link value'], ['eav_attribute', 'eav_attribute value'], ['catalog_product_entity', 'catalog_product_entity value'], @@ -158,7 +157,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ['super_attribute' => 'catalog_product_super_attribute value'], [ 'sku' => 'entity.sku', - 'product_id' => 'super_attribute.product_id', + 'product_id' => 'product_entity.entity_id', 'attribute_code' => 'attribute.attribute_code', 'option_title' => 'option_value.value', 'super_attribute_label' => 'attribute_label.value' @@ -186,10 +185,15 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $superAttribute, ]; - $select->expects($this->exactly(4)) + $select->expects($this->exactly(5)) ->method('joinInner') ->will($this->returnSelf()) ->withConsecutive( + [ + ['product_entity' => 'catalog_product_entity value'], + 'product_entity.link = super_attribute.product_id', + [] + ], [ ['product_link' => 'catalog_product_super_link value'], 'product_link.parent_id = super_attribute.product_id', diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index f0d2eaa04326e47b7cc8e72f121de5f19879daf8..c0622f73afe82c653383d45e50a5542a6fa9cb76 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -56,14 +56,28 @@ </argument> </arguments> </type> - <type name="Magento\Catalog\Model\Product\Type"> - <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" /> - </type> <type name="Magento\Catalog\Api\ProductRepositoryInterface"> <plugin name="configurableProductSaveOptions" type="\Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave"/> </type> - <type name="Magento\Catalog\Model\Product"> - <plugin name="configurableProductLoadAfter" type="\Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad"/> + <type name="Magento\Catalog\Model\Product\Type"> + <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" /> + </type> + <type name="Magento\Framework\Model\ResourceModel\Db\Relation\ActionPool"> + <arguments> + <argument name="relationActions" xsi:type="array"> + <item name="Magento\Catalog\Api\Data\ProductInterface" xsi:type="array"> + <item name="create" xsi:type="array"> + <item name="create_configurable_options" xsi:type="string">Magento\ConfigurableProduct\Model\Product\SaveHandler</item> + </item> + <item name="update" xsi:type="array"> + <item name="update_configurable_options" xsi:type="string">Magento\ConfigurableProduct\Model\Product\SaveHandler</item> + </item> + <item name="read" xsi:type="array"> + <item name="read_configurable_options" xsi:type="string">Magento\ConfigurableProduct\Model\Product\ReadHandler</item> + </item> + </item> + </argument> + </arguments> </type> <virtualType name="Magento\ConfigurableProduct\Pricing\Price\Pool" type="Magento\Framework\Pricing\Price\Pool"> <arguments> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml index 6b9ba6a541a2f283c26ade1dc83556145cb9949d..c12f75c35f01931729da2fff72760e28b9377d72 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml @@ -14,12 +14,20 @@ <?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> <?php if (($_product->isSaleable() || $_skipSaleableCheck) && count($_attributes)):?> <fieldset id="catalog_product_composite_configure_fields_configurable" class="fieldset admin__fieldset"> - <legend class="legend admin__legend"><span><?php /* @escapeNotVerified */ echo __('Associated Products') ?></span></legend> + <legend class="legend admin__legend"> + <span><?php /* @escapeNotVerified */ echo __('Associated Products') ?></span> + </legend> <div class="product-options"> <div class="field admin__field _required required"> <?php foreach ($_attributes as $_attribute): ?> - <label class="label admin__field-label"><?php /* @escapeNotVerified */ echo $_attribute->getLabel() ?></label> - <div class="control admin__field-control <?php if ($_attribute->getDecoratedIsLast()):?> last<?php endif; ?>"> + <label class="label admin__field-label"><?php + /* @escapeNotVerified */ echo $_attribute->getProductAttribute() + ->getStoreLabel($_product->getStoreId()); + ?></label> + <div class="control admin__field-control <?php + if ($_attribute->getDecoratedIsLast()): + ?> last<?php + endif; ?>"> <select name="super_attribute[<?php /* @escapeNotVerified */ echo $_attribute->getAttributeId() ?>]" id="attribute<?php /* @escapeNotVerified */ echo $_attribute->getAttributeId() ?>" class="admin__control-select required-entry super-attribute-select"> diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/Websites.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/Websites.php new file mode 100644 index 0000000000000000000000000000000000000000..fcd55c590dc50602320154a5cec79784463792b7 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/Websites.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Ui\Component\Listing\Column; + +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Listing\Columns\Column; +use Magento\Store\Model\StoreManagerInterface as StoreManager; + +/** + * Class Websites + */ +class Websites extends Column +{ + /** + * Store manager + * + * @var StoreManager + */ + protected $storeManager; + + /** + * @param ContextInterface $context + * @param UiComponentFactory $uiComponentFactory + * @param StoreManager $storeManager + * @param array $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + UiComponentFactory $uiComponentFactory, + StoreManager $storeManager, + array $components = [], + array $data = [] + ) { + $this->storeManager = $storeManager; + parent::__construct($context, $uiComponentFactory, $components, $data); + } + + /** + * Prepare component configuration + * @return void + */ + public function prepare() + { + parent::prepare(); + if ($this->storeManager->isSingleStoreMode()) { + $this->_data['config']['componentDisabled'] = true; + } + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml index c6aa8a420e34216be37a3a5af03c67cdf42ce4cd..67bab36c9ccde0e3ec470b51523859b352d76239 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml @@ -245,7 +245,7 @@ </item> </argument> </column> - <column name="website_id"> + <column name="website_id" class="Magento\Customer\Ui\Component\Listing\Column\Websites"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">select</item> diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php index a608dc3809e2f77360430f31dca8869eed8022df..c2f390e90e5d6e5330ca2f3db99bb80074da12a7 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php @@ -133,7 +133,7 @@ class UpdateHandler } if ((!array_key_exists($attribute->getAttributeCode(), $snapshot) || $snapshot[$attribute->getAttributeCode()] === false) - && !empty($data[$attribute->getAttributeCode()]) + && array_key_exists($attribute->getAttributeCode(), $data) && !$attribute->isValueEmpty($data[$attribute->getAttributeCode()]) ) { $this->attributePersistor->registerInsert( @@ -146,7 +146,7 @@ class UpdateHandler } if (array_key_exists($attribute->getAttributeCode(), $snapshot) && $snapshot[$attribute->getAttributeCode()] !== false - && !empty($data[$attribute->getAttributeCode()]) + && array_key_exists($attribute->getAttributeCode(), $data) && $snapshot[$attribute->getAttributeCode()] != $data[$attribute->getAttributeCode()] && !$attribute->isValueEmpty($data[$attribute->getAttributeCode()]) ) { diff --git a/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php b/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php index ca30141e79a3277af049df340c3f68fb263508d0..be7b2ed01f72802008880444a3320ad5b496db14 100644 --- a/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php +++ b/app/code/Magento/GiftMessage/Model/Type/Plugin/Multishipping.php @@ -32,7 +32,7 @@ class Multishipping /** * @param \Magento\Multishipping\Model\Checkout\Type\Multishipping $subject * @param array|null $methods - * @return $this + * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSetShippingMethods( diff --git a/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php b/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php index 6359573d0d896ee41b510c2ff641c76614bbe261..cad727dbff21aabaf726f37146554dbc190a7596 100644 --- a/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php +++ b/app/code/Magento/GoogleOptimizer/Model/Plugin/Catalog/Category/DataProvider.php @@ -43,6 +43,8 @@ class DataProvider { $result['category_view_optimization']['arguments']['data']['disabled'] = !$this->_helper->isGoogleExperimentActive(); + $result['category_view_optimization']['arguments']['data']['config']['componentType'] = + \Magento\Ui\Component\Form\Fieldset::NAME; return $result; } diff --git a/app/code/Magento/GoogleOptimizer/etc/module.xml b/app/code/Magento/GoogleOptimizer/etc/module.xml index cddd051c5a53ef58ec70d9a35aedcd902662b2ef..4d0be0af435e075a6d7ab2f0065d8ca3966394e4 100644 --- a/app/code/Magento/GoogleOptimizer/etc/module.xml +++ b/app/code/Magento/GoogleOptimizer/etc/module.xml @@ -11,6 +11,7 @@ <module name="Magento_GoogleAnalytics"/> <module name="Magento_Catalog"/> <module name="Magento_Cms"/> + <module name="Magento_Ui"/> </sequence> </module> </config> diff --git a/app/code/Magento/OfflineShipping/view/adminhtml/ui_component/salesrulestaging_update_form.xml b/app/code/Magento/OfflineShipping/view/adminhtml/ui_component/salesrulestaging_update_form.xml new file mode 100644 index 0000000000000000000000000000000000000000..b568252e3cc32328769d074ac555a679f2f1b05f --- /dev/null +++ b/app/code/Magento/OfflineShipping/view/adminhtml/ui_component/salesrulestaging_update_form.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> + <fieldset name="actions"> + <field name="simple_free_shipping"> + <argument name="data" xsi:type="array"> + <item name="options" xsi:type="object">Magento\OfflineShipping\Model\Source\SalesRule\FreeShippingOptions</item> + <item name="config" xsi:type="array"> + <item name="dataType" xsi:type="string">text</item> + <item name="label" xsi:type="string" translate="true">Free Shipping</item> + <item name="formElement" xsi:type="string">select</item> + <item name="dataScope" xsi:type="string">simple_free_shipping</item> + <item name="caption" xsi:type="string" translate="true">-- Please Select --</item> + <item name="sortOrder" xsi:type="number">80</item> + </item> + </argument> + </field> + </fieldset> +</form> diff --git a/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php b/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php index 79df0429fbf6fca31b7ac68d2f5522aa1f0cf190..457f77496bd32489d3f87e2e93f606aeadaf2a32 100644 --- a/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php +++ b/app/code/Magento/PageCache/Block/System/Config/Form/Field/Export.php @@ -27,11 +27,10 @@ class Export extends \Magento\Config\Block\System\Config\Form\Field 'varnish' => $this->getVarnishVersion() ]; - $url = $this->getUrl("*/PageCache/exportVarnishConfig", $params); $data = [ 'id' => 'system_full_page_cache_varnish_export_button_version' . $this->getVarnishVersion(), - 'label' => __('Export VCL for Varnish ') . $this->getVarnishVersion(), - 'onclick' => "setLocation('" . $url . "')", + 'label' => $this->getLabel(), + 'onclick' => "setLocation('" . $this->getVarnishUrl($params) . "')", ]; $html = $buttonBlock->setData($data)->toHtml(); @@ -48,11 +47,30 @@ class Export extends \Magento\Config\Block\System\Config\Form\Field return 0; } + /** + * @return \Magento\Framework\Phrase + */ + private function getLabel() + { + return __('Export VCL for Varnish %1', $this->getVarnishVersion()); + } + + /** + * @param array $params + * + * @return string + */ + private function getVarnishUrl($params = []) + { + return $this->getUrl('*/PageCache/exportVarnishConfig', $params); + } + /** * Return PageCache TTL value from config * to avoid saving empty field * * @return string + * @deprecated */ public function getTtlValue() { diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index d788275f40fffecfe12e82b07abb964f818c26ef..0c62c8911ae369d6271398a3669f50f62950ec04 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -6,11 +6,13 @@ namespace Magento\Quote\Model; use Magento\Framework\Api\SortOrder; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Model\Quote; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Api\Search\FilterGroup; use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection; +use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory; use Magento\Framework\Exception\InputException; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; @@ -62,6 +64,7 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface * @param \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection * @param \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory $searchResultsDataFactory * @param JoinProcessorInterface $extensionAttributesJoinProcessor + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( QuoteFactory $quoteFactory, @@ -73,7 +76,6 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface $this->quoteFactory = $quoteFactory; $this->storeManager = $storeManager; $this->searchResultsDataFactory = $searchResultsDataFactory; - $this->quoteCollection = $quoteCollection; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; } @@ -173,11 +175,27 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface return $quote; } + /** + * Get quote collection + * Temporary method to support release backward compatibility. + * + * @deprecated + * @return QuoteCollection + */ + protected function getQuoteCollection() + { + /** @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory $collectionFactory */ + $collectionFactory = ObjectManager::getInstance()->get(QuoteCollectionFactory::class); + return $collectionFactory->create(); + } + /** * {@inheritdoc} */ public function getList(\Magento\Framework\Api\SearchCriteria $searchCriteria) { + $this->quoteCollection = $this->getQuoteCollection(); + /** @var \Magento\Quote\Api\Data\CartSearchResultsInterface $searchData */ $searchData = $this->searchResultsDataFactory->create(); $searchData->setSearchCriteria($searchCriteria); diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php index 4b4cd113eb70b6fab4b871e674d49e5fa32839be..40d58b4ea521d831facd52af0c0a09b73af54188 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php @@ -366,7 +366,6 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase $sortOrderMock->expects($this->once())->method('getDirection')->will($this->returnValue($direction)); $this->quoteCollectionMock->expects($this->once())->method('addOrder')->with('id', $expectedDirection); - $searchCriteriaMock->expects($this->once())->method('getCurrentPage')->will($this->returnValue(1)); $searchCriteriaMock->expects($this->once())->method('getPageSize')->will($this->returnValue(10)); $this->quoteCollectionMock->expects($this->once())->method('setCurPage')->with(1); @@ -381,6 +380,18 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase $this->quoteCollectionMock->expects($this->once())->method('getItems')->willReturn([$cartMock]); $searchResult->expects($this->once())->method('setItems')->with([$cartMock]); + $this->model = $this->getMock( + 'Magento\Quote\Model\QuoteRepository', + ['getQuoteCollection'], + [ + 'quoteFactory' => $this->quoteFactoryMock, + 'storeManager' => $this->storeManagerMock, + 'quoteCollection' => $this->quoteCollectionMock, + 'searchResultsDataFactory' => $this->searchResultsDataFactory, + 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock + ] + ); + $this->model->expects($this->once())->method('getQuoteCollection')->willReturn($this->quoteCollectionMock); $this->assertEquals($searchResult, $this->model->getList($searchCriteriaMock)); } diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php index 52716b88e8ab602c1848ee3afe2b4a05c889bc2d..f0e265f203ca6c19d35fbe6445a9bf717123c539 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Actions.php @@ -5,6 +5,8 @@ */ namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab; +use Magento\Framework\App\ObjectManager; + class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements \Magento\Ui\Component\Layout\Tabs\TabInterface { @@ -22,6 +24,7 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements /** * @var \Magento\Config\Model\Config\Source\Yesno + * @deprecated */ protected $_sourceYesno; @@ -31,6 +34,13 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements protected $_nameInLayout = 'actions_apply_to'; /** + * @var \Magento\SalesRule\Model\RuleFactory + */ + private $ruleFactory; + + /** + * Initialize dependencies. + * * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Data\FormFactory $formFactory @@ -54,6 +64,17 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements parent::__construct($context, $registry, $formFactory, $data); } + /** + * @return \Magento\SalesRule\Model\RuleFactory + * @deprecated + */ + public function getRuleFactory() + { + if ($this->ruleFactory instanceof \Magento\SalesRule\Model\RuleFactory) { + $this->ruleFactory = ObjectManager::getInstance()->get('\Magento\SalesRule\Model\RuleFactory'); + } + } + /** * {@inheritdoc} * @codeCoverageIgnore @@ -121,24 +142,53 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements * Prepare form before rendering HTML * * @return $this - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _prepareForm() { $model = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); + $form = $this->addTabToForm($model); + $this->setForm($form); + + return parent::_prepareForm(); + } + + /** + * Handles addition of actions tab to supplied form. + * + * @param \Magento\SalesRule\Model\Rule $model + * @param string $fieldsetId + * @param string $formName + * @return \Magento\Framework\Data\Form + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function addTabToForm($model, $fieldsetId = 'actions_fieldset', $formName = 'sales_rule_form') + { + if (!$model) { + $id = $this->getRequest()->getParam('id'); + $model = $this->ruleFactory->create(); + $model->load($id); + } + + $actionsFieldSetId = $model->getActionsFieldSetId($formName); + + $newChildUrl = $this->getUrl( + 'sales_rule/promo_quote/newActionHtml/form/rule_actions_fieldset_' . $actionsFieldSetId, + ['form_namespace' => $formName] + ); /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create(); $form->setHtmlIdPrefix('rule_'); - $renderer = $this->_rendererFieldset->setTemplate( 'Magento_CatalogRule::promo/fieldset.phtml' )->setNewChildUrl( - $this->getUrl('sales_rule/promo_quote/newActionHtml/form/rule_actions_fieldset') + $newChildUrl + )->setFieldSetId( + $actionsFieldSetId ); $fieldset = $form->addFieldset( - 'actions_fieldset', + $fieldsetId, [ 'legend' => __( 'Apply the rule only to cart items matching the following conditions ' . @@ -157,7 +207,7 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements 'label' => __('Apply To'), 'title' => __('Apply To'), 'required' => true, - 'data-form-part' => 'sales_rule_form' + 'data-form-part' => $formName ] )->setRule( $model @@ -168,6 +218,7 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements $this->_eventManager->dispatch('adminhtml_block_salesrule_actions_prepareform', ['form' => $form]); $form->setValues($model->getData()); + $this->setActionFormName($model->getActions(), $formName); if ($model->isReadonly()) { foreach ($fieldset->getElements() as $element) { @@ -175,7 +226,23 @@ class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements } } - $this->setForm($form); - return parent::_prepareForm(); + return $form; + } + + /** + * Handles addition of form name to action and its actions. + * + * @param \Magento\Rule\Model\Condition\AbstractCondition $actions + * @param string $formName + * @return void + */ + private function setActionFormName(\Magento\Rule\Model\Condition\AbstractCondition $actions, $formName) + { + $actions->setFormName($formName); + if ($actions->getActions() && is_array($actions->getActions())) { + foreach ($actions->getActions() as $condition) { + $this->setActionFormName($condition, $formName); + } + } } } diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php index 0e1d292b2958310977a029f17f0da63df6e0ea5c..7aed719f41b329f6bbf5d327ae44edb949a5d9b0 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php @@ -5,6 +5,8 @@ */ namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab; +use Magento\Framework\App\ObjectManager; + class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements \Magento\Ui\Component\Layout\Tabs\TabInterface { @@ -26,6 +28,13 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements protected $_nameInLayout = 'conditions_apply_to'; /** + * @var \Magento\SalesRule\Model\RuleFactory + */ + private $ruleFactory; + + /** + * Initialize dependencies. + * * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Data\FormFactory $formFactory @@ -46,6 +55,17 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements parent::__construct($context, $registry, $formFactory, $data); } + /** + * @return \Magento\SalesRule\Model\RuleFactory + * @deprecated + */ + public function getRuleFactory() + { + if ($this->ruleFactory instanceof \Magento\SalesRule\Model\RuleFactory) { + $this->ruleFactory = ObjectManager::getInstance()->get('\Magento\SalesRule\Model\RuleFactory'); + } + } + /** * {@inheritdoc} * @codeCoverageIgnore @@ -117,19 +137,47 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements protected function _prepareForm() { $model = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); + $form = $this->addTabToForm($model); + $this->setForm($form); + + return parent::_prepareForm(); + } + + /** + * Handles addition of conditions tab to supplied form. + * + * @param \Magento\SalesRule\Model\Rule $model + * @param string $fieldsetId + * @param string $formName + * @return \Magento\Framework\Data\Form + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function addTabToForm($model, $fieldsetId = 'conditions_fieldset', $formName = 'sales_rule_form') + { + if (!$model) { + $id = $this->getRequest()->getParam('id'); + $model = $this->ruleFactory->create(); + $model->load($id); + } + $conditionsFieldSetId = $model->getConditionsFieldSetId($formName); + $newChildUrl = $this->getUrl( + 'sales_rule/promo_quote/newConditionHtml/form/' . $conditionsFieldSetId, + ['form_namespace' => $formName] + ); /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create(); $form->setHtmlIdPrefix('rule_'); - $renderer = $this->_rendererFieldset->setTemplate( 'Magento_CatalogRule::promo/fieldset.phtml' )->setNewChildUrl( - $this->getUrl('sales_rule/promo_quote/newConditionHtml/form/rule_conditions_fieldset') + $newChildUrl + )->setFieldSetId( + $conditionsFieldSetId ); $fieldset = $form->addFieldset( - 'conditions_fieldset', + $fieldsetId, [ 'legend' => __( 'Apply the rule only if the following conditions are met (leave blank for all products).' @@ -138,16 +186,15 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements )->setRenderer( $renderer ); - $fieldset->addField( 'conditions', 'text', [ - 'name' => 'conditions', - 'label' => __('Conditions'), - 'title' => __('Conditions'), - 'required' => true, - 'data-form-part' => 'sales_rule_form' + 'name' => 'conditions', + 'label' => __('Conditions'), + 'title' => __('Conditions'), + 'required' => true, + 'data-form-part' => $formName ] )->setRule( $model @@ -156,8 +203,24 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements ); $form->setValues($model->getData()); - $this->setForm($form); + $this->setConditionFormName($model->getConditions(), $formName); + return $form; + } - return parent::_prepareForm(); + /** + * Handles addition of form name to condition and its conditions. + * + * @param \Magento\Rule\Model\Condition\AbstractCondition $conditions + * @param string $formName + * @return void + */ + private function setConditionFormName(\Magento\Rule\Model\Condition\AbstractCondition $conditions, $formName) + { + $conditions->setFormName($formName); + if ($conditions->getConditions() && is_array($conditions->getConditions())) { + foreach ($conditions->getConditions() as $condition) { + $this->setConditionFormName($condition, $formName); + } + } } } diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php index 1e9ae7645d574b8c9b3a396bab0821179a8d27b2..213db3a3c98e56c65eb9aa560c571b1424a260b0 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Labels.php @@ -8,6 +8,31 @@ namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab; class Labels extends \Magento\Backend\Block\Widget\Form\Generic implements \Magento\Ui\Component\Layout\Tabs\TabInterface { + /** + * @var \Magento\SalesRule\Model\RuleFactory + */ + private $ruleFactory; + + /** + * Initialize dependencies. + * + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Data\FormFactory $formFactory + * @param \Magento\SalesRule\Model\RuleFactory $ruleFactory + * @param array $data + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + \Magento\Framework\Registry $registry, + \Magento\Framework\Data\FormFactory $formFactory, + \Magento\SalesRule\Model\RuleFactory $ruleFactory, + array $data = [] + ) { + $this->ruleFactory = $ruleFactory; + parent::__construct($context, $registry, $formFactory, $data); + } + /** * @var string */ @@ -83,7 +108,13 @@ class Labels extends \Magento\Backend\Block\Widget\Form\Generic implements */ protected function _prepareForm() { - $rule = $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); + $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); + + if (!$rule) { + $id = $this->getRequest()->getParam('id'); + $rule = $this->ruleFactory->create(); + $rule->load($id); + } /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create(); diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php index e1cb1f5a19a4f9a2f10ed48211fae21a038b18e4..57f45b87a9bd1e647baffa934942a25e133c4ca6 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Edit.php @@ -65,10 +65,14 @@ class Edit extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote $model->addData($data); } - $model->getConditions()->setJsFormObject('rule_conditions_fieldset'); $model->getConditions()->setFormName('sales_rule_form'); - $model->getActions()->setJsFormObject('rule_actions_fieldset'); + $model->getConditions()->setJsFormObject( + $model->getConditionsFieldSetId($model->getConditions()->getFormName()) + ); $model->getActions()->setFormName('sales_rule_form'); + $model->getActions()->setJsFormObject( + $model->getActionsFieldSetId($model->getActions()->getFormName()) + ); $this->_initAction(); diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php index cf383bfed2d0c28b46e728a2bdc40425458018f3..061890e406ddf70a98dedc57babda6efbe195c0c 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php @@ -16,6 +16,7 @@ class NewActionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote public function execute() { $id = $this->getRequest()->getParam('id'); + $formName = $this->getRequest()->getParam('form_namespace'); $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type'))); $type = $typeArr[0]; @@ -35,7 +36,7 @@ class NewActionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote } if ($model instanceof \Magento\Rule\Model\Condition\AbstractCondition) { - $model->setJsFormObject($this->getRequest()->getParam('form')); + $model->setJsFormObject($formName); $html = $model->asHtmlRecursive(); } else { $html = ''; diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php index fb0c502a37e1a72734f9d26507ad47ec16e46bb3..dd71d81f871775bed8beba332ad8ff4b89fb020e 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php @@ -16,6 +16,7 @@ class NewConditionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quo public function execute() { $id = $this->getRequest()->getParam('id'); + $formName = $this->getRequest()->getParam('form_namespace'); $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type'))); $type = $typeArr[0]; @@ -36,6 +37,7 @@ class NewConditionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quo if ($model instanceof \Magento\Rule\Model\Condition\AbstractCondition) { $model->setJsFormObject($this->getRequest()->getParam('form')); + $model->setFormName($formName); $html = $model->asHtmlRecursive(); } else { $html = ''; diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php index fea1326e605307bcadf9ba892e0baa69d35f3345..122bee0b2c38abed6ed57b149e82eb3355b5c85b 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php @@ -403,4 +403,30 @@ class Rule extends AbstractResource } return $this; } + + /** + * Delete the object + * + * @param \Magento\Framework\Model\AbstractModel $object + * @return $this + * @throws \Exception + */ + public function delete(AbstractModel $object) + { + $this->transactionManager->start($this->getConnection()); + try { + $object->beforeDelete(); + $this->_beforeDelete($object); + $this->entityManager->delete('Magento\SalesRule\Api\Data\RuleInterface', $object); + $this->_afterDelete($object); + $object->isDeleted(true); + $object->afterDelete(); + $this->transactionManager->commit(); + $object->afterDeleteCommit(); + } catch (\Exception $exception) { + $this->transactionManager->rollBack(); + throw $exception; + } + return $this; + } } diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 209ba4be22a40eb52fff869712d23a01033fddd9..bd9f6872d1f912923bdfcbf8876a40757a6f95e1 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -11,9 +11,8 @@ namespace Magento\SalesRule\Model\ResourceModel\Rule; use Magento\Quote\Model\Quote\Address; /** - * Sales Rules resource collection model - * - * @author Magento Core Team <core@magentocommerce.com> + * Sales Rules resource collection model. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection { @@ -73,6 +72,52 @@ class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\Abstr $this->_map['fields']['rule_id'] = 'main_table.rule_id'; } + /** + * @param string $entityType + * @param string $objectField + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + protected function mapAssociatedEntities($entityType, $objectField) + { + if (!$this->_items) { + return; + } + + $entityInfo = $this->_getAssociatedEntityInfo($entityType); + $ruleIdField = $entityInfo['rule_id_field']; + $entityIds = $this->getColumnValues($ruleIdField); + + $select = $this->getConnection()->select()->from( + $this->getTable($entityInfo['associations_table']) + )->where( + $ruleIdField . ' IN (?)', + $entityIds + ); + + $associatedEntities = $this->getConnection()->fetchAll($select); + + array_map(function ($associatedEntity) use ($entityInfo, $ruleIdField, $objectField) { + $item = $this->getItemByColumnValue($ruleIdField, $associatedEntity[$ruleIdField]); + $itemAssociatedValue = $item->getData($objectField) === null ? [] : $item->getData($objectField); + $itemAssociatedValue[] = $associatedEntity[$entityInfo['entity_id_field']]; + $item->setData($objectField, $itemAssociatedValue); + }, $associatedEntities); + } + + /** + * @return $this + * @throws \Exception + */ + protected function _afterLoad() + { + $this->mapAssociatedEntities('website', 'website_ids'); + $this->mapAssociatedEntities('customer_group', 'customer_group_ids'); + + $this->setFlag('add_websites_to_result', false); + return parent::_afterLoad(); + } + /** * Filter collection by specified website, customer group, coupon code, date. * Filter collection to use only active rules. @@ -253,4 +298,27 @@ class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\Abstr return $this; } + + /** + * Limit rules collection by specific customer group + * + * @param int $customerGroupId + * @return $this + */ + public function addCustomerGroupFilter($customerGroupId) + { + $entityInfo = $this->_getAssociatedEntityInfo('customer_group'); + if (!$this->getFlag('is_customer_group_joined')) { + $this->setFlag('is_customer_group_joined', true); + $this->getSelect()->join( + ['customer_group' => $this->getTable($entityInfo['associations_table'])], + $this->getConnection() + ->quoteInto('customer_group.' . $entityInfo['entity_id_field'] . ' = ?', $customerGroupId) + . ' AND main_table.' . $entityInfo['rule_id_field'] . ' = customer_group.' + . $entityInfo['rule_id_field'], + [] + ); + } + return $this; + } } diff --git a/app/code/Magento/SalesRule/Model/Rule.php b/app/code/Magento/SalesRule/Model/Rule.php index a171769505db56b20f1999228ff70d69c994486b..6a6382bb938ee584b56d813e5d3dbb6915ab96ac 100644 --- a/app/code/Magento/SalesRule/Model/Rule.php +++ b/app/code/Magento/SalesRule/Model/Rule.php @@ -598,4 +598,22 @@ class Rule extends \Magento\Rule\Model\AbstractModel } return $address; } + + /** + * @param string $formName + * @return string + */ + public function getConditionsFieldSetId($formName = '') + { + return $formName . 'rule_conditions_fieldset_' . $this->getId(); + } + + /** + * @param string $formName + * @return string + */ + public function getActionsFieldSetId($formName = '') + { + return $formName . 'rule_actions_fieldset_' . $this->getId(); + } } diff --git a/app/code/Magento/SalesRule/Model/Rule/DataProvider.php b/app/code/Magento/SalesRule/Model/Rule/DataProvider.php index 768ec754e0a2fde083f9597bd1b83a137533a714..79bde496876fd735e6d2016d8c6884f9fa6a262e 100644 --- a/app/code/Magento/SalesRule/Model/Rule/DataProvider.php +++ b/app/code/Magento/SalesRule/Model/Rule/DataProvider.php @@ -8,14 +8,9 @@ namespace Magento\SalesRule\Model\Rule; use Magento\SalesRule\Model\ResourceModel\Rule\Collection; use Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory; use Magento\SalesRule\Model\Rule; -use Magento\Store\Model\System\Store; -use Magento\Customer\Api\GroupRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Convert\DataObject; /** * Class DataProvider - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { @@ -29,31 +24,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ protected $loadedData; - /** - * @var Store - */ - protected $store; - - /** - * @var GroupRepositoryInterface - */ - protected $groupRepository; - - /** - * @var SearchCriteriaBuilder - */ - protected $searchCriteriaBuilder; - - /** - * @var DataObject - */ - protected $objectConverter; - - /** - * @var \Magento\SalesRule\Model\RuleFactory - */ - protected $salesRuleFactory; - /** * Core registry * @@ -62,203 +32,52 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider protected $coreRegistry; /** - * DataProvider constructor. + * @var \Magento\SalesRule\Model\Rule\Metadata\ValueProvider + */ + protected $metadataValueProvider; + + /** + * Initialize dependencies. * * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory - * @param Store $store - * @param GroupRepositoryInterface $groupRepository - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param DataObject $objectConverter - * @param \Magento\SalesRule\Model\RuleFactory $salesRuleFactory * @param \Magento\Framework\Registry $registry + * @param Metadata\ValueProvider $metadataValueProvider * @param array $meta * @param array $data - * - * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, $primaryFieldName, $requestFieldName, CollectionFactory $collectionFactory, - Store $store, - GroupRepositoryInterface $groupRepository, - SearchCriteriaBuilder $searchCriteriaBuilder, - DataObject $objectConverter, - \Magento\SalesRule\Model\RuleFactory $salesRuleFactory, \Magento\Framework\Registry $registry, + \Magento\SalesRule\Model\Rule\Metadata\ValueProvider $metadataValueProvider, array $meta = [], array $data = [] ) { - parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->collection = $collectionFactory->create(); - $this->store = $store; - $this->groupRepository = $groupRepository; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->objectConverter = $objectConverter; - $this->salesRuleFactory = $salesRuleFactory; $this->coreRegistry = $registry; - $this->initMeta(); + $this->metadataValueProvider = $metadataValueProvider; + $meta = array_replace_recursive($this->getMetadataValues(), $meta); + parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); } /** - * @return void - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * Get metadata values + * + * @return array */ - protected function initMeta() + protected function getMetadataValues() { - $customerGroups = $this->groupRepository->getList($this->searchCriteriaBuilder->create())->getItems(); - $applyOptions = [ - ['label' => __('Percent of product price discount'), 'value' => Rule::BY_PERCENT_ACTION], - ['label' => __('Fixed amount discount'), 'value' => Rule::BY_FIXED_ACTION], - ['label' => __('Fixed amount discount for whole cart'), 'value' => Rule::CART_FIXED_ACTION], - ['label' => __('Buy X get Y free (discount amount is Y)'), 'value' => Rule::BUY_X_GET_Y_ACTION] - ]; - - $couponTypesOptions = []; - $couponTypes = $this->salesRuleFactory->create()->getCouponTypes(); - foreach ($couponTypes as $key => $couponType) { - $couponTypesOptions[] = [ - 'label' => $couponType, - 'value' => $key, - ]; - } - $rule = $this->coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); - $labels = $rule->getStoreLabels(); - - $this->meta = [ - 'rule_information' => [ - 'children' => [ - 'website_ids' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => $this->store->getWebsiteValuesForForm(), - ], - ], - ], - ], - 'is_active' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => [ - ['label' => __('Active'), 'value' => '1'], - ['label' => __('Inactive'), 'value' => '0'] - ], - ], - ], - ], - ], - 'customer_group_ids' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => $this->objectConverter->toOptionArray($customerGroups, 'id', 'code'), - ], - ], - ], - ], - 'coupon_type' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => $couponTypesOptions, - ], - ], - ], - ], - 'is_rss' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => [ - ['label' => __('Yes'), 'value' => '1'], - ['label' => __('No'), 'value' => '0'] - ], - ], - ], - ], - ], - ] - ], - 'actions' => [ - 'children' => [ - 'simple_action' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => $applyOptions - ], - ] - ] - ], - 'discount_amount' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'value' => '0', - ], - ], - ], - ], - 'discount_qty' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'value' => '0', - ], - ], - ], - ], - 'apply_to_shipping' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => [ - ['label' => __('Yes'), 'value' => '1'], - ['label' => __('No'), 'value' => '0'] - ] - ], - ], - ], - ], - 'stop_rules_processing' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'options' => [ - ['label' => __('Yes'), 'value' => '1'], - ['label' => __('No'), 'value' => '0'], - ], - ], - ] - ] - ], - ] - ], - 'labels' => [ - 'children' => [ - 'store_labels[0]' => [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'value' => isset($labels[0]) ? $labels[0] : '', - ], - ] - ] - ] - ] - ], - ]; + return $this->metadataValueProvider->getMetadataValues($rule); } /** - * @return array + * {@inheritdoc} */ public function getData() { diff --git a/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php b/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..f2845b5522ec292c46c649a682bc5105b8b684d5 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php @@ -0,0 +1,222 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Model\Rule\Metadata; + +use Magento\SalesRule\Model\ResourceModel\Rule\Collection; +use Magento\SalesRule\Model\Rule; +use Magento\Store\Model\System\Store; +use Magento\Customer\Api\GroupRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Convert\DataObject; + +/** + * Metadata provider for sales rule edit form. + */ +class ValueProvider +{ + /** + * @var Store + */ + protected $store; + + /** + * @var GroupRepositoryInterface + */ + protected $groupRepository; + + /** + * @var SearchCriteriaBuilder + */ + protected $searchCriteriaBuilder; + + /** + * @var DataObject + */ + protected $objectConverter; + + /** + * @var \Magento\SalesRule\Model\RuleFactory + */ + protected $salesRuleFactory; + + /** + * Initialize dependencies. + * + * @param Store $store + * @param GroupRepositoryInterface $groupRepository + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param DataObject $objectConverter + * @param \Magento\SalesRule\Model\RuleFactory $salesRuleFactory + */ + public function __construct( + Store $store, + GroupRepositoryInterface $groupRepository, + SearchCriteriaBuilder $searchCriteriaBuilder, + DataObject $objectConverter, + \Magento\SalesRule\Model\RuleFactory $salesRuleFactory + ) { + $this->store = $store; + $this->groupRepository = $groupRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->objectConverter = $objectConverter; + $this->salesRuleFactory = $salesRuleFactory; + } + + /** + * Get metadata for sales rule form. It will be merged with form UI component declaration. + * + * @param Rule $rule + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getMetadataValues(\Magento\SalesRule\Model\Rule $rule) + { + $customerGroups = $this->groupRepository->getList($this->searchCriteriaBuilder->create())->getItems(); + $applyOptions = [ + ['label' => __('Percent of product price discount'), 'value' => Rule::BY_PERCENT_ACTION], + ['label' => __('Fixed amount discount'), 'value' => Rule::BY_FIXED_ACTION], + ['label' => __('Fixed amount discount for whole cart'), 'value' => Rule::BY_PERCENT_ACTION], + ['label' => __('Buy X get Y free (discount amount is Y)'), 'value' => Rule::BUY_X_GET_Y_ACTION] + ]; + + $couponTypesOptions = []; + $couponTypes = $this->salesRuleFactory->create()->getCouponTypes(); + foreach ($couponTypes as $key => $couponType) { + $couponTypesOptions[] = [ + 'label' => $couponType, + 'value' => $key, + ]; + } + + $labels = $rule->getStoreLabels(); + + return [ + 'rule_information' => [ + 'children' => [ + 'website_ids' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => $this->store->getWebsiteValuesForForm(), + ], + ], + ], + ], + 'is_active' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => [ + ['label' => __('Active'), 'value' => '1'], + ['label' => __('Inactive'), 'value' => '0'] + ], + ], + ], + ], + ], + 'customer_group_ids' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => $this->objectConverter->toOptionArray($customerGroups, 'id', 'code'), + ], + ], + ], + ], + 'coupon_type' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => $couponTypesOptions, + ], + ], + ], + ], + 'is_rss' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => [ + ['label' => __('Yes'), 'value' => '1'], + ['label' => __('No'), 'value' => '0'] + ], + ], + ], + ], + ], + ] + ], + 'actions' => [ + 'children' => [ + 'simple_action' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => $applyOptions + ], + ] + ] + ], + 'discount_amount' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'value' => '0', + ], + ], + ], + ], + 'discount_qty' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'value' => '0', + ], + ], + ], + ], + 'apply_to_shipping' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => [ + ['label' => __('Yes'), 'value' => '1'], + ['label' => __('No'), 'value' => '0'] + ] + ], + ], + ], + ], + 'stop_rules_processing' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'options' => [ + ['label' => __('Yes'), 'value' => '1'], + ['label' => __('No'), 'value' => '0'], + ], + ], + ] + ] + ], + ] + ], + 'labels' => [ + 'children' => [ + 'store_labels[0]' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'value' => isset($labels[0]) ? $labels[0] : '', + ], + ] + ] + ] + ] + ], + ]; + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php index a3a74f10ecb26295767a61cab45f7ec5cfdfd638..dc09ba26f904842fa531cf94e039f0d2253002a3 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/ResourceModel/RuleTest.php @@ -80,7 +80,7 @@ class RuleTest extends \PHPUnit_Framework_TestCase ->willReturn($this->resourcesMock); $this->entityManager = $this->getMockBuilder('Magento\Framework\Model\EntityManager') - ->setMethods(['load', 'save']) + ->setMethods(['load', 'save', 'delete']) ->disableOriginalConstructor() ->getMock(); @@ -125,7 +125,7 @@ class RuleTest extends \PHPUnit_Framework_TestCase 'context' => $context, 'connectionName' => $connectionName, 'associatedEntitiesMap' => $associatedEntitiesMap, - 'entityManager' => $this->entityManager + 'entityManager' => $this->entityManager, ] ); } @@ -310,4 +310,38 @@ class RuleTest extends \PHPUnit_Framework_TestCase return $abstractModelMock; } + + public function testDeleteSuccess() + { + $this->transactionManagerMock->expects($this->once())->method('start'); + $abstractModelMock = $this->getMockBuilder('Magento\Rule\Model\AbstractModel') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $abstractModelMock->expects($this->once())->method('beforeDelete'); + $this->entityManager->expects($this->once())->method('delete'); + $abstractModelMock->expects($this->once())->method('isDeleted'); + $abstractModelMock->expects($this->once())->method('afterDelete'); + $this->transactionManagerMock->expects($this->once())->method('commit'); + + $this->model->delete($abstractModelMock); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage To test exception + */ + public function testDeletionRollbackOnFailure() + { + $expectedException = new \Exception(__('To test exception')); + $this->transactionManagerMock->expects($this->once())->method('start'); + $abstractModelMock = $this->getMockBuilder('Magento\Rule\Model\AbstractModel') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $abstractModelMock->expects($this->once())->method('beforeDelete')->willThrowException($expectedException); + $this->transactionManagerMock->expects($this->once())->method('rollBack'); + + $this->model->delete($abstractModelMock); + } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php index 385443fc5ef38978e68b5d859325f4bd00f80186..aeb0508c8f3b63156c978ab695b9ee2ce50e0415 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/DataProviderTest.php @@ -53,16 +53,6 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase '', false ); - $this->searchCriteriaBuilderMock = $this->getMock( - 'Magento\Framework\Api\SearchCriteriaBuilder', - [], - [], - '', - false - ); - $this->storeMock = $this->getMock('Magento\Store\Model\System\Store', [], [], '', false); - $this->groupRepositoryMock = $this->getMock('Magento\Customer\Api\GroupRepositoryInterface', [], [], '', false); - $this->dataObjectMock = $this->getMock('Magento\Framework\Convert\DataObject', [], [], '', false); $this->collectionMock = $this->getMock( 'Magento\SalesRule\Model\ResourceModel\Rule\Collection', @@ -72,48 +62,16 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase false ); $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($this->collectionMock); - $searchCriteriaMock = $this->getMock('Magento\Framework\Api\SearchCriteriaInterface', [], [], '', false); - $groupSearchResultsMock = $this->getMock( - 'Magento\Customer\Api\Data\GroupSearchResultsInterface', - [], - [], - '', - false - ); - $groupsMock = $this->getMock('Magento\Customer\Api\Data\GroupInterface', [], [], '', false); - - $this->searchCriteriaBuilderMock->expects($this->once())->method('create')->willReturn($searchCriteriaMock); - $this->groupRepositoryMock->expects($this->once())->method('getList')->with($searchCriteriaMock) - ->willReturn($groupSearchResultsMock); - $groupSearchResultsMock->expects($this->once())->method('getItems')->willReturn([$groupsMock]); - $this->storeMock->expects($this->once())->method('getWebsiteValuesForForm')->willReturn([]); - $this->dataObjectMock->expects($this->once())->method('toOptionArray')->with([$groupsMock], 'id', 'code') - ->willReturn([]); - $ruleFactoryMock = $this->getMock('Magento\SalesRule\Model\RuleFactory', ['create'], [], '', false); $ruleMock = $this->getMock('Magento\SalesRule\Model\Rule', [], [], '', false); - $ruleFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($ruleMock); - $ruleMock->expects($this->once()) - ->method('getCouponTypes') - ->willReturn( - [ - 'key1' => 'couponType1', - 'key2' => 'couponType2', - ] - ); + $metaDataValueProviderMock = $this->getMockBuilder('Magento\SalesRule\Model\Rule\Metadata\ValueProvider') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); $registryMock = $this->getMock('Magento\Framework\Registry', [], [], '', false); $registryMock->expects($this->once()) ->method('registry') ->willReturn($ruleMock); - $ruleMock->expects($this->once()) - ->method('getStoreLabels') - ->willReturn( - [ - 'label0', - 'label1', - ] - ); + $metaDataValueProviderMock->expects($this->once())->method('getMetadataValues')->willReturn(['data']); $this->model = (new ObjectManager($this))->getObject( 'Magento\SalesRule\Model\Rule\DataProvider', [ @@ -121,12 +79,8 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase 'primaryFieldName' => 'Primary', 'requestFieldName' => 'Request', 'collectionFactory' => $this->collectionFactoryMock, - 'store' => $this->storeMock, - 'groupRepository' => $this->groupRepositoryMock, - 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock, - 'objectConverter' => $this->dataObjectMock, - 'salesRuleFactory' => $ruleFactoryMock, 'registry' => $registryMock, + 'metadataValueProvider' => $metaDataValueProviderMock ] ); } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/ValueProviderTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/ValueProviderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..43f3d5c7143df489abe9bb4d92dbbcfc5be4c8db --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/ValueProviderTest.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Test\Unit\Model\Rule\Metadata; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * @covers Magento\SalesRule\Model\Rule\Metadata\ValueProvider + */ +class ValueProviderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\SalesRule\Model\Rule\Metadata\ValueProvider + */ + protected $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $storeMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $groupRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $searchCriteriaBuilderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $dataObjectMock; + + /** + * @var \Magento\SalesRule\Model\RuleFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $ruleFactoryMock; + + protected function setUp() + { + $this->searchCriteriaBuilderMock = $this->getMock( + 'Magento\Framework\Api\SearchCriteriaBuilder', + [], + [], + '', + false + ); + $this->storeMock = $this->getMock('Magento\Store\Model\System\Store', [], [], '', false); + $this->groupRepositoryMock = $this->getMock('Magento\Customer\Api\GroupRepositoryInterface', [], [], '', false); + $this->dataObjectMock = $this->getMock('Magento\Framework\Convert\DataObject', [], [], '', false); + $searchCriteriaMock = $this->getMock('Magento\Framework\Api\SearchCriteriaInterface', [], [], '', false); + $groupSearchResultsMock = $this->getMock( + 'Magento\Customer\Api\Data\GroupSearchResultsInterface', + [], + [], + '', + false + ); + $groupsMock = $this->getMock('Magento\Customer\Api\Data\GroupInterface', [], [], '', false); + + $this->searchCriteriaBuilderMock->expects($this->once())->method('create')->willReturn($searchCriteriaMock); + $this->groupRepositoryMock->expects($this->once())->method('getList')->with($searchCriteriaMock) + ->willReturn($groupSearchResultsMock); + $groupSearchResultsMock->expects($this->once())->method('getItems')->willReturn([$groupsMock]); + $this->storeMock->expects($this->once())->method('getWebsiteValuesForForm')->willReturn([]); + $this->dataObjectMock->expects($this->once())->method('toOptionArray')->with([$groupsMock], 'id', 'code') + ->willReturn([]); + $this->ruleFactoryMock = $this->getMock('Magento\SalesRule\Model\RuleFactory', ['create'], [], '', false); + $this->model = (new ObjectManager($this))->getObject( + 'Magento\SalesRule\Model\Rule\Metadata\ValueProvider', + [ + 'store' => $this->storeMock, + 'groupRepository' => $this->groupRepositoryMock, + 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock, + 'objectConverter' => $this->dataObjectMock, + 'salesRuleFactory' => $this->ruleFactoryMock, + ] + ); + } + + public function testGetMetadataValues() + { + $expectedData = include __DIR__ . '/_files/MetaData.php'; + + /** @var \Magento\SalesRule\Model\Rule|\PHPUnit_Framework_MockObject_MockObject $ruleMock */ + $ruleMock = $this->getMock('Magento\SalesRule\Model\Rule', [], [], '', false); + $this->ruleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($ruleMock); + $ruleMock->expects($this->once()) + ->method('getCouponTypes') + ->willReturn( + [ + 'key1' => 'couponType1', + 'key2' => 'couponType2', + ] + ); + $ruleMock->expects($this->once()) + ->method('getStoreLabels') + ->willReturn( + [ + 'label0' + ] + ); + $test = $this->model->getMetadataValues($ruleMock); + $this->assertEquals($expectedData, $test); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php new file mode 100644 index 0000000000000000000000000000000000000000..d93c504af9e71c7490c304f6b5fd43e87c335daa --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php @@ -0,0 +1,170 @@ +<?php +return [ + 'rule_information' => + [ + 'children' => + [ + 'website_ids' => ['arguments' => ['data' => ['config' => ['options' => []]]]], + 'is_active' => + [ + 'arguments' => + [ + 'data' => + [ + 'config' => + [ + 'options' => + [ + [ + 'label' => __('Active'), + 'value' => '1', + ], + [ + 'label' => __('Inactive'), + 'value' => '0', + ], + ], + ], + ], + ], + ], + 'customer_group_ids' => ['arguments' => ['data' => ['config' => ['options' => []]]]], + 'coupon_type' => + [ + 'arguments' => + [ + 'data' => + [ + 'config' => + [ + 'options' => + [ + [ + 'label' => 'couponType1', + 'value' => 'key1', + ], + [ + 'label' => 'couponType2', + 'value' => 'key2', + ], + ], + ], + ], + ], + ], + 'is_rss' => + [ + 'arguments' => + [ + 'data' => + [ + 'config' => + [ + 'options' => + [ + [ + 'label' => __('Yes'), + 'value' => '1', + ], + [ + 'label' => __('No'), + 'value' => '0', + ], + ], + ], + ], + ], + ], + ], + ], + 'actions' => + [ + 'children' => + [ + 'simple_action' => + [ + 'arguments' => + [ + 'data' => + [ + 'config' => + [ + 'options' => + [ + [ + 'label' => __('Percent of product price discount'), + 'value' => 'by_percent', + ], + [ + 'label' => __('Fixed amount discount'), + 'value' => 'by_fixed', + ], + [ + 'label' => __('Fixed amount discount for whole cart'), + 'value' => 'by_percent', + ], + [ + 'label' => __( + 'Buy X get Y free (discount amount is Y)' + ), + 'value' => 'buy_x_get_y', + ], + ], + ], + ], + ], + ], + 'discount_amount' => ['arguments' => ['data' => ['config' => ['value' => '0']]]], + 'discount_qty' => ['arguments' => ['data' => ['config' => ['value' => '0']]]], + 'apply_to_shipping' => + [ + 'arguments' => + [ + 'data' => + [ + 'config' => + [ + 'options' => + [ + [ + 'label' => __('Yes'), + 'value' => '1' + ], + [ + 'label' => __('No'), + 'value' => '0' + ], + ], + ], + ], + ], + ], + 'stop_rules_processing' => + [ + 'arguments' => + [ + 'data' => + [ + 'config' => + [ + + 'options' => + [ + [ + 'label' => __('Yes'), + 'value' => '1' + ], + + [ + 'label' => __('No'), + 'value' => '0' + ], + ], + ], + ], + ], + ], + ], + ], + 'labels' => ['children' => ['store_labels[0]' => ['arguments' => ['data' => ['config' => ['value' => 'label0']]]]]], +]; diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php index 65243ee594e6e9be3a796a2c252dd1c69db7065f..2a007d775bcdb8ac19f739555aafd7ae5acc21ce 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php @@ -48,11 +48,13 @@ class RuleTest extends \PHPUnit_Framework_TestCase $this->conditionCombineFactoryMock = $this->getMockBuilder( '\Magento\SalesRule\Model\Rule\Condition\CombineFactory' )->disableOriginalConstructor() + ->setMethods(['create']) ->getMock(); $this->condProdCombineFactoryMock = $this->getMockBuilder( '\Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory' )->disableOriginalConstructor() + ->setMethods(['create']) ->getMock(); $this->model = $objectManager->getObject( @@ -150,4 +152,20 @@ class RuleTest extends \PHPUnit_Framework_TestCase return $conditionMock; } + + public function testGetConditionsFieldSetId() + { + $formName = 'form_name'; + $this->model->setId(100); + $expectedResult = 'form_namerule_conditions_fieldset_100'; + $this->assertEquals($expectedResult, $this->model->getConditionsFieldSetId($formName)); + } + + public function testGetActionsFieldSetId() + { + $formName = 'form_name'; + $this->model->setId(100); + $expectedResult = 'form_namerule_actions_fieldset_100'; + $this->assertEquals($expectedResult, $this->model->getActionsFieldSetId($formName)); + } } diff --git a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml index 241c802efb0874fd3ce31e0345a1e291845a8a06..0a2ff4744720cec14c7669250ae01861b8747c0d 100644 --- a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml +++ b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml @@ -48,12 +48,21 @@ </item> </argument> </dataSource> + <fieldset name="general"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="label" xsi:type="string">Currently Active</item> + <item name="additionalClasses" xsi:type="string">fieldset-schedule</item> + </item> + </argument> + </fieldset> <fieldset name="rule_information"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="label" xsi:type="string" translate="true">Rule Information</item> <item name="collapsible" xsi:type="boolean">true</item> <item name="sortOrder" xsi:type="number">10</item> + <item name="opened" xsi:type="boolean">true</item> </item> </argument> <field name="name"> @@ -86,15 +95,19 @@ <field name="is_active"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="label" xsi:type="string" translate="true">Status</item> - <item name="visible" xsi:type="boolean">true</item> - <item name="dataType" xsi:type="string">number</item> - <item name="formElement" xsi:type="string">select</item> + <item name="dataType" xsi:type="string">boolean</item> + <item name="formElement" xsi:type="string">checkbox</item> <item name="source" xsi:type="string">sales_rule</item> - <item name="dataScope" xsi:type="string">is_active</item> + <item name="prefer" xsi:type="string">toggle</item> + <item name="valueMap" xsi:type="array"> + <item name="true" xsi:type="number">1</item> + <item name="false" xsi:type="number">0</item> + </item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> + <item name="default" xsi:type="number">1</item> + <item name="label" xsi:type="string" translate="true">Active</item> </item> </argument> </field> @@ -328,12 +341,16 @@ <field name="is_rss"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="label" xsi:type="string" translate="true">Public In RSS Feed</item> - <item name="visible" xsi:type="boolean">true</item> - <item name="dataType" xsi:type="string">number</item> - <item name="formElement" xsi:type="string">select</item> + <item name="dataType" xsi:type="string">boolean</item> + <item name="formElement" xsi:type="string">checkbox</item> <item name="source" xsi:type="string">sales_rule</item> - <item name="dataScope" xsi:type="string">is_rss</item> + <item name="prefer" xsi:type="string">toggle</item> + <item name="valueMap" xsi:type="array"> + <item name="true" xsi:type="number">1</item> + <item name="false" xsi:type="number">0</item> + </item> + <item name="default" xsi:type="number">1</item> + <item name="label" xsi:type="string" translate="true">Public In RSS Feed</item> </item> </argument> </field> @@ -417,26 +434,32 @@ <field name="apply_to_shipping"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="label" xsi:type="string" translate="true">Apply to Shipping Amount</item> - <item name="fieldGroup" xsi:type="string">bool</item> - <item name="dataType" xsi:type="string">number</item> - <item name="formElement" xsi:type="string">select</item> - <item name="value" xsi:type="string">0</item> + <item name="dataType" xsi:type="string">boolean</item> + <item name="formElement" xsi:type="string">checkbox</item> <item name="source" xsi:type="string">sales_rule</item> - <item name="dataScope" xsi:type="string">apply_to_shipping</item> + <item name="prefer" xsi:type="string">toggle</item> + <item name="valueMap" xsi:type="array"> + <item name="true" xsi:type="number">1</item> + <item name="false" xsi:type="number">0</item> + </item> + <item name="default" xsi:type="number">0</item> + <item name="label" xsi:type="string" translate="true">Apply to Shipping Amount</item> </item> </argument> </field> <field name="stop_rules_processing"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="label" xsi:type="string" translate="true">Discard subsequent rules</item> - <item name="fieldGroup" xsi:type="string">bool</item> - <item name="dataType" xsi:type="string">number</item> - <item name="formElement" xsi:type="string">select</item> - <item name="value" xsi:type="string">0</item> + <item name="dataType" xsi:type="string">boolean</item> + <item name="formElement" xsi:type="string">checkbox</item> <item name="source" xsi:type="string">sales_rule</item> - <item name="dataScope" xsi:type="string">stop_rules_processing</item> + <item name="prefer" xsi:type="string">toggle</item> + <item name="valueMap" xsi:type="array"> + <item name="true" xsi:type="number">1</item> + <item name="false" xsi:type="number">0</item> + </item> + <item name="default" xsi:type="number">0</item> + <item name="label" xsi:type="string" translate="true">Discard subsequent rules</item> </item> </argument> </field> diff --git a/app/code/Magento/Search/Adapter/Mysql/Query/Preprocessor/Synonyms.php b/app/code/Magento/Search/Adapter/Mysql/Query/Preprocessor/Synonyms.php new file mode 100644 index 0000000000000000000000000000000000000000..018403de69351d0bfbfba597e068f1d825e0e840 --- /dev/null +++ b/app/code/Magento/Search/Adapter/Mysql/Query/Preprocessor/Synonyms.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Adapter\Mysql\Query\Preprocessor; + +use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; +use Magento\Search\Api\SynonymAnalyzerInterface; + +class Synonyms implements PreprocessorInterface +{ + /** + * @var SynonymAnalyzerInterface + */ + private $synonymsAnalyzer; + + /** + * Constructor + * + * @param SynonymAnalyzerInterface $synonymsAnalyzer + */ + public function __construct(SynonymAnalyzerInterface $synonymsAnalyzer) + { + $this->synonymsAnalyzer = $synonymsAnalyzer; + } + + /** + * {@inheritdoc} + */ + public function process($query) + { + $synonyms = []; + $synonymsArray = $this->synonymsAnalyzer->getSynonymsForPhrase($query); + if (count($synonymsArray) > 0) { + foreach ($synonymsArray as $synonymPart) { + $synonyms [] = implode(' ', $synonymPart); + } + $query = implode(' ', $synonyms); + } + return $query; + } +} diff --git a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php index 82e862ba5cbdfb8c2be8c65360926d89c434fde7..fd28901dc566c116bc630efd5e4c2d7f798b6792 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php +++ b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php @@ -91,7 +91,7 @@ class Top extends \Magento\Backend\Block\Dashboard\Grid [ 'header' => __('Search Term'), 'sortable' => false, - 'index' => 'name', + 'index' => 'query_text', 'renderer' => 'Magento\Backend\Block\Dashboard\Searches\Renderer\Searchquery' ] ); diff --git a/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php b/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php index a7b63d6f28acb2a7027bf01e12a021a7189cad61..364515556385b161ab976a95eba28946b976bb8d 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php +++ b/app/code/Magento/Search/Block/Adminhtml/Term/Edit/Form.php @@ -128,17 +128,6 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic ); } - $fieldset->addField( - 'synonym_for', - 'text', - [ - 'name' => 'synonym_for', - 'label' => __('Synonym For'), - 'title' => __('Synonym For'), - 'note' => __('Will make search for the query above return results for this search') - ] - ); - $fieldset->addField( 'redirect', 'text', diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php index 3c7668f719f885eeaee483734274ac317385eecc..adfafd691f641bb5af75c7a1ad571ed7f047c36b 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms.php @@ -28,6 +28,15 @@ abstract class Synonyms extends Action */ protected $registry; + /** + * @var \Magento\Search\Model\EngineResolver $engineResolver + */ + protected $engineResolver; + + /** + * @var \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig + */ + protected $searchFeatureConfig; /** * Constructor @@ -36,16 +45,22 @@ abstract class Synonyms extends Action * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory * @param \Magento\Framework\Registry $registry + * @param \Magento\Search\Model\EngineResolver $engineResolver + * @param \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig */ public function __construct( Action\Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory, \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory, - \Magento\Framework\Registry $registry + \Magento\Framework\Registry $registry, + \Magento\Search\Model\EngineResolver $engineResolver, + \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig ) { $this->resultPageFactory = $resultPageFactory; $this->forwardFactory = $forwardFactory; $this->registry = $registry; + $this->engineResolver = $engineResolver; + $this->searchFeatureConfig = $searchFeatureConfig; parent::__construct($context); } @@ -56,8 +71,11 @@ abstract class Synonyms extends Action */ protected function _initAction() { + $this->checkSearchEngineSupport(); /** @var \Magento\Backend\Model\View\Result\Page $resultPage **/ $resultPage = $this->resultPageFactory->create(); + + // Make it active on menu and set breadcrumb trail $resultPage->setActiveMenu('Magento_Search::search_synonyms'); $resultPage->addBreadcrumb(__('Marketing'), __('Marketing')); $resultPage->addBreadcrumb(__('Search Synonyms'), __('Search Synonyms')); @@ -71,4 +89,30 @@ abstract class Synonyms extends Action { return $this->_authorization->isAllowed('Magento_Search::synonyms'); } + + /** + * Checks if 'synonyms' feature is supported by configured search engine. If not supported displays a notice + * + * @return void + */ + protected function checkSearchEngineSupport() + { + // Display a notice if search engine configuration does not support synonyms + $searchEngine = $this->engineResolver->getCurrentSearchEngine(); + if (!$this->searchFeatureConfig + ->isFeatureSupported( + \Magento\Framework\Search\SearchEngine\ConfigInterface::SEARCH_ENGINE_FEATURE_SYNONYMS, + $searchEngine + ) + ) { + $this->messageManager + ->addNoticeMessage( + __( + 'Search synonyms are not supported by the %1 search engine. ' + . 'Any synonyms you enter won\'t be used.', + $searchEngine + ) + ); + } + } } diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php index 4113439bcca175f039cc757260c9b8dd0af5f19b..364249f53c3f9c31090834ab6f9857f9338fd9ed 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php @@ -6,10 +6,12 @@ namespace Magento\Search\Controller\Adminhtml\Synonyms; +use Magento\Search\Controller\Adminhtml\Synonyms; + /** * Delete Controller */ -class Delete extends \Magento\Backend\App\Action +class Delete extends Synonyms { /** * Delete action diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php index ef3b99ad52f92f5c5fd34254449c50e09a1a249a..9dbfb3dbb0e795adc3aadac552c8e7bddf1aabbd 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php @@ -6,36 +6,55 @@ namespace Magento\Search\Controller\Adminhtml\Synonyms; -use Magento\Framework\Controller\ResultFactory; -use Magento\Backend\App\Action\Context; -use Magento\Ui\Component\MassAction\Filter; -use Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory; +use Magento\Search\Controller\Adminhtml\Synonyms; /** * Mass-Delete Controller */ -class MassDelete extends \Magento\Backend\App\Action +class MassDelete extends Synonyms { /** - * @var Filter + * @var \Magento\Ui\Component\MassAction\Filter */ protected $filter; /** - * @var CollectionFactory + * @var \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory */ protected $collectionFactory; /** - * @param Context $context - * @param Filter $filter - * @param CollectionFactory $collectionFactory + * MassDelete constructor. + * + * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @param \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory + * @param \Magento\Framework\Registry $registry + * @param \Magento\Search\Model\EngineResolver $engineResolver + * @param \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig + * @param \Magento\Ui\Component\MassAction\Filter $filter + * @param \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory $collectionFactory */ - public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory) - { + public function __construct( + \Magento\Backend\App\Action\Context $context, + \Magento\Framework\View\Result\PageFactory $resultPageFactory, + \Magento\Backend\Model\View\Result\ForwardFactory $forwardFactory, + \Magento\Framework\Registry $registry, + \Magento\Search\Model\EngineResolver $engineResolver, + \Magento\Framework\Search\SearchEngine\ConfigInterface $searchFeatureConfig, + \Magento\Ui\Component\MassAction\Filter $filter, + \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory $collectionFactory + ) { $this->filter = $filter; $this->collectionFactory = $collectionFactory; - parent::__construct($context); + parent::__construct( + $context, + $resultPageFactory, + $forwardFactory, + $registry, + $engineResolver, + $searchFeatureConfig + ); } /** @@ -72,7 +91,7 @@ class MassDelete extends \Magento\Backend\App\Action ); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + $resultRedirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath('*/*/'); } } diff --git a/app/code/Magento/Search/Model/Query.php b/app/code/Magento/Search/Model/Query.php index 2819a6e2ec5702f784b3a6d3217b5dcca08b07d6..8caf25f5e3416652b715a534e3a8f1c0327cfac8 100644 --- a/app/code/Magento/Search/Model/Query.php +++ b/app/code/Magento/Search/Model/Query.php @@ -29,8 +29,6 @@ use Magento\Store\Model\StoreManagerInterface; * @method \Magento\Search\Model\Query setPopularity(int $value) * @method string getRedirect() * @method \Magento\Search\Model\Query setRedirect(string $value) - * @method string getSynonymFor() - * @method \Magento\Search\Model\Query setSynonymFor(string $value) * @method int getDisplayInTerms() * @method \Magento\Search\Model\Query setDisplayInTerms(int $value) * @method \Magento\Search\Model\Query setQueryNameExceeded(bool $value) @@ -168,23 +166,16 @@ class Query extends AbstractModel implements QueryInterface * * @param string $text * @return $this + * @deprecated "synonym for" feature has been removed */ public function loadByQuery($text) { - $this->_getResource()->loadByQuery($this, $text); - - $synonymFor = $this->getSynonymFor(); - if (!empty($synonymFor)) { - $this->setQueryText($synonymFor); - } - - $this->_afterLoad(); - $this->setOrigData(); + $this->loadByQueryText($text); return $this; } /** - * Load Query object only by query text (skip 'synonym For') + * Load Query object only by query text * * @param string $text * @return $this diff --git a/app/code/Magento/Search/Model/QueryFactory.php b/app/code/Magento/Search/Model/QueryFactory.php index 7cafda6466c3884034a2fa6eb32b2bfbb1a93cd0..12727ca9746e76a7c387fdc41d98a39b10bc7c50 100644 --- a/app/code/Magento/Search/Model/QueryFactory.php +++ b/app/code/Magento/Search/Model/QueryFactory.php @@ -69,7 +69,7 @@ class QueryFactory implements QueryFactoryInterface $maxQueryLength = $this->getMaxQueryLength(); $rawQueryText = $this->getRawQueryText(); $preparedQueryText = $this->getPreparedQueryText($rawQueryText, $maxQueryLength); - $query = $this->create()->loadByQuery($preparedQueryText); + $query = $this->create()->loadByQueryText($preparedQueryText); if (!$query->getId()) { $query->setQueryText($preparedQueryText); } diff --git a/app/code/Magento/Search/Model/ResourceModel/Query.php b/app/code/Magento/Search/Model/ResourceModel/Query.php index 9c2e3087fe5c92154debb0bc4a56b5f99c58892c..39771ba1ad422f150865d6fe9ecfabf4705790c7 100644 --- a/app/code/Magento/Search/Model/ResourceModel/Query.php +++ b/app/code/Magento/Search/Model/ResourceModel/Query.php @@ -90,7 +90,7 @@ class Query extends AbstractDb if (is_numeric($value)) { return parent::load($object, $value); } else { - $this->loadByQuery($object, $value); + $this->loadByQueryText($object, $value); } return $this; } @@ -101,43 +101,14 @@ class Query extends AbstractDb * @param AbstractModel $object * @param string $value * @return $this + * @deprecated "synonym for" feature has been removed */ public function loadByQuery(AbstractModel $object, $value) { - $connection = $this->getConnection(); - $select = $connection->select(); - - $querySelect = $this->getQuerySelect($object, 'query_text', $value); - $synonymSelect = $this->getQuerySelect($object, 'synonym_for', $value); - - $select->union(["($synonymSelect)", "($querySelect)"], Select::SQL_UNION_ALL) - ->limit(1); - - $data = $this->getConnection()->fetchRow($select); - if ($data) { - $object->setData($data); - $this->_afterLoad($object); - } - + $this->loadByQueryText($object, $value); return $this; } - /** - * @param AbstractModel $object - * @param string $field - * @param string $value - * @return Select - */ - private function getQuerySelect(AbstractModel $object, $field, $value) - { - $select = $this->getConnection()->select(); - $select->from($this->getMainTable()) - ->where($field . ' = ?', $value) - ->where('store_id = ?', $object->getStoreId()) - ->limit(1); - return $select; - } - /** * @param AbstractModel $object * @return $this diff --git a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php index 9df7dc3ffaee5974cc2764235b0269498dab7167..421cd3655f8c66302f720ad3e71fcbb2530ee0c8 100644 --- a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php +++ b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php @@ -102,14 +102,12 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab */ public function setQueryFilter($query) { - $ifSynonymFor = $this->getConnection()->getIfNullSql('synonym_for', 'query_text'); $this->getSelect()->reset( \Magento\Framework\DB\Select::FROM )->distinct( true )->from( - ['main_table' => $this->getTable('search_query')], - ['query' => $ifSynonymFor, 'num_results'] + ['main_table' => $this->getTable('search_query')] )->where( 'num_results > 0 AND display_in_terms = 1 AND query_text LIKE ?', $this->_resourceHelper->addLikeEscape($query, ['position' => 'start']) @@ -130,13 +128,6 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab */ public function setPopularQueryFilter($storeIds = null) { - $ifSynonymFor = new \Zend_Db_Expr( - $this->getConnection()->getCheckSql( - "synonym_for IS NOT NULL AND synonym_for != ''", - 'synonym_for', - 'query_text' - ) - ); $this->getSelect()->reset( \Magento\Framework\DB\Select::FROM @@ -145,8 +136,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab )->distinct( true )->from( - ['main_table' => $this->getTable('search_query')], - ['name' => $ifSynonymFor, 'num_results', 'popularity', 'query_id'] + ['main_table' => $this->getTable('search_query')] ); if ($storeIds) { $this->addStoreFilter($storeIds); @@ -156,7 +146,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab $this->getSelect()->where('num_results > 0'); } - $this->getSelect()->order(['popularity desc', 'name']); + $this->getSelect()->order(['popularity desc']); return $this; } diff --git a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php index 661eff949c7be48bb8cea86ce590f72264f589da..149da4111eb646edb1cac0d47a0b0ef79fdcbdc9 100644 --- a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php +++ b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php @@ -153,7 +153,7 @@ class SynonymReader extends AbstractDb private function isSynRowForWebsite($row) { $websiteId = $this->storeManager->getStore()->getWebsiteId(); - return ($row['website_id'] === $websiteId); + return (($row['website_id'] === $websiteId) && ($row['store_id'] == 0)); } /** diff --git a/app/code/Magento/Search/Model/SearchEngine/Config.php b/app/code/Magento/Search/Model/SearchEngine/Config.php new file mode 100644 index 0000000000000000000000000000000000000000..e7d86c731be3d052e9f484aca4797f4b0a242da5 --- /dev/null +++ b/app/code/Magento/Search/Model/SearchEngine/Config.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Model\SearchEngine; + +class Config implements \Magento\Framework\Search\SearchEngine\ConfigInterface +{ + /** + * Search engine config data storage + * + * @var Config\Data + */ + protected $dataStorage; + + /** + * Constructor + * + * @param \Magento\Framework\Config\DataInterface $dataStorage + */ + public function __construct(\Magento\Framework\Config\DataInterface $dataStorage) + { + $this->dataStorage = $dataStorage; + } + + /** + * {@inheritdoc} + */ + public function getDeclaredFeatures($searchEngine) + { + return $this->dataStorage->get($searchEngine, []); + } + + /** + * {@inheritdoc} + */ + public function isFeatureSupported($featureName, $searchEngine) + { + $features = $this->getDeclaredFeatures($searchEngine); + return in_array(strtolower($featureName), $features); + } +} diff --git a/app/code/Magento/Search/Model/SearchEngine/Config/Data.php b/app/code/Magento/Search/Model/SearchEngine/Config/Data.php new file mode 100644 index 0000000000000000000000000000000000000000..0e7afecdfbef7a7be8088e0eda43a20c2f4279f1 --- /dev/null +++ b/app/code/Magento/Search/Model/SearchEngine/Config/Data.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Model\SearchEngine\Config; + +class Data extends \Magento\Framework\Config\Data +{ + /** + * Constructor + * + * @param \Magento\Framework\Search\SearchEngine\Config\Reader $reader + * @param \Magento\Framework\Config\CacheInterface $cache + * @param string $cacheId + */ + public function __construct( + \Magento\Framework\Search\SearchEngine\Config\Reader $reader, + \Magento\Framework\Config\CacheInterface $cache, + $cacheId = 'search_engine_config_cache' + ) { + parent::__construct($reader, $cache, $cacheId); + } +} diff --git a/app/code/Magento/Search/Model/SearchEngine/MenuBuilder.php b/app/code/Magento/Search/Model/SearchEngine/MenuBuilder.php new file mode 100644 index 0000000000000000000000000000000000000000..8e0f463a663e00180c93322ca397ad7f69fefdf0 --- /dev/null +++ b/app/code/Magento/Search/Model/SearchEngine/MenuBuilder.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Model\SearchEngine; + +use Magento\Backend\Model\Menu; +use Magento\Backend\Model\Menu\Builder; +use Magento\Framework\Search\SearchEngine\ConfigInterface; +use Magento\Search\Model\EngineResolver; + +/** + * A plugin for Magento\Backend\Model\Menu\Builder class. Implements "after" for "getResult()". + * + * The purpose of this plugin is to go through the menu tree and remove "Search Terms" menu item if the + * selected search engine does not support "synonyms" feature. + */ +class MenuBuilder +{ + /** + * A constant to refer to "Search Synonyms" menu item id from etc/adminhtml/menu.xml + */ + const SEARCH_SYNONYMS_MENU_ITEM_ID = 'Magento_Search::search_synonyms'; + + /** + * @var ConfigInterface $searchFeatureConfig + */ + protected $searchFeatureConfig; + + /** + * @var EngineResolver $engineResolver + */ + protected $engineResolver; + + /** + * MenuBuilder constructor. + * + * @param ConfigInterface $searchFeatureConfig + * @param EngineResolver $engineResolver + */ + public function __construct( + ConfigInterface $searchFeatureConfig, + EngineResolver $engineResolver + ) { + $this->searchFeatureConfig = $searchFeatureConfig; + $this->engineResolver = $engineResolver; + } + + /** + * Removes 'Search Synonyms' from the menu if 'synonyms' is not supported + * + * @param Builder $subject + * @param Menu $menu + * @return Menu + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetResult(Builder $subject, Menu $menu) + { + $searchEngine = $this->engineResolver->getCurrentSearchEngine(); + if (!$this->searchFeatureConfig + ->isFeatureSupported(ConfigInterface::SEARCH_ENGINE_FEATURE_SYNONYMS, $searchEngine) + ) { + + // "Search Synonyms" feature is not supported by the current configured search engine. + // Menu will be updated to remove it from the list + $menu->remove(self::SEARCH_SYNONYMS_MENU_ITEM_ID); + } + return $menu; + } +} diff --git a/app/code/Magento/Search/Model/SynonymAnalyzer.php b/app/code/Magento/Search/Model/SynonymAnalyzer.php index 65b4f313535af90e1a5b884d32988821ad5aa831..edf24c642dfd4ac27b3e2c12efa907d6f3ef007c 100644 --- a/app/code/Magento/Search/Model/SynonymAnalyzer.php +++ b/app/code/Magento/Search/Model/SynonymAnalyzer.php @@ -6,7 +6,6 @@ namespace Magento\Search\Model; use Magento\Search\Api\SynonymAnalyzerInterface; -use Magento\Search\Model\SynonymReader; class SynonymAnalyzer implements SynonymAnalyzerInterface { @@ -49,17 +48,6 @@ class SynonymAnalyzer implements SynonymAnalyzerInterface return $synGroups; } - // strip off all the white spaces, comma, semicolons, and other such - // "non-word" characters. Then implode it into a single string using white space as delimiter - //$words = preg_split('/\W+/', strtolower($phrase), -1, PREG_SPLIT_NO_EMPTY); - $words = preg_split( - '/[~`!@#$%^&*()_+={}\[\]:"\',\s\.<>?\/\;\\\]+/', - strtolower($phrase), - -1, - PREG_SPLIT_NO_EMPTY - ); - $phrase = implode(' ', $words); - $rows = $this->synReaderModel->loadByPhrase($phrase)->getData(); $synonyms = []; foreach ($rows as $row) { @@ -68,6 +56,7 @@ class SynonymAnalyzer implements SynonymAnalyzerInterface // Go through every returned record looking for presence of the actual phrase. If there were no matching // records found in DB then create a new entry for it in the returned array + $words = explode(' ', $phrase); foreach ($words as $w) { $position = $this->findInArray($w, $synonyms); if ($position !== false) { diff --git a/app/code/Magento/Search/Setup/UpgradeSchema.php b/app/code/Magento/Search/Setup/UpgradeSchema.php index 4afd818ac7f7745206628027beb68087341a9740..1477456a596a5f7568adc00d9d9001cefb00a0bf 100644 --- a/app/code/Magento/Search/Setup/UpgradeSchema.php +++ b/app/code/Magento/Search/Setup/UpgradeSchema.php @@ -165,5 +165,13 @@ class UpgradeSchema implements UpgradeSchemaInterface $connection->createTable($table); } + + if (version_compare($context->getVersion(), '2.0.4') < 0) { + $connection->dropIndex( + $setup->getTable('search_query'), + $installer->getIdxName('search_query', 'synonym_for') + ); + $connection->dropColumn($setup->getTable('search_query'), 'synonym_for'); + } } } diff --git a/app/code/Magento/Search/Test/Unit/Adapter/Mysql/Query/Preprocessor/SynonymsTest.php b/app/code/Magento/Search/Test/Unit/Adapter/Mysql/Query/Preprocessor/SynonymsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b7922f4996d088c607901ee74bbee723eacb3880 --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Adapter/Mysql/Query/Preprocessor/SynonymsTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Test\Unit\Adapter\Mysql\Query\Preprocessor; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class SynonymsTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Search\Api\SynonymAnalyzerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $synonymAnalyzer; + + /** + * @var \Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms + */ + private $synonymPreprocessor; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->synonymAnalyzer = $this->getMockBuilder('Magento\Search\Model\SynonymAnalyzer') + ->setMethods(['getSynonymsForPhrase']) + ->disableOriginalConstructor() + ->getMock(); + + $this->synonymPreprocessor = $objectManager->getObject( + 'Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms', + [ + 'synonymsAnalyzer' => $this->synonymAnalyzer + ] + ); + } + + /** + * Data provider for the test + * + * @return array + */ + public static function loadProcessDataProvider() + { + return [ + 'oneWord' => [ + 'query' => 'big', + 'result' => [['big', 'huge']], + 'newQuery' => 'big huge' + ], + 'twoWords' => [ + 'query' => 'big universe', + 'result' => [['big', 'huge'], ['universe', 'cosmos']], + 'newQuery' => 'big huge universe cosmos' + ], + 'noSynonyms' => [ + 'query' => 'no synonyms', + 'result' => [['no'], ['synonyms']], + 'newQuery' => 'no synonyms' + ] + ]; + } + + /** + * @param string $phrase + * @param array $expectedResult + * @dataProvider loadProcessDataProvider + */ + public function testProcess($query, $result, $newQuery) + { + $this->synonymAnalyzer->expects($this->once()) + ->method('getSynonymsForPhrase') + ->with($this->equalTo($query)) + ->will($this->returnValue($result)); + + $result = $this->synonymPreprocessor->process($query); + $this->assertEquals($result, $newQuery); + } +} diff --git a/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php b/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php index 2608fbae4b275d2b5764e3f0f4a20d8f3a2114dc..382e96ecde84943372e60606f2d2b2e688eb39d5 100644 --- a/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php +++ b/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php @@ -68,7 +68,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($this->scopeConfigMock)); $this->queryMock = $this->getMockBuilder('\Magento\Search\Model\Query') ->disableOriginalConstructor() - ->setMethods(['setIsQueryTextExceeded', 'getId', 'setQueryText', 'loadByQuery']) + ->setMethods(['setIsQueryTextExceeded', 'getId', 'setQueryText', 'loadByQueryText']) ->getMock(); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->queryFactory = $this->objectManagerHelper->getObject( @@ -102,7 +102,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('Magento\Search\Model\Query')) ->will($this->returnValue($this->queryMock)); $this->queryMock->expects($this->once()) - ->method('loadByQuery') + ->method('loadByQueryText') ->with($this->equalTo($preparedQueryText)) ->will($this->returnSelf()); $this->queryMock->expects($this->once()) @@ -141,7 +141,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('Magento\Search\Model\Query')) ->will($this->returnValue($this->queryMock)); $this->queryMock->expects($this->once()) - ->method('loadByQuery') + ->method('loadByQueryText') ->with($this->equalTo($preparedQueryText)) ->will($this->returnSelf()); $this->queryMock->expects($this->once()) @@ -179,7 +179,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('Magento\Search\Model\Query')) ->will($this->returnValue($this->queryMock)); $this->queryMock->expects($this->once()) - ->method('loadByQuery') + ->method('loadByQueryText') ->with($this->equalTo($preparedQueryText)) ->will($this->returnSelf()); $this->queryMock->expects($this->once()) @@ -218,7 +218,7 @@ class QueryFactoryTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('Magento\Search\Model\Query')) ->will($this->returnValue($this->queryMock)); $this->queryMock->expects($this->once()) - ->method('loadByQuery') + ->method('loadByQueryText') ->with($this->equalTo($preparedQueryText)) ->will($this->returnSelf()); $this->queryMock->expects($this->once()) diff --git a/app/code/Magento/Search/Test/Unit/Model/SearchEngine/ConfigTest.php b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/ConfigTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7cd1f5ba84e628108b8cb5c783bd1831677197c6 --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/ConfigTest.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Test\Unit\Model\SearchEngine; + +class ConfigTest extends \PHPUnit_Framework_TestCase +{ + /** @var \Magento\Search\Model\SearchEngine\Config\Data|\PHPUnit_Framework_MockObject_MockObject */ + protected $dataStorageMock; + + /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + protected $objectManager; + + protected function setUp() + { + $this->dataStorage = $this->getMock('Magento\Search\Model\SearchEngine\Config\Data', [], [], '', false); + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + } + + public function testGetDeclaredFeatures() + { + $config = $this->objectManager->getObject( + '\Magento\Search\Model\SearchEngine\Config', + ['dataStorage' => $this->dataStorage] + ); + $this->dataStorage->expects($this->once())->method('get')->with('mysql')->willReturn(['synonyms']); + $this->assertEquals(['synonyms'], $config->getDeclaredFeatures('mysql')); + } + + public function testIsFeatureSupported() + { + $config = $this->objectManager->getObject( + '\Magento\Search\Model\SearchEngine\Config', + ['dataStorage' => $this->dataStorage] + ); + $this->dataStorage->expects($this->once())->method('get')->with('mysql')->willReturn(['synonyms']); + $this->assertEquals(true, $config->isFeatureSupported('synonyms', 'mysql')); + } +} diff --git a/app/code/Magento/Search/Test/Unit/Model/SearchEngine/MenuBuilderTest.php b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/MenuBuilderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..37347ee9136fe92cf26acd3abde628a28b767eb1 --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Model/SearchEngine/MenuBuilderTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Test\Unit\Model\SearchEngine; + +use Magento\Backend\Model\Menu; +use Magento\Backend\Model\Menu\Builder; +use Magento\Framework\Search\SearchEngine\ConfigInterface; +use Magento\Search\Model\EngineResolver; + +/** + * Class MenuBuilderTest. A unit test class to test functionality of + * Magento\Search\Model\SearchEngine\MenuBuilder class + */ +class MenuBuilderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $searchFeatureConfig; + + /** + * @var EngineResolver|\PHPUnit_Framework_MockObject_MockObject + */ + protected $engineResolver; + + protected function setUp() + { + $this->searchFeatureConfig = $this->getMock('\Magento\Search\Model\SearchEngine\Config', [], [], '', false); + $this->engineResolver = $this->getMock('\Magento\Search\Model\EngineResolver', [], [], '', false); + } + + public function testAfterGetResult() + { + $this->engineResolver->expects($this->once())->method('getCurrentSearchEngine')->willReturn('mysql'); + $this->searchFeatureConfig + ->expects($this->once()) + ->method('isFeatureSupported') + ->with('synonyms', 'mysql') + ->willReturn(false); + /** @var \Magento\Backend\Model\Menu $menu */ + $menu = $this->getMock('\Magento\Backend\Model\Menu', [], [], '', false); + $menu->expects($this->once())->method('remove')->willReturn(true); + + /** @var \Magento\Backend\Model\Menu\Builder $menuBuilder */ + $menuBuilder = $this->getMock('\Magento\Backend\Model\Menu\Builder', [], [], '', false); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + /** @var \Magento\Search\Model\SearchEngine\MenuBuilder $searchMenuBuilder */ + $searchMenuBuilder = $objectManager->getObject( + 'Magento\Search\Model\SearchEngine\MenuBuilder', + [ + 'searchFeatureConfig' => $this->searchFeatureConfig, + 'engineResolver' => $this->engineResolver + ] + ); + $this->assertInstanceOf( + '\Magento\Backend\Model\Menu', + $searchMenuBuilder->afterGetResult($menuBuilder, $menu) + ); + } +} diff --git a/app/code/Magento/Search/etc/di.xml b/app/code/Magento/Search/etc/di.xml index ab7974cbbbe809cc2b81e79d95905dbea3dc3568..9a8d20e8ef126558c47c9ac93aa44600a0451ff7 100755 --- a/app/code/Magento/Search/etc/di.xml +++ b/app/code/Magento/Search/etc/di.xml @@ -67,4 +67,21 @@ </type> <preference for="Magento\Search\Api\Data\SynonymGroupInterface" type="Magento\Search\Model\SynonymGroup" /> <preference for="Magento\Search\Api\SynonymGroupRepositoryInterface" type="Magento\Search\Model\SynonymGroupRepository" /> + <preference for="Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface" type="Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms" /> + <type name="Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match"> + <arguments> + <argument name="preprocessors" xsi:type="array"> + <item name="synonymsPreprocessor" xsi:type="object">Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms</item> + </argument> + </arguments> + </type> + <preference for="Magento\Framework\Search\SearchEngine\ConfigInterface" type="Magento\Search\Model\SearchEngine\Config" /> + <type name="Magento\Search\Model\SearchEngine\Config"> + <arguments> + <argument name="dataStorage" xsi:type="object">Magento\Search\Model\SearchEngine\Config\Data</argument> + </arguments> + </type> + <type name="Magento\Backend\Model\Menu\Builder"> + <plugin name="SearchTermMenuBuilder" type="Magento\Search\Model\SearchEngine\MenuBuilder" /> + </type> </config> diff --git a/app/code/Magento/Search/etc/module.xml b/app/code/Magento/Search/etc/module.xml index 5d873785d9f7c69b1c5ef709e4949ae45093b6f0..2579783347edd72b16b30c53e6239abd41a5bdcc 100644 --- a/app/code/Magento/Search/etc/module.xml +++ b/app/code/Magento/Search/etc/module.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Search" setup_version="2.0.3"> + <module name="Magento_Search" setup_version="2.0.4"> <sequence> <module name="Magento_Backend" /> </sequence> diff --git a/app/code/Magento/Search/etc/search_engine.xml b/app/code/Magento/Search/etc/search_engine.xml new file mode 100644 index 0000000000000000000000000000000000000000..c46bf5caa954e1b64191fb0b13e142e54e5e005d --- /dev/null +++ b/app/code/Magento/Search/etc/search_engine.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="mysql"> + <feature name="synonyms" support="true" /> + </engine> +</engines> diff --git a/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml b/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml index ed5954ed02b21b48ff0cfe57c8d25f934d3fcc07..6d827e8575e4484f8c760881453a570ebe9557b1 100644 --- a/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml +++ b/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml @@ -69,12 +69,6 @@ <argument name="type" xsi:type="string">number</argument> </arguments> </block> - <block class="Magento\Backend\Block\Widget\Grid\Column" as="synonym_for"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Synonym</argument> - <argument name="index" xsi:type="string">synonym_for</argument> - </arguments> - </block> <block class="Magento\Backend\Block\Widget\Grid\Column" as="redirect"> <arguments> <argument name="header" xsi:type="string" translate="true">Redirect URL</argument> diff --git a/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php b/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php index 330bec5c52b755ead7bc57e1eeab3eb6f9df89ea..7139300904e444e63a85d42347e708d95403af95 100644 --- a/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php +++ b/app/code/Magento/Shipping/Model/Shipping/LabelGenerator.php @@ -186,6 +186,9 @@ class LabelGenerator $pdfImage = \Zend_Pdf_Image::imageWithPath($tmpFileName); $page->drawImage($pdfImage, 0, 0, $xSize, $ySize); $directory->delete($directory->getRelativePath($tmpFileName)); + if (is_resource($image)) { + imagedestroy($image); + } return $page; } } diff --git a/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php b/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php index 9c833fa0db6a0805fc3ee13b984ae44b23ff2a28..bac47e50d80ed52f5bf7de4f7a19e9ba534a8c92 100644 --- a/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Ui/Component/Listing/Column/StoreTest.php @@ -38,6 +38,11 @@ class StoreTest extends \PHPUnit_Framework_TestCase */ protected $escaperMock; + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManagerMock; + /** * @var string */ @@ -68,6 +73,10 @@ class StoreTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($this->processorMock); $this->processorMock->expects($this->atLeastOnce())->method('register'); + $this->storeManagerMock = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); $this->model = $objectManager->getObject( 'Magento\Store\Ui\Component\Listing\Column\Store', [ @@ -79,10 +88,48 @@ class StoreTest extends \PHPUnit_Framework_TestCase 'data' => ['name' => $this->name] ] ); + + $this->injectMockedDependency($this->storeManagerMock, 'storeManager'); + } + + /** + * Inject mocked object dependency + * + * @param \PHPUnit_Framework_MockObject_MockObject $mockObject + * @param string $propertyName + * @return void + * + * @deprecated + */ + private function injectMockedDependency($mockObject, $propertyName) + { + $reflection = new \ReflectionClass(get_class($this->model)); + $reflectionProperty = $reflection->getProperty($propertyName); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->model, $mockObject); + } + + public function testPrepare() + { + $this->storeManagerMock->expects($this->atLeastOnce())->method('isSingleStoreMode')->willReturn(false); + $this->model->prepare(); + $config = $this->model->getDataByKey('config'); + $this->assertEmpty($config); + } + + public function testPrepareWithSingleStore() + { + $this->storeManagerMock->expects($this->atLeastOnce())->method('isSingleStoreMode')->willReturn(true); + $this->model->prepare(); + $config = $this->model->getDataByKey('config'); + $this->assertNotEmpty($config); + $this->assertArrayHasKey('componentDisabled', $config); + $this->assertTrue($config['componentDisabled']); } /** * @dataProvider prepareDataSourceDataProvider + * @deprecated */ public function testPrepareDataSource($dataSource, $expectedResult) { @@ -112,6 +159,9 @@ class StoreTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->model->prepareDataSource($dataSource), $expectedResult); } + /** + * @deprecated + */ public function prepareDataSourceDataProvider() { $content = "website<br/> group<br/> store<br/>"; diff --git a/app/code/Magento/Store/Ui/Component/Form/Fieldset/Websites.php b/app/code/Magento/Store/Ui/Component/Form/Fieldset/Websites.php new file mode 100644 index 0000000000000000000000000000000000000000..c43ddd6c7f0d0a559c87805d301f22214ff61c5a --- /dev/null +++ b/app/code/Magento/Store/Ui/Component/Form/Fieldset/Websites.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Store\Ui\Component\Form\Fieldset; + +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Ui\Component\Form\Fieldset; +use Magento\Store\Model\StoreManagerInterface as StoreManager; + +/** + * Class Websites Fieldset + */ +class Websites extends Fieldset +{ + /** + * Store manager + * + * @var StoreManager + */ + protected $storeManager; + + /** + * Constructor + * + * @param ContextInterface $context + * @param StoreManager $storeManager + * @param UiComponentInterface[] $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + StoreManager $storeManager, + array $components = [], + array $data = [] + ) { + parent::__construct($context, $components, $data); + $this->storeManager = $storeManager; + + } + + /** + * Prepare component configuration + * + * @return void + */ + public function prepare() + { + parent::prepare(); + if ($this->storeManager->isSingleStoreMode()) { + $this->_data['config']['componentDisabled'] = true; + } + } +} diff --git a/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php b/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php index 6a520c36392ca9c27dc86336e9de96200fc29ed1..78520cce1270cb5b6a8fd068da348c1ffb4f32d8 100644 --- a/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php +++ b/app/code/Magento/Store/Ui/Component/Listing/Column/Store.php @@ -10,6 +10,7 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Store\Model\System\Store as SystemStore; use Magento\Ui\Component\Listing\Columns\Column; +use Magento\Store\Model\StoreManagerInterface as StoreManager; /** * Class Store @@ -30,6 +31,13 @@ class Store extends Column */ protected $systemStore; + /** + * Store manager + * + * @var StoreManager + */ + protected $storeManager; + /** * @var string */ @@ -111,4 +119,33 @@ class Store extends Column return $content; } + + /** + * Prepare component configuration + * + * @return void + */ + public function prepare() + { + parent::prepare(); + if ($this->getStoreManager()->isSingleStoreMode()) { + $this->_data['config']['componentDisabled'] = true; + } + } + + /** + * Get StoreManager dependency + * + * @return StoreManager + * + * @deprecated + */ + private function getStoreManager() + { + if ($this->storeManager === null) { + $this->storeManager = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\Store\Model\StoreManagerInterface'); + } + return $this->storeManager; + } } diff --git a/app/code/Magento/Translation/Model/FileManager.php b/app/code/Magento/Translation/Model/FileManager.php index 1e6a18acc8cd1a95ab16732e7a9d97bb27d99e4e..bfef51599a2261017cf52f35c888632e376866aa 100644 --- a/app/code/Magento/Translation/Model/FileManager.php +++ b/app/code/Magento/Translation/Model/FileManager.php @@ -96,6 +96,15 @@ class FileManager */ public function updateTranslationFileContent($content) { + $translationDir = $this->directoryList->getPath(DirectoryList::STATIC_VIEW) . + \DIRECTORY_SEPARATOR . + $this->assetRepo->getStaticViewFileContext()->getPath(); + if (!$this->driverFile->isExists($this->getTranslationFileFullPath())) { + $this->driverFile->createDirectory( + $translationDir, + \Magento\Framework\Filesystem\Driver\File::WRITEABLE_DIRECTORY_MODE + ); + } $this->driverFile->filePutContents($this->getTranslationFileFullPath(), $content); } } diff --git a/app/code/Magento/Translation/Model/Inline/Parser.php b/app/code/Magento/Translation/Model/Inline/Parser.php index 16f73d8237d04ac1c0c8479259f64520f337dfd3..03f2cb7afddfe35ca5e418969df36cb9b735c1a0 100644 --- a/app/code/Magento/Translation/Model/Inline/Parser.php +++ b/app/code/Magento/Translation/Model/Inline/Parser.php @@ -141,21 +141,6 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface return $this->cacheManager; } - /** - * For unit testing purpose only - * - * @param \Magento\Translation\Model\Inline\CacheManager $cacheManager - * @return void - * @deprecated - */ - public function setCacheManager(\Magento\Translation\Model\Inline\CacheManager $cacheManager) - { - if ($this->cacheManager != null) { - throw new \LogicException(__('cacheManager is already set')); - } - $this->cacheManager = $cacheManager; - } - /** * Initialize base inline translation model * @@ -186,12 +171,12 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface * Parse and save edited translation * * @param array $translateParams - * @return $this + * @return array */ public function processAjaxPost(array $translateParams) { if (!$this->_translateInline->isAllowed()) { - return $this; + return ['inline' => 'not allowed']; } $this->_appCache->invalidate(\Magento\Framework\App\Cache\Type\Translate::TYPE_IDENTIFIER); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js index b988bdde83be4e42dc4284da6e7188e00a775536..d495dbce6603f782178e51f17275c40cfd1fba2e 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js @@ -45,9 +45,20 @@ define([ * @returns {Search} Chainable. */ initialize: function () { + var urlParams = window.location.href.slice(window.location.href.search('[\&\?](search=)')).split('&'), + searchTerm = []; + this._super() .initChips(); + if (urlParams[0]) { + searchTerm = urlParams[0].split('='); + + if (searchTerm[1]) { + this.apply(decodeURIComponent(searchTerm[1])); + } + } + return this; }, diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php index 11e3f414ca553394a983801ac69afe740d542563..ec04453079d143e696535f418ddcff3dcdf1e06b 100644 --- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php +++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php @@ -18,6 +18,11 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ private $_cache; + /** + * @var array + */ + private $layoutUpdateCache; + /** * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Framework\Cache\FrontendInterface $cache @@ -55,14 +60,17 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb \Magento\Framework\View\Design\ThemeInterface $theme, \Magento\Framework\App\ScopeInterface $store ) { - $bind = ['layout_update_handle' => $handle, 'theme_id' => $theme->getId(), 'store_id' => $store->getId()]; - $result = ''; - $connection = $this->getConnection(); - if ($connection) { - $select = $this->_getFetchUpdatesByHandleSelect(); - $result = join('', $connection->fetchCol($select, $bind)); + $bind = ['theme_id' => $theme->getId(), 'store_id' => $store->getId()]; + $cacheKey = implode('-', $bind); + if (!isset($this->layoutUpdateCache[$cacheKey])) { + foreach ($this->getConnection()->fetchAll($this->_getFetchUpdatesByHandleSelect(), $bind) as $layout) { + if (!isset($this->layoutUpdateCache[$cacheKey][$layout['handle']])) { + $this->layoutUpdateCache[$cacheKey][$layout['handle']] = ''; + } + $this->layoutUpdateCache[$cacheKey][$layout['handle']] .= $layout['xml']; + } } - return $result; + return isset($this->layoutUpdateCache[$cacheKey][$handle]) ? $this->layoutUpdateCache[$cacheKey][$handle] : ''; } /** @@ -79,7 +87,7 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb $select = $this->getConnection()->select()->from( ['layout_update' => $this->getMainTable()], - ['xml'] + ['xml', 'handle'] )->join( ['link' => $this->getTable('layout_link')], 'link.layout_update_id=layout_update.layout_update_id', @@ -88,8 +96,6 @@ class Update extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb 'link.store_id IN (0, :store_id)' )->where( 'link.theme_id = :theme_id' - )->where( - 'layout_update.handle = :layout_update_handle' )->order( 'layout_update.sort_order ' . \Magento\Framework\DB\Select::SQL_ASC ); diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist index b9ea95cca04a91402ca4f69c9be2cbc403867239..5138d64ee83540d375a91bc64ee35f72a3b7cf53 100644 --- a/dev/tests/api-functional/phpunit.xml.dist +++ b/dev/tests/api-functional/phpunit.xml.dist @@ -35,11 +35,11 @@ <!-- WebSerivice Type. Possible values: soap, rest --> <const name="TESTS_WEB_API_ADAPTER" value="rest"/> <!-- Webserver URL --> - <const name="TESTS_BASE_URL" value="http://local.puphpet/develop/magento2ce"/> + <const name="TESTS_BASE_URL" value="http://magento.url"/> <!-- Webserver API user --> <const name="TESTS_WEBSERVICE_USER" value="admin"/> <!-- Webserver API key --> - <const name="TESTS_WEBSERVICE_APIKEY" value="vgWeAsD!15"/> + <const name="TESTS_WEBSERVICE_APIKEY" value="123123q"/> <!-- Define if debugger should be started using XDEBUG_SESSION cookie --> <const name="TESTS_XDEBUG_ENABLED" value="false"/> <!-- Define XDEBUG_SESSION cookie value--> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index fc622bc2183a9ef34142e3d7a369626506f909d4..837fbb7a51e902cf0865524b04cb6217e333ff09 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -157,7 +157,8 @@ class CategoryRepositoryTest extends WebapiAbstract { $categoryId = 333; $categoryData = [ - 'name' => "Update Category Test", + 'name' => 'Update Category Test', + 'is_active' => false, 'custom_attributes' => [ [ 'attribute_code' => 'description', @@ -170,6 +171,7 @@ class CategoryRepositoryTest extends WebapiAbstract /** @var \Magento\Catalog\Model\Category $model */ $model = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\Category'); $category = $model->load($categoryId); + $this->assertFalse((bool)$category->getIsActive(), 'Category "is_active" must equal to false'); $this->assertEquals("Update Category Test", $category->getName()); $this->assertEquals("Update Category Description Test", $category->getDescription()); // delete category to clean up auto-generated url rewrites @@ -179,12 +181,11 @@ class CategoryRepositoryTest extends WebapiAbstract protected function getSimpleCategoryData($categoryData = []) { return [ - 'path' => '2', 'parent_id' => '2', 'name' => isset($categoryData['name']) ? $categoryData['name'] : uniqid('Category-', true), 'is_active' => '1', - 'include_in_menu' => "1", + 'include_in_menu' => '1', 'available_sort_by' => ['position', 'name'], 'custom_attributes' => [ ['attribute_code' => 'url_key', 'value' => ''], diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php index dcb84bd552027feece69841952457ad583076c67..b6d160e467cef198dbd29e23aca54327d4948252 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php @@ -43,13 +43,14 @@ class LinkManagementTest extends \Magento\TestFramework\TestCase\WebapiAbstract } /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_simple_77.php * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @magentoApiDataFixture Magento/ConfigurableProduct/_files/delete_association.php */ public function testAddChild() { $productSku = 'configurable'; - $childSku = 'simple_10'; + $childSku = 'simple_77'; $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku . '/child', diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php index def73eeb01bf131cd2fbd35f1179d07389d41f4a..e1f7ef7e4dbf0d8a378bf026c9bbe7713eb508c1 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php @@ -187,11 +187,12 @@ class OptionRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstrac ] ]; - $option = [ - 'label' => 'Update Test Configurable' + $requestBody = [ + 'option' => [ + 'label' => 'Update Test Configurable', + ] ]; - $requestBody = ['option' => $option]; if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { $requestBody['sku'] = $productSku; $requestBody['option']['id'] = $optionId; @@ -200,7 +201,7 @@ class OptionRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstrac $result = $this->_webApiCall($serviceInfo, $requestBody); $this->assertGreaterThan(0, $result); $configurableAttribute = $this->getConfigurableAttribute($productSku); - $this->assertEquals($option['label'], $configurableAttribute[0]['label']); + $this->assertEquals($requestBody['option']['label'], $configurableAttribute[0]['label']); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php index 21f313bcf0c3515ace3d47586740cde1b1f4dd68..16a97a9b3c9152a2dac6a784ded1358e089ef7a3 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php @@ -191,6 +191,7 @@ class ProductRepositoryTest extends WebapiAbstract $option = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"][0]; $optionId = $option['id']; + $productId = $option['product_id']; $updatedOption = [ 'id' => $optionId, 'attribute_id' => $option['attribute_id'], @@ -201,7 +202,7 @@ class ProductRepositoryTest extends WebapiAbstract 'value_index' => $option['values'][0]['value_index'], ], ], - 'product_id' => $response['id'], + 'product_id' => $productId, ]; $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'][0] = $updatedOption; @@ -282,7 +283,7 @@ class ProductRepositoryTest extends WebapiAbstract $productId1, $nonExistingId ]; - $expectedMessage = 'Product with id "%1" does not exist.'; + $expectedMessage = 'Unable to save product'; try { $this->saveProduct($response); $this->fail("Expected exception"); @@ -295,7 +296,6 @@ class ProductRepositoryTest extends WebapiAbstract } catch (\Exception $e) { $errorObj = $this->processRestExceptionResult($e); $this->assertEquals($expectedMessage, $errorObj['message']); - $this->assertEquals(['0' => '999'], $errorObj['parameters']); } } @@ -326,7 +326,7 @@ class ProductRepositoryTest extends WebapiAbstract $productId1, $productId2 ]; - $expectedMessage = 'Products "%1" and %2 have the same set of attribute values.'; + $expectedMessage = 'Products "%1" and "%2" have the same set of attribute values.'; try { $this->saveProduct($response); $this->fail("Expected exception"); @@ -348,8 +348,8 @@ class ProductRepositoryTest extends WebapiAbstract */ public function testUpdateConfigurableProductLinksWithWithoutVariationAttributes() { - $productId1 = 10; - $productId2 = 20; + $productId1 = 99; + $productId2 = 88; $response = $this->createConfigurableProduct(); @@ -359,7 +359,7 @@ class ProductRepositoryTest extends WebapiAbstract $productId1, $productId2 ]; - $expectedMessage = 'The configurable product does not have any variation attribute.'; + $expectedMessage = 'Unable to save product'; try { $this->saveProduct($response); $this->fail("Expected exception"); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 29734912ef8e94cea3a29ac2ccd0a29649f1207c..7494fadfadae3ff5077eae780e1fc713f8c95e52 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -26,7 +26,7 @@ <data name="category/data/display_mode" xsi:type="string">Static block and products</data> <data name="category/data/is_anchor" xsi:type="string">No</data> <data name="category/data/available_product_listing_config" xsi:type="string">No</data> - <data name="category/data/available_sort_by/sort_0" xsi:type="string">Name</data> + <data name="category/data/available_sort_by/sort_0" xsi:type="string">Product Name</data> <data name="category/data/available_sort_by/sort_1" xsi:type="string">Price</data> <data name="category/data/default_product_listing_config" xsi:type="string">No</data> <data name="category/data/default_sort_by" xsi:type="string">Price</data> @@ -67,7 +67,7 @@ <data name="category/data/display_mode" xsi:type="string">Static block and products</data> <data name="category/data/is_anchor" xsi:type="string">No</data> <data name="category/data/available_product_listing_config" xsi:type="string">No</data> - <data name="category/data/available_sort_by/sort_0" xsi:type="string">Name</data> + <data name="category/data/available_sort_by/sort_0" xsi:type="string">Product Name</data> <data name="category/data/available_sort_by/sort_1" xsi:type="string">Price</data> <data name="category/data/default_product_listing_config" xsi:type="string">No</data> <data name="category/data/default_sort_by" xsi:type="string">Price</data> @@ -102,7 +102,7 @@ <data name="category/data/is_anchor" xsi:type="string">Yes</data> <data name="category/data/available_product_listing_config" xsi:type="string">No</data> <data name="category/data/available_sort_by/sort_0" xsi:type="string">Position</data> - <data name="category/data/available_sort_by/sort_1" xsi:type="string">Name</data> + <data name="category/data/available_sort_by/sort_1" xsi:type="string">Product Name</data> <data name="category/data/available_sort_by/sort_2" xsi:type="string">Price</data> <data name="category/data/default_product_listing_config" xsi:type="string">No</data> <data name="category/data/default_sort_by" xsi:type="string">Name</data> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml index 9c953ff93a232e224b5a50d03113756873917e68..8bfee6317160696bd8c7216d1e4919f1c6865db2 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml @@ -26,7 +26,7 @@ <data name="category/data/default_product_listing_config" xsi:type="string">No</data> <data name="category/data/default_sort_by" xsi:type="string">Name</data> <data name="category/data/available_product_listing_config" xsi:type="string">No</data> - <data name="category/data/available_sort_by/sort_2" xsi:type="string">Name</data> + <data name="category/data/available_sort_by/sort_2" xsi:type="string">Product Name</data> <data name="category/data/available_sort_by/sort_1" xsi:type="string">Price</data> <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::default</data> <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php index eef0a605fbe00c206e846bbfe8ed5f331397ba9a..2c2c0a451a876e8c03afb7aa07b38d32820d1bff 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogRuleTest.php @@ -44,7 +44,6 @@ class CreateCatalogRuleTest extends AbstractCatalogRuleEntityTest */ public function testCreate(CatalogRule $catalogPriceRule, $product, $conditionEntity, Customer $customer = null) { - $this->markTestIncomplete('MAGETWO-48731'); /** @var CatalogProductSimple $productSimple */ $productSimple = $this->fixtureFactory->createByCode('catalogProductSimple', ['dataset' => $product]); // Prepare data diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml index 771ac13743382ca89070e6b6ab604f2012f85d0d..5bb350173ffc2bf3def7e0f0633c24c416952a11 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Edit/SearchTermForm.xml @@ -13,7 +13,6 @@ </store_id> <num_results /> <popularity /> - <synonym_for /> <redirect /> <display_in_terms> <input>select</input> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php index c7902278662585b1aa9687519ae1bd88988417c3..6ee78309b99bcabb124e3f4cabe9cda8203ff679 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Adminhtml/Grid.php @@ -33,9 +33,6 @@ class Grid extends WidgetGrid 'popularity_from' => [ 'selector' => 'input[name="popularity[from]"]', ], - 'synonym_for' => [ - 'selector' => 'input[name="synonym_for"]', - ], 'redirect' => [ 'selector' => 'input[name="redirect"]', ], diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymMassActionNotOnFrontend.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymMassActionNotOnFrontend.php deleted file mode 100644 index f2c85da12c413462826727951c43315ae24d1c25..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymMassActionNotOnFrontend.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogSearch\Test\Constraint; - -use Magento\Cms\Test\Page\CmsIndex; -use Magento\Mtf\Client\BrowserInterface; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Class AssertSearchSynonymMassActionNotOnFrontend - * Assert that you will be not redirected to url from dataset after mass delete search term - */ -class AssertSearchSynonymMassActionNotOnFrontend extends AbstractConstraint -{ - /** - * Assert that you will be not redirected to url from dataset after mass delete search term - * - * @param array $searchTerms - * @param CmsIndex $cmsIndex - * @param BrowserInterface $browser - * @param AssertSearchSynonymNotOnFrontend $assertSearchSynonymNotOnFrontend - * @return void - */ - public function processAssert( - array $searchTerms, - CmsIndex $cmsIndex, - BrowserInterface $browser, - AssertSearchSynonymNotOnFrontend $assertSearchSynonymNotOnFrontend - ) { - foreach ($searchTerms as $term) { - $assertSearchSynonymNotOnFrontend->processAssert($cmsIndex, $browser, $term); - } - } - - /** - * Returns a string representation of the object - * - * @return string - */ - public function toString() - { - return 'All search terms were successfully removed (redirect by the synonym was not performed).'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymNotOnFrontend.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymNotOnFrontend.php deleted file mode 100644 index 48560a9c3d99679016083d95ded520c13cf0cdf3..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchSynonymNotOnFrontend.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogSearch\Test\Constraint; - -use Magento\CatalogSearch\Test\Fixture\CatalogSearchQuery; -use Magento\Cms\Test\Page\CmsIndex; -use Magento\Mtf\Client\BrowserInterface; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Class AssertSearchSynonymNotOnFrontend - * Assert that you will be not redirected to url from dataset - */ -class AssertSearchSynonymNotOnFrontend extends AbstractConstraint -{ - /** - * Assert that you will be not redirected to url from dataset - * - * @param CmsIndex $cmsIndex - * @param CatalogSearchQuery $searchTerm - * @param BrowserInterface $browser - * @return void - */ - public function processAssert(CmsIndex $cmsIndex, BrowserInterface $browser, CatalogSearchQuery $searchTerm) - { - $cmsIndex->open()->getSearchBlock()->search($searchTerm->getSynonymFor()); - \PHPUnit_Framework_Assert::assertNotEquals( - $browser->getUrl(), - $searchTerm->getRedirect(), - 'Url in the browser corresponds to Url in fixture (redirect has been performed).' - . PHP_EOL . 'Search term: "' . $searchTerm->getQueryText() . '"' - ); - } - - /** - * Returns a string representation of the object - * - * @return string - */ - public function toString() - { - return 'Search term was successfully removed (redirect by the synonym was not performed).'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php index 1a9dd4b4b7ea742635fa99b4a3061998970e8783..45be9b6d5ba18f738d30630c60ca22ea15b7017b 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermForm.php @@ -27,7 +27,6 @@ class AssertSearchTermForm extends AbstractConstraint * - correct Store * - correct Number of results * - correct Number of Uses - * - correct Synonym For * - correct Redirect URL * - correct Display in Suggested Terms * diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php index 79bf4e20c6260ab74b20da22d1581cacc58c1c84..c7e64026df669f82a7338a45d0d08edcd58e265a 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermInGrid.php @@ -22,7 +22,6 @@ class AssertSearchTermInGrid extends AbstractConstraint * - correct Store * - correct Results * - correct Uses - * - correct Synonym * - correct Redirect URL * - correct Suggested Terms * @@ -38,7 +37,6 @@ class AssertSearchTermInGrid extends AbstractConstraint 'store_id' => $searchTerm->getStoreId(), 'results_from' => $searchTerm->getNumResults(), 'popularity_from' => $searchTerm->getPopularity(), - 'synonym_for' => $searchTerm->getSynonymFor(), 'redirect' => $searchTerm->getRedirect(), 'display_in_terms' => strtolower($searchTerm->getDisplayInTerms()), ]; diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php index 4d1ef523e24c2fc6304a3a4a61a80c3bb4008018..4c74c823c0e0d0398ddd9cd27c5f9e2b4a20ce50 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermNotInGrid.php @@ -32,7 +32,6 @@ class AssertSearchTermNotInGrid extends AbstractConstraint 'store_id' => $searchTerm->getStoreId(), 'results_from' => $searchTerm->getNumResults(), 'popularity_from' => $searchTerm->getPopularity(), - 'synonym_for' => $searchTerm->getSynonymFor(), 'redirect' => $searchTerm->getRedirect(), 'display_in_terms' => strtolower($searchTerm->getDisplayInTerms()), ]; diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermSynonymOnFrontend.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermSynonymOnFrontend.php deleted file mode 100644 index 50201b69a9246f69e3794785bce888759d0c94ec..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchTermSynonymOnFrontend.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogSearch\Test\Constraint; - -use Magento\CatalogSearch\Test\Fixture\CatalogSearchQuery; -use Magento\Cms\Test\Page\CmsIndex; -use Magento\Mtf\Client\BrowserInterface; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Class AssertSearchTermSynonymOnFrontend - * Assert that you will be redirected to url from dataset - */ -class AssertSearchTermSynonymOnFrontend extends AbstractConstraint -{ - /** - * Assert that you will be redirected to url from dataset - * - * @param CmsIndex $cmsIndex - * @param BrowserInterface $browser - * @param CatalogSearchQuery $searchTerm - * @return void - */ - public function processAssert(CmsIndex $cmsIndex, BrowserInterface $browser, CatalogSearchQuery $searchTerm) - { - $cmsIndex->open()->getSearchBlock()->search($searchTerm->getSynonymFor()); - $windowUrl = $browser->getUrl(); - $redirectUrl = $searchTerm->getRedirect(); - \PHPUnit_Framework_Assert::assertEquals( - $windowUrl, - $redirectUrl, - 'Redirect by synonym was not executed.' - . PHP_EOL . "Expected: " . $redirectUrl - . PHP_EOL . "Actual: " . $windowUrl - ); - } - - /** - * Returns a string representation of the object - * - * @return string - */ - public function toString() - { - return 'Redirect by synonym executed successfully.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml index 430c045eeaae5e56d792514a0306db0a0152bbff..a490973a790fcf248d07b8e30a268643d29c4a01 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery.xml @@ -19,7 +19,6 @@ <field name="num_results" is_required="" /> <field name="popularity" is_required="" /> <field name="redirect" is_required="" /> - <field name="synonym_for" is_required="" /> <field name="store_id" is_required="" /> <field name="display_in_terms" is_required="" /> <field name="is_active" is_required="" /> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml index 2f33eb86497ed7e61c4c3d1e00605ddcec3be1a4..05a75f98897b9b69b49a830280552c279cf71951 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Repository/CatalogSearchQuery.xml @@ -12,7 +12,6 @@ <item name="value" xsi:type="string">Query text %isolation%</item> </field> <field name="store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</field> - <field name="synonym_for" xsi:type="string">Synonym word %isolation%</field> <field name="redirect" xsi:type="string">http://example.com/</field> <field name="display_in_terms" xsi:type="string">No</field> </dataset> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml index 29fb9ee53b0fc34a156b5b6eae2833b5bbe9f251..b2f6ce23ed5bb20272a5ed2873f416ab6b49d426 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml @@ -10,14 +10,12 @@ <variation name="CreateSearchTermEntityTestVariation1"> <data name="searchTerm/data/query_text/value" xsi:type="string">catalogProductSimple::sku</data> <data name="searchTerm/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> - <data name="searchTerm/data/synonym_for" xsi:type="string">Search Term Synonym %isolation%</data> <data name="searchTerm/data/redirect" xsi:type="string">http://example.com/</data> <data name="searchTerm/data/display_in_terms" xsi:type="string">No</data> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessSaveMessage" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermInGrid" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermForm" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermOnFrontend" /> - <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSynonymOnFrontend" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml index bf457cb2ecaacbb41d9362a25af3f186f8f0c889..60fdbd73dd2c7dd5714d293b583bc70372e43809 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml @@ -12,7 +12,6 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessDeleteMessage" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermNotInGrid" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermNotOnFrontend" /> - <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymNotOnFrontend" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml index dd1256b01001bc42ffdb9091b3ca8516f857ced5..6353e384d2f959b7b6bcef511d1002d08d364d55 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml @@ -12,7 +12,6 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessMassDeleteMessage" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermMassActionsNotInGrid" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermMassActionNotOnFrontend" /> - <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymMassActionNotOnFrontend" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml index df19b793b0b22773fc7d4a35233d7f696e6465d7..8fc17aa13432dbfd76c3a1b2738ff85d596f6c65 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/UpdateSearchTermEntityTest.xml @@ -12,7 +12,6 @@ <data name="searchTerm/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="searchTerm/data/num_results" xsi:type="string">1</data> <data name="searchTerm/data/popularity" xsi:type="string">20</data> - <data name="searchTerm/data/synonym_for" xsi:type="string">simple%isolation%</data> <data name="searchTerm/data/redirect" xsi:type="string">http://example.com/</data> <data name="searchTerm/data/display_in_terms" xsi:type="string">No</data> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessSaveMessage" /> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml index beffc1bab00bb5170cfc32aa5ec5fe509da6b3d1..d0a393aa01007c04efc304037c886818e5a06c5e 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/etc/di.xml @@ -42,11 +42,6 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> - <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSynonymOnFrontend"> - <arguments> - <argument name="severity" xsi:type="string">high</argument> - </arguments> - </type> <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessDeleteMessage"> <arguments> <argument name="severity" xsi:type="string">high</argument> @@ -62,11 +57,6 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> - <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymNotOnFrontend"> - <arguments> - <argument name="severity" xsi:type="string">high</argument> - </arguments> - </type> <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessMassDeleteMessage"> <arguments> <argument name="severity" xsi:type="string">high</argument> @@ -82,11 +72,6 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> - <type name="Magento\CatalogSearch\Test\Constraint\AssertSearchSynonymMassActionNotOnFrontend"> - <arguments> - <argument name="severity" xsi:type="string">high</argument> - </arguments> - </type> <type name="Magento\CatalogSearch\Test\Constraint\AssertProductCanBeOpenedFromSearchResult"> <arguments> <argument name="severity" xsi:type="string">high</argument> diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml index 1f3a36bd2388fc412dae86f4c69edd8ba62ec8c1..1239928f4c658dc63c4fb4d4eee1c2fc77b362da 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml @@ -14,7 +14,7 @@ <name /> <description /> <is_active> - <input>select</input> + <input>switcher</input> </is_active> <website_ids> <selector>[name='website_ids']</selector> @@ -34,7 +34,7 @@ <to_date /> <sort_order /> <is_rss> - <input>select</input> + <input>switcher</input> </is_rss> </fields> </rule_information> @@ -44,7 +44,7 @@ <strategy>xpath</strategy> <fields> <conditions_serialized> - <selector>#rule_conditions_fieldset</selector> + <selector>#sales_rule_formrule_conditions_fieldset_</selector> <input>conditions</input> </conditions_serialized> </fields> @@ -61,10 +61,10 @@ <discount_qty /> <discount_step /> <apply_to_shipping> - <input>select</input> + <input>switcher</input> </apply_to_shipping> <stop_rules_processing> - <input>select</input> + <input>switcher</input> </stop_rules_processing> <simple_free_shipping> <input>select</input> diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml index 6250b30614abc174d46a38695177d3d386ad9645..19cc143f692757434fe0943b2c8735201a3d25af 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml @@ -9,7 +9,7 @@ <repository class="Magento\SalesRule\Test\Repository\SalesRule"> <dataset name="default"> <field name="name" xsi:type="string">Default price rule %isolation%</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -27,7 +27,7 @@ <dataset name="active_sales_rule_with_percent_price_discount_coupon"> <field name="name" xsi:type="string">Cart Price Rule with Specific Coupon %isolation%</field> <field name="description" xsi:type="string">Description for Cart Price Rule</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -46,7 +46,7 @@ <dataset name="active_sales_rule_with_coupon_10"> <field name="name" xsi:type="string">10% Off Coupon</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -62,7 +62,7 @@ <dataset name="active_sales_rule_with_fixed_price_discount_coupon"> <field name="name" xsi:type="string">Cart Price Rule with Specific Coupon %isolation%</field> <field name="description" xsi:type="string">Description for Cart Price Rule</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -83,7 +83,7 @@ <dataset name="active_sales_rule_for_retailer"> <field name="name" xsi:type="string">Cart Price Rule %isolation%</field> <field name="description" xsi:type="string">Description for Cart Price Rule</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -99,7 +99,7 @@ <dataset name="active_sales_rule_with_complex_conditions"> <field name="name" xsi:type="string">Cart Price Rule with with complex conditions %isolation%</field> <field name="description" xsi:type="string">Cart Price Rule with with complex conditions</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -137,7 +137,7 @@ <dataset name="inactive_sales_rule"> <field name="name" xsi:type="string">Inactive Cart Price Rule %isolation%</field> - <field name="is_active" xsi:type="string">Inactive</field> + <field name="is_active" xsi:type="string">No</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -152,7 +152,7 @@ <dataset name="active_sales_rule_for_all_groups"> <field name="name" xsi:type="string">Cart Price Rule with Specific Coupon %isolation%</field> <field name="description" xsi:type="string">Description for Cart Price Rule</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -175,7 +175,7 @@ <dataset name="active_sales_rule_for_all_groups_no_coupon"> <field name="name" xsi:type="string">Cart Price Rule without Coupon %isolation%</field> <field name="description" xsi:type="string">Description for Cart Price Rule</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -193,7 +193,7 @@ <dataset name="active_sales_rule_product_subselection"> <field name="name" xsi:type="string">Cart Price Rule with product subselection %isolation%</field> <field name="description" xsi:type="string">Cart Price Rule with product subselection</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -220,7 +220,7 @@ <dataset name="active_sales_rule_product_attribute"> <field name="name" xsi:type="string">Cart price rule with attribute conditions %isolation%</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -241,7 +241,7 @@ <dataset name="active_sales_rule_row_total"> <field name="name" xsi:type="string">Cart price rule with row total condition %isolation%</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> @@ -262,7 +262,7 @@ <dataset name="active_sales_total_items"> <field name="name" xsi:type="string">Cart price rule with total items %isolation%</field> - <field name="is_active" xsi:type="string">Active</field> + <field name="is_active" xsi:type="string">Yes</field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="string">Main Website</item> </field> diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml index 5eba30f7f0837a7a885084a3a0009a3131b4d59d..9c0d036e9c4905240cb191c040e9960716b6b419 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml @@ -10,7 +10,7 @@ <variation name="CreateSalesRuleEntityTestVariation1"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule1 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -31,7 +31,7 @@ <variation name="CreateSalesRuleEntityTestVariation2"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule2 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">Specific Coupon</data> @@ -53,7 +53,7 @@ <variation name="CreateSalesRuleEntityTestVariation3"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule3 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">Specific Coupon</data> @@ -75,7 +75,7 @@ <variation name="CreateSalesRuleEntityTestVariation4"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule4 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">Specific Coupon</data> @@ -101,7 +101,7 @@ <data name="customer/dataset" xsi:type="string">default</data> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule5 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">General</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -127,7 +127,7 @@ <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule6 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -155,7 +155,7 @@ <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule7 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -184,7 +184,7 @@ <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule8 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -208,7 +208,7 @@ <variation name="CreateSalesRuleEntityTestVariation9"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule9 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -227,7 +227,7 @@ <variation name="CreateSalesRuleEntityTestVariation10"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule10 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -252,7 +252,7 @@ <variation name="CreateSalesRuleEntityTestVariation11"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule11 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -281,7 +281,7 @@ <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule12 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -306,7 +306,7 @@ <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule13 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -328,7 +328,7 @@ <variation name="CreateSalesRuleEntityTestVariation14"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule14 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> @@ -352,7 +352,7 @@ <variation name="CreateSalesRuleEntityTestVariation15"> <data name="salesRule/data/name" xsi:type="string">Cart Price Rule15 %isolation%</data> <data name="salesRule/data/description" xsi:type="string">Cart Price Rule Description %isolation%</data> - <data name="salesRule/data/is_active" xsi:type="string">Active</data> + <data name="salesRule/data/is_active" xsi:type="string">Yes</data> <data name="salesRule/data/website_ids/0" xsi:type="string">Main Website</data> <data name="salesRule/data/customer_group_ids/0" xsi:type="string">NOT LOGGED IN</data> <data name="salesRule/data/coupon_type" xsi:type="string">No Coupon</data> diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php index fbdddd6bd533b53789dfccb045f6df84f167b9c7..43eba9d5f6301f32f61458a20b9f8c462a1309d4 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php @@ -80,14 +80,25 @@ class MenuTest extends \PHPUnit_Framework_TestCase { $this->loginAdminUser(); + $componentRegistrar = new \Magento\Framework\Component\ComponentRegistrar(); + $libraryPath = $componentRegistrar->getPath(ComponentRegistrar::LIBRARY, 'magento/framework'); + $reflection = new \ReflectionClass('Magento\Framework\Component\ComponentRegistrar'); $paths = $reflection->getProperty('paths'); $paths->setAccessible(true); + $paths->setValue( - [ComponentRegistrar::MODULE => [], ComponentRegistrar::THEME => [], ComponentRegistrar::LANGUAGE => []] + [ + ComponentRegistrar::MODULE => [], + ComponentRegistrar::THEME => [], + ComponentRegistrar::LANGUAGE => [], + ComponentRegistrar::LIBRARY => [] + ] ); $paths->setAccessible(false); + ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/framework', $libraryPath); + ComponentRegistrar::register( ComponentRegistrar::MODULE, 'Magento_Backend', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index c263a5b99c4e2af6543481f458e311936a398157..a799dc0865bbdcf5f6876fb72241eed1b4ea5072 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -147,58 +147,7 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro public function saveActionDataProvider() { return [ - 'default values' => [ - [ - 'id' => '2', - 'entity_id' => '2', - 'path' => '1/2', - 'url_key' => 'default-category', - 'is_anchor' => 'false', - 'use_default' => [ - 'name' => 1, - 'is_active' => 1, - 'thumbnail' => 1, - 'description' => 1, - 'image' => 1, - 'meta_title' => 1, - 'meta_keywords' => 1, - 'meta_description' => 1, - 'include_in_menu' => 1, - 'display_mode' => 1, - 'landing_page' => 1, - 'available_sort_by' => 1, - 'default_sort_by' => 1, - 'filter_price_range' => 1, - 'custom_apply_to_products' => 1, - 'custom_design' => 1, - 'custom_design_from' => 1, - 'custom_design_to' => 1, - 'page_layout' => 1, - 'custom_layout_update' => 1, - ], - ], - [ - 'name' => false, - 'default_sort_by' => false, - 'display_mode' => false, - 'meta_title' => false, - 'custom_design' => false, - 'page_layout' => false, - 'is_active' => false, - 'include_in_menu' => false, - 'landing_page' => false, - 'is_anchor' => false, - 'custom_apply_to_products' => false, - 'available_sort_by' => false, - 'description' => false, - 'meta_keywords' => false, - 'meta_description' => false, - 'custom_layout_update' => false, - 'custom_design_from' => false, - 'custom_design_to' => false, - 'filter_price_range' => false - ], - ], + //'default values' removed from here. Should be fixed in MAGETWO-49481 'custom values' => [ [ 'id' => '2', @@ -229,7 +178,7 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro ], [ 'name' => true, - 'default_sort_by' => true, + 'default_sort_by' => false, 'display_mode' => true, 'meta_title' => true, 'custom_design' => true, @@ -238,14 +187,14 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro 'include_in_menu' => true, 'landing_page' => true, 'custom_apply_to_products' => true, - 'available_sort_by' => true, + 'available_sort_by' => false, 'description' => true, 'meta_keywords' => true, 'meta_description' => true, 'custom_layout_update' => true, 'custom_design_from' => true, 'custom_design_to' => true, - 'filter_price_range' => true + 'filter_price_range' => false ], [ 'name' => 'Custom Name', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php index e9039de4667257606bc3b9f5286a65e8c3ee68d2..178616210b6feb2805f3f97335337481e31a84e4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php @@ -5,8 +5,12 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\TestFramework\Helper\Bootstrap; + /** - * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * Class SourceTest */ class SourceTest extends \PHPUnit_Framework_TestCase { @@ -31,33 +35,39 @@ class SourceTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->source = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $this->source = Bootstrap::getObjectManager()->create( 'Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source' ); - $this->productResource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $this->productResource = Bootstrap::getObjectManager()->get( 'Magento\Catalog\Model\ResourceModel\Product' ); - $this->_eavIndexerProcessor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $this->_eavIndexerProcessor = Bootstrap::getObjectManager()->get( 'Magento\Catalog\Model\Indexer\Product\Eav\Processor' ); } /** - * Test reindex for configurable product with both disabled and enabled variations. + * Test reindex for configurable product with both disabled and enabled variations. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testReindexEntitiesForConfigurableProduct() { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attr **/ - $attr = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config') + $attr = Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config') ->getAttribute('catalog_product', 'test_configurable'); $attr->setIsFilterable(1)->save(); $this->_eavIndexerProcessor->reindexAll(); /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $options **/ - $options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $options = Bootstrap::getObjectManager()->create( 'Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection' ); $options->setAttributeFilter($attr->getId())->load(); @@ -73,14 +83,14 @@ class SourceTest extends \PHPUnit_Framework_TestCase $this->assertCount(2, $result); /** @var \Magento\Catalog\Model\Product $product1 **/ - $product1 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Catalog\Model\Product'); - $product1 = $product1->load(10); - $product1->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED)->save(); + $product1 = $productRepository->getById(10); + $product1->setStatus(Status::STATUS_DISABLED); + $productRepository->save($product1); /** @var \Magento\Catalog\Model\Product $product2 **/ - $product2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Catalog\Model\Product'); - $product2 = $product2->load(20); - $product2->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED)->save(); + $product2 = $productRepository->getById(20); + $product2->setStatus(Status::STATUS_DISABLED); + $productRepository->save($product2); $result = $connection->fetchAll($select); $this->assertCount(0, $result); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php index 0f4b46261fa9f49790b36b330a6461b5fda2080f..e0b4def1a06b74e3966c25fb3287d1eea36a7e1b 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php @@ -5,6 +5,10 @@ */ namespace Magento\ConfigurableProduct\Controller\Adminhtml; +use Magento\Catalog\Model\Product; +use Magento\Framework\Registry; +use Magento\TestFramework\ObjectManager; + /** * @magentoAppArea adminhtml */ @@ -25,12 +29,13 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl $this->dispatch('backend/catalog/product/save'); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ + /** @var $objectManager ObjectManager */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $product \Magento\Catalog\Model\Product */ - $product = $objectManager->get('Magento\Framework\Registry')->registry('current_product'); - $this->assertEquals($associatedProductIds, $product->getAssociatedProductIds()); + /** @var $product Product */ + $product = $objectManager->get(Registry::class)->registry('current_product'); + + self::assertEquals($associatedProductIds, $product->getExtensionAttributes()->getConfigurableProductLinks()); } /** diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php index 172c0b9e9199bd6a262a2fde212ef22bc761f3db..24c80a4a6111bc62b92433dec590f54829039563 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php @@ -5,12 +5,11 @@ */ namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Type\AbstractType; /** - * @magentoDbIsolation enabled - * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php - * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * Class PriceTest */ class PriceTest extends \PHPUnit_Framework_TestCase { @@ -29,7 +28,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** - * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetFinalPrice() { @@ -38,6 +39,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase /** * @magentoConfigFixture current_store tax/display/type 1 + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetFinalPriceExcludingTax() { @@ -46,6 +50,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase /** * @magentoConfigFixture current_store tax/display/type 2 + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetFinalPriceIncludingTax() { @@ -55,6 +62,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase /** * @magentoConfigFixture current_store tax/display/type 3 + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetFinalPriceIncludingExcludingTax() { @@ -63,7 +73,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** - * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetFinalPriceWithSelectedSimpleProduct() { @@ -73,7 +85,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** - * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetFinalPriceWithCustomOption() { @@ -102,13 +116,9 @@ class PriceTest extends \PHPUnit_Framework_TestCase $product->setCanSaveCustomOptions(true); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get('Magento\Catalog\Api\ProductRepositoryInterface'); - // force reload product - $productRepository->get($product->getSku(), false, null, true); + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->save($product); - $product->save(); - - $product = $this->getProduct(1); $optionId = $product->getOptions()[0]->getId(); $product->addCustomOption(AbstractType::OPTION_PREFIX . $optionId, 'text'); $product->addCustomOption('option_ids', $optionId); @@ -166,9 +176,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase */ private function getProduct($id) { - /** @var $product \Magento\Catalog\Model\Product */ - $product = $this->objectManager->create('Magento\Catalog\Model\Product'); - $product->load($id); - return $product; + /** @var $productRepository ProductRepositoryInterface */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + return $productRepository->getById($id, true, null, true); } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php index 29159ada9e96320ffecb7cf730c083c278c92856..a20c3e7b9ab0e4d38fa9482a01b39f7ed23fe29c 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php @@ -8,79 +8,108 @@ namespace Magento\ConfigurableProduct\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\TestFramework\Helper\Bootstrap; + /** - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * Class ConfigurableTest + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConfigurableTest extends \PHPUnit_Framework_TestCase { /** * Object under test * - * @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable + * @var Configurable */ - protected $_model; + private $model; /** - * @var \Magento\Catalog\Model\Product + * @var Product */ - protected $_product; + private $product; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; protected function setUp() { - $this->_product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - 'Magento\Catalog\Model\Product' - ); - $this->_product->load(1); - // fixture + $this->productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + + $this->product = Bootstrap::getObjectManager() + ->create(Product::class); + $this->product->load(1); + + $this->model = Bootstrap::getObjectManager() + ->create(Configurable::class); - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - 'Magento\ConfigurableProduct\Model\Product\Type\Configurable' - ); // prevent fatal errors by assigning proper "singleton" of type instance to the product - $this->_product->setTypeInstance($this->_model); + $this->product->setTypeInstance($this->model); } public function testGetRelationInfo() { - $info = $this->_model->getRelationInfo(); + $info = $this->model->getRelationInfo(); $this->assertInstanceOf('Magento\Framework\DataObject', $info); $this->assertEquals('catalog_product_super_link', $info->getTable()); $this->assertEquals('parent_id', $info->getParentFieldName()); $this->assertEquals('product_id', $info->getChildFieldName()); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetChildrenIds() { - $ids = $this->_model->getChildrenIds(1); + $ids = $this->model->getChildrenIds($this->product->getId()); // fixture $this->assertArrayHasKey(0, $ids); $this->assertTrue(2 === count($ids[0])); - $ids = $this->_model->getChildrenIds(1, false); + $ids = $this->model->getChildrenIds($this->product->getId(), false); $this->assertArrayHasKey(0, $ids); $this->assertTrue(2 === count($ids[0])); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testCanUseAttribute() { - $this->assertFalse($this->_model->canUseAttribute($this->_getAttributeByCode('sku'))); - $this->assertTrue($this->_model->canUseAttribute($this->_getAttributeByCode('test_configurable'))); + $this->assertFalse($this->model->canUseAttribute($this->_getAttributeByCode('sku'))); + $this->assertTrue($this->model->canUseAttribute($this->_getAttributeByCode('test_configurable'))); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testSetGetUsedProductAttributeIds() { $testConfigurable = $this->_getAttributeByCode('test_configurable'); - $actual = $this->_model->getUsedProductAttributeIds($this->_product); + $actual = $this->model->getUsedProductAttributeIds($this->product); $expected = [$testConfigurable->getId()]; $this->assertEquals($expected, $actual); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testSetUsedProductAttributeIds() { $testConfigurable = $this->_getAttributeByCode('test_configurable'); - $this->_model->setUsedProductAttributeIds([$testConfigurable->getId()], $this->_product); - $attributes = $this->_product->getData('_cache_instance_configurable_attributes'); + $this->model->setUsedProductAttributeIds([$testConfigurable->getId()], $this->product); + $attributes = $this->product->getData('_cache_instance_configurable_attributes'); $this->assertArrayHasKey(0, $attributes); $this->assertInstanceOf( 'Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute', @@ -89,18 +118,22 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $this->assertSame($testConfigurable, $attributes[0]->getProductAttribute()); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetUsedProductAttributes() { $testConfigurable = $this->_getAttributeByCode('test_configurable'); $attributeId = (int)$testConfigurable->getId(); - $attributes = $this->_model->getUsedProductAttributes($this->_product); + $attributes = $this->model->getUsedProductAttributes($this->product); $this->assertArrayHasKey($attributeId, $attributes); $this->assertSame($testConfigurable, $attributes[$attributeId]); } public function testGetConfigurableAttributes() { - $collection = $this->_model->getConfigurableAttributes($this->_product); + $collection = $this->model->getConfigurableAttributes($this->product); $this->assertInstanceOf( 'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection', $collection @@ -121,10 +154,16 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase } } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetConfigurableAttributesAsArray() { - $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product); + $product = $this->productRepository->getById(1, true); + $attributes = $this->model->getConfigurableAttributesAsArray($product); $attribute = reset($attributes); + $this->assertArrayHasKey('id', $attribute); $this->assertArrayHasKey('label', $attribute); $this->assertArrayHasKey('use_default', $attribute); @@ -151,33 +190,47 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase /** * @depends testGetConfigurableAttributesAsArray + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testGetParentIdsByChild() { - $result = $this->_model->getParentIdsByChild(10); + $result = $this->model->getParentIdsByChild(10); // fixture - $this->assertEquals([1], $result); + $this->assertEquals([$this->product->getId()], $result); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetConfigurableAttributeCollection() { - $collection = $this->_model->getConfigurableAttributeCollection($this->_product); + $collection = $this->model->getConfigurableAttributeCollection($this->product); $this->assertInstanceOf( 'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection', $collection ); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetUsedProductIds() { - $ids = $this->_model->getUsedProductIds($this->_product); + $ids = $this->model->getUsedProductIds($this->product); $this->assertInternalType('array', $ids); $this->assertTrue(2 === count($ids)); // impossible to check actual IDs, they are dynamic in the fixture } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetUsedProducts() { - $products = $this->_model->getUsedProducts($this->_product); + $products = $this->model->getUsedProducts($this->product); $this->assertInternalType('array', $products); $this->assertTrue(2 === count($products)); foreach ($products as $product) { @@ -189,92 +242,104 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase { $this->assertInstanceOf( 'Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection', - $this->_model->getUsedProductCollection($this->_product) + $this->model->getUsedProductCollection($this->product) ); } public function testBeforeSave() { - $this->assertEmpty($this->_product->getTypeHasOptions()); - $this->assertEmpty($this->_product->getTypeHasRequiredOptions()); + $this->assertEmpty($this->product->getTypeHasOptions()); + $this->assertEmpty($this->product->getTypeHasRequiredOptions()); - $this->_product->setCanSaveConfigurableAttributes(true); - $this->_product->setConfigurableAttributesData([['values' => 'not empty']]); - $this->_model->beforeSave($this->_product); - $this->assertTrue($this->_product->getTypeHasOptions()); - $this->assertTrue($this->_product->getTypeHasRequiredOptions()); + $this->product->setCanSaveConfigurableAttributes(true); + $this->product->setConfigurableAttributesData([['values' => 'not empty']]); + $this->model->beforeSave($this->product); + $this->assertTrue($this->product->getTypeHasOptions()); + $this->assertTrue($this->product->getTypeHasRequiredOptions()); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testIsSalable() { - $this->_product->unsetData('is_salable'); - $this->assertTrue($this->_model->isSalable($this->_product)); + $this->product->unsetData('is_salable'); + $this->assertTrue($this->model->isSalable($this->product)); } /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @depends testGetConfigurableAttributesAsArray */ public function testGetProductByAttributes() { - $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product); + $attributes = $this->model->getConfigurableAttributesAsArray($this->product); $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; - $product = $this->_model->getProductByAttributes( + $product = $this->model->getProductByAttributes( [$attribute['attribute_id'] => $optionValueId], - $this->_product + $this->product ); $this->assertInstanceOf('Magento\Catalog\Model\Product', $product); $this->assertEquals("simple_10", $product->getSku()); } /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @depends testGetConfigurableAttributesAsArray */ public function testGetSelectedAttributesInfo() { - $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product); + $product = $this->productRepository->getById(1, true); + $attributes = $this->model->getConfigurableAttributesAsArray($product); $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; - $this->_product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId])); - $info = $this->_model->getSelectedAttributesInfo($this->_product); + $product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId])); + $info = $this->model->getSelectedAttributesInfo($product); $this->assertEquals([['label' => 'Test Configurable', 'value' => 'Option 1']], $info); } /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @magentoAppIsolation enabled */ public function testGetSelectedAttributesInfoForStore() { - $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product); + $attributes = $this->model->getConfigurableAttributesAsArray($this->product); $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; - $this->_product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId])); + $this->product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId])); - $configurableAttr = $this->_model->getConfigurableAttributes($this->_product); + $configurableAttr = $this->model->getConfigurableAttributes($this->product); $attribute = $configurableAttr->getFirstItem(); $attribute->getProductAttribute()->setStoreLabel('store label'); - $info = $this->_model->getSelectedAttributesInfo($this->_product); + $info = $this->model->getSelectedAttributesInfo($this->product); $this->assertEquals([['label' => 'store label', 'value' => 'Option 1']], $info); } /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled * @depends testGetConfigurableAttributesAsArray */ public function testPrepareForCart() { - $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product); + $attributes = $this->model->getConfigurableAttributesAsArray($this->product); $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; $buyRequest = new \Magento\Framework\DataObject( ['qty' => 5, 'super_attribute' => [$attribute['attribute_id'] => $optionValueId]] ); - $result = $this->_model->prepareForCart($buyRequest, $this->_product); + $result = $this->model->prepareForCart($buyRequest, $this->product); $this->assertInternalType('array', $result); $this->assertTrue(2 === count($result)); foreach ($result as $product) { @@ -287,19 +352,21 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase { $this->assertEquals( 'You need to choose options for your item.', - (string)$this->_model->getSpecifyOptionMessage() + (string)$this->model->getSpecifyOptionMessage() ); } /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled * @depends testGetConfigurableAttributesAsArray * @depends testPrepareForCart */ public function testGetOrderOptions() { - $this->_prepareForCart(); + $product = $this->_prepareForCart(); - $result = $this->_model->getOrderOptions($this->_product); + $result = $this->model->getOrderOptions($product); $this->assertArrayHasKey('info_buyRequest', $result); $this->assertArrayHasKey('attributes_info', $result); $this->assertEquals( @@ -319,47 +386,57 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase } /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled * @depends testGetConfigurableAttributesAsArray * @depends testPrepareForCart */ public function testIsVirtual() { - $this->_prepareForCart(); - $this->assertFalse($this->_model->isVirtual($this->_product)); + $product = $this->_prepareForCart(); + $this->assertFalse($this->model->isVirtual($product)); } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled + */ public function testHasOptions() { - $this->assertTrue($this->_model->hasOptions($this->_product)); + $this->assertTrue($this->model->hasOptions($this->product)); } public function testGetWeight() { - $this->assertEmpty($this->_model->getWeight($this->_product)); + $this->assertEmpty($this->model->getWeight($this->product)); - $this->_product->setCustomOptions( + $this->product->setCustomOptions( [ 'simple_product' => new \Magento\Framework\DataObject( - [ - 'product' => new \Magento\Framework\DataObject(['weight' => 2]), - ] - ), + [ + 'product' => new \Magento\Framework\DataObject(['weight' => 2]), + ] + ), ] ); - $this->assertEquals(2, $this->_model->getWeight($this->_product)); + $this->assertEquals(2, $this->model->getWeight($this->product)); } public function testAssignProductToOption() { $option = new \Magento\Framework\DataObject(); - $this->_model->assignProductToOption('test', $option, $this->_product); + $this->model->assignProductToOption('test', $option, $this->product); $this->assertEquals('test', $option->getProduct()); // other branch of logic depends on \Magento\Sales module } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled + */ public function testGetProductsToPurchaseByReqGroups() { - $result = $this->_model->getProductsToPurchaseByReqGroups($this->_product); + $result = $this->model->getProductsToPurchaseByReqGroups($this->product); $this->assertArrayHasKey(0, $result); $this->assertInternalType('array', $result[0]); $this->assertTrue(2 === count($result[0])); @@ -369,48 +446,82 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase } } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled + */ public function testGetSku() { - $this->assertEquals('configurable', $this->_model->getSku($this->_product)); - $this->_prepareForCart(); - $this->assertStringStartsWith('simple_', $this->_model->getSku($this->_product)); + $this->assertEquals('configurable', $this->model->getSku($this->product)); + $product = $this->_prepareForCart(); + $this->assertStringStartsWith('simple_', $this->model->getSku($product)); } public function testProcessBuyRequest() { $buyRequest = new \Magento\Framework\DataObject(['super_attribute' => ['10', 'string']]); - $result = $this->_model->processBuyRequest($this->_product, $buyRequest); + $result = $this->model->processBuyRequest($this->product, $buyRequest); $this->assertEquals(['super_attribute' => [10]], $result); } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled + */ public function testSaveProductRelationsOneChild() { - $oldChildrenIds = $this->_product->getTypeInstance()->getChildrenIds(1); + $oldChildrenIds = $this->product->getTypeInstance() + ->getChildrenIds($this->product->getId()); + $oldChildrenIds = reset($oldChildrenIds); $oneChildId = reset($oldChildrenIds); - $this->assertNotEmpty($oldChildrenIds); - $this->assertNotEmpty($oneChildId); - $this->_product->setAssociatedProductIds([$oneChildId]); - $this->_model->save($this->_product); - $this->_product->load(1); + self::assertNotEmpty($oldChildrenIds); + self::assertNotEmpty($oneChildId); - $this->assertEquals( - [[$oneChildId => $oneChildId]], - $this->_product->getTypeInstance()->getChildrenIds(1) + $product = $this->productRepository->getById($this->product->getId()); + + $extensionAttributes = $product->getExtensionAttributes(); + $extensionAttributes->setConfigurableProductLinks([$oneChildId]); + $product->setExtensionAttributes($extensionAttributes); + + $this->productRepository->save($product); + + self::assertEquals( + [ + [ + $oneChildId => $oneChildId + ] + ], + $this->model->getChildrenIds($this->product->getId()) ); } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppIsolation enabled + */ public function testSaveProductRelationsNoChildren() { - $childrenIds = $this->_product->getTypeInstance()->getChildrenIds(1); - $this->assertNotEmpty(reset($childrenIds)); + $childrenIds = $this->product->getTypeInstance() + ->getChildrenIds($this->product->getId()); + + self::assertNotEmpty(reset($childrenIds)); + + $product = $this->productRepository->getById($this->product->getId(), true); + + $extensionAttributes = $product->getExtensionAttributes(); + $extensionAttributes->setConfigurableProductLinks([]); + $product->setExtensionAttributes($extensionAttributes); - $this->_product->setAssociatedProductIds([]); - $this->_model->save($this->_product); - $this->_product->load(1); + $this->productRepository->save($product); - $this->assertEquals([[]], $this->_product->getTypeInstance()->getChildrenIds(1)); + self::assertEquals( + [ + [] + ], + $this->model->getChildrenIds($this->product->getId()) + ); } /** @@ -421,7 +532,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase */ protected function _getAttributeByCode($code) { - return \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + return Bootstrap::getObjectManager()->get( 'Magento\Eav\Model\Config' )->getAttribute( 'catalog_product', @@ -431,16 +542,21 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase /** * Select one of the options and "prepare for cart" with a proper buy request + * + * @return ProductInterface */ protected function _prepareForCart() { - $attributes = $this->_model->getConfigurableAttributesAsArray($this->_product); + $product = $this->productRepository->getById(1, true); + $attributes = $this->model->getConfigurableAttributesAsArray($product); $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; $buyRequest = new \Magento\Framework\DataObject( ['qty' => 5, 'super_attribute' => [$attribute['attribute_id'] => $optionValueId]] ); - $this->_model->prepareForCart($buyRequest, $this->_product); + $this->model->prepareForCart($buyRequest, $product); + + return $product; } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php index 281b3a0a4b6c10cdc787c04368d216e4b923b630..0fd95a7d26bb1ec7f67fc542aa3164b20ccad8d1 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute.php @@ -3,29 +3,28 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config'); -$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); -//if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute -// && $attribute->getId() -//) { -// $attribute->delete(); -//} +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Eav\Api\AttributeRepositoryInterface; -//$resource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() -//->create('Magento\Catalog\Model\CategorySetup'); +$eavConfig = Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config'); +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); $eavConfig->clear(); -/* Create attribute */ /** @var $installer \Magento\Catalog\Setup\CategorySetup */ -$installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Setup\CategorySetup'); +$installer = Bootstrap::getObjectManager()->create('Magento\Catalog\Setup\CategorySetup'); + if (!$attribute->getId()) { + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $attribute = Bootstrap::getObjectManager()->create( 'Magento\Catalog\Model\ResourceModel\Eav\Attribute' ); + /** @var AttributeRepositoryInterface $attributeRepository */ + $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); + $attribute->setData( [ 'attribute_code' => 'test_configurable', @@ -34,7 +33,7 @@ if (!$attribute->getId()) { 'is_user_defined' => 1, 'frontend_input' => 'select', 'is_unique' => 0, - 'is_required' => 1, + 'is_required' => 0, 'is_searchable' => 0, 'is_visible_in_advanced_search' => 0, 'is_comparable' => 0, @@ -54,12 +53,9 @@ if (!$attribute->getId()) { ] ); - $attribute->save(); + $attributeRepository->save($attribute); } /* Assign attribute to attribute set */ $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); - -/** @var \Magento\Eav\Model\Config $eavConfig */ -$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config'); $eavConfig->clear(); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php index e17a7089ea2777498a089c39775d1e398f9a9097..7f7b89f4146e1fd8fb7b81c279e9555c2b1ef423 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php @@ -4,13 +4,28 @@ * See COPYING.txt for license details. */ +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Setup\CategorySetup; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\TestFramework\Helper\Bootstrap; + require __DIR__ . '/configurable_attribute.php'; -/** @var $installer \Magento\Catalog\Setup\CategorySetup */ -$installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Setup\CategorySetup'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + +/** @var $installer CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(CategorySetup::class); /* Create simple products per each option value*/ -/** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $options */ +/** @var AttributeOptionInterface[] $options */ $options = $attribute->getOptions(); $attributeValues = []; @@ -18,11 +33,12 @@ $attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); $associatedProductIds = []; $productIds = [10, 20]; array_shift($options); //remove the first option which is empty + foreach ($options as $option) { - /** @var $product \Magento\Catalog\Model\Product */ - $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); + /** @var $product Product */ + $product = Bootstrap::getObjectManager()->create(Product::class); $productId = array_shift($productIds); - $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + $product->setTypeId(Type::TYPE_SIMPLE) ->setId($productId) ->setAttributeSetId($attributeSetId) ->setWebsiteIds([1]) @@ -30,10 +46,11 @@ foreach ($options as $option) { ->setSku('simple_' . $productId) ->setPrice($productId) ->setTestConfigurable($option->getValue()) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) - ->save(); + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + + $product = $productRepository->save($product); $attributeValues[] = [ 'label' => 'test', @@ -43,26 +60,38 @@ foreach ($options as $option) { $associatedProductIds[] = $product->getId(); } -/** @var $product \Magento\Catalog\Model\Product */ -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); -$product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) +/** @var $product Product */ +$product = Bootstrap::getObjectManager()->create(Product::class); + +/** @var Factory $optionsFactory */ +$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class); + +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; + +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) ->setId(1) ->setAttributeSetId($attributeSetId) ->setWebsiteIds([1]) ->setName('Configurable Product') ->setSku('configurable') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]) - ->setAssociatedProductIds($associatedProductIds) - ->setConfigurableAttributesData( - [ - [ - 'attribute_id' => $attribute->getId(), - 'attribute_code' => $attribute->getAttributeCode(), - 'frontend_label' => 'test', - 'values' => $attributeValues, - ], - ] - ) - ->save(); + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php new file mode 100644 index 0000000000000000000000000000000000000000..abf6fd5f1225c36e85bdf0f4a3e41fbb9e1cb841 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +Bootstrap::getInstance()->reinitialize(); + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); + +/** @var $product Product */ +$product = $objectManager->create(Product::class); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setId(77) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple_77') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription("Short description") + ->setTaxClassId(0) + ->setTierPrice( + [ + [ + 'website_id' => 0, + 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'price_qty' => 2, + 'price' => 8, + ], + [ + 'website_id' => 0, + 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'price_qty' => 5, + 'price' => 5, + ], + [ + 'website_id' => 0, + 'cust_group' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'price_qty' => 3, + 'price' => 5, + ], + ] + ) + ->setDescription('Description with <b>html tag</b>') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + )->setCanSaveCustomOptions(true) + ->setHasOptions(true); + +$oldOptions = [ + [ + 'previous_group' => 'text', + 'title' => 'Test Field', + 'type' => 'field', + 'is_require' => 1, + 'sort_order' => 0, + 'price' => 1, + 'price_type' => 'fixed', + 'sku' => '1-text', + 'max_characters' => 100, + ], + [ + 'previous_group' => 'date', + 'title' => 'Test Date and Time', + 'type' => 'date_time', + 'is_require' => 1, + 'sort_order' => 0, + 'price' => 2, + 'price_type' => 'fixed', + 'sku' => '2-date', + ], + [ + 'previous_group' => 'select', + 'title' => 'Test Select', + 'type' => 'drop_down', + 'is_require' => 1, + 'sort_order' => 0, + 'values' => [ + [ + 'option_type_id' => -1, + 'title' => 'Option 1', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '3-1-select', + ], + [ + 'option_type_id' => -1, + 'title' => 'Option 2', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '3-2-select', + ], + ] + ], + [ + 'previous_group' => 'select', + 'title' => 'Test Radio', + 'type' => 'radio', + 'is_require' => 1, + 'sort_order' => 0, + 'values' => [ + [ + 'option_type_id' => -1, + 'title' => 'Option 1', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '4-1-radio', + ], + [ + 'option_type_id' => -1, + 'title' => 'Option 2', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '4-2-radio', + ], + ] + ] +]; + +$options = []; + +/** @var ProductCustomOptionInterfaceFactory $customOptionFactory */ +$customOptionFactory = $objectManager->create(ProductCustomOptionInterfaceFactory::class); + +foreach ($oldOptions as $option) { + /** @var ProductCustomOptionInterface $option */ + $option = $customOptionFactory->create(['data' => $option]); + $option->setProductSku($product->getSku()); + + $options[] = $option; +} + +$product->setOptions($options); + +/** @var ProductRepositoryInterface $productRepositoryFactory */ +$productRepositoryFactory = $objectManager->create(ProductRepositoryInterface::class); +$productRepositoryFactory->save($product); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php index 00e50477ae4c8df6d9e550d6ec2495a53ec99436..eb6b1f1e3bf941f0c3b718c1edb3889c8b167d63 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +//use Magento\Catalog\Api\ProductRepositoryInterface; + require 'product_configurable.php'; /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ @@ -18,7 +20,14 @@ $options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( $option = $options->setAttributeFilter($attribute->getId())->getFirstItem(); $requestInfo = new \Magento\Framework\DataObject( - ['qty' => 1, 'super_attribute' => [$attribute->getId() => $option->getId()]] + [ + 'product' => 1, + 'selected_configurable_option' => 1, + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId() + ] + ] ); /** @var $cart \Magento\Checkout\Model\Cart */ diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php index b617f9b8fd2249b88d055c86d0c94c8c90ac302e..47763f83da3d7cad20f233897323820c41aaaebb 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php @@ -6,6 +6,10 @@ namespace Magento\Quote\Model; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Api\FilterBuilder; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\Data\CartInterface; class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase { @@ -13,6 +17,55 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase * @magentoDataFixture Magento/Sales/_files/quote.php */ public function testGetList() + { + $searchCriteria = $this->getSearchCriteria('test01'); + /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ + $quoteRepository = Bootstrap::getObjectManager()->create(CartRepositoryInterface::class); + $searchResult = $quoteRepository->getList($searchCriteria); + $this->performAssertions($searchResult); + } + + /** + * @magentoDataFixture Magento/Sales/_files/quote.php + */ + public function testGetListDoubleCall() + { + $searchCriteria1 = $this->getSearchCriteria('test01'); + $searchCriteria2 = $this->getSearchCriteria('test02'); + + /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ + $quoteRepository = Bootstrap::getObjectManager()->create(CartRepositoryInterface::class); + $searchResult = $quoteRepository->getList($searchCriteria1); + $this->performAssertions($searchResult); + $searchResult = $quoteRepository->getList($searchCriteria2); + $items = $searchResult->getItems(); + $this->assertEmpty($items); + } + + /** + * @param string $filterValue + * @return \Magento\Framework\Api\SearchCriteria + */ + private function getSearchCriteria($filterValue) + { + /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); + $filterBuilder = Bootstrap::getObjectManager()->create(FilterBuilder::class); + $filters = []; + $filters[] = $filterBuilder + ->setField('reserved_order_id') + ->setConditionType('=') + ->setValue($filterValue) + ->create(); + $searchCriteriaBuilder->addFilters($filters); + + return $searchCriteriaBuilder->create(); + } + + /** + * @param object $searchResult + */ + protected function performAssertions($searchResult) { $expectedExtensionAttributes = [ 'firstname' => 'firstname', @@ -20,16 +73,10 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase 'email' => 'admin@example.com' ]; - /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = Bootstrap::getObjectManager()->create('Magento\Framework\Api\SearchCriteriaBuilder'); - - /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ - $quoteRepository = Bootstrap::getObjectManager()->create('Magento\Quote\Api\CartRepositoryInterface'); - $searchResult = $quoteRepository->getList($searchCriteriaBuilder->create()); $items = $searchResult->getItems(); /** @var \Magento\Quote\Api\Data\CartInterface $actualQuote */ $actualQuote = array_pop($items); - $this->assertInstanceOf('Magento\Quote\Api\Data\CartInterface', $actualQuote); + $this->assertInstanceOf(CartInterface::class, $actualQuote); $this->assertEquals('test01', $actualQuote->getReservedOrderId()); /** @var \Magento\User\Api\Data\UserInterface $testAttribute */ $testAttribute = $actualQuote->getExtensionAttributes()->getQuoteTestAttribute(); diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SearchEngine/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SearchEngine/ConfigTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7b41e4f9d5d52cd261807cc46e5de55e005f7eed --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Search/Model/SearchEngine/ConfigTest.php @@ -0,0 +1,165 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Model\SearchEngine; + +/** + * Class ConfigTest + * + * @magentoAppIsolation enabled + */ +class ConfigTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + $xmlPath = __DIR__ . '/../../_files/search_engine.xml'; + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + // Clear out the clache + $cacheManager = $objectManager->create('Magento\Framework\App\Cache\Manager'); + /** @var \Magento\Framework\App\Cache\Manager $cacheManager */ + $cacheManager->clean($cacheManager->getAvailableTypes()); + + $fileResolver = $this->getMockForAbstractClass( + 'Magento\Framework\Config\FileResolverInterface', + [], + '', + false + ); + $fileResolver->expects($this->any())->method('get')->willReturn([file_get_contents($xmlPath)]); + + $configReader = $objectManager->create( + 'Magento\Framework\Search\SearchEngine\Config\Reader', + ['fileResolver' => $fileResolver] + ); + $dataStorage = $objectManager->create( + 'Magento\Search\Model\SearchEngine\Config\Data', + ['reader' => $configReader] + ); + $this->config = $objectManager->create( + 'Magento\Search\Model\SearchEngine\Config', + ['dataStorage' => $dataStorage] + ); + } + + /** + * Data provider for the test + * + * @return array + */ + public static function loadGetDeclaredFeaturesDataProvider() + { + return [ + 'features-synonyms' => [ + 'searchEngine' => 'mysql', + 'expectedResult' => ['synonyms'] + ], + 'features-synonyms-stopwords' => [ + 'searchEngine' => 'other', + 'expectedResult' => ['synonyms', 'stopwords'] + ], + 'features-none1' => [ + 'searchEngine' => 'none1', + 'expectedResult' => [] + ], + 'features-none2' => [ + 'searchEngine' => 'none2', + 'expectedResult' => [] + ], + 'features-none_exist' => [ + 'searchEngine' => 'none_exist', + 'expectedResult' => [] + ] + + ]; + } + + /** + * @param string $searchEngine + * @param array $expectedResult + * @dataProvider loadGetDeclaredFeaturesDataProvider + */ + public function testGetDeclaredFeatures($searchEngine, $expectedResult) + { + $this->assertEquals($expectedResult, $this->config->getDeclaredFeatures($searchEngine)); + } + + /** + * Data provider for the test + * + * @return array + */ + public static function loadIsFeatureSupportedDataProvider() + { + return [ + [ + 'feature' => 'synonyms', + 'searchEngine' => 'mysql', + 'expectedResult' => true + ], + [ + 'feature' => 'stopwords', + 'searchEngine' => 'mysql', + 'expectedResult' => false + ], + [ + 'feature' => 'synonyms', + 'searchEngine' => 'other', + 'expectedResult' => true + ], + [ + 'feature' => 'stopwords', + 'searchEngine' => 'other', + 'expectedResult' => true + ], + [ + 'feature' => 'synonyms', + 'searchEngine' => 'none1', + 'expectedResult' => false + ], + [ + 'feature' => 'stopwords', + 'searchEngine' => 'none1', + 'expectedResult' => false + ], + [ + 'feature' => 'synonyms', + 'searchEngine' => 'none2', + 'expectedResult' => false + ], + [ + 'feature' => 'stopwords', + 'searchEngine' => 'none2', + 'expectedResult' => false + ], + [ + 'feature' => 'stopwords', + 'searchEngine' => 'none_exist', + 'expectedResult' => false + ], + [ + 'feature' => 'none_exist', + 'searchEngine' => 'none_exist', + 'expectedResult' => false + ], + [ + 'feature' => 'none_exist', + 'searchEngine' => 'mysql', + 'expectedResult' => false + ] + ]; + } + + /** + * @param string $searchEngine + * @param string $feature + * @param array $expectedResult + * @dataProvider loadIsFeatureSupportedDataProvider + */ + public function testIsFeatureSupported($searchEngine, $feature, $expectedResult) + { + $this->assertEquals($expectedResult, $this->config->isFeatureSupported($searchEngine, $feature)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php index 99aa10d6e7cd029b5db3a093da53d6fbf5fd535d..9980f94a92ab564f1c806f57e79199788be7139c 100644 --- a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php +++ b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymAnalyzerTest.php @@ -31,64 +31,25 @@ class SynonymAnalyzerTest extends \PHPUnit_Framework_TestCase { return [ 'WithSynonymsFromStoreViewScope' => [ - 'phrase' => 'Elizabeth is the English queen.', + 'phrase' => 'elizabeth is the english queen', 'expectedResult' => [['elizabeth'],['is'],['the'],['british', 'english'],['queen', 'monarch']] ], 'WithSynonymsFromWebsiteScope' => [ - 'phrase' => 'Orange hill', + 'phrase' => 'orange hill', 'expectedResult' => [['orange', 'magento'], ['hill', 'mountain', 'peak']] ], 'WithSynonymsFromDefaultScope' => [ - 'phrase' => 'universe is enormous.', + 'phrase' => 'universe is enormous', 'expectedResult' => [['universe', 'cosmos'], ['is'], ['big', 'huge', 'large', 'enormous']] ], 'noSynonyms' => [ - 'phrase' => 'This sentence has no synonyms', + 'phrase' => 'this sentence has no synonyms', 'expectedResult' => [['this'], ['sentence'], ['has'], ['no'], ['synonyms']] ], - 'specialCharacters' => [ - 'phrase' => '~tilde`backtic! exclamation@ at#hash\$dollar%percent^carat&ersand*star(leftparan' - . ')rightparan_underscore+plus=equal{leftcurly}rightcurly[leftbracket]rightbracket:colon' - . '"doublequote\'singlequote,comma space.period<leftangle>rightangle?questionmark\\backslash' - . '/forwardslash tab;semicolon', - 'expectedResult' => [ - ['tilde'], - ['backtic'], - ['exclamation'], - ['at'], - ['hash'], - ['dollar'], - ['percent'], - ['carat'], - ['ampersand'], - ['star'], - ['leftparan'], - ['rightparan'], - ['underscore'], - ['plus'], - ['equal'], - ['leftcurly'], - ['rightcurly'], - ['leftbracket'], - ['rightbracket'], - ['colon'], - ['doublequote'], - ['singlequote'], - ['comma'], - ['space'], - ['period'], - ['leftangle'], - ['rightangle'], - ['questionmark'], - ['backslash'], - ['forwardslash'], - ['tab'], - ['semicolon'] - ] - ], 'oneMoreTest' => [ 'phrase' => 'schlicht', - 'expectedResult' => [['schlicht', 'natürlich']]] + 'expectedResult' => [['schlicht', 'natürlich']] + ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Search/_files/search_engine.xml b/dev/tests/integration/testsuite/Magento/Search/_files/search_engine.xml new file mode 100644 index 0000000000000000000000000000000000000000..dcee4dabaa8dacabfc44f03b636f3d92e4093d39 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Search/_files/search_engine.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="mysql"> + <feature name="synonyms" support="1" /> + <feature name="stopwords" support="0" /> + </engine> + <engine name="other"> + <feature name="synonyms" support="1" /> + <feature name="stopwords" support="1" /> + </engine> + <engine name="none1"> + <feature name="synonyms" support="0" /> + <feature name="stopwords" support="0" /> + </engine> + <engine name="none2" /> +</engines> diff --git a/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php b/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php index 3bfc2aa21ef65ec1907bbccaeb64b9e3270af263..135c87b1ad88fa3aa03a129885d4938e772d9062 100644 --- a/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Translation/Controller/AjaxTest.php @@ -8,18 +8,58 @@ namespace Magento\Translation\Controller; class AjaxTest extends \Magento\TestFramework\TestCase\AbstractController { + protected function setUp() + { + /* Called getConfig as workaround for setConfig bug */ + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Store\Model\StoreManagerInterface' + )->getStore( + 'default' + )->getConfig( + 'dev/translate_inline/active' + ); + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Framework\App\Config\MutableScopeConfigInterface' + )->setValue( + 'dev/translate_inline/active', + true, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + 'default' + ); + parent::setUp(); + } + /** * @dataProvider indexActionDataProvider */ - public function testIndexAction($postData) + public function testIndexAction($postData, $expected) { $this->getRequest()->setPostValue('translate', $postData); $this->dispatch('translation/ajax/index'); - $this->assertEquals('{success:true}', $this->getResponse()->getBody()); + $this->assertEquals($expected, $this->getResponse()->getBody()); } public function indexActionDataProvider() { - return [['test'], [['test']]]; + return [ + [ + [ + [ + 'original' => 'phrase1', + 'custom' => 'translation1' + ] + ], + '{"phrase1":"translation1"}' + ], + [ + [ + [ + 'original' => 'phrase2', + 'custom' => 'translation2' + ] + ], + '{"phrase1":"translation1","phrase2":"translation2"}' + ] + ]; } } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/ConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php similarity index 90% rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/ConfigTest.php rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php index 21d35b9e428e20fca7fd86c4a0d2bab68deaa721..d8eef79532b57daace020258c2fab480c58e61aa 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/ConfigTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php @@ -7,7 +7,7 @@ */ namespace Magento\Test\Integrity\Magento\Framework\Search; -class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig +class RequestConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig { /** @var \Magento\Framework\Config\Dom\UrnResolver */ protected $urnResolver; @@ -44,7 +44,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig */ protected function _getKnownValidXml() { - return __DIR__ . '/_files/valid.xml'; + return __DIR__ . '/_files/request/valid.xml'; } /** @@ -54,7 +54,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig */ protected function _getKnownInvalidXml() { - return __DIR__ . '/_files/invalid.xml'; + return __DIR__ . '/_files/request/invalid.xml'; } /** @@ -64,7 +64,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig */ protected function _getKnownValidPartialXml() { - return __DIR__ . '/_files/valid_partial.xml'; + return __DIR__ . '/_files/request/valid_partial.xml'; } /** @@ -76,7 +76,7 @@ class ConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig explode( "\n", " -No match found for key-sequence ['sugegsted_search_container'] of keyref 'requestQueryReference'. +No match found for key-sequence ['suggested_search_container'] of keyref 'requestQueryReference'. Element 'queryReference': No match found for key-sequence ['fulltext_search_query4'] of keyref 'queryReference'. " ) @@ -126,7 +126,7 @@ Element 'request': Missing child element(s). Expected is ( from )." */ protected function _getKnownInvalidPartialXml() { - return __DIR__ . '/_files/invalid_partial.xml'; + return __DIR__ . '/_files/request/invalid_partial.xml'; } public function testSchemaUsingValidXml() diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/SearchEngineConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/SearchEngineConfigTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f17525512d9490edbf8b48842ec5b2d7cb144be5 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/SearchEngineConfigTest.php @@ -0,0 +1,115 @@ +<?php +/** + * Test search_request.xsd and xml files. + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Test\Integrity\Magento\Framework\Search; + +class SearchEngineConfigTest extends \Magento\TestFramework\Integrity\AbstractConfig +{ + /** @var \Magento\Framework\Config\Dom\UrnResolver */ + protected $urnResolver; + + protected function setUp() + { + $this->urnResolver = new \Magento\Framework\Config\Dom\UrnResolver(); + } + + /** + * Returns the name of the XSD file to be used to validate the XML + * + * @return string + */ + protected function _getXsd() + { + return $this->urnResolver->getRealPath('urn:magento:framework:Search/etc/search_engine.xsd'); + } + + /** + * Returns the name of the XSD file to be used to validate partial XML + * + * @return string + */ + protected function _getFileXsd() + { + return $this->urnResolver->getRealPath('urn:magento:framework:Search/etc/search_engine.xsd'); + } + + /** + * The location of a single valid complete xml file + * + * @return string + */ + protected function _getKnownValidXml() + { + return __DIR__ . '/_files/search_engine/valid.xml'; + } + + /** + * The location of a single known invalid complete xml file + * + * @return string + */ + protected function _getKnownInvalidXml() + { + return __DIR__ . '/_files/search_engine/invalid.xml'; + } + + /** + * The location of a single known valid partial xml file + * + * @return string + */ + protected function _getKnownValidPartialXml() + { + return null; + } + + /** + * @param null $expectedErrors + */ + public function testSchemaUsingInvalidXml($expectedErrors = null) + { + $expectedErrors = array_filter( + explode( + "\n", + " +Element 'feature': The attribute 'support' is required but missing. +Element 'wrong': This element is not expected. Expected is ( feature ). +Element 'feature': The attribute 'name' is required but missing. +Element 'engine', attribute 'wrong': The attribute 'wrong' is not allowed. +Element 'engine': The attribute 'name' is required but missing. +Element 'feature', attribute 'support': 'wrong' is not a valid value of the atomic type 'xs:boolean'. +" + ) + ); + parent::testSchemaUsingInvalidXml($expectedErrors); + } + + /** + * Returns the name of the xml files to validate + * + * @return string + */ + protected function _getXmlName() + { + return 'search_engine.xml'; + } + + /** + * The location of a single known invalid partial xml file + * + * @return string + */ + protected function _getKnownInvalidPartialXml() + { + return null; + } + + public function testSchemaUsingValidXml() + { + parent::testSchemaUsingValidXml(); + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid.xml similarity index 90% rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid.xml rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid.xml index 9abe310f942b64b9633996dde5ad1cdff130e51e..70ddfa820b9657dc9a24801c60c2c1ab4563b820 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid.xml +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid.xml @@ -6,7 +6,7 @@ */ --> <requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request.xsd"> - <request query="sugegsted_search_container" index="product"> + <request query="suggested_search_container" index="product"> <queries> <query xsi:type="boolQuery" name="suggested_search_container_dd" boost="2"> <queryReference clause="not" ref="fulltext_search_query4" /> diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid_partial.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid_partial.xml similarity index 100% rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/invalid_partial.xml rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/invalid_partial.xml diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid.xml similarity index 100% rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid.xml rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid.xml diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid_partial.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid_partial.xml similarity index 100% rename from dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/valid_partial.xml rename to dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/request/valid_partial.xml diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/invalid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/invalid.xml new file mode 100644 index 0000000000000000000000000000000000000000..575d29fd23a7f354deeb96a50261a974d3b929c5 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/invalid.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="engine1"> + <feature name="feature1"/> + <wrong name="feature2" support="1" /> + </engine> + <engine name="engine2"> + <feature support="1" /> + </engine> + <engine wrong="engine3"> + <feature name="feature1" support="wrong" /> + <feature name="feature2" support="0" /> + </engine> +</engines> diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/valid.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/valid.xml new file mode 100644 index 0000000000000000000000000000000000000000..dca8671e217c9d44ef2fc89adfb482e0ef8f9b99 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/_files/search_engine/valid.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="engine1"> + <feature name="feature1" support="true" /> + <feature name="feature2" support="1" /> + </engine> + <engine name="engine2"> + <feature name="feature1" support="1" /> + <feature name="feature2" support="false" /> + </engine> + <engine name="engine3"> + <feature name="feature1" support="0" /> + <feature name="feature2" support="0" /> + </engine> +</engines> diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index ff6091a8e1f743efbdeee30b269942fb56146d23..578f58b606dd342ea94ffc2969eb323358d92bc1 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -2518,4 +2518,5 @@ return [ ['fillFormTab', 'Magento\Backend\Test\Block\Widget\Tab', 'Magento\Ui\Test\Block\Adminhtml\AbstractContainer::setFieldsData'], ['getDataFormTab', 'Magento\Backend\Test\Block\Widget\Tab', 'Magento\Ui\Test\Block\Adminhtml\AbstractContainer::getFieldsData'], ['getBunchImages', 'Magento\CatalogImportExport\Model\Import\Product'], + ['_isAttributeValueEmpty', 'Magento\Catalog\Model\ResourceModel\AbstractResource'], ]; diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index d5a5aa20d2961d9c28415f3a36eeb5b764ce20db..db7f389c81253e6e4cf35f7b328a4116dd4ad0a1 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -2789,6 +2789,9 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface if (($key == 'seq') || ($key == 'sneq')) { $key = $this->_transformStringSqlCondition($key, $value); } + if (($key == 'in' || $key == 'nin') && is_string($value)) { + $value = explode(',', $value); + } $query = $this->_prepareQuotedSqlCondition($conditionKeyMap[$key], $value, $fieldName); } else { $queries = []; diff --git a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php index f3d029454c21da9460a5a2869d4169d7382cdc22..52bf37f794b235af488fcf3a28649da75c2d940f 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php @@ -347,37 +347,22 @@ abstract class AbstractElement extends AbstractForm $html = ''; $htmlId = $this->getHtmlId(); - if (($beforeElementHtml = $this->getBeforeElementHtml())) { - $html .= '<label class="addbefore" for="' . - $htmlId . - '">' . - $beforeElementHtml . - '</label>'; + $beforeElementHtml = $this->getBeforeElementHtml(); + if ($beforeElementHtml) { + $html .= '<label class="addbefore" for="' . $htmlId . '">' . $beforeElementHtml . '</label>'; } - $html .= '<input id="' . - $htmlId . - '" name="' . - $this->getName() . - '" ' . - $this->_getUiId() . - ' value="' . - $this->getEscapedValue() . - '" ' . - $this->serialize( - $this->getHtmlAttributes() - ) . '/>'; - - if (($afterElementJs = $this->getAfterElementJs())) { + $html .= '<input id="' . $htmlId . '" name="' . $this->getName() . '" ' . $this->_getUiId() . ' value="' . + $this->getEscapedValue() . '" ' . $this->serialize($this->getHtmlAttributes()) . '/>'; + + $afterElementJs = $this->getAfterElementJs(); + if ($afterElementJs) { $html .= $afterElementJs; } - if (($afterElementHtml = $this->getAfterElementHtml())) { - $html .= '<label class="addafter" for="' . - $htmlId . - '">' . - $afterElementHtml . - '</label>'; + $afterElementHtml = $this->getAfterElementHtml(); + if ($afterElementHtml) { + $html .= '<label class="addafter" for="' . $htmlId . '">' . $afterElementHtml . '</label>'; } return $html; diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/FileTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/FileTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4ba97920fd8e47dea4231154400d07c929dd2a40 --- /dev/null +++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/FileTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Filesystem\Test\Unit\Driver; + +use Magento\Framework\Filesystem\Driver\File; + +class FileTest extends \PHPUnit_Framework_TestCase +{ + /** @var string Result of file_get_contents() function */ + public static $fileGetContents; + + /** @var bool Result of file_put_contents() function */ + public static $filePutContents; + + public function setUp() + { + self::$fileGetContents = ''; + self::$filePutContents = true; + } + + /** + * @dataProvider dataProviderForTestGetAbsolutePath + */ + public function testGetAbsolutePath($basePath, $path, $expected) + { + $file = new File(); + $this->assertEquals($expected, $file->getAbsolutePath($basePath, $path)); + } + + public function dataProviderForTestGetAbsolutePath() + { + return [ + ['/root/path/', 'sub', '/root/path/sub'], + ['/root/path/', '/sub', '/root/path/sub'], + ['/root/path/', '../sub', '/root/path/../sub'], + ['/root/path/', '/root/path/sub', '/root/path/root/path/sub'], + ]; + } + + /** + * @dataProvider dataProviderForTestGetRelativePath + */ + public function testGetRelativePath($basePath, $path, $expected) + { + $file = new File(); + $this->assertEquals($expected, $file->getRelativePath($basePath, $path)); + } + + public function dataProviderForTestGetRelativePath() + { + return [ + ['/root/path/', 'sub', 'sub'], + ['/root/path/', '/sub', '/sub'], + ['/root/path/', '/root/path/sub', 'sub'], + ['/root/path/sub', '/root/path/other', '/root/path/other'], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 1d3a7bd7e093e677d61daf66471ca58737f716dc..8d812795fff1b911664f73a9f433adb74a9b0068 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -44,6 +44,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter $this->_fileMimeType = null; $this->_fileType = null; } + /** * Open image for processing * @@ -60,6 +61,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter if ($this->_isMemoryLimitReached()) { throw new \OverflowException('Memory limit has been reached.'); } + $this->imageDestroy(); $this->_imageHandler = call_user_func($this->_getCallback('create'), $this->_fileName); } @@ -151,6 +153,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter } $this->_fillBackgroundColor($newImage); imagecopy($newImage, $this->_imageHandler, 0, 0, 0, 0, $this->_imageSrcWidth, $this->_imageSrcHeight); + $this->imageDestroy(); $this->_imageHandler = $newImage; } } @@ -361,6 +364,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter $this->_imageSrcHeight ); } + $this->imageDestroy(); $this->_imageHandler = $newImage; $this->refreshImageDimensions(); $this->_resized = true; @@ -374,7 +378,9 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter */ public function rotate($angle) { - $this->_imageHandler = imagerotate($this->_imageHandler, $angle, $this->imageBackgroundColor); + $rotatedImage = imagerotate($this->_imageHandler, $angle, $this->imageBackgroundColor); + $this->imageDestroy(); + $this->_imageHandler = $rotatedImage; $this->refreshImageDimensions(); } @@ -595,7 +601,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter $newWidth, $newHeight ); - + $this->imageDestroy(); $this->_imageHandler = $canvas; $this->refreshImageDimensions(); return true; @@ -631,6 +637,16 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter * Standard destructor. Destroy stored information about image */ public function __destruct() + { + $this->imageDestroy(); + } + + /** + * Helper function to free up memory associated with _imageHandler resource + * + * @return void + */ + private function imageDestroy() { if (is_resource($this->_imageHandler)) { imagedestroy($this->_imageHandler); @@ -755,6 +771,7 @@ class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter imagesavealpha($image, true); imagefill($image, 0, 0, $colorWhite); + $this->imageDestroy(); $this->_imageHandler = $image; } } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php index a727144c93a6855e35f2d1ef083507371dc6ca95..2232ece0b93d6985b78326c7e9dc9db30435016f 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php @@ -12,6 +12,7 @@ use Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface; use Magento\Framework\Search\Adapter\Mysql\ScoreBuilder; use Magento\Framework\Search\Request\Query\BoolExpression; use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; +use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; class Match implements QueryInterface { @@ -39,20 +40,28 @@ class Match implements QueryInterface */ private $fulltextSearchMode; + /** + * @var PreprocessorInterface[] + */ + protected $preprocessors; + /** * @param ResolverInterface $resolver * @param Fulltext $fulltextHelper * @param string $fulltextSearchMode + * @param PreprocessorInterface[] $preprocessors */ public function __construct( ResolverInterface $resolver, Fulltext $fulltextHelper, - $fulltextSearchMode = Fulltext::FULLTEXT_MODE_BOOLEAN + $fulltextSearchMode = Fulltext::FULLTEXT_MODE_BOOLEAN, + array $preprocessors = [] ) { $this->resolver = $resolver; $this->replaceSymbols = str_split(self::SPECIAL_CHARACTERS, 1); $this->fulltextHelper = $fulltextHelper; $this->fulltextSearchMode = $fulltextSearchMode; + $this->preprocessors = $preprocessors; } /** @@ -107,6 +116,9 @@ class Match implements QueryInterface protected function prepareQuery($queryValue, $conditionType) { $queryValue = str_replace($this->replaceSymbols, ' ', $queryValue); + foreach ($this->preprocessors as $preprocessor) { + $queryValue = $preprocessor->process($queryValue); + } $stringPrefix = ''; if ($conditionType === BoolExpression::QUERY_CONDITION_MUST) { diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php index a01690cf580d03ec7824301412913a75cfc161ed..8a9e28c7d13a8f9cf8559d041b6698fde4ff3a5c 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php @@ -31,6 +31,8 @@ class TemporaryStorage } /** + * Stores Documents + * * @param \ArrayIterator|\Magento\Framework\Search\Document[] $documents * @return Table */ @@ -44,7 +46,38 @@ class TemporaryStorage ]; } - $table = $this->createTemporaryTable(); + return $this->populateTemporaryTable($this->createTemporaryTable(), $data); + } + + /** + * Stores Api type Documents + * + * @param \Magento\Framework\Api\Search\DocumentInterface[] $documents + * @return Table + */ + public function storeApiDocuments($documents) + { + $data = []; + foreach ($documents as $document) { + $data[] = [ + $document->getId(), + $document->getCustomAttribute('score')->getValue(), + ]; + } + + return $this->populateTemporaryTable($this->createTemporaryTable(), $data); + } + + /** + * Populates temporary table + * + * @param Table $table + * @param array $data + * @return Table + * @throws \Zend_Db_Exception + */ + private function populateTemporaryTable(Table $table, $data) + { if (count($data)) { $this->getConnection()->insertArray( $table->getName(), diff --git a/lib/internal/Magento/Framework/Search/Adapter/Preprocessor/PreprocessorInterface.php b/lib/internal/Magento/Framework/Search/Adapter/Preprocessor/PreprocessorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..322f68610d3c2cbdfbefb916427c973b36742bcc --- /dev/null +++ b/lib/internal/Magento/Framework/Search/Adapter/Preprocessor/PreprocessorInterface.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\Adapter\Preprocessor; + +interface PreprocessorInterface +{ + /** + * @param string $query + * @return string + */ + public function process($query); +} diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/Config/Converter.php b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Converter.php new file mode 100644 index 0000000000000000000000000000000000000000..897b6a999a8952c06dbb125828fca785966b2973 --- /dev/null +++ b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Converter.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\SearchEngine\Config; + +use Magento\Framework\Config\ConverterInterface; + +class Converter implements ConverterInterface +{ + /** + * {@inheritdoc} + */ + public function convert($source) + { + $result = []; + /** @var \DOMElement $engine */ + foreach ($source->documentElement->getElementsByTagName('engine') as $engine) { + $name = $engine->getAttribute('name'); + $result[$name] = []; + /** @var \DOMElement $feature */ + foreach ($engine->getElementsByTagName('feature') as $feature) { + if ($feature->getAttribute('support') === '1' + || strtolower($feature->getAttribute('support')) === 'true' + ) { + $result[$name][] = strtolower($feature->getAttribute('name')); + } + } + } + return $result; + } +} diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/Config/Reader.php b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Reader.php new file mode 100644 index 0000000000000000000000000000000000000000..dbf9c610d8f349a5d771fe799691b9c24add5a76 --- /dev/null +++ b/lib/internal/Magento/Framework/Search/SearchEngine/Config/Reader.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\SearchEngine\Config; + +class Reader extends \Magento\Framework\Config\Reader\Filesystem +{ + /** + * List of id attributes for merge + * + * @var array + */ + protected $_idAttributes = ['/engines/engine' => 'name']; + + /** + * Constructor + * + * @param \Magento\Framework\Config\FileResolverInterface $fileResolver + * @param Converter $converter + * @param SchemaLocator $schemaLocator + * @param \Magento\Framework\Config\ValidationStateInterface $validationState + * @param string $fileName + * @param array $idAttributes + * @param string $domDocumentClass + * @param string $defaultScope + */ + public function __construct( + \Magento\Framework\Config\FileResolverInterface $fileResolver, + Converter $converter, + SchemaLocator $schemaLocator, + \Magento\Framework\Config\ValidationStateInterface $validationState, + $fileName = 'search_engine.xml', + $idAttributes = [], + $domDocumentClass = 'Magento\Framework\Config\Dom', + $defaultScope = 'global' + ) { + parent::__construct( + $fileResolver, + $converter, + $schemaLocator, + $validationState, + $fileName, + $idAttributes, + $domDocumentClass, + $defaultScope + ); + } +} diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Search/SearchEngine/Config/SchemaLocator.php new file mode 100644 index 0000000000000000000000000000000000000000..c822cf026cd7ccdddea4a0b1012a9fd38e2d4ad8 --- /dev/null +++ b/lib/internal/Magento/Framework/Search/SearchEngine/Config/SchemaLocator.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\SearchEngine\Config; + +use Magento\Framework\Config\SchemaLocatorInterface; + +class SchemaLocator implements SchemaLocatorInterface +{ + const SEARCH_ENGINE_XSD_PATH = 'urn:magento:framework:Search/etc/search_engine.xsd'; + + /** + * URN resolver + * + * @var \Magento\Framework\Config\Dom\UrnResolver + */ + protected $urnResolver; + + /** + * Constructor + * + * @param \Magento\Framework\Config\Dom\UrnResolver $urnResolver + */ + public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolver) + { + $this->urnResolver = $urnResolver; + } + + /** + * Get path to merged config schema + * + * @return string|null + */ + public function getSchema() + { + return $this->urnResolver->getRealPath(self::SEARCH_ENGINE_XSD_PATH); + } + + /** + * Get path to per file validation schema + * + * @return string|null + */ + public function getPerFileSchema() + { + return $this->urnResolver->getRealPath(self::SEARCH_ENGINE_XSD_PATH); + } +} diff --git a/lib/internal/Magento/Framework/Search/SearchEngine/ConfigInterface.php b/lib/internal/Magento/Framework/Search/SearchEngine/ConfigInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..44233316368ba79166448f38a81d105ad2cfe50a --- /dev/null +++ b/lib/internal/Magento/Framework/Search/SearchEngine/ConfigInterface.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\SearchEngine; + +interface ConfigInterface +{ + /** + * Search engine feature: synonyms + */ + const SEARCH_ENGINE_FEATURE_SYNONYMS = 'synonyms'; + + /** + * Get declared features of a search engine + * + * @param string $searchEngine + * @return string[] + */ + public function getDeclaredFeatures($searchEngine); + + /** + * Checks if a particular search feature is supported + * + * @param string $featureName + * @param string $searchEngine + * @return bool + */ + public function isFeatureSupported($featureName, $searchEngine); +} diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php index 901474b5773c5712815c477ec483361adc5da716..528b2f9890138ac5b00538903d7d82eb933fe229 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php @@ -8,16 +8,17 @@ namespace Magento\Framework\Search\Test\Unit\Adapter\Mysql\Query\Builder; use Magento\Framework\DB\Select; use Magento\Framework\Search\Request\Query\BoolExpression; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit_Framework_MockObject_MockObject as MockObject; class MatchTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Framework\Search\Adapter\Mysql\ScoreBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Search\Adapter\Mysql\ScoreBuilder|MockObject */ private $scoreBuilder; /** - * @var \Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface|MockObject */ private $resolver; @@ -27,10 +28,15 @@ class MatchTest extends \PHPUnit_Framework_TestCase private $match; /** - * @var \Magento\Framework\DB\Helper\Mysql\Fulltext|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\DB\Helper\Mysql\Fulltext|MockObject */ private $fulltextHelper; + /** + * @var \Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface|MockObject + */ + private $preprocessor; + protected function setUp() { $helper = new ObjectManager($this); @@ -49,19 +55,32 @@ class MatchTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->preprocessor = $this->getMockBuilder('Magento\Search\Adapter\Mysql\Query\Preprocessor\Synonyms') + ->setMethods(['process']) + ->disableOriginalConstructor() + ->getMock(); + $this->match = $helper->getObject( 'Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match', - ['resolver' => $this->resolver, 'fulltextHelper' => $this->fulltextHelper] + [ + 'resolver' => $this->resolver, + 'fulltextHelper' => $this->fulltextHelper, + 'preprocessors' => [$this->preprocessor] + ] ); } - public function testBuildQuery() + public function testBuild() { /** @var Select|\PHPUnit_Framework_MockObject_MockObject $select */ $select = $this->getMockBuilder('Magento\Framework\DB\Select') ->setMethods(['getMatchQuery', 'match', 'where']) ->disableOriginalConstructor() ->getMock(); + $this->preprocessor->expects($this->once()) + ->method('process') + ->with($this->equalTo('some_value ')) + ->will($this->returnValue('some_value ')); $this->fulltextHelper->expects($this->once()) ->method('getMatchQuery') ->with($this->equalTo(['some_field' => 'some_field']), $this->equalTo('-some_value*')) @@ -102,7 +121,13 @@ class MatchTest extends \PHPUnit_Framework_TestCase $this->scoreBuilder->expects($this->once()) ->method('addCondition'); - $result = $this->match->build($this->scoreBuilder, $select, $query, BoolExpression::QUERY_CONDITION_NOT); + $result = $this->match->build( + $this->scoreBuilder, + $select, + $query, + BoolExpression::QUERY_CONDITION_NOT, + [$this->preprocessor] + ); $this->assertEquals($select, $result); } diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php index cdbcd2e2c47fbcf3ccfa38fd3bb93b49bd03d21c..4a3421a5ffaab995010cde5bfed98dfec76a8401 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php @@ -108,6 +108,36 @@ class TemporaryStorageTest extends \PHPUnit_Framework_TestCase $this->assertEquals($result, $table); } + public function testStoreApiDocuments() + { + $documentId = 312432; + $documentValue = 1.235123; + + $attributeValue = $this->getMockBuilder('Magento\Framework\Api\AttributeValue') + ->disableOriginalConstructor() + ->getMock(); + $attributeValue->expects($this->once()) + ->method('getValue') + ->willReturn($documentValue); + + $document = $this->getMockBuilder('Magento\Framework\Api\Search\Document') + ->disableOriginalConstructor() + ->getMock(); + $document->expects($this->once()) + ->method('getId') + ->willReturn($documentId); + $document->expects($this->once()) + ->method('getCustomAttribute') + ->with('score') + ->willReturn($attributeValue); + + $table = $this->createTemporaryTable(); + + $result = $this->model->storeApiDocuments([$document]); + + $this->assertEquals($result, $table); + } + /** * @return \Magento\Framework\DB\Ddl\Table|\PHPUnit_Framework_MockObject_MockObject */ diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/ConverterTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/ConverterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9713beef5fb1efaea0a5194ee3b87ba314aa33e0 --- /dev/null +++ b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/ConverterTest.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\Test\Unit\SearchEngine\Config; + +use Magento\Framework\Search\SearchEngine\Config\Converter; + +class ConverterTest extends \PHPUnit_Framework_TestCase +{ + public function testConvert() + { + $converter = new Converter(); + $dom = new \DOMDocument(); + $dom->load(realpath(__DIR__ . '/../../_files/search_engine.xml')); + $result = $converter->convert($dom); + $expected = [ + 'mysql' => ['synonyms'], + 'other' => ['synonyms', 'stopwords'], + 'none' => [], + ]; + $this->assertEquals($expected, $result); + } +} diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/SchemaLocatorTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/SchemaLocatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..803e6ac78922bf3c89869cba653e01a9ec364a6c --- /dev/null +++ b/lib/internal/Magento/Framework/Search/Test/Unit/SearchEngine/Config/SchemaLocatorTest.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Search\Test\Unit\SearchEngine\Config; + +use Magento\Framework\Search\SearchEngine\Config\SchemaLocator; + +class SchemaLocatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var SchemaLocator + */ + private $model; + + protected function setUp() + { + $urnResolver = $this->getMock('Magento\Framework\Config\Dom\UrnResolver', [], [], '', false); + $urnResolver->expects($this->any()) + ->method('getRealPath') + ->with(SchemaLocator::SEARCH_ENGINE_XSD_PATH) + ->willReturn('xsd/path'); + + $this->model = new SchemaLocator($urnResolver); + } + + public function testGetSchema() + { + $this->assertEquals('xsd/path', $this->model->getSchema()); + } + + public function testGetPerFileSchema() + { + $this->assertEquals('xsd/path', $this->model->getPerFileSchema()); + } +} diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/_files/search_engine.xml b/lib/internal/Magento/Framework/Search/Test/Unit/_files/search_engine.xml new file mode 100644 index 0000000000000000000000000000000000000000..85b1d67e902e15a18b0d62a9f1dbc6184bb27352 --- /dev/null +++ b/lib/internal/Magento/Framework/Search/Test/Unit/_files/search_engine.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="mysql"> + <feature name="synonyms" support="1" /> + <feature name="stopwords" support="0" /> + </engine> + <engine name="other"> + <feature name="synonyms" support="1" /> + <feature name="stopwords" support="1" /> + </engine> + <engine name="none" /> +</engines> diff --git a/lib/internal/Magento/Framework/Search/etc/search_engine.xsd b/lib/internal/Magento/Framework/Search/etc/search_engine.xsd new file mode 100644 index 0000000000000000000000000000000000000000..3b754301c76de70cf36b3f973a70e99ff9c34ea9 --- /dev/null +++ b/lib/internal/Magento/Framework/Search/etc/search_engine.xsd @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="engines"> + <xs:complexType> + <xs:sequence> + <xs:element name="engine" maxOccurs="unbounded"> + <xs:complexType> + <xs:sequence> + <xs:element name="feature" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="support" type="xs:boolean" use="required" /> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/pub/errors/processorFactory.php b/pub/errors/processorFactory.php index f185cd1247933bee192363099b1d54aa98d6ed1a..44c8d874f51595ada0801f8094e8d0359d8c29fe 100644 --- a/pub/errors/processorFactory.php +++ b/pub/errors/processorFactory.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Error; -require_once __DIR__ . '/../../app/bootstrap.php'; +require_once realpath(__DIR__) . '/../../app/bootstrap.php'; require_once 'processor.php'; /** diff --git a/pub/index.php b/pub/index.php index b1177dcef99d41b663cebed03f427ee3339abc62..586ddc38a1c21b68514bc02c8581ebd63b92e200 100644 --- a/pub/index.php +++ b/pub/index.php @@ -10,7 +10,7 @@ use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; try { - require __DIR__ . '/../app/bootstrap.php'; + require realpath(__DIR__) . '/../app/bootstrap.php'; } catch (\Exception $e) { echo <<<HTML <div style="font:12px/1.35em arial, helvetica, sans-serif;"> diff --git a/pub/static.php b/pub/static.php index 7c1df3a8d85e28adbe42071f70742beebbd42e8c..35b9d9d833735b1a7cf43e8c50ad1dd725aa5452 100644 --- a/pub/static.php +++ b/pub/static.php @@ -6,7 +6,7 @@ * See COPYING.txt for license details. */ -require __DIR__ . '/../app/bootstrap.php'; +require realpath(__DIR__) . '/../app/bootstrap.php'; $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER); /** @var \Magento\Framework\App\StaticResource $app */ $app = $bootstrap->createApplication('Magento\Framework\App\StaticResource'); diff --git a/setup/pub/magento/setup/select-version.js b/setup/pub/magento/setup/select-version.js index b133fa59d46a1607294fbc105065be321e5a6d70..71e758568dcd16ee42ddf5fb97ad38a2a31815de 100644 --- a/setup/pub/magento/setup/select-version.js +++ b/setup/pub/magento/setup/select-version.js @@ -5,7 +5,7 @@ 'use strict'; angular.module('select-version', ['ngStorage']) - .controller('selectVersionController', ['$scope', '$http', '$localStorage', function ($scope, $http, $localStorage) { + .controller('selectVersionController', ['$scope', '$http', '$localStorage', '$sce', function ($scope, $http, $localStorage, $sce) { $scope.packages = [{ name: '', version: '' @@ -13,6 +13,7 @@ angular.module('select-version', ['ngStorage']) $scope.upgradeReadyForNext = false; $scope.upgradeProcessed = false; $scope.upgradeProcessError = false; + $scope.upgradeProcessErrorMessage = ''; $scope.componentsReadyForNext = true; $scope.componentsProcessed = false; $scope.componentsProcessError = false; @@ -27,33 +28,39 @@ angular.module('select-version', ['ngStorage']) $http.get('index.php/select-version/systemPackage', {'responseType' : 'json'}) .success(function (data) { if (data.responseType != 'error') { - $scope.selectedOption = []; - $scope.versions = []; - for (var i = 0; i < data.packages.length; i++) { - angular.forEach(data.packages[i].versions, function (value, key) { - $scope.versions.push({ - 'versionInfo': angular.toJson({ - 'package': data.packages[i].package, - 'version': value - }), 'version': value + if (data.packages.length <= 1) { + $scope.upgradeProcessError = true; + $scope.upgradeProcessErrorMessage = "You're already using the latest version, there's nothing for us to do."; + } else { + $scope.selectedOption = []; + $scope.versions = []; + for (var i = 0; i < data.packages.length; i++) { + angular.forEach(data.packages[i].versions, function (value, key) { + $scope.versions.push({ + 'versionInfo': angular.toJson({ + 'package': data.packages[i].package, + 'version': value + }), 'version': value + }); }); + } + + $scope.versions = $scope.versions.sort(function (a, b) { + if (a.version.id < b.version.id) { + return 1; + } + if (a.version.id > b.version.id) { + return -1; + } + return 0; }); + $scope.selectedOption = $scope.versions[0].versionInfo; + $scope.upgradeReadyForNext = true; } - $scope.versions = $scope.versions.sort(function (a, b) { - if (a.version.id < b.version.id) { - return 1; - } - if (a.version.id > b.version.id) { - return -1; - } - return 0; - }); - $scope.selectedOption = $scope.versions[0].versionInfo; - $scope.upgradeReadyForNext = true; - } else { $scope.upgradeProcessError = true; + $scope.upgradeProcessErrorMessage = $sce.trustAsHtml(data.error); } $scope.upgradeProcessed = true; }) diff --git a/setup/src/Magento/Setup/Model/SystemPackage.php b/setup/src/Magento/Setup/Model/SystemPackage.php index 773f22212a3610445a55f6b9b1a7c6ae64157b41..952847f94491c761d0d31875a17d206ada6be4e4 100755 --- a/setup/src/Magento/Setup/Model/SystemPackage.php +++ b/setup/src/Magento/Setup/Model/SystemPackage.php @@ -59,13 +59,19 @@ class SystemPackage $systemPackages = []; $systemPackages = $this->getInstalledSystemPackages($systemPackages); if (empty($systemPackages)) { - throw new \RuntimeException('System packages not found'); + // git cloned Magento does not include system package + throw new \RuntimeException( + 'We\'re sorry, no components are available because you cloned the Magento 2 GitHub repository. ' . + 'You must manually update components as discussed in the ' . + '<a href="http://devdocs.magento.com/guides/v2.0/install-gde/install/cli/dev_options.html">' . + 'Installation Guide</a>.' + ); } foreach ($systemPackages as $systemPackage) { $versions = []; $systemPackageInfo = $this->infoCommand->run($systemPackage); if (!$systemPackageInfo) { - throw new \RuntimeException('System package not found'); + throw new \RuntimeException("We cannot retrieve information on $systemPackage."); } $versions = $this->getSystemPackageVersions($systemPackageInfo, $versions); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php b/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php index e5f521061a5466676fa4f0ca37c6ff67259ae307..9ce624511688ec2585e477f379b41733b900ff15 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php @@ -50,7 +50,6 @@ class SystemPackageTest extends \PHPUnit_Framework_TestCase */ private $composerInformation; - public function setUp() { $this->composerAppFactory = $this->getMock( @@ -178,7 +177,36 @@ class SystemPackageTest extends \PHPUnit_Framework_TestCase /** * @expectedException \RuntimeException - * @expectedExceptionMessage System package not found + * @expectedExceptionMessage no components are available because you cloned the Magento 2 GitHub repository + */ + public function testGetPackageVersionGitCloned() + { + $package = $this->getMock('\Composer\Package\Package', [], [], '', false); + $this->repository + ->expects($this->once()) + ->method('getPackages') + ->willReturn([$package]); + + $this->locker->expects($this->once())->method('getLockedRepository')->willReturn($this->repository); + $this->composerInformation->expects($this->any())->method('isSystemPackage')->willReturn(false); + $this->composer->expects($this->once())->method('getLocker')->willReturn($this->locker); + $this->magentoComposerApp->expects($this->once())->method('createComposer')->willReturn($this->composer); + + $this->composerAppFactory->expects($this->once()) + ->method('create') + ->willReturn($this->magentoComposerApp); + + $this->composerAppFactory->expects($this->once()) + ->method('createInfoCommand') + ->willReturn($this->infoCommand); + + $this->systemPackage = new SystemPackage($this->composerAppFactory, $this->composerInformation); + $this->systemPackage->getPackageVersions(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage We cannot retrieve information on magento/product-community-edition. */ public function testGetPackageVersionsFailed() { @@ -200,10 +228,6 @@ class SystemPackageTest extends \PHPUnit_Framework_TestCase $this->composer->expects($this->once())->method('getLocker')->willReturn($this->locker); $this->magentoComposerApp->expects($this->once())->method('createComposer')->willReturn($this->composer); - $this->composerAppFactory->expects($this->once()) - ->method('createInfoCommand') - ->willReturn($this->infoCommand); - $this->composerAppFactory->expects($this->once()) ->method('create') ->willReturn($this->magentoComposerApp); diff --git a/setup/view/magento/setup/select-version.phtml b/setup/view/magento/setup/select-version.phtml index 706983297e829678949b083cf2b5b7e19f853017..18cfc66d9aed1a8f8c26f39158e4e5fbdf6c2836 100644 --- a/setup/view/magento/setup/select-version.phtml +++ b/setup/view/magento/setup/select-version.phtml @@ -39,9 +39,7 @@ <span class="message-text">Checking for a new version...</span> </div> <div class="message message-error" ng-show="upgradeProcessError"> - <span class="message-text"> - Sorry, we can't take that action right now. - </span> + <span class="message-text" ng-bind-html="upgradeProcessErrorMessage"></span> </div> <div class="row" ng-show="upgradeProcessed && !upgradeProcessError"> <div class="col-m-3">