diff --git a/app/code/Magento/Backend/Model/Menu.php b/app/code/Magento/Backend/Model/Menu.php index 516319ad983f9a35804e9933d27bbb42575e7b30..ccae5ebce16a01926aa0166344976185ad6769bf 100644 --- a/app/code/Magento/Backend/Model/Menu.php +++ b/app/code/Magento/Backend/Model/Menu.php @@ -5,6 +5,12 @@ */ namespace Magento\Backend\Model; +use Magento\Backend\Model\Menu\Item; +use Magento\Backend\Model\Menu\Item\Factory; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\SerializerInterface; +use Psr\Log\LoggerInterface; + /** * Backend menu model */ @@ -18,33 +24,54 @@ class Menu extends \ArrayObject protected $_path = ''; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ protected $_logger; /** - * @param \Psr\Log\LoggerInterface $logger + * @var Factory + */ + private $menuItemFactory; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * Menu constructor + * + * @param LoggerInterface $logger * @param string $pathInMenuStructure + * @param Factory|null $menuItemFactory + * @param SerializerInterface|null $serializer */ - public function __construct(\Psr\Log\LoggerInterface $logger, $pathInMenuStructure = '') - { + public function __construct( + LoggerInterface $logger, + $pathInMenuStructure = '', + Factory $menuItemFactory = null, + SerializerInterface $serializer = null + ) { if ($pathInMenuStructure) { $this->_path = $pathInMenuStructure . '/'; } $this->_logger = $logger; $this->setIteratorClass(\Magento\Backend\Model\Menu\Iterator::class); + $this->menuItemFactory = $menuItemFactory ?: ObjectManager::getInstance() + ->create(Factory::class); + $this->serializer = $serializer ?: ObjectManager::getInstance()->create(SerializerInterface::class); } /** * Add child to menu item * - * @param \Magento\Backend\Model\Menu\Item $item + * @param Item $item * @param string $parentId * @param int $index * @return void * @throws \InvalidArgumentException */ - public function add(\Magento\Backend\Model\Menu\Item $item, $parentId = null, $index = null) + public function add(Item $item, $parentId = null, $index = null) { if ($parentId !== null) { $parentItem = $this->get($parentId); @@ -69,13 +96,13 @@ class Menu extends \ArrayObject * Retrieve menu item by id * * @param string $itemId - * @return \Magento\Backend\Model\Menu\Item|null + * @return Item|null */ public function get($itemId) { $result = null; + /** @var Item $item */ foreach ($this as $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { $result = $item; break; @@ -116,8 +143,8 @@ class Menu extends \ArrayObject public function remove($itemId) { $result = false; + /** @var Item $item */ foreach ($this as $key => $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { unset($this[$key]); $result = true; @@ -144,8 +171,8 @@ class Menu extends \ArrayObject public function reorder($itemId, $position) { $result = false; + /** @var Item $item */ foreach ($this as $key => $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { unset($this[$key]); $this->add($item, null, $position); @@ -161,10 +188,10 @@ class Menu extends \ArrayObject /** * Check whether provided item is last in list * - * @param \Magento\Backend\Model\Menu\Item $item + * @param Item $item * @return bool */ - public function isLast(\Magento\Backend\Model\Menu\Item $item) + public function isLast(Item $item) { return $this->offsetGet(max(array_keys($this->getArrayCopy())))->getId() == $item->getId(); } @@ -172,12 +199,12 @@ class Menu extends \ArrayObject /** * Find first menu item that user is able to access * - * @return \Magento\Backend\Model\Menu\Item|null + * @return Item|null */ public function getFirstAvailable() { $result = null; - /** @var $item \Magento\Backend\Model\Menu\Item */ + /** @var Item $item */ foreach ($this as $item) { if ($item->isAllowed() && !$item->isDisabled()) { if ($item->hasChildren()) { @@ -198,7 +225,7 @@ class Menu extends \ArrayObject * Get parent items by item id * * @param string $itemId - * @return \Magento\Backend\Model\Menu\Item[] + * @return Item[] */ public function getParentItems($itemId) { @@ -217,8 +244,8 @@ class Menu extends \ArrayObject */ protected function _findParentItems($menu, $itemId, &$parents) { + /** @var Item $item */ foreach ($menu as $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { return true; } @@ -233,16 +260,54 @@ class Menu extends \ArrayObject } /** - * Hack to unset logger instance which cannot be serialized + * Serialize menu * * @return string */ public function serialize() { - $logger = $this->_logger; - unset($this->_logger); - $result = parent::serialize(); - $this->_logger = $logger; - return $result; + return $this->serializer->serialize($this->toArray()); + } + + /** + * Get menu data represented as an array + * + * @return array + */ + public function toArray() + { + $data = []; + foreach ($this as $item) { + $data[] = $item->toArray(); + } + return $data; + } + + /** + * Unserialize menu + * + * @param string $serialized + * @return void + */ + public function unserialize($serialized) + { + $data = $this->serializer->unserialize($serialized); + $this->populateFromArray($data); + } + + /** + * Populate the menu with data from array + * + * @param array $data + * @return void + */ + public function populateFromArray(array $data) + { + $items = []; + foreach ($data as $itemData) { + $item = $this->menuItemFactory->create($itemData); + $items[] = $item; + } + $this->exchangeArray($items); } } diff --git a/app/code/Magento/Backend/Model/Menu/Item.php b/app/code/Magento/Backend/Model/Menu/Item.php index a49920a67663c7115fde12d799846c1b1da353b1..ef1aa864588f412126bf74e32ca462a41a3182d5 100644 --- a/app/code/Magento/Backend/Model/Menu/Item.php +++ b/app/code/Magento/Backend/Model/Menu/Item.php @@ -4,10 +4,11 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Backend\Model\Menu; +use Magento\Backend\Model\Menu; +use Magento\Store\Model\ScopeInterface; + /** * Menu item. Should be used to create nested menu structures with \Magento\Backend\Model\Menu * @@ -102,7 +103,7 @@ class Item /** * Submenu item list * - * @var \Magento\Backend\Model\Menu + * @var Menu */ protected $_submenu; @@ -130,6 +131,7 @@ class Item * Serialized submenu string * * @var string + * @deprecated */ protected $_serializedSubmenu; @@ -167,22 +169,13 @@ class Item ) { $this->_validator = $validator; $this->_validator->validate($data); - $this->_moduleManager = $moduleManager; $this->_acl = $authorization; $this->_scopeConfig = $scopeConfig; $this->_menuFactory = $menuFactory; $this->_urlModel = $urlModel; - $this->_moduleName = isset($data['module']) ? $data['module'] : 'Magento_Backend'; $this->_moduleList = $moduleList; - - $this->_id = $data['id']; - $this->_title = $data['title']; - $this->_action = $this->_getArgument($data, 'action'); - $this->_resource = $this->_getArgument($data, 'resource'); - $this->_dependsOnModule = $this->_getArgument($data, 'dependsOnModule'); - $this->_dependsOnConfig = $this->_getArgument($data, 'dependsOnConfig'); - $this->_tooltip = $this->_getArgument($data, 'toolTip', ''); + $this->populateFromArray($data); } /** @@ -215,13 +208,13 @@ class Item */ public function hasChildren() { - return !is_null($this->_submenu) && (bool)$this->_submenu->count(); + return (null !== $this->_submenu) && (bool)$this->_submenu->count(); } /** * Retrieve submenu * - * @return \Magento\Backend\Model\Menu + * @return Menu */ public function getChildren() { @@ -425,7 +418,7 @@ class Item protected function _isConfigDependenciesAvailable() { if ($this->_dependsOnConfig) { - return $this->_scopeConfig->isSetFlag((string)$this->_dependsOnConfig, \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + return $this->_scopeConfig->isSetFlag((string)$this->_dependsOnConfig, ScopeInterface::SCOPE_STORE); } return true; } @@ -445,45 +438,53 @@ class Item } /** - * @return string[] + * Get menu item data represented as an array + * + * @return array */ - public function __sleep() + public function toArray() { - if ($this->_submenu) { - $this->_serializedSubmenu = $this->_submenu->serialize(); - } return [ - '_parentId', - '_moduleName', - '_sortIndex', - '_dependsOnConfig', - '_id', - '_resource', - '_path', - '_action', - '_dependsOnModule', - '_tooltip', - '_title', - '_serializedSubmenu' + 'parent_id' => $this->_parentId, + 'module_name' => $this->_moduleName, + 'sort_index' => $this->_sortIndex, + 'depends_on_config' => $this->_dependsOnConfig, + 'id' => $this->_id, + 'resource' => $this->_resource, + 'path' => $this->_path, + 'action' => $this->_action, + 'depends_on_module' => $this->_dependsOnModule, + 'tooltip' => $this->_tooltip, + 'title' => $this->_title, + 'sub_menu' => isset($this->_submenu) ? $this->_submenu->toArray() : null ]; } /** + * Populate the menu item with data from array + * + * @param array $data * @return void */ - public function __wakeup() + public function populateFromArray(array $data) { - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->_moduleManager = $objectManager->get(\Magento\Framework\Module\Manager::class); - $this->_validator = $objectManager->get(\Magento\Backend\Model\Menu\Item\Validator::class); - $this->_acl = $objectManager->get(\Magento\Framework\AuthorizationInterface::class); - $this->_scopeConfig = $objectManager->get(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_menuFactory = $objectManager->get(\Magento\Backend\Model\MenuFactory::class); - $this->_urlModel = $objectManager->get(\Magento\Backend\Model\UrlInterface::class); - $this->_moduleList = $objectManager->get(\Magento\Framework\Module\ModuleListInterface::class); - if ($this->_serializedSubmenu) { - $this->_submenu = $this->_menuFactory->create(); - $this->_submenu->unserialize($this->_serializedSubmenu); + $this->_parentId = $this->_getArgument($data, 'parent_id'); + $this->_moduleName = $this->_getArgument($data, 'module_name', 'Magento_Backend'); + $this->_sortIndex = $this->_getArgument($data, 'sort_index'); + $this->_dependsOnConfig = $this->_getArgument($data, 'depends_on_config'); + $this->_id = $this->_getArgument($data, 'id'); + $this->_resource = $this->_getArgument($data, 'resource'); + $this->_path = $this->_getArgument($data, 'path', ''); + $this->_action = $this->_getArgument($data, 'action'); + $this->_dependsOnModule = $this->_getArgument($data, 'depends_on_module'); + $this->_tooltip = $this->_getArgument($data, 'tooltip', ''); + $this->_title = $this->_getArgument($data, 'title'); + if (isset($data['sub_menu'])) { + $menu = $this->_menuFactory->create(); + $menu->populateFromArray($data['sub_menu']); + $this->_submenu = $menu; + } else { + $this->_submenu = null; } } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/BuilderTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/BuilderTest.php index 7bda9b38a2b95a3a338f5e8e8e07c2463ab734d1..ec015029675eae4591b14bfb9cc02ea0417cdad6 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/BuilderTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/BuilderTest.php @@ -5,33 +5,36 @@ */ namespace Magento\Backend\Test\Unit\Model\Menu; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + class BuilderTest extends \PHPUnit_Framework_TestCase { /** * @var \Magento\Backend\Model\Menu\Builder */ - protected $_model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Model\Menu|\PHPUnit_Framework_MockObject_MockObject */ - protected $_menuMock; + private $menuMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Model\Menu\Item\Factory|\PHPUnit_Framework_MockObject_MockObject */ - protected $_factoryMock; + private $factoryMock; protected function setUp() { - $this->_factoryMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Factory::class, [], [], '', false); - $this->_menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] + $this->factoryMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Factory::class, [], [], '', false); + $this->menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); + + $this->model = (new ObjectManager($this))->getObject( + \Magento\Backend\Model\Menu\Builder::class, + [ + 'menuItemFactory' => $this->factoryMock + ] ); - - $this->_model = new \Magento\Backend\Model\Menu\Builder($this->_factoryMock, $this->_menuMock); } public function testProcessCommand() @@ -41,20 +44,20 @@ class BuilderTest extends \PHPUnit_Framework_TestCase $command2 = $this->getMock(\Magento\Backend\Model\Menu\Builder\Command\Update::class, [], [], '', false); $command2->expects($this->any())->method('getId')->will($this->returnValue(1)); $command->expects($this->once())->method('chain')->with($this->equalTo($command2)); - $this->_model->processCommand($command); - $this->_model->processCommand($command2); + $this->model->processCommand($command); + $this->model->processCommand($command2); } public function testGetResultBuildsTreeStructure() { $item1 = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $item1->expects($this->once())->method('getChildren')->will($this->returnValue($this->_menuMock)); - $this->_factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); + $item1->expects($this->once())->method('getChildren')->will($this->returnValue($this->menuMock)); + $this->factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); $item2 = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_factoryMock->expects($this->at(1))->method('create')->will($this->returnValue($item2)); + $this->factoryMock->expects($this->at(1))->method('create')->will($this->returnValue($item2)); - $this->_menuMock->expects( + $this->menuMock->expects( $this->at(0) )->method( 'add' @@ -64,7 +67,7 @@ class BuilderTest extends \PHPUnit_Framework_TestCase $this->equalTo(2) ); - $this->_menuMock->expects( + $this->menuMock->expects( $this->at(1) )->method( 'add' @@ -74,7 +77,7 @@ class BuilderTest extends \PHPUnit_Framework_TestCase $this->equalTo(4) ); - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 'item1', @@ -85,7 +88,7 @@ class BuilderTest extends \PHPUnit_Framework_TestCase ] ) ); - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 'item2', @@ -98,12 +101,12 @@ class BuilderTest extends \PHPUnit_Framework_TestCase ) ); - $this->_model->getResult($this->_menuMock); + $this->model->getResult($this->menuMock); } public function testGetResultSkipsRemovedItems() { - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 1, @@ -113,11 +116,11 @@ class BuilderTest extends \PHPUnit_Framework_TestCase ] ) ); - $this->_model->processCommand(new \Magento\Backend\Model\Menu\Builder\Command\Remove(['id' => 1])); + $this->model->processCommand(new \Magento\Backend\Model\Menu\Builder\Command\Remove(['id' => 1])); - $this->_menuMock->expects($this->never())->method('addChild'); + $this->menuMock->expects($this->never())->method('addChild'); - $this->_model->getResult($this->_menuMock); + $this->model->getResult($this->menuMock); } /** @@ -126,9 +129,9 @@ class BuilderTest extends \PHPUnit_Framework_TestCase public function testGetResultSkipItemsWithInvalidParent() { $item1 = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); + $this->factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 'item1', @@ -140,6 +143,6 @@ class BuilderTest extends \PHPUnit_Framework_TestCase ) ); - $this->_model->getResult($this->_menuMock); + $this->model->getResult($this->menuMock); } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php index dee7518be3ac911b8e64b2d9cda0b84f9496b611..5f50c17db8d326466e9eeb90cc8e3d9e50295610 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php @@ -5,59 +5,43 @@ */ namespace Magento\Backend\Test\Unit\Model\Menu; -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + class ConfigTest extends \PHPUnit_Framework_TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_cacheInstanceMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_directorMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Cache\Type\Config|\PHPUnit_Framework_MockObject_MockObject */ - protected $_configReaderMock; + private $cacheInstanceMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Model\Menu\Config\Reader|\PHPUnit_Framework_MockObject_MockObject */ - protected $_menuFactoryMock; + private $configReaderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Model\Menu|\PHPUnit_Framework_MockObject_MockObject */ - protected $_eventManagerMock; + private $menuMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Model\Menu\Builder|\PHPUnit_Framework_MockObject_MockObject */ - protected $_menuMock; + private $menuBuilderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_menuBuilderMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_logger; + private $logger; /** * @var \Magento\Backend\Model\Menu\Config */ - protected $_model; + private $model; protected function setUp() { - $this->_cacheInstanceMock = $this->getMock( + $this->cacheInstanceMock = $this->getMock( \Magento\Framework\App\Cache\Type\Config::class, [], [], @@ -65,15 +49,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase false ); - $this->_directorMock = $this->getMock( - \Magento\Backend\Model\Menu\AbstractDirector::class, - [], - [], - '', - false - ); - - $this->_menuFactoryMock = $this->getMock( + $menuFactoryMock = $this->getMock( \Magento\Backend\Model\MenuFactory::class, ['create'], [], @@ -81,7 +57,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase false ); - $this->_configReaderMock = $this->getMock( + $this->configReaderMock = $this->getMock( \Magento\Backend\Model\Menu\Config\Reader::class, [], [], @@ -89,30 +65,15 @@ class ConfigTest extends \PHPUnit_Framework_TestCase false ); - $this->_eventManagerMock = $this->getMock( - \Magento\Framework\Event\ManagerInterface::class, - [], - [], - '', - false, - false - ); - - $this->_logger = $this->getMock(\Psr\Log\LoggerInterface::class); - - $this->_menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $this->logger = $this->getMock(\Psr\Log\LoggerInterface::class); - $this->_menuBuilderMock = $this->getMock(\Magento\Backend\Model\Menu\Builder::class, [], [], '', false); + $this->menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); - $this->_menuFactoryMock->expects($this->any())->method('create')->will($this->returnValue($this->_menuMock)); + $this->menuBuilderMock = $this->getMock(\Magento\Backend\Model\Menu\Builder::class, [], [], '', false); - $scopeConfig = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $menuFactoryMock->expects($this->any())->method('create')->will($this->returnValue($this->menuMock)); - $this->_configReaderMock->expects($this->any())->method('read')->will($this->returnValue([])); + $this->configReaderMock->expects($this->any())->method('read')->will($this->returnValue([])); $appState = $this->getMock(\Magento\Framework\App\State::class, ['getAreaCode'], [], '', false); $appState->expects( @@ -123,22 +84,22 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->returnValue(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) ); - $this->_model = new \Magento\Backend\Model\Menu\Config( - $this->_menuBuilderMock, - $this->_directorMock, - $this->_menuFactoryMock, - $this->_configReaderMock, - $this->_cacheInstanceMock, - $this->_eventManagerMock, - $this->_logger, - $scopeConfig, - $appState + $this->model = (new ObjectManager($this))->getObject( + \Magento\Backend\Model\Menu\Config::class, + [ + 'menuBuilder' => $this->menuBuilderMock, + 'menuFactory' => $menuFactoryMock, + 'configReader' => $this->configReaderMock, + 'configCacheType' => $this->cacheInstanceMock, + 'logger' => $this->logger, + 'appState' => $appState, + ] ); } public function testGetMenuWithCachedObjectReturnsUnserializedObject() { - $this->_cacheInstanceMock->expects( + $this->cacheInstanceMock->expects( $this->once() )->method( 'load' @@ -148,14 +109,14 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->returnValue('menu_cache') ); - $this->_menuMock->expects($this->once())->method('unserialize')->with('menu_cache'); + $this->menuMock->expects($this->once())->method('unserialize')->with('menu_cache'); - $this->assertEquals($this->_menuMock, $this->_model->getMenu()); + $this->assertEquals($this->menuMock, $this->model->getMenu()); } public function testGetMenuWithNotCachedObjectBuidlsObject() { - $this->_cacheInstanceMock->expects( + $this->cacheInstanceMock->expects( $this->at(0) )->method( 'load' @@ -165,17 +126,17 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->returnValue(false) ); - $this->_configReaderMock->expects($this->once())->method('read')->will($this->returnValue([])); + $this->configReaderMock->expects($this->once())->method('read')->will($this->returnValue([])); - $this->_menuBuilderMock->expects( + $this->menuBuilderMock->expects( $this->exactly(1) )->method( 'getResult' )->will( - $this->returnValue($this->_menuMock) + $this->returnValue($this->menuMock) ); - $this->assertEquals($this->_menuMock, $this->_model->getMenu()); + $this->assertEquals($this->menuMock, $this->model->getMenu()); } /** @@ -186,7 +147,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetMenuExceptionLogged($expectedException) { $this->setExpectedException($expectedException); - $this->_menuBuilderMock->expects( + $this->menuBuilderMock->expects( $this->exactly(1) )->method( 'getResult' @@ -194,7 +155,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->throwException(new $expectedException()) ); - $this->_model->getMenu(); + $this->model->getMenu(); } public function getMenuExceptionLoggedDataProvider() @@ -208,9 +169,9 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetMenuGenericExceptionIsNotLogged() { - $this->_logger->expects($this->never())->method('critical'); + $this->logger->expects($this->never())->method('critical'); - $this->_menuBuilderMock->expects( + $this->menuBuilderMock->expects( $this->exactly(1) )->method( 'getResult' @@ -218,7 +179,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $this->throwException(new \Exception()) ); try { - $this->_model->getMenu(); + $this->model->getMenu(); } catch (\Exception $e) { return; } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Filter/IteratorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/Filter/IteratorTest.php index 19836ba7c94017d575aaae060ab120667a273ff2..06dee1f01fd88439ad1a1bcf1f959e19eb13e9be 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Filter/IteratorTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Filter/IteratorTest.php @@ -5,49 +5,49 @@ */ namespace Magento\Backend\Test\Unit\Model\Menu\Filter; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + class IteratorTest extends \PHPUnit_Framework_TestCase { /** * @var \Magento\Backend\Model\Menu */ - protected $_menuModel; + private $menuModel; /** * @var \Magento\Backend\Model\Menu\Item[] */ - protected $_items = []; + private $items = []; protected function setUp() { - $this->_items['item1'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_items['item1']->expects($this->any())->method('getId')->will($this->returnValue('item1')); - $this->_items['item1']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); - $this->_items['item1']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); - - $this->_items['item2'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_items['item2']->expects($this->any())->method('getId')->will($this->returnValue('item2')); - $this->_items['item2']->expects($this->any())->method('isDisabled')->will($this->returnValue(true)); - $this->_items['item2']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); - - $this->_items['item3'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_items['item3']->expects($this->any())->method('getId')->will($this->returnValue('item3')); - $this->_items['item3']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); - $this->_items['item3']->expects($this->any())->method('isAllowed')->will($this->returnValue(false)); - - $loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); - - $this->_menuModel = new \Magento\Backend\Model\Menu($loggerMock); + $this->items['item1'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $this->items['item1']->expects($this->any())->method('getId')->will($this->returnValue('item1')); + $this->items['item1']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); + $this->items['item1']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); + + $this->items['item2'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $this->items['item2']->expects($this->any())->method('getId')->will($this->returnValue('item2')); + $this->items['item2']->expects($this->any())->method('isDisabled')->will($this->returnValue(true)); + $this->items['item2']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); + + $this->items['item3'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $this->items['item3']->expects($this->any())->method('getId')->will($this->returnValue('item3')); + $this->items['item3']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); + $this->items['item3']->expects($this->any())->method('isAllowed')->will($this->returnValue(false)); + + $this->menuModel = (new ObjectManager($this))->getObject(\Magento\Backend\Model\Menu::class); } public function testLoopWithAllItemsDisabledDoesntIterate() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -59,15 +59,15 @@ class IteratorTest extends \PHPUnit_Framework_TestCase public function testLoopIteratesOnlyValidItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); + $this->menuModel->add($this->items['item1']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -79,16 +79,16 @@ class IteratorTest extends \PHPUnit_Framework_TestCase public function testLoopIteratesDosntIterateDisabledItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); - $this->_menuModel->add($this->_items['item2']); + $this->menuModel->add($this->items['item1']); + $this->menuModel->add($this->items['item2']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -100,16 +100,16 @@ class IteratorTest extends \PHPUnit_Framework_TestCase public function testLoopIteratesDosntIterateNotAllowedItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); - $this->_menuModel->add($this->_items['item3']); + $this->menuModel->add($this->items['item1']); + $this->menuModel->add($this->items['item3']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -121,17 +121,17 @@ class IteratorTest extends \PHPUnit_Framework_TestCase public function testLoopIteratesMixedItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); - $this->_menuModel->add($this->_items['item2']); - $this->_menuModel->add($this->_items['item3']); + $this->menuModel->add($this->items['item1']); + $this->menuModel->add($this->items['item2']); + $this->menuModel->add($this->items['item3']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php index fcef1bd374c01b4160880143c095464e3020fb18..b6cd61870d60f69f6b93eea88dd6aa56b121d66a 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php @@ -40,16 +40,14 @@ class ItemTest extends \PHPUnit_Framework_TestCase */ protected $_moduleManager; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_validatorMock; - /** * @var \PHPUnit_Framework_MockObject_MockObject */ protected $_moduleListMock; + /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + private $objectManager; + /** * @var array */ @@ -58,8 +56,8 @@ class ItemTest extends \PHPUnit_Framework_TestCase 'title' => 'Item Title', 'action' => '/system/config', 'resource' => 'Magento_Config::config', - 'dependsOnModule' => 'Magento_Backend', - 'dependsOnConfig' => 'system/config/isEnabled', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', 'tooltip' => 'Item tooltip', ]; @@ -76,15 +74,15 @@ class ItemTest extends \PHPUnit_Framework_TestCase ); $this->_urlModelMock = $this->getMock(\Magento\Backend\Model\Url::class, [], [], '', false); $this->_moduleManager = $this->getMock(\Magento\Framework\Module\Manager::class, [], [], '', false); - $this->_validatorMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Validator::class); - $this->_validatorMock->expects($this->any())->method('validate'); + $validatorMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Validator::class); + $validatorMock->expects($this->any())->method('validate'); $this->_moduleListMock = $this->getMock(\Magento\Framework\Module\ModuleListInterface::class); - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $helper->getObject( + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->_model = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, [ - 'validator' => $this->_validatorMock, + 'validator' => $validatorMock, 'authorization' => $this->_aclMock, 'scopeConfig' => $this->_scopeConfigMock, 'menuFactory' => $this->_menuFactoryMock, @@ -99,8 +97,7 @@ class ItemTest extends \PHPUnit_Framework_TestCase public function testGetUrlWithEmptyActionReturnsHashSign() { $this->_params['action'] = ''; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $item = $helper->getObject( + $item = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, ['menuFactory' => $this->_menuFactoryMock, 'data' => $this->_params] ); @@ -129,8 +126,7 @@ class ItemTest extends \PHPUnit_Framework_TestCase public function testHasClickCallbackReturnsTrueIfItemHasNoAction() { $this->_params['action'] = ''; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $item = $helper->getObject( + $item = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, ['menuFactory' => $this->_menuFactoryMock, 'data' => $this->_params] ); @@ -140,8 +136,7 @@ class ItemTest extends \PHPUnit_Framework_TestCase public function testGetClickCallbackReturnsStoppingJsIfItemDoesntHaveAction() { $this->_params['action'] = ''; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $item = $helper->getObject( + $item = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, ['menuFactory' => $this->_menuFactoryMock, 'data' => $this->_params] ); @@ -218,15 +213,86 @@ class ItemTest extends \PHPUnit_Framework_TestCase public function testGetChildrenCreatesSubmenuOnFirstCall() { - $menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); $this->_menuFactoryMock->expects($this->once())->method('create')->will($this->returnValue($menuMock)); $this->_model->getChildren(); $this->_model->getChildren(); } + + /** + * @param array $data + * @param array $expected + * @dataProvider toArrayDataProvider + */ + public function testToArray(array $data, array $expected) + { + $menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); + $this->_menuFactoryMock->method('create')->will($this->returnValue($menuMock)); + $menuMock->method('toArray') + ->willReturn($data['sub_menu']); + + $model = $this->objectManager->getObject( + \Magento\Backend\Model\Menu\Item::class, + [ + 'authorization' => $this->_aclMock, + 'scopeConfig' => $this->_scopeConfigMock, + 'menuFactory' => $this->_menuFactoryMock, + 'urlModel' => $this->_urlModelMock, + 'moduleList' => $this->_moduleListMock, + 'moduleManager' => $this->_moduleManager, + 'data' => $data + ] + ); + $this->assertEquals($expected, $model->toArray()); + } + + /** + * @return array + */ + public function toArrayDataProvider() + { + return include __DIR__ . '/../_files/menu_item_data.php'; + } + + /** + * @param array $constructorData + * @param array $populateFromData + * @param array $expected + * @dataProvider populateFromArrayDataProvider + */ + public function testPopulateFromArray( + array $constructorData, + array $populateFromData, + array $expected + ) { + $menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); + $this->_menuFactoryMock->method('create')->willReturn($menuMock); + $menuMock->method('toArray') + ->willReturn(['submenuArray']); + + $model = $this->objectManager->getObject( + \Magento\Backend\Model\Menu\Item::class, + [ + 'authorization' => $this->_aclMock, + 'scopeConfig' => $this->_scopeConfigMock, + 'menuFactory' => $this->_menuFactoryMock, + 'urlModel' => $this->_urlModelMock, + 'moduleList' => $this->_moduleListMock, + 'moduleManager' => $this->_moduleManager, + 'data' => $constructorData + ] + ); + $model->populateFromArray($populateFromData); + $this->assertEquals($expected, $model->toArray()); + } + + /** + * @return array + */ + public function populateFromArrayDataProvider() + { + return include __DIR__ . '/../_files/menu_item_constructor_data.php'; + } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/MenuTest.php b/app/code/Magento/Backend/Test/Unit/Model/MenuTest.php index c985bec92074a8f93e4df4b9fb73e336a5037f99..12968df9f340f888a2999d516a1304510b181316 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/MenuTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/MenuTest.php @@ -5,6 +5,10 @@ */ namespace Magento\Backend\Test\Unit\Model; +use Magento\Backend\Model\Menu\Item; +use Magento\Backend\Model\Menu\Item\Factory; +use Magento\Framework\Serialize\SerializerInterface; + class MenuTest extends \PHPUnit_Framework_TestCase { /** @@ -22,8 +26,14 @@ class MenuTest extends \PHPUnit_Framework_TestCase */ protected $_items = []; + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManagerHelper; + protected function setUp() { + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->_items['item1'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); $this->_items['item1']->expects($this->any())->method('getId')->will($this->returnValue('item1')); @@ -35,7 +45,12 @@ class MenuTest extends \PHPUnit_Framework_TestCase $this->_logger = $this->getMock(\Psr\Log\LoggerInterface::class); - $this->_model = new \Magento\Backend\Model\Menu($this->_logger); + $this->_model = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); } public function testAdd() @@ -53,7 +68,7 @@ class MenuTest extends \PHPUnit_Framework_TestCase public function testAddToItem() { - $subMenu = $this->getMock(\Magento\Backend\Model\Menu::class, [], [$this->_logger]); + $subMenu = $this->getMockBuilder(\Magento\Backend\Model\Menu::class)->disableOriginalConstructor()->getMock(); $subMenu->expects($this->once())->method("add")->with($this->_items['item2']); $this->_items['item1']->expects($this->once())->method("getChildren")->will($this->returnValue($subMenu)); @@ -101,19 +116,29 @@ class MenuTest extends \PHPUnit_Framework_TestCase public function testGetRecursive() { - $menu1 = new \Magento\Backend\Model\Menu($this->_logger); - $menu2 = new \Magento\Backend\Model\Menu($this->_logger); + $menuOne = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); + $menuTwo = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); $this->_items['item1']->expects($this->any())->method('hasChildren')->will($this->returnValue(true)); - $this->_items['item1']->expects($this->any())->method('getChildren')->will($this->returnValue($menu1)); + $this->_items['item1']->expects($this->any())->method('getChildren')->will($this->returnValue($menuOne)); $this->_model->add($this->_items['item1']); $this->_items['item2']->expects($this->any())->method('hasChildren')->will($this->returnValue(true)); - $this->_items['item2']->expects($this->any())->method('getChildren')->will($this->returnValue($menu2)); - $menu1->add($this->_items['item2']); + $this->_items['item2']->expects($this->any())->method('getChildren')->will($this->returnValue($menuTwo)); + $menuOne->add($this->_items['item2']); $this->_items['item3']->expects($this->any())->method('hasChildren')->will($this->returnValue(false)); - $menu2->add($this->_items['item3']); + $menuTwo->add($this->_items['item3']); $this->assertEquals($this->_items['item1'], $this->_model->get('item1')); $this->assertEquals($this->_items['item2'], $this->_model->get('item2')); @@ -126,11 +151,7 @@ class MenuTest extends \PHPUnit_Framework_TestCase $this->_model->add($this->_items['item2']); $this->_model->add($this->_items['item3']); - $subMenu = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $subMenu = $this->getMockBuilder(\Magento\Backend\Model\Menu::class)->disableOriginalConstructor()->getMock(); $subMenu->expects($this->once())->method("add")->with($this->_items['item3']); $this->_items['item1']->expects($this->once())->method("getChildren")->will($this->returnValue($subMenu)); @@ -179,11 +200,7 @@ class MenuTest extends \PHPUnit_Framework_TestCase public function testRemoveRemovesMenuItemRecursively() { - $menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $menuMock = $this->getMockBuilder(\Magento\Backend\Model\Menu::class)->disableOriginalConstructor()->getMock(); $menuMock->expects($this->once())->method('remove')->with($this->equalTo('item2')); $this->_items['item1']->expects($this->any())->method('hasChildren')->will($this->returnValue(true)); @@ -214,7 +231,12 @@ class MenuTest extends \PHPUnit_Framework_TestCase { $this->_logger->expects($this->any())->method('log'); - $subMenu = new \Magento\Backend\Model\Menu($this->_logger); + $subMenu = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); $this->_items['item1']->expects($this->any())->method("hasChildren")->will($this->returnValue(true)); @@ -285,11 +307,11 @@ class MenuTest extends \PHPUnit_Framework_TestCase $items[] = $item->getId(); } - $items2 = []; + $itemsTwo = []; foreach ($this->_model as $item) { - $items2[] = $item->getId(); + $itemsTwo[] = $item->getId(); } - $this->assertEquals($items, $items2); + $this->assertEquals($items, $itemsTwo); } /** @@ -307,10 +329,10 @@ class MenuTest extends \PHPUnit_Framework_TestCase 'item3' => ['item1', 'item2', 'item3'], ]; $actual = []; - foreach ($this->_model as $valLoop1) { - $keyLevel1 = $valLoop1->getId(); - foreach ($this->_model as $valLoop2) { - $actual[$keyLevel1][] = $valLoop2->getId(); + foreach ($this->_model as $valLoopOne) { + $keyLevelOne = $valLoopOne->getId(); + foreach ($this->_model as $valLoopTwo) { + $actual[$keyLevelOne][] = $valLoopTwo->getId(); } } $this->assertEquals($expected, $actual); @@ -318,7 +340,45 @@ class MenuTest extends \PHPUnit_Framework_TestCase public function testSerialize() { - $this->assertNotEmpty($this->_model->serialize()); - $this->_model->add($this->_items['item1']); + $serializerMock = $this->getMock(SerializerInterface::class); + $serializerMock->expects($this->once()) + ->method('serialize') + ->with([['arrayData']]) + ->willReturn('serializedString'); + $menu = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger, + 'serializer' => $serializerMock, + ] + ); + $itemMock = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $itemMock->expects($this->any())->method('getId')->will($this->returnValue('item1')); + $itemMock->expects($this->once()) + ->method('toArray') + ->willReturn(['arrayData']); + $menu->add($itemMock); + $this->assertEquals('serializedString', $menu->serialize()); + } + + public function testUnserialize() + { + $serializerMock = $this->getMock(SerializerInterface::class); + $serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn([['unserializedData']]); + $menuItemFactoryMock = $this->getMock(Factory::class, [], [], '', false); + $menuItemFactoryMock->expects($this->once()) + ->method('create') + ->with(['unserializedData']); + $menu = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger, + 'serializer' => $serializerMock, + 'menuItemFactory' => $menuItemFactoryMock, + ] + ); + $menu->unserialize('serializedString'); } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php index 4eda145156c6dbf881eadf3d88981ffb9318d32c..fa83eb4210d467ba7d33935f934cbcc4801470fa 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php @@ -75,11 +75,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $this->_menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); $this->_menuConfigMock = $this->getMock(\Magento\Backend\Model\Menu\Config::class, [], [], '', false); $this->_menuConfigMock->expects($this->any())->method('getMenu')->will($this->returnValue($this->_menuMock)); diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php new file mode 100644 index 0000000000000000000000000000000000000000..213e98de13866b610be60c311b984466d27dc7c0 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +return [ + 'default data to constructor' => [ + [], + [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + [ + 'parent_id' => null, + 'module_name' => 'Magento_Backend', + 'sort_index' => null, + 'depends_on_config' => 'system/config/isEnabled', + 'id' => 'item', + 'resource' => 'Magento_Config::config', + 'path' => '', + 'action' => '/system/config', + 'depends_on_module' => 'Magento_Backend', + 'tooltip' => 'Item tooltip', + 'title' => 'Item Title', + 'sub_menu' => null + ], + ], + 'data without submenu to constructor' => [ + [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => null, + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => ['submenuArray'] + ], + ], + 'data with submenu to constructor' => [ + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => null, + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => null, + 'resource' => null, + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => ['submenuArray'] + ], + ] +]; diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php new file mode 100644 index 0000000000000000000000000000000000000000..c4c97e95af68e7a95b4a7c53df64b35c6fb85d14 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +return [ + 'no submenu' => [ + [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + 'sub_menu' => null, + ], + [ + 'parent_id' => null, + 'module_name' => 'Magento_Backend', + 'sort_index' => null, + 'depends_on_config' => 'system/config/isEnabled', + 'id' => 'item', + 'resource' => 'Magento_Config::config', + 'path' => '', + 'action' => '/system/config', + 'depends_on_module' => 'Magento_Backend', + 'tooltip' => 'Item tooltip', + 'title' => 'Item Title', + 'sub_menu' => null, + ] + ], + 'with submenu' => [ + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => null, + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ] + ] + ], + 'small set of data' => [ + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => null, + 'resource' => null, + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ] + ] + ] +]; diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/Items/Renderer.php b/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/Items/Renderer.php index ad9a0b9bbbebfba225c71f532e14a860598e1567..914da1144bc0b22ef41acc2ff6be9d358259fa72 100644 --- a/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/Items/Renderer.php +++ b/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/Items/Renderer.php @@ -6,12 +6,42 @@ namespace Magento\Bundle\Block\Adminhtml\Sales\Order\Items; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Framework\Serialize\Serializer\Json; /** * Adminhtml sales order item renderer */ class Renderer extends \Magento\Sales\Block\Adminhtml\Items\Renderer\DefaultRenderer { + /** + * Serializer + * + * @var Json + */ + private $serializer; + + /** + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + * @param \Magento\Framework\Registry $registry + * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, + \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, + \Magento\Framework\Registry $registry, + array $data = [], + Json $serializer = null + ) { + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Json::class); + + parent::__construct($context, $stockRegistry, $stockConfiguration, $registry, $data); + } + /** * Truncate string * @@ -153,7 +183,7 @@ class Renderer extends \Magento\Sales\Block\Adminhtml\Items\Renderer\DefaultRend $options = $item->getOrderItem()->getProductOptions(); } if (isset($options['bundle_selection_attributes'])) { - return unserialize($options['bundle_selection_attributes']); + return $this->serializer->unserialize($options['bundle_selection_attributes']); } return null; } diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/View/Items/Renderer.php b/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/View/Items/Renderer.php index 01e122a56b52d635ceed284a2a2d370a93677181..0cb7cc0b45ad5f7459c28f87639c77b8760ba810 100644 --- a/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/View/Items/Renderer.php +++ b/app/code/Magento/Bundle/Block/Adminhtml/Sales/Order/View/Items/Renderer.php @@ -6,12 +6,54 @@ namespace Magento\Bundle\Block\Adminhtml\Sales\Order\View\Items; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Framework\Serialize\Serializer\Json; /** * Adminhtml sales order item renderer */ class Renderer extends \Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\DefaultRenderer { + /** + * Serializer + * + * @var Json + */ + private $serializer; + + /** + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + * @param \Magento\Framework\Registry $registry + * @param \Magento\GiftMessage\Helper\Message $messageHelper + * @param \Magento\Checkout\Helper\Data $checkoutHelper + * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, + \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, + \Magento\Framework\Registry $registry, + \Magento\GiftMessage\Helper\Message $messageHelper, + \Magento\Checkout\Helper\Data $checkoutHelper, + array $data = [], + Json $serializer = null + ) { + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Json::class); + + parent::__construct( + $context, + $stockRegistry, + $stockConfiguration, + $registry, + $messageHelper, + $checkoutHelper, + $data + ); + } + /** * Truncate string * @@ -110,7 +152,7 @@ class Renderer extends \Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\ $options = $item->getOrderItem()->getProductOptions(); } if (isset($options['bundle_selection_attributes'])) { - return unserialize($options['bundle_selection_attributes']); + return $this->serializer->unserialize($options['bundle_selection_attributes']); } return null; } diff --git a/app/code/Magento/Bundle/Block/Sales/Order/Items/Renderer.php b/app/code/Magento/Bundle/Block/Sales/Order/Items/Renderer.php index f8b243c84c05855f7c868a5746e4cc7f1132405e..a8e85e6c5fa66ac407ed10c4c1d2f032d205221f 100644 --- a/app/code/Magento/Bundle/Block/Sales/Order/Items/Renderer.php +++ b/app/code/Magento/Bundle/Block/Sales/Order/Items/Renderer.php @@ -6,6 +6,7 @@ namespace Magento\Bundle\Block\Sales\Order\Items; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Framework\Serialize\Serializer\Json; /** * Order item render block @@ -14,6 +15,33 @@ use Magento\Catalog\Model\Product\Type\AbstractType; */ class Renderer extends \Magento\Sales\Block\Order\Item\Renderer\DefaultRenderer { + /** + * Serializer + * + * @var Json + */ + private $serializer; + + /** + * @param \Magento\Framework\View\Element\Template\Context $context + * @param \Magento\Framework\Stdlib\StringUtils $string + * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory + * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + */ + public function __construct( + \Magento\Framework\View\Element\Template\Context $context, + \Magento\Framework\Stdlib\StringUtils $string, + \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, + array $data = [], + Json $serializer = null + ) { + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Json::class); + + parent::__construct($context, $string, $productOptionFactory, $data); + } + /** * @param mixed $item * @return bool @@ -100,7 +128,7 @@ class Renderer extends \Magento\Sales\Block\Order\Item\Renderer\DefaultRenderer $options = $item->getOrderItem()->getProductOptions(); } if (isset($options['bundle_selection_attributes'])) { - return unserialize($options['bundle_selection_attributes']); + return $this->serializer->unserialize($options['bundle_selection_attributes']); } return null; } diff --git a/app/code/Magento/Bundle/Helper/Catalog/Product/Configuration.php b/app/code/Magento/Bundle/Helper/Catalog/Product/Configuration.php index 8305bb0137ed2b091b0d70916917276dcfc4b7a7..3ad004fabead10b6073e82cf0e2091f74eecfbc5 100644 --- a/app/code/Magento/Bundle/Helper/Catalog/Product/Configuration.php +++ b/app/code/Magento/Bundle/Helper/Catalog/Product/Configuration.php @@ -35,21 +35,32 @@ class Configuration extends AbstractHelper implements ConfigurationInterface */ protected $escaper; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Catalog\Helper\Product\Configuration $productConfiguration * @param \Magento\Framework\Pricing\Helper\Data $pricingHelper * @param \Magento\Framework\Escaper $escaper + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Catalog\Helper\Product\Configuration $productConfiguration, \Magento\Framework\Pricing\Helper\Data $pricingHelper, - \Magento\Framework\Escaper $escaper + \Magento\Framework\Escaper $escaper, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->productConfiguration = $productConfiguration; $this->pricingHelper = $pricingHelper; $this->escaper = $escaper; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($context); } @@ -113,7 +124,10 @@ class Configuration extends AbstractHelper implements ConfigurationInterface // get bundle options $optionsQuoteItemOption = $item->getOptionByCode('bundle_option_ids'); - $bundleOptionsIds = $optionsQuoteItemOption ? unserialize($optionsQuoteItemOption->getValue()) : []; + $bundleOptionsIds = $optionsQuoteItemOption + ? $this->serializer->unserialize($optionsQuoteItemOption->getValue()) + : []; + if ($bundleOptionsIds) { /** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */ $optionsCollection = $typeInstance->getOptionsByIds($bundleOptionsIds, $product); @@ -121,7 +135,7 @@ class Configuration extends AbstractHelper implements ConfigurationInterface // get and add bundle selections collection $selectionsQuoteItemOption = $item->getOptionByCode('bundle_selection_ids'); - $bundleSelectionIds = unserialize($selectionsQuoteItemOption->getValue()); + $bundleSelectionIds = $this->serializer->unserialize($selectionsQuoteItemOption->getValue()); if (!empty($bundleSelectionIds)) { $selectionsCollection = $typeInstance->getSelectionsByIds($bundleSelectionIds, $product); diff --git a/app/code/Magento/Bundle/Model/Product/Price.php b/app/code/Magento/Bundle/Model/Product/Price.php index d0aee24945f9d21df7c3d73350093206065b0429..f5043f1f7edc67c861b7e31fb5386f790adbf1f0 100644 --- a/app/code/Magento/Bundle/Model/Product/Price.php +++ b/app/code/Magento/Bundle/Model/Product/Price.php @@ -39,6 +39,13 @@ class Price extends \Magento\Catalog\Model\Product\Type\Price */ protected $_catalogData = null; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * Price constructor. * @@ -52,7 +59,7 @@ class Price extends \Magento\Catalog\Model\Product\Type\Price * @param \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory * @param \Magento\Framework\App\Config\ScopeConfigInterface $config * @param \Magento\Catalog\Helper\Data $catalogData - * + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -65,9 +72,12 @@ class Price extends \Magento\Catalog\Model\Product\Type\Price GroupManagementInterface $groupManagement, \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory, \Magento\Framework\App\Config\ScopeConfigInterface $config, - \Magento\Catalog\Helper\Data $catalogData + \Magento\Catalog\Helper\Data $catalogData, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_catalogData = $catalogData; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct( $ruleFactory, $storeManager, @@ -154,7 +164,7 @@ class Price extends \Magento\Catalog\Model\Product\Type\Price { $customOption = $product->getCustomOption('bundle_selection_ids'); if ($customOption) { - $selectionIds = unserialize($customOption->getValue()); + $selectionIds = $this->serializer->unserialize($customOption->getValue()); if (!empty($selectionIds) && is_array($selectionIds)) { return $selectionIds; } diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 3e8ae11c4faf362831ead26e549621ea483b9c7c..cef57f610fc2103eb729d7957b1ac5eef6b4d567 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -9,8 +9,8 @@ namespace Magento\Bundle\Model\Product; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\Serialize\Serializer\Json; /** * Bundle Type Model @@ -168,6 +168,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType * @param PriceCurrencyInterface $priceCurrency * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry * @param \Magento\CatalogInventory\Api\StockStateInterface $stockState + * @param \Magento\Framework\Serialize\Serializer\Json $serializer * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -192,7 +193,8 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType \Magento\Store\Model\StoreManagerInterface $storeManager, PriceCurrencyInterface $priceCurrency, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, - \Magento\CatalogInventory\Api\StockStateInterface $stockState + \Magento\CatalogInventory\Api\StockStateInterface $stockState, + Json $serializer = null ) { $this->_catalogProduct = $catalogProduct; $this->_catalogData = $catalogData; @@ -206,6 +208,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType $this->priceCurrency = $priceCurrency; $this->_stockRegistry = $stockRegistry; $this->_stockState = $stockState; + parent::__construct( $catalogProductOption, $eavConfig, @@ -215,7 +218,8 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType $filesystem, $coreRegistry, $logger, - $productRepository + $productRepository, + $serializer ); } @@ -277,7 +281,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType if ($product->hasCustomOptions()) { $customOption = $product->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); + $selectionIds = $this->serializer->unserialize($customOption->getValue()); if (!empty($selectionIds)) { $selections = $this->getSelectionsByIds($selectionIds, $product); foreach ($selections->getItems() as $selection) { @@ -305,7 +309,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType if ($product->hasCustomOptions()) { $customOption = $product->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); + $selectionIds = $this->serializer->unserialize($customOption->getValue()); $selections = $this->getSelectionsByIds($selectionIds, $product); foreach ($selections->getItems() as $selection) { $qtyOption = $product->getCustomOption('selection_qty_' . $selection->getSelectionId()); @@ -331,7 +335,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType { if ($product->hasCustomOptions()) { $customOption = $product->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); + $selectionIds = $this->serializer->unserialize($customOption->getValue()); $selections = $this->getSelectionsByIds($selectionIds, $product); $virtualCount = 0; foreach ($selections->getItems() as $selection) { @@ -697,8 +701,14 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType $this->checkIsResult($_result); $result[] = $_result[0]->setParentProductId($product->getId()) - ->addCustomOption('bundle_option_ids', serialize(array_map('intval', $optionIds))) - ->addCustomOption('bundle_selection_attributes', serialize($attributes)); + ->addCustomOption( + 'bundle_option_ids', + $this->serializer->serialize(array_map('intval', $optionIds)) + ) + ->addCustomOption( + 'bundle_selection_attributes', + $this->serializer->serialize($attributes) + ); if ($isStrictProcessMode) { $_result[0]->setCartQty($qty); @@ -715,8 +725,13 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType foreach ($result as $item) { $item->addCustomOption('bundle_identity', $uniqueKey); } - $product->addCustomOption('bundle_option_ids', serialize(array_map('intval', $optionIds))); - $product->addCustomOption('bundle_selection_ids', serialize($selectionIds)); + $product->addCustomOption( + 'bundle_option_ids', + $this->serializer->serialize( + array_map('intval', $optionIds) + ) + ); + $product->addCustomOption('bundle_selection_ids', $this->serializer->serialize($selectionIds)); return $result; } @@ -826,7 +841,10 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType $usedOptions = $product->getData($this->_keyUsedOptions); $usedOptionsIds = $product->getData($this->_keyUsedOptionsIds); - if (!$usedOptions || serialize($usedOptionsIds) != serialize($optionIds)) { + if ( + !$usedOptions + || $this->serializer->serialize($usedOptionsIds) != $this->serializer->serialize($optionIds) + ) { $usedOptions = $this->_bundleOption ->create() ->getResourceCollection() @@ -858,10 +876,10 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType if ($product->hasCustomOptions()) { $customOption = $product->getCustomOption('bundle_option_ids'); - $optionIds = unserialize($customOption->getValue()); + $optionIds = $this->serializer->unserialize($customOption->getValue()); $options = $this->getOptionsByIds($optionIds, $product); $customOption = $product->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); + $selectionIds = $this->serializer->unserialize($customOption->getValue()); $selections = $this->getSelectionsByIds($selectionIds, $product); foreach ($selections->getItems() as $selection) { if ($selection->isSalable()) { @@ -1010,10 +1028,10 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType $productOptionIds = $this->getOptionsIds($product); $productSelections = $this->getSelectionsCollection($productOptionIds, $product); $selectionIds = $product->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($selectionIds->getValue()); + $selectionIds = $this->serializer->unserialize($selectionIds->getValue()); $buyRequest = $product->getCustomOption('info_buyRequest'); - $buyRequest = new \Magento\Framework\DataObject(unserialize($buyRequest->getValue())); - $bundleOption = $buyRequest->getBundleOption(); + $buyRequest = new \Magento\Framework\DataObject($this->serializer->unserialize($buyRequest->getValue())); + $bundleOption = $buyRequest->getBundleOption(); if (empty($bundleOption)) { throw new \Magento\Framework\Exception\LocalizedException($this->getSpecifyOptionMessage()); diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php index 2b479b4a2506ec5f62cadbb3f70082bc08dc83eb..d96294eacc0b7a22257f7276abdaa07516af7465 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php @@ -6,12 +6,60 @@ namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; /** - * Sales Order Pdf Items renderer + * Order pdf items renderer + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractItems extends \Magento\Sales\Model\Order\Pdf\Items\AbstractItems { + /** + * Serializer + * + * @var Json + */ + private $serializer; + + /** + * Constructor + * + * @param \Magento\Framework\Model\Context $context + * @param \Magento\Framework\Registry $registry + * @param \Magento\Tax\Helper\Data $taxData + * @param \Magento\Framework\Filesystem $filesystem + * @param \Magento\Framework\Filter\FilterManager $filterManager + * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource + * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + */ + public function __construct( + \Magento\Framework\Model\Context $context, + \Magento\Framework\Registry $registry, + \Magento\Tax\Helper\Data $taxData, + \Magento\Framework\Filesystem $filesystem, + \Magento\Framework\Filter\FilterManager $filterManager, + \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, + \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $data = [], + Json $serializer = null + ) { + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + parent::__construct( + $context, + $registry, + $taxData, + $filesystem, + $filterManager, + $resource, + $resourceCollection, + $data + ); + } + /** * Getting all available children for Invoice, Shipment or CreditMemo item * @@ -157,7 +205,7 @@ abstract class AbstractItems extends \Magento\Sales\Model\Order\Pdf\Items\Abstra $options = $item->getOrderItem()->getProductOptions(); } if (isset($options['bundle_selection_attributes'])) { - return unserialize($options['bundle_selection_attributes']); + return $this->serializer->unserialize($options['bundle_selection_attributes']); } return null; } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php index 2ac6e484bb5b51902c5e4cc0743791dfb5561e8f..b9efefa03ebe11083dab8edee385a03f2dd772af 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php @@ -5,8 +5,11 @@ */ namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; + /** - * Sales Order Creditmemo Pdf default items renderer + * Order creditmemo pdf default items renderer */ class Creditmemo extends AbstractItems { @@ -18,6 +21,8 @@ class Creditmemo extends AbstractItems protected $string; /** + * Constructor + * * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Tax\Helper\Data $taxData @@ -27,6 +32,8 @@ class Creditmemo extends AbstractItems * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -37,7 +44,8 @@ class Creditmemo extends AbstractItems \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Json $serializer = null ) { $this->string = $string; parent::__construct( @@ -48,7 +56,8 @@ class Creditmemo extends AbstractItems $filterManager, $resource, $resourceCollection, - $data + $data, + $serializer ); } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php index 9b5d0b4a9c0188727eab52350525397bfc412cc5..e164ffa394a9687a51563d25d8bbd9c7d11aad47 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php @@ -3,13 +3,15 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; + /** - * Sales Order Invoice Pdf default items renderer + * Order invoice pdf default items renderer + * + * @codingStandardsIgnoreFile */ class Invoice extends AbstractItems { @@ -19,6 +21,8 @@ class Invoice extends AbstractItems protected $string; /** + * Constructor + * * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Tax\Helper\Data $taxData @@ -28,6 +32,8 @@ class Invoice extends AbstractItems * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -38,7 +44,8 @@ class Invoice extends AbstractItems \Magento\Framework\Stdlib\StringUtils $coreString, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Json $serializer = null ) { $this->string = $coreString; parent::__construct( @@ -49,7 +56,8 @@ class Invoice extends AbstractItems $filterManager, $resource, $resourceCollection, - $data + $data, + $serializer ); } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php index fec75878c305319241eb1e845fb8896bdc63a4a3..0df79bd57b1b4f2523a2f10bcb584b0ad3233f80 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php @@ -5,8 +5,11 @@ */ namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; + /** - * Sales Order Shipment Pdf items renderer + * Order shipment pdf items renderer */ class Shipment extends AbstractItems { @@ -16,6 +19,8 @@ class Shipment extends AbstractItems protected $string; /** + * Constructor + * * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Tax\Helper\Data $taxData @@ -25,6 +30,8 @@ class Shipment extends AbstractItems * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -35,7 +42,8 @@ class Shipment extends AbstractItems \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Json $serializer = null ) { $this->string = $string; parent::__construct( @@ -46,7 +54,8 @@ class Shipment extends AbstractItems $filterManager, $resource, $resourceCollection, - $data + $data, + $serializer ); } diff --git a/app/code/Magento/Bundle/Pricing/Price/ConfiguredPrice.php b/app/code/Magento/Bundle/Pricing/Price/ConfiguredPrice.php index e6c0f90e24a5cd3ef1bd59bcdd20ebb98b14f962..2069b91426fad1f16352691c2280b060259832ef 100644 --- a/app/code/Magento/Bundle/Pricing/Price/ConfiguredPrice.php +++ b/app/code/Magento/Bundle/Pricing/Price/ConfiguredPrice.php @@ -32,21 +32,32 @@ class ConfiguredPrice extends CatalogPrice\FinalPrice implements ConfiguredPrice */ protected $item; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param Product $saleableItem * @param float $quantity * @param BundleCalculatorInterface $calculator * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency * @param ItemInterface $item + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( Product $saleableItem, $quantity, BundleCalculatorInterface $calculator, \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, - ItemInterface $item = null + ItemInterface $item = null, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->item = $item; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency); } @@ -74,13 +85,16 @@ class ConfiguredPrice extends CatalogPrice\FinalPrice implements ConfiguredPrice // get bundle options $optionsQuoteItemOption = $this->item->getOptionByCode('bundle_option_ids'); - $bundleOptionsIds = $optionsQuoteItemOption ? unserialize($optionsQuoteItemOption->getValue()) : []; + $bundleOptionsIds = $optionsQuoteItemOption + ? $this->serializer->unserialize($optionsQuoteItemOption->getValue()) + : []; + if ($bundleOptionsIds) { /** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */ $optionsCollection = $typeInstance->getOptionsByIds($bundleOptionsIds, $bundleProduct); // get and add bundle selections collection $selectionsQuoteItemOption = $this->item->getOptionByCode('bundle_selection_ids'); - $bundleSelectionIds = unserialize($selectionsQuoteItemOption->getValue()); + $bundleSelectionIds = $this->serializer->unserialize($selectionsQuoteItemOption->getValue()); if ($bundleSelectionIds) { $selectionsCollection = $typeInstance->getSelectionsByIds($bundleSelectionIds, $bundleProduct); $bundleOptions = $optionsCollection->appendSelections($selectionsCollection, true); diff --git a/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/Items/RendererTest.php b/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/Items/RendererTest.php index aee7b81edc1c4e57a6edc5d29ed7fb3a9687f5ad..af02f9d6129c4d400a7437a289cee0e9aec3f938 100644 --- a/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/Items/RendererTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/Items/RendererTest.php @@ -13,6 +13,9 @@ class RendererTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Bundle\Block\Adminhtml\Sales\Order\Items\Renderer $model */ protected $model; + /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject $serializer */ + protected $serializer; + protected function setUp() { $this->orderItem = $this->getMock( @@ -22,9 +25,12 @@ class RendererTest extends \PHPUnit_Framework_TestCase '', false ); - + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->model = $objectManager->getObject(\Magento\Bundle\Block\Adminhtml\Sales\Order\Items\Renderer::class); + $this->model = $objectManager->getObject( + \Magento\Bundle\Block\Adminhtml\Sales\Order\Items\Renderer::class, + ['serializer' => $this->serializer] + ); } /** @@ -221,21 +227,25 @@ class RendererTest extends \PHPUnit_Framework_TestCase ]; } - /** - * @dataProvider getSelectionAttributesDataProvider - */ - public function testGetSelectionAttributes($productOptions, $result) + public function testGetSelectionAttributes() { - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); - $this->assertSame($result, $this->model->getSelectionAttributes($this->orderItem)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue([])); + $this->assertNull($this->model->getSelectionAttributes($this->orderItem)); } - public function getSelectionAttributesDataProvider() + public function testGetSelectionAttributesWithBundle() { - return [ - [[], null], - [['bundle_selection_attributes' => 'a:1:{i:0;i:1;}'], [0 => 1]], - ]; + $bundleAttributes = 'Serialized value'; + $options = ['bundle_selection_attributes' => $bundleAttributes]; + $unserializedResult = 'result of "bundle_selection_attributes" unserialization'; + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->with($bundleAttributes) + ->will($this->returnValue($unserializedResult)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($options)); + + $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItem)); } public function testGetOrderOptions() diff --git a/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/View/Items/RendererTest.php b/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/View/Items/RendererTest.php index 72c4a40fbbb54f1ea43ddf6337ce7d17be5fc2df..a2802f5c39ac653da9eda0e3f1afa410dc3224ab 100644 --- a/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/View/Items/RendererTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Block/Adminhtml/Sales/Order/View/Items/RendererTest.php @@ -13,6 +13,9 @@ class RendererTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Bundle\Block\Adminhtml\Sales\Order\View\Items\Renderer $model */ protected $model; + /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject $serializer */ + protected $serializer; + protected function setUp() { $this->orderItem = $this->getMock( @@ -22,10 +25,11 @@ class RendererTest extends \PHPUnit_Framework_TestCase '', false ); - + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Bundle\Block\Adminhtml\Sales\Order\View\Items\Renderer::class + \Magento\Bundle\Block\Adminhtml\Sales\Order\View\Items\Renderer::class, + ['serializer' => $this->serializer] ); } @@ -141,13 +145,25 @@ class RendererTest extends \PHPUnit_Framework_TestCase ]; } - /** - * @dataProvider getSelectionAttributesDataProvider - */ - public function testGetSelectionAttributes($productOptions, $result) + public function testGetSelectionAttributes() { - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); - $this->assertSame($result, $this->model->getSelectionAttributes($this->orderItem)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue([])); + $this->assertNull($this->model->getSelectionAttributes($this->orderItem)); + } + + public function testGetSelectionAttributesWithBundle() + { + $bundleAttributes = 'Serialized value'; + $options = ['bundle_selection_attributes' => $bundleAttributes]; + $unserializedResult = 'result of "bundle_selection_attributes" unserialization'; + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->with($bundleAttributes) + ->will($this->returnValue($unserializedResult)); + + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($options)); + $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItem)); } public function getSelectionAttributesDataProvider() diff --git a/app/code/Magento/Bundle/Test/Unit/Block/Sales/Order/Items/RendererTest.php b/app/code/Magento/Bundle/Test/Unit/Block/Sales/Order/Items/RendererTest.php index d81908bd625068920715aa3c15416eb8409ffa29..1e2c0ed3b5df8804ee556b0969103eecf033dd22 100644 --- a/app/code/Magento/Bundle/Test/Unit/Block/Sales/Order/Items/RendererTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Block/Sales/Order/Items/RendererTest.php @@ -13,6 +13,9 @@ class RendererTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Bundle\Block\Sales\Order\Items\Renderer $model */ protected $model; + /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject $serializer */ + protected $serializer; + protected function setUp() { $this->orderItem = $this->getMock( @@ -23,8 +26,12 @@ class RendererTest extends \PHPUnit_Framework_TestCase false ); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->model = $objectManager->getObject(\Magento\Bundle\Block\Sales\Order\Items\Renderer::class); + $this->model = $objectManager->getObject( + \Magento\Bundle\Block\Sales\Order\Items\Renderer::class, + ['serializer' => $this->serializer] + ); } /** @@ -221,21 +228,25 @@ class RendererTest extends \PHPUnit_Framework_TestCase ]; } - /** - * @dataProvider getSelectionAttributesDataProvider - */ - public function testGetSelectionAttributes($productOptions, $result) + public function testGetSelectionAttributes() { - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); - $this->assertSame($result, $this->model->getSelectionAttributes($this->orderItem)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue([])); + $this->assertNull($this->model->getSelectionAttributes($this->orderItem)); } - public function getSelectionAttributesDataProvider() + public function testGetSelectionAttributesWithBundle() { - return [ - [[], null], - [['bundle_selection_attributes' => 'a:1:{i:0;i:1;}'], [0 => 1]], - ]; + $bundleAttributes = 'Serialized value'; + $options = ['bundle_selection_attributes' => $bundleAttributes]; + $unserializedResult = 'result of "bundle_selection_attributes" unserialization'; + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->with($bundleAttributes) + ->will($this->returnValue($unserializedResult)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($options)); + + $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItem)); } /** diff --git a/app/code/Magento/Bundle/Test/Unit/Helper/Catalog/Product/ConfigurationTest.php b/app/code/Magento/Bundle/Test/Unit/Helper/Catalog/Product/ConfigurationTest.php index 0919e95a9a09de549758c79a0f94eab871598a90..656d37c710ddda841f05a443ea82eb95a7e78db0 100644 --- a/app/code/Magento/Bundle/Test/Unit/Helper/Catalog/Product/ConfigurationTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Helper/Catalog/Product/ConfigurationTest.php @@ -27,6 +27,11 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $item; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + protected function setUp() { $this->pricingHelper = $this->getMock( @@ -48,6 +53,16 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface::class, ['getQty', 'getProduct', 'getOptionByCode', 'getFileDownloadParams'] ); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->getMockForAbstractClass(); + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); $this->helper = (new ObjectManager($this))->getObject( \Magento\Bundle\Helper\Catalog\Product\Configuration::class, @@ -55,6 +70,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase 'pricingHelper' => $this->pricingHelper, 'productConfiguration' => $this->productConfiguration, 'escaper' => $this->escaper, + 'serializer' => $this->serializer ] ); } @@ -127,8 +143,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase public function testGetBundleOptionsEmptyBundleSelectionIds() { - $optionIds = 'a:1:{i:0;i:1;}'; - + $optionIds = '{"0":"1"}'; $collection = $this->getMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class, [], [], '', false); $product = $this->getMock( \Magento\Catalog\Model\Product::class, @@ -152,7 +167,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase $selectionOption->expects($this->once())->method('getValue')->will($this->returnValue('')); $itemOption->expects($this->once())->method('getValue')->will($this->returnValue($optionIds)); - $typeInstance->expects($this->once())->method('getOptionsByIds')->with(unserialize($optionIds), $product) + $typeInstance->expects($this->once())->method('getOptionsByIds')->with(json_decode($optionIds, true), $product) ->will($this->returnValue($collection)); $product->expects($this->once())->method('getTypeInstance')->will($this->returnValue($typeInstance)); $this->item->expects($this->once())->method('getProduct')->will($this->returnValue($product)); @@ -169,8 +184,8 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase */ public function testGetOptions() { - $optionIds = 'a:1:{i:0;i:1;}'; - $selectionIds = 'a:1:{i:0;s:1:"2";}'; + $optionIds = '{"0":"1"}'; + $selectionIds = '{"0":"2"}'; $selectionId = '2'; $product = $this->getMock( \Magento\Catalog\Model\Product::class, @@ -237,9 +252,11 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase $collection->expects($this->once())->method('appendSelections')->with($collection2, true) ->will($this->returnValue([$bundleOption])); $itemOption->expects($this->once())->method('getValue')->will($this->returnValue($optionIds)); - $typeInstance->expects($this->once())->method('getOptionsByIds')->with(unserialize($optionIds), $product) + $typeInstance->expects($this->once())->method('getOptionsByIds')->with(json_decode($optionIds, true), $product) ->will($this->returnValue($collection)); - $typeInstance->expects($this->once())->method('getSelectionsByIds')->with(unserialize($selectionIds), $product) + $typeInstance->expects($this->once()) + ->method('getSelectionsByIds') + ->with(json_decode($selectionIds, true), $product) ->will($this->returnValue($collection2)); $product->expects($this->once())->method('getTypeInstance')->will($this->returnValue($typeInstance)); $product->expects($this->any())->method('getCustomOption')->with('selection_qty_' . $selectionId) diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php index 10ca7335668470db793498273a998b64e34f4c4f..3dae21a30968d9061d7fbb6892b9aa146e0fae0d 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php @@ -64,6 +64,13 @@ class PriceTest extends \PHPUnit_Framework_TestCase */ private $groupManagement; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * Set up. * @@ -97,6 +104,16 @@ class PriceTest extends \PHPUnit_Framework_TestCase false ); $scopeConfig = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); $objectManagerHelper = new ObjectManagerHelper($this); $this->model = $objectManagerHelper->getObject( @@ -111,7 +128,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase 'groupManagement' => $this->groupManagement, 'tierPriceFactory' => $tpFactory, 'config' => $scopeConfig, - 'catalogData' => $this->catalogHelperMock + 'catalogData' => $this->catalogHelperMock, + 'serializer' => $this->serializer ] ); } @@ -222,7 +240,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase public function dataProviderWithEmptyOptions() { return [ - ['a:0:{}'], + ['{}'], [''], [null], ]; @@ -268,8 +286,10 @@ class PriceTest extends \PHPUnit_Framework_TestCase ->method('getStoreId') ->willReturn($storeId); - $customOptionValue = 'a:1:{i:0;s:1:"1";}'; - $dataObjectMock->expects($this->once())->method('getValue')->willReturn($customOptionValue); + $dataObjectMock->expects($this->once()) + ->method('getValue') + ->willReturn('{"0":1}'); + $productTypeMock->expects($this->once()) ->method('getSelectionsByIds') ->with([1], $productMock) diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php index 2be68359909ef547a8f4b03a5d807aebfe509ed4..62036a33724449af8adda45b7c297fa7e2cd562e 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php @@ -9,6 +9,10 @@ use Magento\Bundle\Model\ResourceModel\Option\Collection; use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; use Magento\Catalog\Model\Product\Option\Type\DefaultType; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\DataObject; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Bundle\Model\Selection; +use Magento\Catalog\Model\Product; /** * Class TypeTest @@ -71,6 +75,11 @@ class TypeTest extends \PHPUnit_Framework_TestCase */ private $priceCurrency; + /** + * @var Json + */ + private $serializer; + /** * @return void */ @@ -78,7 +87,19 @@ class TypeTest extends \PHPUnit_Framework_TestCase { $this->bundleCollection = $this->getMockBuilder(\Magento\Bundle\Model\ResourceModel\Selection\CollectionFactory::class) - ->setMethods(['create']) + ->setMethods( + [ + 'create', + 'addAttributeToSelect', + 'setFlag', + 'setPositionOrder', + 'addStoreFilter', + 'setStoreId', + 'addFilterByRequiredOptions', + 'setOptionIdsFilter', + 'getItemById' + ] + ) ->disableOriginalConstructor() ->getMock(); $this->catalogData = $this->getMockBuilder(\Magento\Catalog\Helper\Data::class) @@ -121,6 +142,12 @@ class TypeTest extends \PHPUnit_Framework_TestCase ) ->disableOriginalConstructor() ->getMock(); + + $this->serializer = $this->getMockBuilder(Json::class) + ->setMethods(null) + ->disableOriginalConstructor() + ->getMock(); + $objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->model = $objectHelper->getObject( \Magento\Bundle\Model\Product\Type::class, @@ -135,7 +162,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase 'stockState' => $this->stockState, 'catalogProduct' => $this->catalogProduct, 'priceCurrency' => $this->priceCurrency, - + 'serializer' => $this->serializer ] ); } @@ -1535,7 +1562,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase $sku = 'sku'; $itemSku = 'item'; $selectionIds = [1, 2, 3]; - $serializeIds = serialize($selectionIds); + $serializeIds = json_encode($selectionIds); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['__wakeup', 'getData', 'hasCustomOptions', 'getCustomOption']) ->disableOriginalConstructor() @@ -1612,7 +1639,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase { $weight = 5; $selectionIds = [1, 2, 3]; - $serializeIds = serialize($selectionIds); + $serializeIds = json_encode($selectionIds); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['__wakeup', 'getData', 'hasCustomOptions', 'getCustomOption']) ->disableOriginalConstructor() @@ -1666,7 +1693,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase $weight = 5; $qtyOption = 5; $selectionIds = [1, 2, 3]; - $serializeIds = serialize($selectionIds); + $serializeIds = json_encode($selectionIds); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['__wakeup', 'getData', 'hasCustomOptions', 'getCustomOption']) ->disableOriginalConstructor() @@ -1741,7 +1768,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase public function testIsVirtual() { $selectionIds = [1, 2, 3]; - $serializeIds = serialize($selectionIds); + $serializeIds = json_encode($selectionIds); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() @@ -2551,4 +2578,168 @@ class TypeTest extends \PHPUnit_Framework_TestCase $this->assertTrue($this->model->hasOptions($product)); } + + /** + * Bundle product without options should not be possible to buy. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Please specify product option + */ + public function testCheckProductBuyStateEmptyOptionsException() + { + $this->mockBundleCollection(); + $product = $this->getProductMock(); + $product->method('getCustomOption')->willReturnMap([ + ['bundle_selection_ids', new DataObject(['value' => ''])], + ['info_buyRequest', new DataObject(['value' => json_encode(['bundle_option' => ''])])], + ]); + $product->setCustomOption(json_encode([])); + + $this->model->checkProductBuyState($product); + } + + /** + * Previously selected options are not more available for buying. + * + * @param object $element + * @param string $expectedMessage + * @param bool $check + * + * @throws LocalizedException + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @dataProvider notAvailableOptionProvider + */ + public function testCheckProductBuyStateMissedOptionException($element, $expectedMessage, $check) + { + $this->mockBundleCollection(); + $product = $this->getProductMock(); + $product->method('getCustomOption')->willReturnMap([ + ['bundle_selection_ids', new DataObject(['value' => json_encode([1])])], + ['info_buyRequest', new DataObject(['value' => json_encode(['bundle_option' => [1]])])], + ]); + $product->setCustomOption(json_encode([])); + + $this->bundleCollection->method('getItemById')->willReturn($element); + $this->catalogProduct->setSkipSaleableCheck($check); + + try { + $this->model->checkProductBuyState($product); + } catch (LocalizedException $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage() + ); + throw $e; + } + } + + /** + * In case of missed selection for required options, bundle product should be not able to buy. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testCheckProductBuyStateRequiredOptionException() + { + $this->mockBundleCollection(); + $product = $this->getProductMock(); + $product->method('getCustomOption')->willReturnMap([ + ['bundle_selection_ids', new DataObject(['value' => json_encode([])])], + ['info_buyRequest', new DataObject(['value' => json_encode(['bundle_option' => [1]])])], + ]); + $product->setCustomOption(json_encode([])); + + $falseSelection = $this->getMockBuilder(Selection::class) + ->disableOriginalConstructor() + ->setMethods(['isSalable']) + ->getMock(); + $falseSelection->method('isSalable')->willReturn(false); + + $this->bundleCollection->method('getItemById')->willReturn($falseSelection); + $this->catalogProduct->setSkipSaleableCheck(false); + + try { + $this->model->checkProductBuyState($product); + } catch (LocalizedException $e) { + $this->assertContains( + 'Please select all required options', + $e->getMessage() + ); + + throw $e; + } + } + + /** + * Prepare product mock for testing. + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function getProductMock() + { + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods([ + '_wakeup', + 'getHasOptions', + 'getId', + 'getStoreId', + 'getCustomOption', + 'getTypeInstance', + 'setStoreFilter', + ]) + ->getMock(); + $product->method('getTypeInstance')->willReturn($product); + $product->method('setStoreFilter')->willReturn($product); + $optionCollectionCache = new DataObject(); + $optionCollectionCache->setAllIds([]); + $optionCollectionCache->setItems([ + new DataObject([ + 'required' => true, + 'id' => 1 + ]), + ]); + $product->setData('_cache_instance_options_collection', $optionCollectionCache); + return $product; + } + + /** + * Preparation mocks for checkProductsBuyState. + */ + public function mockBundleCollection() + { + $this->bundleCollection->method('create')->willReturn($this->bundleCollection); + $this->bundleCollection->method('addAttributeToSelect')->willReturn($this->bundleCollection); + $this->bundleCollection->method('setFlag')->willReturn($this->bundleCollection); + $this->bundleCollection->method('setPositionOrder')->willReturn($this->bundleCollection); + $this->bundleCollection->method('addStoreFilter')->willReturn($this->bundleCollection); + $this->bundleCollection->method('setStoreId')->willReturn($this->bundleCollection); + $this->bundleCollection->method('addFilterByRequiredOptions')->willReturn($this->bundleCollection); + $this->bundleCollection->method('setOptionIdsFilter')->willReturn($this->bundleCollection); + } + + /** + * Data provider for not available option. + * @return array + */ + public function notAvailableOptionProvider() + { + $falseSelection = $this->getMockBuilder(Selection::class) + ->disableOriginalConstructor() + ->setMethods(['isSalable']) + ->getMock(); + $falseSelection->method('isSalable')->willReturn(false); + return [ + [ + false, + 'The required options you selected are not available', + false, + ], + [ + $falseSelection, + 'The required options you selected are not available', + false + ], + ]; + } } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php index 83c38d8d9ad32bfb077b0f626122992cc05a0c1a..b74deab061f245ebaf7ab24332db95ab0dff4671 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php @@ -13,6 +13,9 @@ class AbstractItemsTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment $model */ protected $model; + /** @var \Magento\Framework\Serialize\Serializer\Json $serializer */ + protected $serializer; + protected function setUp() { $this->orderItem = $this->getMock( @@ -24,7 +27,13 @@ class AbstractItemsTest extends \PHPUnit_Framework_TestCase ); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->model = $objectManager->getObject(\Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment::class); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class); + $this->model = $objectManager->getObject( + \Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment::class, + [ + 'serializer' => $this->serializer + ] + ); } /** @@ -234,21 +243,25 @@ class AbstractItemsTest extends \PHPUnit_Framework_TestCase ]; } - /** - * @dataProvider getSelectionAttributesDataProvider - */ - public function testGetSelectionAttributes($productOptions, $result) + public function testGetSelectionAttributes() { - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); - $this->assertSame($result, $this->model->getSelectionAttributes($this->orderItem)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue([])); + $this->assertNull($this->model->getSelectionAttributes($this->orderItem)); } - public function getSelectionAttributesDataProvider() + public function testGetSelectionAttributesWithBundle() { - return [ - [[], null], - [['bundle_selection_attributes' => 'a:1:{i:0;i:1;}'], [0 => 1]], - ]; + $bundleAttributes = 'Serialized value'; + $options = ['bundle_selection_attributes' => $bundleAttributes]; + $unserializedResult = 'result of "bundle_selection_attributes" unserialization'; + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->with($bundleAttributes) + ->will($this->returnValue($unserializedResult)); + $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($options)); + + $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItem)); } public function testGetOrderOptions() diff --git a/app/code/Magento/Catalog/Helper/Product/Configuration.php b/app/code/Magento/Catalog/Helper/Product/Configuration.php index eb9e84afa8402ace13d612a20fd74d7f4b302e80..2e8290a1ed2adebe96c06250c29d9c3c0238a657 100644 --- a/app/code/Magento/Catalog/Helper/Product/Configuration.php +++ b/app/code/Magento/Catalog/Helper/Product/Configuration.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Helper\Product; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Catalog\Helper\Product\Configuration\ConfigurationInterface; use Magento\Framework\App\Helper\AbstractHelper; @@ -36,21 +38,29 @@ class Configuration extends AbstractHelper implements ConfigurationInterface */ protected $string; + /** + * @var Json + */ + private $serializer; + /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory * @param \Magento\Framework\Filter\FilterManager $filter * @param \Magento\Framework\Stdlib\StringUtils $string + * @param Json $serializer */ public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, \Magento\Framework\Filter\FilterManager $filter, - \Magento\Framework\Stdlib\StringUtils $string + \Magento\Framework\Stdlib\StringUtils $string, + Json $serializer = null ) { $this->_productOptionFactory = $productOptionFactory; $this->filter = $filter; $this->string = $string; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); parent::__construct($context); } @@ -105,7 +115,7 @@ class Configuration extends AbstractHelper implements ConfigurationInterface $addOptions = $item->getOptionByCode('additional_options'); if ($addOptions) { - $options = array_merge($options, unserialize($addOptions->getValue())); + $options = array_merge($options, $this->serializer->unserialize($addOptions->getValue())); } return $options; diff --git a/app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php b/app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php index 5b55f9cb66cfeffae4e468bd98131dd723b1f2be..7fe7147d1d2724e30452cd6de1c0bd46def267e5 100644 --- a/app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php +++ b/app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php @@ -28,22 +28,33 @@ class CustomOptionProcessor implements CartItemProcessorInterface /** @var \Magento\Catalog\Model\Product\Option\UrlBuilder */ private $urlBuilder; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param DataObject\Factory $objectFactory * @param ProductOptionFactory $productOptionFactory * @param ProductOptionExtensionFactory $extensionFactory * @param CustomOptionFactory $customOptionFactory + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( \Magento\Framework\DataObject\Factory $objectFactory, \Magento\Quote\Model\Quote\ProductOptionFactory $productOptionFactory, \Magento\Quote\Api\Data\ProductOptionExtensionFactory $extensionFactory, - \Magento\Catalog\Model\CustomOptions\CustomOptionFactory $customOptionFactory + \Magento\Catalog\Model\CustomOptions\CustomOptionFactory $customOptionFactory, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->objectFactory = $objectFactory; $this->productOptionFactory = $productOptionFactory; $this->extensionFactory = $extensionFactory; $this->customOptionFactory = $customOptionFactory; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -99,7 +110,7 @@ class CustomOptionProcessor implements CartItemProcessorInterface protected function getOptions(CartItemInterface $cartItem) { $buyRequest = !empty($cartItem->getOptionByCode('info_buyRequest')) - ? unserialize($cartItem->getOptionByCode('info_buyRequest')->getValue()) + ? $this->serializer->unserialize($cartItem->getOptionByCode('info_buyRequest')->getValue()) : null; return is_array($buyRequest) && isset($buyRequest['options']) ? $buyRequest['options'] diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php index afa44c42d87b8e6351b8334e60fc49ebd87ca5b2..3d121cbc741c3000f42560efae0c361f11745c29 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php @@ -22,19 +22,30 @@ class Date extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ protected $_localeDate; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_localeDate = $localeDate; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($checkoutSession, $scopeConfig, $data); } @@ -269,7 +280,7 @@ class Date extends \Magento\Catalog\Model\Product\Option\Type\DefaultType $confItem = $this->getConfigurationItem(); $infoBuyRequest = $confItem->getOptionByCode('info_buyRequest'); try { - $value = unserialize($infoBuyRequest->getValue()); + $value = $this->serializer->unserialize($infoBuyRequest->getValue()); if (is_array($value) && isset($value['options']) && isset($value['options'][$this->getOption()->getId()]) ) { return $value['options'][$this->getOption()->getId()]; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File.php index 70c20a4e314903ce9defb0e40f50c912a83d7307..865e57f9b71f0b42617da27e698bf2c8f0e6a627 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File.php @@ -9,6 +9,8 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Framework\Exception\LocalizedException; use Magento\Catalog\Model\Product\Exception as ProductException; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; /** * Catalog product option file type @@ -70,6 +72,11 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ protected $validatorFile; + /** + * @var Json + */ + private $serializer; + /** * @var Filesystem */ @@ -86,6 +93,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType * @param \Magento\Framework\Escaper $escaper * @param array $data * @param Filesystem $filesystem + * @param Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -98,7 +106,8 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType \Magento\Catalog\Model\Product\Option\UrlBuilder $urlBuilder, \Magento\Framework\Escaper $escaper, array $data = [], - Filesystem $filesystem = null + Filesystem $filesystem = null, + Json $serializer = null ) { $this->_itemOptionFactory = $itemOptionFactory; $this->_urlBuilder = $urlBuilder; @@ -108,6 +117,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType $this->_rootDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); $this->validatorInfo = $validatorInfo; $this->validatorFile = $validatorFile; + $this->serializer = $serializer ? $serializer : ObjectManager::getInstance()->get(Json::class); parent::__construct($checkoutSession, $scopeConfig, $data); } @@ -275,7 +285,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType // Save option in request, because we have no $_FILES['options'] $requestOptions[$this->getOption()->getId()] = $value; - $result = serialize($value); + $result = $this->serializer->serialize($value); } else { /* * Clear option info from request, so it won't be stored in our db upon @@ -306,7 +316,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType { if ($this->_formattedOptionValue === null) { try { - $value = unserialize($optionValue); + $value = $this->serializer->unserialize($optionValue); $customOptionUrlParams = $this->getCustomOptionUrlParams() ? $this->getCustomOptionUrlParams() : [ 'id' => $this->getConfigurationItemOption()->getId(), @@ -316,7 +326,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType $value['url'] = ['route' => $this->_customOptionDownloadUrl, 'params' => $customOptionUrlParams]; $this->_formattedOptionValue = $this->_getOptionHtml($value); - $this->getConfigurationItemOption()->setValue(serialize($value)); + $this->getConfigurationItemOption()->setValue($this->serializer->serialize($value)); return $this->_formattedOptionValue; } catch (\Exception $e) { return $optionValue; @@ -364,7 +374,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType if (is_array($value)) { return $value; } elseif (is_string($value) && !empty($value)) { - return unserialize($value); + return $this->serializer->unserialize($value); } else { return []; } @@ -386,11 +396,13 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType * * @param string $optionValue Prepared for cart option value * @return string + * + * @deprecated */ public function getEditableOptionValue($optionValue) { try { - $value = unserialize($optionValue); + $value = $this->serializer->unserialize($optionValue); return sprintf( '%s [%d]', $this->_escaper->escapeHtml($value['title']), @@ -409,6 +421,8 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @deprecated */ public function parseOptionValue($optionValue, $productOptionValues) { @@ -417,7 +431,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType $confItemOptionId = $matches[1]; $option = $this->_itemOptionFactory->create()->load($confItemOptionId); try { - unserialize($option->getValue()); + $this->serializer->unserialize($option->getValue()); return $option->getValue(); } catch (\Exception $e) { return null; @@ -436,7 +450,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType public function prepareOptionValueForRequest($optionValue) { try { - $result = unserialize($optionValue); + $result = $this->serializer->unserialize($optionValue); return $result; } catch (\Exception $e) { return null; @@ -452,7 +466,7 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType { $quoteOption = $this->getConfigurationItemOption(); try { - $value = unserialize($quoteOption->getValue()); + $value = $this->serializer->unserialize($quoteOption->getValue()); if (!isset($value['quote_path'])) { throw new \Exception(); } diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 11b8d03fc7ee5cc23dd627e65ad9651a5b243b7d..8745903acc4a8256e3232b8f94070eaeeac143a6 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -161,6 +161,13 @@ abstract class AbstractType */ protected $productRepository; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + protected $serializer; + /** * Construct * @@ -173,6 +180,7 @@ abstract class AbstractType * @param \Magento\Framework\Registry $coreRegistry * @param \Psr\Log\LoggerInterface $logger * @param ProductRepositoryInterface $productRepository + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -184,7 +192,8 @@ abstract class AbstractType \Magento\Framework\Filesystem $filesystem, \Magento\Framework\Registry $coreRegistry, \Psr\Log\LoggerInterface $logger, - ProductRepositoryInterface $productRepository + ProductRepositoryInterface $productRepository, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_catalogProductOption = $catalogProductOption; $this->_eavConfig = $eavConfig; @@ -195,6 +204,8 @@ abstract class AbstractType $this->_filesystem = $filesystem; $this->_logger = $logger; $this->productRepository = $productRepository; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -394,8 +405,7 @@ abstract class AbstractType $product->prepareCustomOptions(); $buyRequest->unsetData('_processing_params'); // One-time params only - $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData())); - + $product->addCustomOption('info_buyRequest', $this->serializer->serialize($buyRequest->getData())); if ($options) { $optionIds = array_keys($options); $product->addCustomOption('option_ids', implode(',', $optionIds)); @@ -645,7 +655,7 @@ abstract class AbstractType $optionArr = []; $info = $product->getCustomOption('info_buyRequest'); if ($info) { - $optionArr['info_buyRequest'] = unserialize($info->getValue()); + $optionArr['info_buyRequest'] = $this->serializer->unserialize($info->getValue()); } $optionIds = $product->getCustomOption('option_ids'); diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..902f63c578ab66b96361779ee01928a4f9dbada4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Helper\Product; + +class ConfigurationTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + */ + protected $serializer; + + /** + * @var \Magento\Catalog\Helper\Product\Configuration + */ + protected $helper; + + protected function setUp() + { + $contextMock = $this->getMock(\Magento\Framework\App\Helper\Context::class, [], [], '', false); + $optionFactoryMock = $this->getMock(\Magento\Catalog\Model\Product\OptionFactory::class, [], [], '', false); + $filterManagerMock = $this->getMock(\Magento\Framework\Filter\FilterManager::class, [], [], '', false); + $stringUtilsMock = $this->getMock(\Magento\Framework\Stdlib\StringUtils::class, [], [], '', false); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class, [], [], '', false); + + $this->helper = new \Magento\Catalog\Helper\Product\Configuration( + $contextMock, + $optionFactoryMock, + $filterManagerMock, + $stringUtilsMock, + $this->serializer + ); + } + + /** + * Retrieves product additional options + */ + public function testGetAdditionalOptionOnly() + { + $additionalOptionResult = ['additional_option' => 1]; + + $itemMock = $this->getMock( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface::class, + [], + [], + '', + false + ); + $optionMock = $this->getMock( + \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class, + [], + [], + '', + false + ); + $additionalOptionMock = $this->getMock( + \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class, + [], + [], + '', + false + ); + $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); + + $this->serializer->expects($this->once())->method('unserialize')->willReturn($additionalOptionResult); + $optionMock->expects($this->once())->method('getValue')->willReturn(null); + $additionalOptionMock->expects($this->once())->method('getValue'); + + $itemMock->expects($this->once())->method('getProduct')->willReturn($productMock); + $itemMock->expects($this->any())->method('getOptionByCode')->will($this->returnValueMap( + [ + ['option_ids', $optionMock], + ['additional_options', $additionalOptionMock] + ] + )); + + $this->assertEquals($additionalOptionResult, $this->helper->getCustomOptions($itemMock)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionProcessorTest.php index e3bdffbf5aefc74c881a14fa72970c0056d760ed..5e8e8bde253ae5c1f75005b63105b3937459afcb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionProcessorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionProcessorTest.php @@ -51,6 +51,9 @@ class CustomOptionProcessorTest extends \PHPUnit_Framework_TestCase /** @var CustomOptionProcessor */ protected $processor; + /** @var \Magento\Framework\Serialize\Serializer\Json */ + private $serializer; + protected function setUp() { $this->objectFactory = $this->getMockBuilder(\Magento\Framework\DataObject\Factory::class) @@ -90,12 +93,16 @@ class CustomOptionProcessorTest extends \PHPUnit_Framework_TestCase $this->buyRequest = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(['unserialize']) + ->getMockForAbstractClass(); $this->processor = new CustomOptionProcessor( $this->objectFactory, $this->productOptionFactory, $this->extensionFactory, - $this->customOptionFactory + $this->customOptionFactory, + $this->serializer ); } @@ -126,6 +133,9 @@ class CustomOptionProcessorTest extends \PHPUnit_Framework_TestCase $this->assertSame($this->buyRequest, $this->processor->convertToBuyRequest($this->cartItem)); } + /** + * @covers \Magento\Catalog\Model\CustomOptions\CustomOptionProcessor::getOptions() + */ public function testProcessCustomOptions() { $optionId = 23; @@ -136,9 +146,12 @@ class CustomOptionProcessorTest extends \PHPUnit_Framework_TestCase ->method('getOptionByCode') ->with('info_buyRequest') ->willReturn($quoteItemOption); - $quoteItemOption->expects($this->once()) + $quoteItemOption->expects($this->any()) ->method('getValue') - ->willReturn('a:1:{s:7:"options";a:1:{i:' . $optionId . ';a:2:{i:0;s:1:"5";i:1;s:1:"6";}}} '); + ->willReturn('{"options":{"' . $optionId . '":["5","6"]}}'); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturn(json_decode($quoteItemOption->getValue(), true)); $this->customOptionFactory->expects($this->once()) ->method('create') ->willReturn($this->customOption); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php index 6682b295476325f2df099faa183d5edb451cadb5..5584f63a2ddf80d047bfe0329f3916523498b75d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php @@ -11,6 +11,11 @@ use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\ReadInterface; use Magento\Framework\Filesystem\DriverPool; +/** + * Class FileTest. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class FileTest extends \PHPUnit_Framework_TestCase { /** @@ -33,6 +38,21 @@ class FileTest extends \PHPUnit_Framework_TestCase */ private $filesystemMock; + /** + * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + + /** + * @var \Magento\Catalog\Model\Product\Option\UrlBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilder; + + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaper; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -44,11 +64,23 @@ class FileTest extends \PHPUnit_Framework_TestCase $this->rootDirectory = $this->getMockBuilder(ReadInterface::class) ->getMock(); - $this->filesystemMock->expects($this->once()) + $this->filesystemMock->expects($this->any()) ->method('getDirectoryRead') ->with(DirectoryList::MEDIA, DriverPool::FILE) ->willReturn($this->rootDirectory); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->urlBuilder = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option\UrlBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class) + ->disableOriginalConstructor() + ->getMock(); + $this->coreFileStorageDatabase = $this->getMock( \Magento\MediaStorage\Helper\File\Storage\Database::class, ['copyFile'], @@ -67,11 +99,40 @@ class FileTest extends \PHPUnit_Framework_TestCase \Magento\Catalog\Model\Product\Option\Type\File::class, [ 'filesystem' => $this->filesystemMock, - 'coreFileStorageDatabase' => $this->coreFileStorageDatabase + 'coreFileStorageDatabase' => $this->coreFileStorageDatabase, + 'serializer' => $this->serializer, + 'urlBuilder' => $this->urlBuilder, + 'escaper' => $this->escaper ] ); } + public function testGetCustomizedView() + { + $fileObject = $this->getFileObject(); + $optionInfo = ['option_value' => 'some serialized data']; + + $dataAfterSerialize = ['some' => 'array']; + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with('some serialized data') + ->willReturn($dataAfterSerialize); + + $this->urlBuilder->expects($this->once()) + ->method('getUrl') + ->willReturn('someUrl'); + + $this->escaper->expects($this->once()) + ->method('escapeHtml') + ->willReturn('string'); + + $this->assertEquals( + '<a href="someUrl" target="_blank">string</a> ', + $fileObject->getCustomizedView($optionInfo) + ); + } + public function testCopyQuoteToOrder() { $optionMock = $this->getMockBuilder(OptionInterface::class) @@ -82,11 +143,22 @@ class FileTest extends \PHPUnit_Framework_TestCase $quotePath = '/quote/path/path/uploaded.file'; $orderPath = '/order/path/path/uploaded.file'; + $quoteValue = "{\"quote_path\":\"$quotePath\",\"order_path\":\"$orderPath\"}"; + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($quoteValue) + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $optionMock->expects($this->any()) ->method('getValue') - ->will($this->returnValue(serialize(['quote_path' => $quotePath, 'order_path' => $orderPath]))); + ->will($this->returnValue($quoteValue)); - $this->rootDirectory->expects($this->once()) + $this->rootDirectory->expects($this->any()) ->method('isFile') ->with($this->equalTo($quotePath)) ->will($this->returnValue(true)); @@ -112,4 +184,55 @@ class FileTest extends \PHPUnit_Framework_TestCase $fileObject->copyQuoteToOrder() ); } + + public function testGetFormattedOptionValue() + { + $resultValue = ['result']; + $optionValue = json_encode($resultValue); + $urlParameter = 'parameter'; + + $fileObject = $this->getFileObject(); + $fileObject->setCustomOptionUrlParams($urlParameter); + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($optionValue) + ->willReturn($resultValue); + + $resultValue['url'] = [ + 'route' => 'sales/download/downloadCustomOption', + 'params' => $fileObject->getCustomOptionUrlParams() + ]; + + $this->serializer->expects($this->once()) + ->method('serialize') + ->with($resultValue) + ->willReturn(json_encode($resultValue)); + + $option = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item\Option::class) + ->setMethods(['setValue']) + ->disableOriginalConstructor() + ->getMock(); + + $option->expects($this->once()) + ->method('setValue') + ->with(json_encode($resultValue)); + + $fileObject->setConfigurationItemOption($option); + + $fileObject->getFormattedOptionValue($optionValue); + } + + public function testPrepareOptionValueForRequest() + { + $optionValue = 'string'; + $resultValue = ['result']; + $fileObject = $this->getFileObject(); + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($optionValue) + ->willReturn($resultValue); + + $this->assertEquals($resultValue, $fileObject->prepareOptionValueForRequest($optionValue)); + } } diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 79e76feb4366190cb1d81389e7c54ac4f97cca9e..4a0f502da20dfdc94545a81c20dfa061ffef0008 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -14,6 +14,7 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor { /** * @var \Magento\Quote\Api\BillingAddressManagementInterface + * @deprecated This call was substituted to eliminate extra quote::save call */ protected $billingAddressManagement; @@ -42,6 +43,11 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor */ private $logger; + /** + * @var \Magento\Quote\Api\CartRepositoryInterface + */ + private $cartRepository; + /** * @param \Magento\Quote\Api\BillingAddressManagementInterface $billingAddressManagement * @param \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement @@ -99,7 +105,19 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor \Magento\Quote\Api\Data\AddressInterface $billingAddress = null ) { if ($billingAddress) { - $this->billingAddressManagement->assign($cartId, $billingAddress); + /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->getCartRepository(); + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $quoteRepository->getActive($cartId); + $quote->removeAddress($quote->getBillingAddress()->getId()); + $quote->setBillingAddress($billingAddress); + $quote->setDataChanges(true); + $shippingAddress = $quote->getShippingAddress(); + if ($shippingAddress && $shippingAddress->getShippingMethod()) { + $shippingDataArray = explode('_', $shippingAddress->getShippingMethod()); + $shippingCarrier = array_shift($shippingDataArray); + $shippingAddress->setLimitCarrier($shippingCarrier); + } } $this->paymentMethodManagement->set($cartId, $paymentMethod); return true; @@ -130,4 +148,19 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor } return $this->logger; } + + /** + * Get Cart repository + * + * @return \Magento\Quote\Api\CartRepositoryInterface + * @deprecated + */ + private function getCartRepository() + { + if (!$this->cartRepository) { + $this->cartRepository = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Quote\Api\CartRepositoryInterface::class); + } + return $this->cartRepository; + } } diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index 237d28e2845e430493d1ba7f73b15160995c7c9c..1b4f8b8c64a864efefb03e56cd52aa8dbb41e304 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -144,6 +144,7 @@ class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInf /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); + $address->setLimitCarrier($carrierCode); $quote = $this->prepareShippingAssignment($quote, $address, $carrierCode . '_' . $methodCode); $this->validateQuote($quote); $quote->setIsMultiShipping(false); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index 8da67a1fcb71512dd218a2c62d51b7922bb36c25..f256d8edefd1c9b2bd0dc41c838293cf4b9dfa32 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -5,8 +5,9 @@ */ namespace Magento\Checkout\Test\Unit\Model; -use Magento\Framework\Exception\CouldNotSaveException; - +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase { /** @@ -34,6 +35,11 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase */ private $loggerMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $cartRepositoryMock; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -46,7 +52,7 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $this->cartManagementMock = $this->getMock(\Magento\Quote\Api\CartManagementInterface::class); $this->loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); - + $this->cartRepositoryMock = $this->getMockBuilder(\Magento\Quote\Api\CartRepositoryInterface::class)->getMock(); $this->model = $objectManager->getObject( \Magento\Checkout\Model\PaymentInformationManagement::class, [ @@ -56,6 +62,7 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase ] ); $objectManager->setBackwardCompatibleProperty($this->model, 'logger', $this->loggerMock); + $objectManager->setBackwardCompatibleProperty($this->model, 'cartRepository', $this->cartRepositoryMock); } public function testSavePaymentInformationAndPlaceOrder() @@ -65,9 +72,7 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class); - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->cartManagementMock->expects($this->once())->method('placeOrder')->with($cartId)->willReturn($orderId); @@ -87,9 +92,7 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class); - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $exception = new \Exception(__('DB exception')); $this->loggerMock->expects($this->once())->method('critical'); @@ -104,7 +107,6 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $orderId = 200; $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); - $this->billingAddressManagementMock->expects($this->never())->method('assign'); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->cartManagementMock->expects($this->once())->method('placeOrder')->with($cartId)->willReturn($orderId); @@ -120,9 +122,7 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class); - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->assertTrue($this->model->savePaymentInformation($cartId, $paymentMock, $billingAddressMock)); @@ -133,7 +133,6 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $cartId = 100; $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); - $this->billingAddressManagementMock->expects($this->never())->method('assign'); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->assertTrue($this->model->savePaymentInformation($cartId, $paymentMock)); @@ -149,9 +148,8 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class); - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); + $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $phrase = new \Magento\Framework\Phrase(__('DB exception')); $exception = new \Magento\Framework\Exception\LocalizedException($phrase); @@ -160,4 +158,31 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); } + + /** + * @param int $cartId + * @param \PHPUnit_Framework_MockObject_MockObject $billingAddressMock + */ + private function getMockForAssignBillingAddress($cartId, $billingAddressMock) + { + $billingAddressId = 1; + $quoteMock = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); + $quoteBillingAddress = $this->getMock(\Magento\Quote\Model\Quote\Address::class, [], [], '', false); + $quoteShippingAddress = $this->getMock( + \Magento\Quote\Model\Quote\Address::class, + ['setLimitCarrier', 'getShippingMethod'], + [], + '', + false + ); + $this->cartRepositoryMock->expects($this->any())->method('getActive')->with($cartId)->willReturn($quoteMock); + $quoteMock->expects($this->once())->method('getBillingAddress')->willReturn($quoteBillingAddress); + $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($quoteShippingAddress); + $quoteBillingAddress->expects($this->once())->method('getId')->willReturn($billingAddressId); + $quoteMock->expects($this->once())->method('removeAddress')->with($billingAddressId); + $quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddressMock); + $quoteMock->expects($this->once())->method('setDataChanges')->willReturnSelf(); + $quoteShippingAddress->expects($this->any())->method('getShippingMethod')->willReturn('flatrate_flatrate'); + $quoteShippingAddress->expects($this->once())->method('setLimitCarrier')->with('flatrate')->willReturnSelf(); + } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php index 402a0c8228356a7ae8a148d7626e06bb5dc15a27..751bcee6db2a9fd6e2d15405c57f899a2d7ad972 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php @@ -109,7 +109,8 @@ class ShippingInformationManagementTest extends \PHPUnit_Framework_TestCase 'importCustomerAddressData', 'save', 'getShippingRateByCode', - 'getShippingMethod' + 'getShippingMethod', + 'setLimitCarrier' ], [], '', @@ -208,7 +209,7 @@ class ShippingInformationManagementTest extends \PHPUnit_Framework_TestCase private function setShippingAssignmentsMocks($shippingMethod) { $this->quoteMock->expects($this->once())->method('getExtensionAttributes')->willReturn(null); - + $this->shippingAddressMock->expects($this->once())->method('setLimitCarrier'); $this->cartExtensionMock = $this->getMock( \Magento\Quote\Api\Data\CartExtension::class, ['getShippingAssignments', 'setShippingAssignments'], diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 0bd2f23418221770d4562eb67d6af9d3e8bd4646..d9ff7a92f08c6d86f17e3cfc568853e09ee5c59f 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -183,7 +183,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 \Magento\Framework\Serialize\Serializer\Json $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -205,7 +205,8 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, \Magento\Framework\Cache\FrontendInterface $cache = null, - \Magento\Customer\Model\Session $customerSession = null + \Magento\Customer\Model\Session $customerSession = null, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->typeConfigurableFactory = $typeConfigurableFactory; $this->_eavAttributeFactory = $eavAttributeFactory; @@ -226,7 +227,8 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType $filesystem, $coreRegistry, $logger, - $productRepository + $productRepository, + $serializer ); } @@ -414,47 +416,15 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType ['group' => 'CONFIGURABLE', 'method' => __METHOD__] ); if (!$product->hasData($this->_configurableAttributes)) { - $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); - $productId = $product->getData($metadata->getLinkField()); - $cacheId = __CLASS__ . $productId . '_' . $product->getStoreId(); - $configurableAttributes = $this->getCache()->load($cacheId); - $configurableAttributes = $this->hasCacheData($configurableAttributes); - if ($configurableAttributes) { - $configurableAttributes->setProductFilter($product); - } else { - $configurableAttributes = $this->getConfigurableAttributeCollection($product); - $this->extensionAttributesJoinProcessor->process($configurableAttributes); - $configurableAttributes->orderByPosition()->load(); - $this->getCache()->save( - serialize($configurableAttributes), - $cacheId, - array_merge($product->getIdentities(), [self::TYPE_CODE . '_' . $productId]) - ); - } + $configurableAttributes = $this->getConfigurableAttributeCollection($product); + $this->extensionAttributesJoinProcessor->process($configurableAttributes); + $configurableAttributes->orderByPosition()->load(); $product->setData($this->_configurableAttributes, $configurableAttributes); } \Magento\Framework\Profiler::stop('CONFIGURABLE:' . __METHOD__); return $product->getData($this->_configurableAttributes); } - /** - * @param mixed $configurableAttributes - * @return bool - */ - protected function hasCacheData($configurableAttributes) - { - $configurableAttributes = $configurableAttributes ?: unserialize($configurableAttributes); - if (is_array($configurableAttributes) && count($configurableAttributes)) { - foreach ($configurableAttributes as $attribute) { - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $attribute */ - if ($attribute->getData('options')) { - return $configurableAttributes; - } - } - } - return false; - } - /** * Reset the cached configurable attributes of a product * @@ -463,13 +433,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ public function resetConfigurableAttributes($product) { - $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); - $productId = $product->getData($metadata->getLinkField()); $product->unsetData($this->_configurableAttributes); - $cacheId = __CLASS__ . $productId . '_' . $product->getStoreId(); - $this->getCache()->remove($cacheId); - $this->getCache()->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::TYPE_CODE . '_' . $productId]); - return $this; } @@ -566,7 +530,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType ) ); $collection = $this->getUsedProductCollection($product); - $data = unserialize($this->getCache()->load($key)); + $data = $this->serializer->unserialize($this->getCache()->load($key)); if (!empty($data)) { $usedProducts = []; foreach ($data as $item) { @@ -592,7 +556,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType $usedProducts = $collection->getItems(); $this->getCache()->save( - serialize(array_map( + $this->serializer->serialize(array_map( function ($item) { return $item->getData(); }, @@ -883,7 +847,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType ['group' => 'CONFIGURABLE', 'method' => __METHOD__] ); if ($attributesOption = $product->getCustomOption('attributes')) { - $data = unserialize($attributesOption->getValue()); + $data = $this->serializer->unserialize($attributesOption->getValue()); $this->getUsedProductAttributeIds($product); $usedAttributes = $product->getData($this->_usedAttributes); @@ -906,7 +870,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType 'value' => $value, 'option_id' => $attributeId, 'option_value' => $attributeValue - ]; + ]; } } } @@ -965,7 +929,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType if ($subProduct) { $subProductLinkFieldId = $subProduct->getId(); - $product->addCustomOption('attributes', serialize($attributes)); + $product->addCustomOption('attributes', $this->serializer->serialize($attributes)); $product->addCustomOption('product_qty_' . $subProductLinkFieldId, 1, $subProduct); $product->addCustomOption('simple_product', $subProductLinkFieldId, $subProduct); @@ -1026,7 +990,7 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType parent::checkProductBuyState($product); $option = $product->getCustomOption('info_buyRequest'); if ($option instanceof \Magento\Quote\Model\Quote\Item\Option) { - $buyRequest = new \Magento\Framework\DataObject(unserialize($option->getValue())); + $buyRequest = new \Magento\Framework\DataObject($this->serializer->unserialize($option->getValue())); $attributes = $buyRequest->getSuperAttribute(); if (is_array($attributes)) { foreach ($attributes as $key => $val) { diff --git a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php index 8e661f99c5238d9ac811048e6f32829c388fc39b..a4ff02482b0190b961eaeddd31588ba7337f305e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php +++ b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php @@ -7,6 +7,8 @@ namespace Magento\ConfigurableProduct\Model\Quote\Item; use Magento\Quote\Model\Quote\Item\CartItemProcessorInterface; use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; class CartItemProcessor implements CartItemProcessorInterface { @@ -30,22 +32,30 @@ class CartItemProcessor implements CartItemProcessorInterface */ protected $itemOptionValueFactory; + /** + * @var Json + */ + private $serializer; + /** * @param \Magento\Framework\DataObject\Factory $objectFactory * @param \Magento\Quote\Model\Quote\ProductOptionFactory $productOptionFactory * @param \Magento\Quote\Api\Data\ProductOptionExtensionFactory $extensionFactory * @param \Magento\ConfigurableProduct\Model\Quote\Item\ConfigurableItemOptionValueFactory $itemOptionValueFactory + * @param Json $serializer */ public function __construct( \Magento\Framework\DataObject\Factory $objectFactory, \Magento\Quote\Model\Quote\ProductOptionFactory $productOptionFactory, \Magento\Quote\Api\Data\ProductOptionExtensionFactory $extensionFactory, - \Magento\ConfigurableProduct\Model\Quote\Item\ConfigurableItemOptionValueFactory $itemOptionValueFactory + \Magento\ConfigurableProduct\Model\Quote\Item\ConfigurableItemOptionValueFactory $itemOptionValueFactory, + Json $serializer = null ) { $this->objectFactory = $objectFactory; $this->productOptionFactory = $productOptionFactory; $this->extensionFactory = $extensionFactory; $this->itemOptionValueFactory = $itemOptionValueFactory; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); } /** @@ -73,7 +83,7 @@ class CartItemProcessor implements CartItemProcessorInterface public function processOptions(CartItemInterface $cartItem) { $attributesOption = $cartItem->getProduct()->getCustomOption('attributes'); - $selectedConfigurableOptions = unserialize($attributesOption->getValue()); + $selectedConfigurableOptions = $this->serializer->unserialize($attributesOption->getValue()); if (is_array($selectedConfigurableOptions)) { $configurableOptions = []; 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 4f7ae98cc11cee8543581ea02c70a252183c4aa5..a1a153731d07318b27b62a470ca5ccc43f7d44af 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 @@ -15,7 +15,15 @@ use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\Framework\EntityManager\EntityMetadata; use Magento\Framework\EntityManager\MetadataPool; use Magento\Customer\Model\Session; -use Magento\Framework\Cache\FrontendInterface; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\CollectionFactory; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; +use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory; +use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection as ProductCollection; /** * Class \Magento\ConfigurableProduct\Test\Unit\Model\Product\Type\ConfigurableTest @@ -94,6 +102,9 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase /** @var \PHPUnit_Framework_MockObject_MockObject */ private $cache; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $serializer; + /** * @var Config */ @@ -105,76 +116,67 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->_objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $eventManager = $this->getMock(\Magento\Framework\Event\ManagerInterface::class, [], [], '', false); - $fileStorageDbMock = $this->getMock( - \Magento\MediaStorage\Helper\File\Storage\Database::class, - [], - [], - '', - false - ); + $eventManager = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $fileStorageDbMock = $this->getMockBuilder(\Magento\MediaStorage\Helper\File\Storage\Database::class) + ->disableOriginalConstructor() + ->getMock(); $filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class) ->disableOriginalConstructor() ->getMock(); - $coreRegistry = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); + $coreRegistry = $this->getMockBuilder(\Magento\Framework\Registry::class) + ->disableOriginalConstructor() + ->getMock(); $logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) ->disableOriginalConstructor() - ->setMethods([]) - ->getMockForAbstractClass(); - $this->_typeConfigurableFactory = $this->getMock( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory::class, - ['create', 'saveProducts'], - [], - '', - false - ); - $this->_configurableAttributeFactoryMock = $this->getMock( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory::class, - ['create'], - [], - '', - false - ); - $this->_productCollectionFactory = $this->getMock( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory::class, - ['create'], - [], - '', - false - ); - $this->_attributeCollectionFactory = $this->getMock( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\CollectionFactory::class, - ['create'], - [], - '', - false - ); - $this->productRepository = $this->getMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->extensionAttributesJoinProcessorMock = $this->getMock( - \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::class, - [], - [], - '', - false - ); + ->getMock(); + $this->_typeConfigurableFactory = $this->getMockBuilder(ConfigurableFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create', 'saveProducts']) + ->getMock(); + $this->_configurableAttributeFactoryMock = $this->getMockBuilder(AttributeFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->_productCollectionFactory = $this->getMockBuilder( + \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->_attributeCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->productRepository = $this->getMockBuilder(\Magento\Catalog\Api\ProductRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->extensionAttributesJoinProcessorMock = $this->getMockBuilder(JoinProcessorInterface::class) + ->disableOriginalConstructor() + ->getMock(); $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->cache = $this->getMockBuilder(\Magento\Framework\Cache\FrontendInterface::class) - ->getMockForAbstractClass(); + ->disableOriginalConstructor() + ->getMock(); $this->catalogConfig = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - - $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class)->disableOriginalConstructor() + $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() ->getMock(); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->metadataPool->expects($this->any()) + ->method('getMetadata') + ->with(ProductInterface::class) + ->willReturn($this->entityMetadata); $this->_model = $this->_objectHelper->getObject( Configurable::class, @@ -194,6 +196,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase 'customerSession' => $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(), 'cache' => $this->cache, 'catalogConfig' => $this->catalogConfig, + 'serializer' => $this->serializer, ] ); $refClass = new \ReflectionClass(Configurable::class); @@ -223,19 +226,20 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase 'getData', 'hasData', 'getAssociatedProductIds', - '__wakeup', - '__sleep', ] )->disableOriginalConstructor() ->getMock(); - $product->expects($this->any())->method('dataHasChangedFor')->will($this->returnValue('false')); - $product->expects($this->any())->method('getConfigurableAttributesData') - ->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('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->once())->method('dataHasChangedFor')->willReturn('false'); + $product->expects($this->once()) + ->method('getConfigurableAttributesData') + ->willReturn($this->attributeData); + $product->expects($this->once())->method('getIsDuplicate')->willReturn(true); + $product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $product->expects($this->once())->method('getAssociatedProductIds')->willReturn([2]); + $product->expects($this->once()) + ->method('hasData') + ->with('_cache_instance_used_product_attribute_ids') + ->willReturn(true); $extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class) ->setMethods([ 'getConfigurableProductOptions', @@ -250,7 +254,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ['_cache_instance_used_product_attribute_ids', null, 1], ['link', null, 1], ]; - $product->expects($this->any()) + $product->expects($this->atLeastOnce()) ->method('getData') ->willReturnMap($dataMap); $attribute = $this->getMockBuilder( @@ -260,24 +264,26 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->getMock(); $expectedAttributeData = $this->attributeData[1]; unset($expectedAttributeData['id']); - $attribute->expects($this->once())->method('addData')->with($expectedAttributeData)->will($this->returnSelf()); - $attribute->expects($this->once())->method('setStoreId')->with(1)->will($this->returnSelf()); - $attribute->expects($this->once())->method('setProductId')->with(1)->will($this->returnSelf()); - $attribute->expects($this->once())->method('save')->will($this->returnSelf()); - - $this->_configurableAttributeFactoryMock->expects($this->any())->method('create') - ->will($this->returnValue($attribute)); - - $attributeCollection = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class - )->setMethods(['setProductFilter', 'addFieldToFilter', 'walk'])->disableOriginalConstructor() - ->getMock(); - $this->_attributeCollectionFactory->expects($this->any())->method('create') - ->will($this->returnValue($attributeCollection)); - - $this->_typeConfigurableFactory->expects($this->once())->method('create')->will($this->returnSelf()); - $this->_typeConfigurableFactory->expects($this->once())->method('saveProducts')->withAnyParameters() - ->will($this->returnSelf()); + $attribute->expects($this->once())->method('addData')->with($expectedAttributeData)->willReturnSelf(); + $attribute->expects($this->once())->method('setStoreId')->with(1)->willReturnSelf(); + $attribute->expects($this->once())->method('setProductId')->with(1)->willReturnSelf(); + $attribute->expects($this->once())->method('save')->willReturnSelf(); + + $this->_configurableAttributeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($attribute); + $attributeCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->_attributeCollectionFactory->expects($this->once()) + ->method('create') + ->willReturn($attributeCollection); + $this->_typeConfigurableFactory->expects($this->once()) + ->method('create') + ->willReturnSelf(); + $this->_typeConfigurableFactory->expects($this->once()) + ->method('saveProducts') + ->willReturnSelf(); $this->_model->save($product); } @@ -293,174 +299,117 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testCanUseAttribute() { - $attribute = $this->getMock( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - [ - 'getIsGlobal', - 'getIsVisible', - 'usesSource', - 'getIsUserDefined', - '__wakeup', - '__sleep' - ], - [], - '', - false - ); + $attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); $attribute->expects($this->once()) ->method('getIsGlobal') - ->will($this->returnValue(1)); + ->willReturn(1); $attribute->expects($this->once()) ->method('getIsVisible') - ->will($this->returnValue(1)); + ->willReturn(1); $attribute->expects($this->once()) ->method('usesSource') - ->will($this->returnValue(1)); + ->willReturn(1); $attribute->expects($this->once()) ->method('getIsUserDefined') - ->will($this->returnValue(1)); + ->willReturn(1); $this->assertTrue($this->_model->canUseAttribute($attribute)); } public function testGetUsedProducts() { - $attributeCollection = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class - )->setMethods(['setProductFilter', 'addFieldToFilter', 'walk'])->disableOriginalConstructor() + $productCollectionItemData = ['array']; + + $productCollectionItem = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() ->getMock(); - $attributeCollection->expects($this->any())->method('setProductFilter')->will($this->returnSelf()); - $this->_attributeCollectionFactory->expects($this->any())->method('create') - ->will($this->returnValue($attributeCollection)); $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods( - [ - 'dataHasChangedFor', - 'getConfigurableAttributesData', - 'getStoreId', - 'getId', - 'getData', - 'hasData', - 'getAssociatedProductIds', - 'getIdentities', - '__wakeup', - '__sleep', - ] - )->disableOriginalConstructor() + ->disableOriginalConstructor() + ->getMock(); + $productCollection = $this->getMockBuilder(ProductCollection::class) + ->disableOriginalConstructor() ->getMock(); - $product->expects($this->any())->method('getConfigurableAttributesData') - ->will($this->returnValue($this->attributeData)); - $product->expects($this->any())->method('getStoreId')->will($this->returnValue(5)); - $product->expects($this->any())->method('getId')->will($this->returnValue(1)); - $product->expects($this->any())->method('getIdentities')->willReturn(['123']); - $product->expects($this->any())->method('getAssociatedProductIds')->will($this->returnValue([2])); - $product->expects($this->any())->method('hasData') - ->will( - $this->returnValueMap( - [ - ['_cache_instance_used_product_attribute_ids', 1], - ['_cache_instance_products', 0], - ['_cache_instance_configurable_attributes', 1], - ['_cache_instance_used_product_attributes', 1], - ] - ) + $productCollectionItem->expects($this->once())->method('getData')->willReturn($productCollectionItemData); + $attributeCollection->expects($this->any())->method('setProductFilter')->willReturnSelf(); + $product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(5); + $product->expects($this->once())->method('getIdentities')->willReturn(['123']); + + $product->expects($this->exactly(2)) + ->method('hasData') + ->willReturnMap( + [ + ['_cache_instance_products', null], + ['_cache_instance_used_product_attributes', 1], + ] ); - $product->expects($this->any())->method('getData') - ->will($this->returnValueMap( + $product->expects($this->any()) + ->method('getData') + ->willReturnMap( [ - ['_cache_instance_used_product_attributes', null, []], + ['_cache_instance_used_product_attributes', null, []] ] - )); - - - $productCollection = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection::class - )->setMethods( - [ - 'setFlag', - 'setProductFilter', - 'addStoreFilter', - 'addAttributeToSelect', - 'addFilterByRequiredOptions', - 'setStoreId', - 'addTierPriceData', - 'getIterator', - 'load', - ] - )->disableOriginalConstructor() - ->getMock(); - $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('setProductFilter')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('setFlag')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addTierPriceData')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addFilterByRequiredOptions')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('setStoreId')->with(5)->will($this->returnValue([])); - $productCollection->expects($this->any())->method('getIterator')->willReturn( - new \ArrayIterator([]) - ); - + ); - $this->_productCollectionFactory->expects($this->any())->method('create') - ->will($this->returnValue($productCollection)); + $productCollection->expects($this->atLeastOnce())->method('addAttributeToSelect')->willReturnSelf(); + $productCollection->expects($this->once())->method('setProductFilter')->willReturnSelf(); + $productCollection->expects($this->atLeastOnce())->method('setFlag')->willReturnSelf(); + $productCollection->expects($this->once())->method('addTierPriceData')->willReturnSelf(); + $productCollection->expects($this->once())->method('addFilterByRequiredOptions')->willReturnSelf(); + $productCollection->expects($this->once())->method('setStoreId')->with(5)->willReturn([]); + $productCollection->expects($this->once())->method('getItems')->willReturn([$productCollectionItem]); + + $this->serializer->expects($this->once())->method('unserialize')->willReturn([]); + $this->serializer->expects($this->once()) + ->method('serialize') + ->with([$productCollectionItemData]) + ->willReturn('result'); + + $this->_productCollectionFactory->expects($this->any())->method('create')->willReturn($productCollection); $this->_model->getUsedProducts($product); } /** * @param int $productStore - * @param int $attributeStore * * @dataProvider getConfigurableAttributesAsArrayDataProvider */ - public function testGetConfigurableAttributesAsArray($productStore, $attributeStore) + public function testGetConfigurableAttributesAsArray($productStore) { - $attributeSource = $this->getMockForAbstractClass( - \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource::class, - [], - '', - false, - true, - true, - ['getAllOptions'] - ); - $attributeSource->expects($this->any())->method('getAllOptions')->will($this->returnValue([])); - - $attributeFrontend = $this->getMockForAbstractClass( - \Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend::class, - [], - '', - false, - true, - true, - ['getLabel'] - ); - $attributeFrontend->expects($this->any())->method('getLabel')->will($this->returnValue('Label')); - - $eavAttribute = $this->getMock( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - ['getFrontend', 'getSource', 'getStoreLabel', '__wakeup', 'setStoreId', '__sleep'], - [], - '', - false - ); - $eavAttribute->expects($this->any())->method('getFrontend')->will($this->returnValue($attributeFrontend)); - $eavAttribute->expects($this->any())->method('getSource')->will($this->returnValue($attributeSource)); - $eavAttribute->expects($this->any())->method('getStoreLabel')->will($this->returnValue('Store Label')); - $eavAttribute->expects($this->any())->method('setStoreId')->with($attributeStore); + $attributeSource = $this->getMockBuilder(AbstractSource::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeFrontend = $this->getMockBuilder(AbstractFrontend::class) + ->disableOriginalConstructor() + ->getMock(); + $eavAttribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + + $attributeSource->expects($this->once())->method('getAllOptions')->willReturn([]); + $attributeFrontend->expects($this->once())->method('getLabel')->willReturn('Label'); + $eavAttribute->expects($this->once())->method('getFrontend')->willReturn($attributeFrontend); + $eavAttribute->expects($this->once())->method('getSource')->willReturn($attributeSource); + $eavAttribute->expects($this->atLeastOnce())->method('getStoreLabel')->willReturn('Store Label'); $attribute = $this->getMockBuilder( \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) ->disableOriginalConstructor() ->setMethods(['getProductAttribute', '__wakeup', '__sleep']) ->getMock(); - $attribute->expects($this->any())->method('getProductAttribute')->will($this->returnValue($eavAttribute)); + $attribute->expects($this->any())->method('getProductAttribute')->willReturn($eavAttribute); $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getStoreId', 'getData', 'hasData', '__wakeup', '__sleep']) ->disableOriginalConstructor() ->getMock(); - $product->expects($this->any())->method('getStoreId')->will($this->returnValue($productStore)); - $product->expects($this->any())->method('hasData') + $product->expects($this->atLeastOnce())->method('getStoreId')->willReturn($productStore); + $product->expects($this->atLeastOnce())->method('hasData') ->will( $this->returnValueMap( [ @@ -487,69 +436,56 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function getConfigurableAttributesAsArrayDataProvider() { return [ - [5, 5], - [null, 0], + [5], + [null], ]; } public function testGetConfigurableAttributes() { - $expectedData = [1]; $configurableAttributes = '_cache_instance_configurable_attributes'; /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['getData', 'hasData', 'setData', 'getIdentities', 'getId', 'getStoreId']) + ->setMethods(['getData', 'hasData', 'setData']) ->disableOriginalConstructor() ->getMock(); - $product->expects($this->once())->method('hasData')->with($configurableAttributes)->willReturn(false); - $product->expects($this->once())->method('getStoreId')->willReturn(0); - $product->expects($this->any())->method('getId')->willReturn(0); - $product->expects($this->any())->method('getIdentities')->willReturn(['123']); - $product->expects($this->once())->method('setData')->willReturnSelf(); - $product->expects($this->exactly(2)) - ->method('getData') - ->willReturnMap( - [ - [$configurableAttributes, null, $expectedData], - ['link', null, 1], - ] - ); - $product->expects($this->once())->method('getIdentities')->willReturn([1,2,3]); - $this->entityMetadata->expects($this->once()) - ->method('getLinkField') - ->willReturn('link'); + $product->expects($this->once())->method('hasData')->with($configurableAttributes)->willReturn(false); - $attributeCollection = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class - ) + $attributeCollection = $this->getMockBuilder(Collection::class) ->setMethods(['setProductFilter', 'orderByPosition', 'load']) ->disableOriginalConstructor() ->getMock(); - $attributeCollection->expects($this->any())->method('setProductFilter')->will($this->returnSelf()); - $attributeCollection->expects($this->any())->method('orderByPosition')->will($this->returnSelf()); - $this->_attributeCollectionFactory->expects($this->any())->method('create')->willReturn($attributeCollection); + $attributeCollection->expects($this->once())->method('setProductFilter')->willReturnSelf(); + $attributeCollection->expects($this->once())->method('orderByPosition')->willReturnSelf(); + $attributeCollection->expects($this->once())->method('load')->willReturnSelf(); - $this->extensionAttributesJoinProcessorMock->expects($this->once()) - ->method('process') - ->with( - $this->isInstanceOf(\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class - ) - ); + $this->_attributeCollectionFactory->expects($this->once())->method('create')->willReturn($attributeCollection); + + $product->expects($this->once()) + ->method('setData') + ->with($configurableAttributes, $attributeCollection) + ->willReturnSelf(); + + $product->expects($this->once()) + ->method('getData') + ->with($configurableAttributes) + ->willReturn($attributeCollection); - $this->assertEquals($expectedData, $this->_model->getConfigurableAttributes($product)); + $this->assertEquals($attributeCollection, $this->_model->getConfigurableAttributes($product)); } public function testResetConfigurableAttributes() { $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['unsetData', '__wakeup', '__sleep', 'getStoreId', 'getId']) + ->setMethods(['unsetData']) ->disableOriginalConstructor() ->getMock(); - $product->expects($this->any())->method('unsetData') + $product->expects($this->once()) + ->method('unsetData') ->with('_cache_instance_configurable_attributes') - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertEquals($this->_model, $this->_model->resetConfigurableAttributes($product)); } @@ -629,13 +565,26 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testGetSelectedAttributesInfo() { + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getCustomOption', 'hasData', 'getData']) ->disableOriginalConstructor() ->getMock(); - $optionMock = $this->getMockBuilder( - \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class) - ->setMethods(['getValue']) + $optionMock = $this->getMockBuilder(OptionInterface::class) ->disableOriginalConstructor() ->getMock(); $usedAttributeMock = $this->getMockBuilder( @@ -645,11 +594,10 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->setMethods(['getStoreLabel', 'getSourceModel']) ->disableOriginalConstructor() ->getMock(); - $optionMock->expects($this->once())->method('getValue')->willReturn(serialize($this->attributeData)); + $optionMock->expects($this->once())->method('getValue')->willReturn(json_encode($this->attributeData)); $productMock->expects($this->once())->method('getCustomOption')->with('attributes')->willReturn($optionMock); $productMock->expects($this->once())->method('hasData')->willReturn(true); $productMock->expects($this->at(2))->method('getData')->willReturn(true); @@ -662,24 +610,25 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $this->_model->getSelectedAttributesInfo($productMock), [ [ - 'label' => 'attr_store_label', - 'value' => '', - 'option_id' => 1, - 'option_value' => '' + 'label' => 'attr_store_label', + 'value' => '', + 'option_id' => 1, + 'option_value' => '' ] ] ); } + /** + * @covers \Magento\ConfigurableProduct\Model\Product\Type\Configurable::checkProductBuyState() + */ public function testCheckProductBuyState() { - $this->markTestIncomplete('checkProductBuyState() method is not complete in parent class'); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getCustomOption', 'getSkipCheckRequiredOption']) + ->setMethods(['getSkipCheckRequiredOption', 'getCustomOption']) ->disableOriginalConstructor() ->getMock(); $optionMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item\Option::class) - ->setMethods(['getValue']) ->disableOriginalConstructor() ->getMock(); @@ -690,24 +639,30 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->willReturn($optionMock); $optionMock->expects($this->once()) ->method('getValue') - ->willReturn(serialize(['super_attribute' => ['test_key' => 'test_value', 'empty_key' => '']])); + ->willReturn(json_encode(['super_attribute' => ['test_key' => 'test_value', 'empty_key' => '']])); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); $this->assertEquals($this->_model, $this->_model->checkProductBuyState($productMock)); } /** + * @covers \Magento\ConfigurableProduct\Model\Product\Type\Configurable::checkProductBuyState() * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage You need to choose options for your item. */ public function testCheckProductBuyStateException() { - $this->markTestIncomplete('checkProductBuyState() method is not complete in parent class'); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getCustomOption', 'getSkipCheckRequiredOption']) + ->setMethods(['getSkipCheckRequiredOption', 'getCustomOption']) ->disableOriginalConstructor() ->getMock(); $optionMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item\Option::class) - ->setMethods(['getValue']) ->disableOriginalConstructor() ->getMock(); @@ -716,7 +671,14 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->method('getCustomOption') ->with('info_buyRequest') ->willReturn($optionMock); - $optionMock->expects($this->once())->method('getValue')->willReturn(serialize([])); + $optionMock->expects($this->once())->method('getValue')->willReturn(json_encode([])); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); $this->_model->checkProductBuyState($productMock); } @@ -724,47 +686,29 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testGetProductByAttributesReturnUsedProduct() { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'hasData', 'getData', 'getResource', 'getAttributeSetId']) ->disableOriginalConstructor() ->getMock(); $firstItemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getId']) ->disableOriginalConstructor() ->getMock(); $usedProductMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getData']) ->disableOriginalConstructor() ->getMock(); $eavAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) - ->setMethods(['__wakeup', 'getId', 'getAttributeCode']) ->disableOriginalConstructor() ->getMock(); - $productCollection = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection::class - ) - ->setMethods( - [ - 'setFlag', - 'setProductFilter', - 'addStoreFilter', - 'addAttributeToSelect', - 'addAttributeToFilter', - 'getFirstItem', - 'getIterator' - ] - ) + $productCollection = $this->getMockBuilder(ProductCollection::class) ->disableOriginalConstructor() ->getMock(); - $this->_productCollectionFactory->expects($this->any())->method('create') - ->will($this->returnValue($productCollection)); - $productCollection->expects($this->any())->method('setProductFilter')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('setFlag')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addAttributeToFilter')->will($this->returnSelf()); + $this->_productCollectionFactory->expects($this->once())->method('create')->willReturn($productCollection); + $productCollection->expects($this->once())->method('setProductFilter')->willReturnSelf(); + $productCollection->expects($this->once())->method('setFlag')->willReturnSelf(); + $productCollection->expects($this->once())->method('addAttributeToSelect')->willReturnSelf(); + $productCollection->expects($this->once())->method('addAttributeToFilter')->willReturnSelf(); $productCollection->expects($this->once())->method('getFirstItem')->willReturn($firstItemMock); - $productCollection->expects($this->any())->method('getIterator')->willReturn( - new \ArrayIterator([$usedProductMock]) + $productCollection->expects($this->once())->method('getIterator')->willReturn( + new \ArrayIterator([$usedProductMock]) ); $firstItemMock->expects($this->once())->method('getId')->willReturn(false); @@ -789,42 +733,24 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testGetProductByAttributesReturnFirstItem() { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'hasData', 'getData', 'getResource', 'getAttributeSetId']) ->disableOriginalConstructor() ->getMock(); $firstItemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getId']) ->disableOriginalConstructor() ->getMock(); - $productCollection = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection::class - ) - ->setMethods( - [ - 'setFlag', - 'setProductFilter', - 'addStoreFilter', - 'addAttributeToSelect', - 'addAttributeToFilter', - 'getFirstItem', - ] - ) + $productCollection = $this->getMockBuilder(ProductCollection::class) ->disableOriginalConstructor() ->getMock(); - $this->_productCollectionFactory->expects($this->any())->method('create') - ->will($this->returnValue($productCollection)); - $productCollection->expects($this->any())->method('setProductFilter')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('setFlag')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addAttributeToFilter')->will($this->returnSelf()); + $this->_productCollectionFactory->expects($this->any())->method('create')->willReturn($productCollection); + $productCollection->expects($this->once())->method('setProductFilter')->willReturnSelf(); + $productCollection->expects($this->once())->method('setFlag')->willReturnSelf(); + $productCollection->expects($this->once())->method('addAttributeToSelect')->willReturnSelf(); + $productCollection->expects($this->once())->method('addAttributeToFilter')->willReturnSelf(); $productCollection->expects($this->once())->method('getFirstItem')->willReturn($firstItemMock); - $firstItemMock->expects(static::once()) - ->method('getId') - ->willReturn(3); + $firstItemMock->expects($this->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) @@ -834,11 +760,10 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testSetImageFromChildProduct() { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'hasData', 'getData', 'setImage']) + ->setMethods(['hasData', 'getData', 'setImage']) ->disableOriginalConstructor() ->getMock(); $childProductMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getData']) ->disableOriginalConstructor() ->getMock(); 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 ab4f5e3be7cfac0185c21086fa7c82bf85b3f8fe..227c9cc36b308424aa1ebd472e331353fa62ef32 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 @@ -43,6 +43,9 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase */ private $productOptionExtensionAttributes; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $serializer; + protected function setUp() { $this->objectFactoryMock = $this->getMock( @@ -84,11 +87,36 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase ['setConfigurableItemOptions'] ); + $this->serializer = $this->getMock( + \Magento\Framework\Serialize\Serializer\Json::class, + [], + [], + '', + false + ); + + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $this->model = new \Magento\ConfigurableProduct\Model\Quote\Item\CartItemProcessor( $this->objectFactoryMock, $this->optionFactoryMock, $this->optionExtensionFactoryMock, - $this->optionValueFactoryMock + $this->optionValueFactoryMock, + $this->serializer ); } @@ -174,7 +202,7 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase '', false ); - $customOption->expects($this->once())->method('getValue')->willReturn(serialize([$optionId => $optionValue])); + $customOption->expects($this->once())->method('getValue')->willReturn(json_encode([$optionId => $optionValue])); $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); $productMock->expects($this->once())->method('getCustomOption')->with('attributes')->willReturn($customOption); @@ -227,7 +255,7 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase '', false ); - $customOption->expects($this->once())->method('getValue')->willReturn(serialize([$optionId => $optionValue])); + $customOption->expects($this->once())->method('getValue')->willReturn(json_encode([$optionId => $optionValue])); $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); $productMock->expects($this->once())->method('getCustomOption')->with('attributes')->willReturn($customOption); diff --git a/app/code/Magento/Downloadable/Model/Product/Type.php b/app/code/Magento/Downloadable/Model/Product/Type.php index 64c00d208b86fc233fc2908dca8afca644ec667d..1ab363d5b2b583df219ee931a05f01f858fbb31a 100644 --- a/app/code/Magento/Downloadable/Model/Product/Type.php +++ b/app/code/Magento/Downloadable/Model/Product/Type.php @@ -85,6 +85,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\Virtual * @param \Magento\Downloadable\Model\LinkFactory $linkFactory * @param TypeHandler\TypeHandlerInterface $typeHandler * @param JoinProcessorInterface $extensionAttributesJoinProcessor + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -104,7 +105,8 @@ class Type extends \Magento\Catalog\Model\Product\Type\Virtual \Magento\Downloadable\Model\SampleFactory $sampleFactory, \Magento\Downloadable\Model\LinkFactory $linkFactory, \Magento\Downloadable\Model\Product\TypeHandler\TypeHandlerInterface $typeHandler, - JoinProcessorInterface $extensionAttributesJoinProcessor + JoinProcessorInterface $extensionAttributesJoinProcessor, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_sampleResFactory = $sampleResFactory; $this->_linkResource = $linkResource; @@ -123,7 +125,8 @@ class Type extends \Magento\Catalog\Model\Product\Type\Virtual $filesystem, $coreRegistry, $logger, - $productRepository + $productRepository, + $serializer ); } @@ -249,14 +252,17 @@ class Type extends \Magento\Catalog\Model\Product\Type\Virtual parent::checkProductBuyState($product); $option = $product->getCustomOption('info_buyRequest'); if ($option instanceof \Magento\Quote\Model\Quote\Item\Option) { - $buyRequest = new \Magento\Framework\DataObject(unserialize($option->getValue())); + $buyRequest = new \Magento\Framework\DataObject($this->serializer->unserialize($option->getValue())); if (!$buyRequest->hasLinks()) { if (!$product->getLinksPurchasedSeparately()) { $allLinksIds = $this->_linksFactory->create()->addProductToFilter( $product->getEntityId() )->getAllIds(); $buyRequest->setLinks($allLinksIds); - $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData())); + $product->addCustomOption( + 'info_buyRequest', + $this->serializer->serialize($buyRequest->getData()) + ); } else { throw new \Magento\Framework\Exception\LocalizedException(__('Please specify product link(s).')); } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Product/TypeTest.php index 61d66d47bc2815bc37f33c1550dfab9f8e3bdbaf..b93a6a8386116f16ed5048226d29a784af6b74b2 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Product/TypeTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Product/TypeTest.php @@ -6,6 +6,7 @@ namespace Magento\Downloadable\Test\Unit\Model\Product; use Magento\Downloadable\Model\Product\TypeHandler\TypeHandlerInterface; +use Magento\Framework\Serialize\Serializer\Json; /** * Class TypeTest @@ -30,12 +31,27 @@ class TypeTest extends \PHPUnit_Framework_TestCase */ private $product; + /** + * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializerMock; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + * @var \Magento\Downloadable\Model\ResourceModel\Link\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $linksFactory; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { - $objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $eventManager = $this->getMock(\Magento\Framework\Event\ManagerInterface::class, [], [], '', false); $fileStorageDb = $this->getMockBuilder( \Magento\MediaStorage\Helper\File\Storage\Database::class @@ -54,9 +70,9 @@ class TypeTest extends \PHPUnit_Framework_TestCase false ); $linkResource = $this->getMock(\Magento\Downloadable\Model\ResourceModel\Link::class, [], [], '', false); - $linksFactory = $this->getMock( + $this->linksFactory = $this->getMock( \Magento\Downloadable\Model\ResourceModel\Link\CollectionFactory::class, - [], + ['create'], [], '', false @@ -81,6 +97,30 @@ class TypeTest extends \PHPUnit_Framework_TestCase ); $resourceProductMock->expects($this->any())->method('getEntityType')->will($this->returnValue($entityTypeMock)); + $this->serializerMock = $this->getMock( + Json::class, + [], + ['serialize', 'unserialize'], + '', + false + ); + + $this->serializerMock->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + + $this->serializerMock->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $this->product = $this->getMock( \Magento\Catalog\Model\Product::class, [ @@ -94,6 +134,9 @@ class TypeTest extends \PHPUnit_Framework_TestCase 'setLinksExist', 'getDownloadableLinks', '__wakeup', + 'getCustomOption', + 'addCustomOption', + 'getEntityId' ], [], '', @@ -109,8 +152,6 @@ class TypeTest extends \PHPUnit_Framework_TestCase $this->product->expects($this->any())->method('setTypeHasOptions')->with($this->equalTo(false)); $this->product->expects($this->any())->method('setLinksExist')->with($this->equalTo(false)); $this->product->expects($this->any())->method('canAffectOptions')->with($this->equalTo(true)); - $this->product->expects($this->any())->method('getLinksPurchasedSeparately')->will($this->returnValue(true)); - $this->product->expects($this->any())->method('getLinksPurchasedSeparately')->will($this->returnValue(true)); $eavConfigMock = $this->getMock(\Magento\Eav\Model\Config::class, ['getEntityAttributeCodes'], [], '', false); $eavConfigMock->expects($this->any()) @@ -123,7 +164,7 @@ class TypeTest extends \PHPUnit_Framework_TestCase ->setMethods(['save']) ->getMock(); - $this->target = $objectHelper->getObject( + $this->target = $this->objectManager->getObject( \Magento\Downloadable\Model\Product\Type::class, [ 'eventManager' => $eventManager, @@ -134,13 +175,13 @@ class TypeTest extends \PHPUnit_Framework_TestCase 'productFactory' => $productFactoryMock, 'sampleResFactory' => $sampleResFactory, 'linkResource' => $linkResource, - 'linksFactory' => $linksFactory, + 'linksFactory' => $this->linksFactory, 'samplesFactory' => $samplesFactory, 'sampleFactory' => $sampleFactory, 'linkFactory' => $linkFactory, 'eavConfig' => $eavConfigMock, 'typeHandler' => $this->typeHandler, - + 'serializer' => $this->serializerMock ] ); } @@ -157,9 +198,79 @@ class TypeTest extends \PHPUnit_Framework_TestCase public function testHasLinks() { + $this->product->expects($this->any())->method('getLinksPurchasedSeparately')->will($this->returnValue(true)); $this->product->expects($this->exactly(2)) ->method('getDownloadableLinks') ->willReturn(['link1', 'link2']); $this->assertTrue($this->target->hasLinks($this->product)); } + + public function testCheckProductBuyState() + { + $optionMock = $this->getMock(\Magento\Quote\Model\Quote\Item\Option::class, [], ['getValue'], '', false); + + $optionMock->expects($this->any())->method('getValue')->will($this->returnValue('{}')); + + $this->product->expects($this->any()) + ->method('getCustomOption') + ->with('info_buyRequest') + ->will($this->returnValue($optionMock)); + + $this->product->expects($this->any()) + ->method('getLinksPurchasedSeparately') + ->will($this->returnValue(false)); + + $this->product->expects($this->any()) + ->method('getEntityId') + ->will($this->returnValue(123)); + + $linksCollectionMock = $this->getMock( + \Magento\Downloadable\Model\ResourceModel\Link\Collection::class, + [], + ['addProductToFilter', 'getAllIds'], + '', + false + ); + + $linksCollectionMock->expects($this->once()) + ->method('addProductToFilter') + ->with(123) + ->will($this->returnSelf()); + + $linksCollectionMock->expects($this->once()) + ->method('getAllIds') + ->will($this->returnValue([1, 2, 3])); + + $this->linksFactory->expects($this->any()) + ->method('create') + ->will($this->returnValue($linksCollectionMock)); + + $this->product->expects($this->once()) + ->method('addCustomOption') + ->with('info_buyRequest', '{"links":[1,2,3]}'); + + $this->target->checkProductBuyState($this->product); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Please specify product link(s). + */ + public function testCheckProductBuyStateException() + { + $optionMock = $this->getMock(\Magento\Quote\Model\Quote\Item\Option::class, [], ['getValue'], '', false); + + $optionMock->expects($this->any())->method('getValue')->will($this->returnValue('{}')); + + $this->product->expects($this->any()) + ->method('getCustomOption') + ->with('info_buyRequest') + ->will($this->returnValue($optionMock)); + + $this->product->expects($this->any()) + ->method('getLinksPurchasedSeparately') + ->will($this->returnValue(true)); + + $this->target->checkProductBuyState($this->product); + } } diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php index b3e7bf2bc3925224cace683b4aba2fe57b29e63e..9b3a4ef42fc5e3499241f00b608f8af86f22e4bf 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php @@ -5,12 +5,10 @@ */ namespace Magento\Eav\Model\ResourceModel\Entity\Attribute; -use Magento\Framework\Serialize\SerializerInterface; - class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** - * EAV cache ids + * EAV cache id */ const ATTRIBUTES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_BY_SET_ID'; @@ -25,16 +23,12 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $eavConfig; /** - * @var SerializerInterface - */ - private $serializer; - - /** + * Constructor + * * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param GroupFactory $attrGroupFactory * @param \Magento\Eav\Model\Config $eavConfig - * @param string $connectionName - * @codeCoverageIgnore + * @param string|null $connectionName */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -235,19 +229,4 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb } return $connection->fetchAll($select, $bind); } - - /** - * Get serializer - * - * @return SerializerInterface - * @deprecated - */ - private function getSerializer() - { - if (null === $this->serializer) { - $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() - ->get(SerializerInterface::class); - } - return $this->serializer; - } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/SetTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/SetTest.php index e00a8ee97648c0768c7dac4c10359e1378856674..8a588a6cbe76c4c8f298c1c56e8a577ac30678fa 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/SetTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/Attribute/SetTest.php @@ -7,7 +7,7 @@ namespace Magento\Eav\Test\Unit\Model\ResourceModel\Entity\Attribute; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Serialize\Serializer\Json; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -50,7 +50,7 @@ class SetTest extends \PHPUnit_Framework_TestCase protected $relationProcessor; /** - * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Json|\PHPUnit_Framework_MockObject_MockObject */ private $serializerMock; @@ -88,7 +88,7 @@ class SetTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $this->serializerMock = $this->getMock(SerializerInterface::class); + $this->serializerMock = $this->getMock(Json::class); $attributeGroupFactoryMock = $this->getMock( \Magento\Eav\Model\ResourceModel\Entity\Attribute\GroupFactory::class, diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 74a7e8112aa0ddb528b46168a16df15310fd227c..08dd1553d99b1208d7cc8e10bc89b5f814b3a0e2 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -95,7 +95,7 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType * @param \Magento\Catalog\Model\Product\Attribute\Source\Status $catalogProductStatus * @param \Magento\Framework\App\State $appState * @param \Magento\Msrp\Helper\Data $msrpData - * + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -112,7 +112,8 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Model\Product\Attribute\Source\Status $catalogProductStatus, \Magento\Framework\App\State $appState, - \Magento\Msrp\Helper\Data $msrpData + \Magento\Msrp\Helper\Data $msrpData, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->productLinks = $catalogProductLink; $this->_storeManager = $storeManager; @@ -128,7 +129,8 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType $filesystem, $coreRegistry, $logger, - $productRepository + $productRepository, + $serializer ); } @@ -384,7 +386,7 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType $_result[0]->addCustomOption('product_type', self::TYPE_CODE, $product); $_result[0]->addCustomOption( 'info_buyRequest', - serialize( + $this->serializer->serialize( [ 'super_product_config' => [ 'product_type' => self::TYPE_CODE, @@ -402,7 +404,7 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType if (!$isStrictProcessMode || count($associatedProductsInfo)) { $product->addCustomOption('product_type', self::TYPE_CODE, $product); - $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData())); + $product->addCustomOption('info_buyRequest', $this->serializer->serialize($buyRequest->getData())); $products[] = $product; } diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php index 3efc0b7058be1e8fa803d5dc6a43cd66bb03d145..42afe71e16c30deb8d9fdced21bcd57ca7da46e3 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php @@ -37,6 +37,11 @@ class GroupedTest extends \PHPUnit_Framework_TestCase */ protected $objectHelper; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + protected function setUp() { $this->objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -67,6 +72,10 @@ class GroupedTest extends \PHPUnit_Framework_TestCase '', false ); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(['serialize']) + ->getMockForAbstractClass(); + $this->_model = $this->objectHelper->getObject( \Magento\GroupedProduct\Model\Product\Type\Grouped::class, [ @@ -77,7 +86,8 @@ class GroupedTest extends \PHPUnit_Framework_TestCase 'logger' => $logger, 'productFactory' => $productFactoryMock, 'catalogProductLink' => $this->catalogProductLink, - 'catalogProductStatus' => $this->productStatusMock + 'catalogProductStatus' => $this->productStatusMock, + 'serializer' => $this->serializer ] ); } @@ -429,6 +439,9 @@ class GroupedTest extends \PHPUnit_Framework_TestCase ->expects($this->atLeastOnce()) ->method('getData') ->will($this->returnValue($associatedProducts)); + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturn(json_encode($buyRequest->getData())); $this->assertEquals( [0 => $this->product], @@ -540,6 +553,10 @@ class GroupedTest extends \PHPUnit_Framework_TestCase $buyRequest = new \Magento\Framework\DataObject(); $buyRequest->setSuperGroup([$associatedId => 1]); + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturn(json_encode($buyRequest->getData())); + $cached = true; $this->product ->expects($this->atLeastOnce()) @@ -583,6 +600,10 @@ class GroupedTest extends \PHPUnit_Framework_TestCase $buyRequest = new \Magento\Framework\DataObject(); $buyRequest->setSuperGroup([$associatedId => 1]); + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturn(json_encode($buyRequest->getData())); + $cached = true; $this->product ->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Quote/Model/CustomerManagement.php b/app/code/Magento/Quote/Model/CustomerManagement.php index b796ebe9d0db4c0305c9e1d9c8e09838540ba820..04707e152674808b45c72c695861bd11b42cf13a 100644 --- a/app/code/Magento/Quote/Model/CustomerManagement.php +++ b/app/code/Magento/Quote/Model/CustomerManagement.php @@ -62,8 +62,6 @@ class CustomerManagement $quote->getPasswordHash() ); $quote->setCustomer($customer); - } else { - $this->customerRepository->save($customer); } if (!$quote->getBillingAddress()->getId() && $customer->getDefaultBilling()) { $quote->getBillingAddress()->importCustomerAddressData( diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index f1764da5e555b06d5055fe7e1ed7bf9cbad065ad..b1126549e3bcfbdb2ddcd8b3f179f1b6c9ed26e6 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -8,7 +8,8 @@ namespace Magento\Quote\Model\Quote; use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\RegionInterfaceFactory; -use Magento\Quote\Api\Data\AddressInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; /** * Sales Quote address model @@ -233,6 +234,11 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements */ protected $totalsReader; + /** + * @var Json + */ + private $serializer; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -266,6 +272,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data + * @param Json $serializer * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -301,7 +308,8 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements \Magento\Quote\Model\Quote\TotalsReader $totalsReader, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Json $serializer = null ) { $this->_scopeConfig = $scopeConfig; $this->_addressItemFactory = $addressItemFactory; @@ -320,6 +328,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements $this->attributeList = $attributeList; $this->totalsCollector = $totalsCollector; $this->totalsReader = $totalsReader; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); parent::__construct( $context, $registry, @@ -1143,7 +1152,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements */ public function getAppliedTaxes() { - return unserialize($this->getData('applied_taxes')); + return $this->serializer->unserialize($this->getData('applied_taxes')); } /** @@ -1154,7 +1163,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements */ public function setAppliedTaxes($data) { - return $this->setData('applied_taxes', serialize($data)); + return $this->setData('applied_taxes', $this->serializer->serialize($data)); } /******************************* Start Total Collector Interface *******************************************/ diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index aeaa71fb8daad7c4fa8d832e66cad83dadb45f34..cd7aa54e1497b68a512f23e61d176afbd605f797 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -17,6 +17,28 @@ class Total extends \Magento\Framework\DataObject */ protected $baseTotalAmounts; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + + /** + * Constructor + * + * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + */ + public function __construct( + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null + ) { + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); + parent::__construct($data); + } + /** * Set total amount value * @@ -159,7 +181,7 @@ class Total extends \Magento\Framework\DataObject { $fullInfo = $this->getData('full_info'); if (is_string($fullInfo)) { - $fullInfo = unserialize($fullInfo); + $fullInfo = $this->serializer->unserialize($fullInfo); } return $fullInfo; } diff --git a/app/code/Magento/Quote/Model/Quote/Item.php b/app/code/Magento/Quote/Model/Quote/Item.php index ac3a1109d7172cee98f38a7bf5ec5f2a71527b00..a8aa2b2dc273bbb4d750eff28b8fda5436edde99 100644 --- a/app/code/Magento/Quote/Model/Quote/Item.php +++ b/app/code/Magento/Quote/Model/Quote/Item.php @@ -175,6 +175,13 @@ class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Mage */ protected $stockRegistry; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -191,6 +198,7 @@ class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Mage * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * + * @param \Magento\Framework\Serialize\Serializer\Json $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -207,13 +215,16 @@ class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Mage \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_errorInfos = $statusListFactory->create(); $this->_localeFormat = $localeFormat; $this->_itemOptionFactory = $itemOptionFactory; $this->quoteItemCompare = $quoteItemCompare; $this->stockRegistry = $stockRegistry; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct( $context, $registry, @@ -808,7 +819,8 @@ class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Mage public function getBuyRequest() { $option = $this->getOptionByCode('info_buyRequest'); - $buyRequest = new \Magento\Framework\DataObject($option ? unserialize($option->getValue()) : []); + $data = $option ? $this->serializer->unserialize($option->getValue()) : []; + $buyRequest = new \Magento\Framework\DataObject($data); // Overwrite standard buy request qty, because item qty could have changed since adding to quote $buyRequest->setOriginalQty($buyRequest->getQty())->setQty($this->getQty() * 1); diff --git a/app/code/Magento/Quote/Model/Quote/Item/Compare.php b/app/code/Magento/Quote/Model/Quote/Item/Compare.php index 10bb712025462fc11f99c2f55206c6cf10f8b854..ca788f0985ca2d580457aef7c196afdc38516738 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Compare.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Compare.php @@ -12,6 +12,22 @@ use Magento\Quote\Model\Quote\Item; */ class Compare { + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + + /** + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + */ + public function __construct(\Magento\Framework\Serialize\Serializer\Json $serializer = null) + { + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); + } + /** * Returns option values adopted to compare * @@ -20,8 +36,8 @@ class Compare */ protected function getOptionValues($value) { - if (is_string($value) && is_array(@unserialize($value))) { - $value = @unserialize($value); + if (is_string($value) && is_array($this->serializer->unserialize($value))) { + $value = $this->serializer->unserialize($value); unset($value['qty'], $value['uenc']); $value = array_filter($value, function ($optionValue) { return !empty($optionValue); diff --git a/app/code/Magento/Quote/Model/Quote/Item/Updater.php b/app/code/Magento/Quote/Model/Quote/Item/Updater.php index 45f9d3d90e39e0cd517853cf218b5ac8b08f3858..1ea7676cca405a5fb1abc58cdb341b6b51d39f97 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Updater.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Updater.php @@ -32,19 +32,30 @@ class Updater */ protected $objectFactory; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param ProductFactory $productFactory * @param FormatInterface $localeFormat * @param ObjectFactory $objectFactory + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( ProductFactory $productFactory, FormatInterface $localeFormat, - ObjectFactory $objectFactory + ObjectFactory $objectFactory, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->productFactory = $productFactory; $this->localeFormat = $localeFormat; $this->objectFactory = $objectFactory; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -104,7 +115,7 @@ class Updater if ($infoBuyRequest) { $infoBuyRequest->setCustomPrice($itemPrice); - $infoBuyRequest->setValue(serialize($infoBuyRequest->getData())); + $infoBuyRequest->setValue($this->serializer->serialize($infoBuyRequest->getData())); $infoBuyRequest->setCode('info_buyRequest'); $infoBuyRequest->setProduct($item->getProduct()); @@ -128,7 +139,7 @@ class Updater if ($infoBuyRequest->hasData('custom_price')) { $infoBuyRequest->unsetData('custom_price'); - $infoBuyRequest->setValue(serialize($infoBuyRequest->getData())); + $infoBuyRequest->setValue($this->serializer->serialize($infoBuyRequest->getData())); $infoBuyRequest->setCode('info_buyRequest'); $infoBuyRequest->setProduct($item->getProduct()); $item->addOption($infoBuyRequest); diff --git a/app/code/Magento/Quote/Model/Quote/Payment.php b/app/code/Magento/Quote/Model/Quote/Payment.php index 612802ccd279d58e3aa0349a8fb1da1ea4213f60..03acf1d6500e10ce8bd27194524658ba4b73982d 100644 --- a/app/code/Magento/Quote/Model/Quote/Payment.php +++ b/app/code/Magento/Quote/Model/Quote/Payment.php @@ -65,6 +65,13 @@ class Payment extends \Magento\Payment\Model\Info implements PaymentInterface */ private $additionalChecks; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -77,6 +84,7 @@ class Payment extends \Magento\Payment\Model\Info implements PaymentInterface * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param array $additionalChecks + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -90,10 +98,13 @@ class Payment extends \Magento\Payment\Model\Info implements PaymentInterface \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - array $additionalChecks = [] + array $additionalChecks = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->methodSpecificationFactory = $methodSpecificationFactory; $this->additionalChecks = $additionalChecks; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct( $context, $registry, @@ -326,7 +337,7 @@ class Payment extends \Magento\Payment\Model\Info implements PaymentInterface { $additionalDataValue = $this->getData(self::KEY_ADDITIONAL_DATA); if (is_string($additionalDataValue)) { - $additionalData = @unserialize($additionalDataValue); + $additionalData = $this->serializer->unserialize($additionalDataValue); if (is_array($additionalData)) { return $additionalData; } diff --git a/app/code/Magento/Quote/Setup/ConvertSerializedDataToJson.php b/app/code/Magento/Quote/Setup/ConvertSerializedDataToJson.php new file mode 100644 index 0000000000000000000000000000000000000000..7b3da6ba95c69a77b6661cfa562837a51a4aba83 --- /dev/null +++ b/app/code/Magento/Quote/Setup/ConvertSerializedDataToJson.php @@ -0,0 +1,140 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Quote\Setup; + +use Magento\Framework\DB\FieldDataConverterFactory; +use Magento\Framework\DB\Select\QueryModifierFactory; +use Magento\Framework\DB\Query\Generator; + +/** + * Convert serialized data in quote tables to JSON + */ +class ConvertSerializedDataToJson +{ + /** + * @var QuoteSetup + */ + private $quoteSetup; + + /** + * @var FieldDataConverterFactory + */ + private $fieldDataConverterFactory; + + /** + * @var QueryModifierFactory + */ + private $queryModifierFactory; + + /** + * @var Generator + */ + private $queryGenerator; + + /** + * Constructor + * + * @param QuoteSetup $quoteSetup + * @param FieldDataConverterFactory $fieldDataConverterFactory + * @param QueryModifierFactory $queryModifierFactory + * @param Generator $queryGenerator + */ + public function __construct( + QuoteSetup $quoteSetup, + FieldDataConverterFactory $fieldDataConverterFactory, + QueryModifierFactory $queryModifierFactory, + Generator $queryGenerator + ) { + $this->quoteSetup = $quoteSetup; + $this->fieldDataConverterFactory = $fieldDataConverterFactory; + $this->queryModifierFactory = $queryModifierFactory; + $this->queryGenerator = $queryGenerator; + } + + /** + * Convert data for additional_information field in quote_payment table from serialized + * to JSON format + * + * @return void + * @throws \InvalidArgumentException + */ + public function convert() + { + $fieldDataConverter = $this->fieldDataConverterFactory->create( + \Magento\Framework\DB\DataConverter\SerializedToJson::class + ); + $fieldDataConverter->convert( + $this->quoteSetup->getConnection(), + $this->quoteSetup->getTable('quote_payment'), + 'payment_id', + 'additional_information' + ); + $fieldDataConverter->convert( + $this->quoteSetup->getConnection(), + $this->quoteSetup->getTable('quote_address'), + 'address_id', + 'applied_taxes' + ); + $queryModifier = $this->queryModifierFactory->create( + 'in', + [ + 'values' => [ + 'code' => [ + 'parameters', + 'info_buyRequest', + 'attributes', + 'bundle_option_ids', + 'bundle_selection_ids', + 'bundle_selection_attributes', + ] + ] + ] + ); + $fieldDataConverter->convert( + $this->quoteSetup->getConnection(), + $this->quoteSetup->getTable('quote_item_option'), + 'option_id', + 'value', + $queryModifier + ); + $select = $this->quoteSetup->getSetup() + ->getConnection() + ->select() + ->from( + $this->quoteSetup->getSetup() + ->getTable('catalog_product_option'), + ['option_id'] + ) + ->where('type = ?', 'file'); + $iterator = $this->queryGenerator->generate('option_id', $select); + foreach ($iterator as $selectByRange) { + $codes = $this->quoteSetup->getSetup() + ->getConnection() + ->fetchCol($selectByRange); + $codes = array_map( + function ($id) { + return 'option_' . $id; + }, + $codes + ); + $queryModifier = $this->queryModifierFactory->create( + 'in', + [ + 'values' => [ + 'code' => $codes + ] + ] + ); + $fieldDataConverter->convert( + $this->quoteSetup->getConnection(), + $this->quoteSetup->getTable('quote_item_option'), + 'option_id', + 'value', + $queryModifier + ); + } + } +} diff --git a/app/code/Magento/Quote/Setup/QuoteSetup.php b/app/code/Magento/Quote/Setup/QuoteSetup.php index 1ef5f4b0dae2ee91a9580ae9097f6d129ce8f24e..cbd357d537fb12e2f42037a432d138012dfe446d 100644 --- a/app/code/Magento/Quote/Setup/QuoteSetup.php +++ b/app/code/Magento/Quote/Setup/QuoteSetup.php @@ -13,7 +13,8 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; /** - * Setup Model of Quote Module + * Quote module setup class + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @codeCoverageIgnore */ @@ -75,9 +76,9 @@ class QuoteSetup extends EavSetup */ protected function _flatTableExist($table) { - $tablesList = $this->getSetup()->getConnection(self::$connectionName)->listTables(); + $tablesList = $this->getConnection()->listTables(); return in_array( - strtoupper($this->getSetup()->getTable($table, self::$connectionName)), + strtoupper($this->getTable($table)), array_map('strtoupper', $tablesList) ); } @@ -115,15 +116,14 @@ class QuoteSetup extends EavSetup */ protected function _addFlatAttribute($table, $attribute, $attr) { - $tableInfo = $this->getSetup() - ->getConnection(self::$connectionName) - ->describeTable($this->getSetup()->getTable($table, self::$connectionName)); + $tableInfo = $this->getConnection() + ->describeTable($this->getTable($table)); if (isset($tableInfo[$attribute])) { return $this; } $columnDefinition = $this->_getAttributeColumnDefinition($attribute, $attr); - $this->getSetup()->getConnection(self::$connectionName)->addColumn( - $this->getSetup()->getTable($table, self::$connectionName), + $this->getConnection()->addColumn( + $this->getTable($table), $attribute, $columnDefinition ); @@ -195,4 +195,25 @@ class QuoteSetup extends EavSetup { return $this->_encryptor; } + + /** + * Get quote connection + * + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + public function getConnection() + { + return $this->getSetup()->getConnection(self::$connectionName); + } + + /** + * Get table name + * + * @param string $table + * @return string + */ + public function getTable($table) + { + return $this->getSetup()->getTable($table, self::$connectionName); + } } diff --git a/app/code/Magento/Quote/Setup/UpgradeData.php b/app/code/Magento/Quote/Setup/UpgradeData.php new file mode 100644 index 0000000000000000000000000000000000000000..5b9e3482081cd24d358fd2fa165b87c8081c7f75 --- /dev/null +++ b/app/code/Magento/Quote/Setup/UpgradeData.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Quote\Setup; + +use Magento\Framework\Setup\UpgradeDataInterface; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; + +class UpgradeData implements UpgradeDataInterface +{ + /** + * @var QuoteSetupFactory + */ + private $quoteSetupFactory; + + /** + * @var ConvertSerializedDataToJsonFactory + */ + private $convertSerializedDataToJsonFactory; + + /** + * Constructor + * + * @param QuoteSetupFactory $quoteSetupFactory + * @param ConvertSerializedDataToJsonFactory $convertSerializedDataToJsonFactory + */ + public function __construct( + QuoteSetupFactory $quoteSetupFactory, + ConvertSerializedDataToJsonFactory $convertSerializedDataToJsonFactory + ) { + $this->quoteSetupFactory = $quoteSetupFactory; + $this->convertSerializedDataToJsonFactory = $convertSerializedDataToJsonFactory; + } + + /** + * {@inheritdoc} + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + if (version_compare($context->getVersion(), '2.0.4', '<')) { + $quoteSetup = $this->quoteSetupFactory->create(['setup' => $setup]); + $this->convertSerializedDataToJsonFactory->create(['quoteSetup' => $quoteSetup]) + ->convert(); + } + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/CustomerManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/CustomerManagementTest.php index 600bf1723a5c091a7879c3c1eb706c261f197c18..a1caac3473ccb3a352af6be8e64e36c4c6061cf3 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/CustomerManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/CustomerManagementTest.php @@ -158,4 +158,34 @@ class CustomerManagementTest extends \PHPUnit_Framework_TestCase ->willReturn($this->customerMock); $this->customerManagement->populateCustomerInfo($this->quoteMock); } + + public function testPopulateCustomerInfoForExistingCustomer() + { + $this->quoteMock->expects($this->once()) + ->method('getCustomer') + ->willReturn($this->customerMock); + $this->customerMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(1); + $this->customerMock->expects($this->atLeastOnce()) + ->method('getDefaultBilling') + ->willReturn(100500); + $this->quoteMock->expects($this->atLeastOnce()) + ->method('getBillingAddress') + ->willReturn($this->quoteAddressMock); + $this->quoteMock->expects($this->atLeastOnce()) + ->method('getShippingAddress') + ->willReturn($this->quoteAddressMock); + $this->quoteAddressMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(null); + $this->customerAddressRepositoryMock->expects($this->atLeastOnce()) + ->method('getById') + ->with(100500) + ->willReturn($this->customerAddressMock); + $this->quoteAddressMock->expects($this->atLeastOnce()) + ->method('importCustomerAddressData') + ->willReturnSelf(); + $this->customerManagement->populateCustomerInfo($this->quoteMock); + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php index efe6a81ce9242f510561f54cd6b484a336b37dbf..b29fbe698811c7d98bd9b2e0e5ce8103a84b0dbb 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php @@ -15,7 +15,22 @@ class TotalTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->model = new \Magento\Quote\Model\Quote\Address\Total(); + $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback(function ($value) { + return json_decode($value, true); + }); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManagerHelper->getObject( + \Magento\Quote\Model\Quote\Address\Total::class, + [ + 'serializer' => $serializer + ] + ); } /** @@ -171,6 +186,7 @@ class TotalTest extends \PHPUnit_Framework_TestCase /** * Verify handling of serialized, non-serialized input into and out of getFullInfo() * + * @covers \Magento\Quote\Model\Quote\Address\Total::getFullInfo() * @param $input * @param $expected * @dataProvider getFullInfoDataProvider @@ -187,7 +203,7 @@ class TotalTest extends \PHPUnit_Framework_TestCase public function getFullInfoDataProvider() { $myArray = ['team' => 'kiwis']; - $serializedInput = serialize($myArray); + $serializedInput = json_encode($myArray); return [ 'simple array' => [ diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php index 7a8ccb232ba06690780323988374b26ba644f2fc..507b2b00203d0ea95bb3bfe41f1dec39c3407778 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php @@ -32,16 +32,23 @@ class AddressTest extends \PHPUnit_Framework_TestCase */ private $scopeConfig; + /** + * @var \Magento\Framework\Serialize\Serializer\Json | \PHPUnit_Framework_MockObject_MockObject + */ + protected $serializer; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->scopeConfig = $this->getMock(\Magento\Framework\App\Config::class, [], [], '', false); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class, [], [], '', false); $this->address = $objectManager->getObject( \Magento\Quote\Model\Quote\Address::class, [ - 'scopeConfig' => $this->scopeConfig + 'scopeConfig' => $this->scopeConfig, + 'serializer' => $this->serializer ] ); $this->quote = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); @@ -134,4 +141,23 @@ class AddressTest extends \PHPUnit_Framework_TestCase $this->assertTrue($this->address->validateMinimumAmount()); } + + public function testSetAndGetAppliedTaxes() + { + $data = ['data']; + $result = json_encode($data); + + $this->serializer->expects($this->once()) + ->method('serialize') + ->with($data) + ->willReturn($result); + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($result) + ->willReturn($data); + + $this->assertInstanceOf(\Magento\Quote\Model\Quote\Address::class, $this->address->setAppliedTaxes($data)); + $this->assertEquals($data, $this->address->getAppliedTaxes()); + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php index f8b72e4df6b7c00dbef493def4072d07bf037eaf..1811a2e4ce6a80f7f464114eb7c92067f4a8053c 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php @@ -38,29 +38,42 @@ class CompareTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->itemMock = $this->getMock( + $this->itemMock = $this->getMock( \Magento\Quote\Model\Quote\Item::class, ['__wakeup', 'getProductId', 'getOptions'], [], '', false ); - $this->comparedMock = $this->getMock( + $this->comparedMock = $this->getMock( \Magento\Quote\Model\Quote\Item::class, ['__wakeup', 'getProductId', 'getOptions'], [], '', false ); - $this->optionMock = $this->getMock( + $this->optionMock = $this->getMock( \Magento\Quote\Model\Quote\Item\Option::class, ['__wakeup', 'getCode', 'getValue'], [], '', false ); - - $this->helper = new \Magento\Quote\Model\Quote\Item\Compare(); + $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback(function ($value) { + return json_decode($value, true); + }); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->helper = $objectManagerHelper->getObject( + \Magento\Quote\Model\Quote\Item\Compare::class, + [ + 'serializer' => $serializer + ]); } /** @@ -112,7 +125,7 @@ class CompareTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue([ $this->getOptionMock('option-1', 1), $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', serialize([ + $this->getOptionMock('option-3', json_encode([ 'value' => 'value-1', 'qty' => 2, ]) @@ -123,7 +136,7 @@ class CompareTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue([ $this->getOptionMock('option-4', 1), $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', serialize([ + $this->getOptionMock('option-3', json_encode([ 'value' => 'value-1', 'qty' => 2, ])), @@ -148,7 +161,7 @@ class CompareTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue([ $this->getOptionMock('option-1', 1), $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', serialize([ + $this->getOptionMock('option-3', json_encode([ 'value' => 'value-1', 'qty' => 2, ]) @@ -176,7 +189,7 @@ class CompareTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue([ $this->getOptionMock('option-1', 1), $this->getOptionMock('option-2', 'option-value'), - $this->getOptionMock('option-3', serialize([ + $this->getOptionMock('option-3', json_encode([ 'value' => 'value-1', 'qty' => 2, ]) @@ -201,13 +214,13 @@ class CompareTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(1)); $this->itemMock->expects($this->once())->method('getOptions')->willReturn([ - $this->getOptionMock('option-1', serialize([ + $this->getOptionMock('option-1', json_encode([ 'non-empty-option' => 'test', 'empty_option' => '' ])) ]); $this->comparedMock->expects($this->once())->method('getOptions')->willReturn([ - $this->getOptionMock('option-1', serialize([ + $this->getOptionMock('option-1', json_encode([ 'non-empty-option' => 'test' ])) ]); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php index fc86df0819e68bdf63c89202574400fa651613ca..ebf857fd0f1fa7b378fd76c7637ca91763051bdf 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php @@ -38,6 +38,11 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase */ protected $productMock; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + protected function setUp() { $this->productMock = $this->getMock( @@ -94,12 +99,16 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase '', false ); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(['serialize']) + ->getMockForAbstractClass(); $this->object = (new ObjectManager($this)) ->getObject( \Magento\Quote\Model\Quote\Item\Updater::class, [ - 'localeFormat' => $this->localeFormat + 'localeFormat' => $this->localeFormat, + 'serializer' => $this->serializer ] ); } @@ -226,6 +235,9 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase $this->object->update($this->itemMock, ['qty' => 3, 'use_discount' => true]); } + /** + * @covers \Magento\Quote\Model\Quote\Item\Updater::setCustomPrice() + */ public function testUpdateCustomPrice() { $customPrice = 9.99; @@ -249,10 +261,12 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase $buyRequestMock->expects($this->any()) ->method('getData') ->will($this->returnValue(['custom_price' => $customPrice])); - + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturn(json_encode($buyRequestMock->getData())); $buyRequestMock->expects($this->any()) ->method('setValue') - ->with($this->equalTo(serialize(['custom_price' => $customPrice]))); + ->with($this->equalTo('{"custom_price":' . $customPrice . '}')); $buyRequestMock->expects($this->any()) ->method('setCode') ->with($this->equalTo('info_buyRequest')); @@ -293,6 +307,9 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase $this->object->update($this->itemMock, ['qty' => $qty, 'custom_price' => $customPrice]); } + /** + * @covers \Magento\Quote\Model\Quote\Item\Updater::unsetCustomPrice() + */ public function testUpdateUnsetCustomPrice() { $qty = 3; @@ -313,6 +330,14 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase ); $buyRequestMock->expects($this->never())->method('setCustomPrice'); $buyRequestMock->expects($this->once())->method('getData')->will($this->returnValue([])); + $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(['serialize']) + ->getMockForAbstractClass(); + $serializer->expects($this->any()) + ->method('serialize') + ->willReturn('{}'); + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper->setBackwardCompatibleProperty($this->object, 'serializer', $serializer); $buyRequestMock->expects($this->once())->method('unsetData')->with($this->equalTo('custom_price')); $buyRequestMock->expects($this->once()) ->method('hasData') @@ -321,7 +346,7 @@ class UpdaterTest extends \PHPUnit_Framework_TestCase $buyRequestMock->expects($this->any()) ->method('setValue') - ->with($this->equalTo(serialize([]))); + ->with($this->equalTo('{}')); $buyRequestMock->expects($this->any()) ->method('setCode') ->with($this->equalTo('info_buyRequest')); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/ItemTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/ItemTest.php index 7e07fcddf4abaafb572a67034f58f936500e7bdb..b012d7db61f004c16f2f5e711ea3c9267ea143a0 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/ItemTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/ItemTest.php @@ -61,6 +61,11 @@ class ItemTest extends \PHPUnit_Framework_TestCase */ protected $stockRegistry; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + const PRODUCT_ID = 1; const PRODUCT_TYPE = 'simple'; const PRODUCT_SKU = '12345'; @@ -134,6 +139,10 @@ class ItemTest extends \PHPUnit_Framework_TestCase ->method('getStockItem') ->will($this->returnValue($this->stockItemMock)); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(['unserialize']) + ->getMockForAbstractClass(); + $this->model = $this->objectManagerHelper->getObject( \Magento\Quote\Model\Quote\Item::class, [ @@ -142,7 +151,8 @@ class ItemTest extends \PHPUnit_Framework_TestCase 'statusListFactory' => $statusListFactory, 'itemOptionFactory' => $this->itemOptionFactory, 'quoteItemCompare' => $this->compareHelper, - 'stockRegistry' => $this->stockRegistry + 'stockRegistry' => $this->stockRegistry, + 'serializer' => $this->serializer ] ); } @@ -1058,9 +1068,9 @@ class ItemTest extends \PHPUnit_Framework_TestCase $optionMock->expects($this->exactly(3)) ->method('getCode') ->will($this->returnValue($optionCode)); - $optionMock->expects($this->once()) + $optionMock->expects($this->any()) ->method('getValue') - ->will($this->returnValue(serialize(['qty' => $buyRequestQuantity]))); + ->will($this->returnValue('{"qty":23}')); $this->model->addOption($optionMock); @@ -1071,6 +1081,9 @@ class ItemTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($quantity)); $this->model->setQty($quantity); $this->assertEquals($quantity, $this->model->getQty()); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturn(json_decode($optionMock->getValue(), true)); $buyRequest = $this->model->getBuyRequest(); $this->assertEquals($buyRequestQuantity, $buyRequest->getOriginalQty()); $this->assertEquals($quantity, $buyRequest->getQty()); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/PaymentTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/PaymentTest.php index cc23683cb481bce0a8acc53a1b923c75ccf1eada..b710dba828a4c007432c5087a1cda0c9d0af06ea 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/PaymentTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/PaymentTest.php @@ -41,12 +41,21 @@ class PaymentTest extends \PHPUnit_Framework_TestCase )->disableOriginalConstructor() ->getMock(); $this->eventManager = $this->getMock(ManagerInterface::class); + $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback(function ($value) { + return json_decode($value, true); + }); $this->model = $objectManager->getObject( Payment::class, [ 'methodSpecificationFactory' => $this->specificationFactory, - 'eventDispatcher' => $this->eventManager + 'eventDispatcher' => $this->eventManager, + 'serializer' => $serializer ] ); } @@ -144,6 +153,55 @@ class PaymentTest extends \PHPUnit_Framework_TestCase $this->model->importData($data); } + /** + * @covers \Magento\Quote\Model\Quote\Payment::getAdditionalData() + * @dataProvider getAdditionalDataDataProvider + * @param mixed $expected + * @param mixed $additionalData + */ + public function testGetAdditionalData($expected, $additionalData) + { + $this->model->setData(Payment::KEY_ADDITIONAL_DATA, $additionalData); + $this->assertSame($expected, $this->model->getAdditionalData()); + } + + /** + * @return array + */ + public function getAdditionalDataDataProvider() + { + return [ + // Variation #1 + [ + //$expected + ['field1' => 'value1', 'field2' => 'value2'], + //$additionalData + ['field1' => 'value1', 'field2' => 'value2'], + ], + // Variation #2 + [ + //$expected + ['field1' => 'value1', 'field2' => 'value2'], + //$additionalData + '{"field1":"value1","field2":"value2"}', + ], + // Variation #3 + [ + //$expected + null, + //$additionalData + '{"field1":field2":"value2"}', + ], + // Variation #4 + [ + //$expected + null, + //$additionalData + 123, + ], + ]; + } + /** * @return array */ diff --git a/app/code/Magento/Quote/etc/module.xml b/app/code/Magento/Quote/etc/module.xml index 281cde9eeb9d18bc559cd0979e593a43c3b70ebb..52c92cf2d912a95f413634be2fa20976929462b3 100644 --- a/app/code/Magento/Quote/etc/module.xml +++ b/app/code/Magento/Quote/etc/module.xml @@ -6,6 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Quote" setup_version="2.0.3"> + <module name="Magento_Quote" setup_version="2.0.4"> </module> </config> diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Items/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Items/Grid.php index 3af13c7b1ead281347a901a9993a2010ee1e7cb3..d5d0da9c4676cb06c68d1ca0867f16f8ed9cb283 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Items/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Items/Grid.php @@ -425,6 +425,8 @@ class Grid extends \Magento\Sales\Block\Adminhtml\Order\Create\AbstractCreate * * @param Item $item * @return string + * + * @deprecated */ public function getCustomOptions(Item $item) { diff --git a/app/code/Magento/Sales/Controller/Download/DownloadCustomOption.php b/app/code/Magento/Sales/Controller/Download/DownloadCustomOption.php index a2dcebe8440b1479cf4557495b2d5abb68124f62..951fef40b8e3566cd96ac6012e877aa8257e2c00 100644 --- a/app/code/Magento/Sales/Controller/Download/DownloadCustomOption.php +++ b/app/code/Magento/Sales/Controller/Download/DownloadCustomOption.php @@ -1,17 +1,20 @@ <?php /** - * * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Sales\Controller\Download; -use Magento\Sales\Model\Download; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Action\Context; use Magento\Catalog\Model\Product\Type\AbstractType; use Magento\Framework\Controller\Result\ForwardFactory; -use \Magento\Framework\Unserialize\Unserialize; +/** + * Class DownloadCustomOption + * @package Magento\Sales\Controller\Download + */ class DownloadCustomOption extends \Magento\Framework\App\Action\Action { /** @@ -20,31 +23,42 @@ class DownloadCustomOption extends \Magento\Framework\App\Action\Action protected $resultForwardFactory; /** - * @var Download + * @var \Magento\Sales\Model\Download */ protected $download; /** - * @var Unserialize + * @var \Magento\Framework\Unserialize\Unserialize + * @deprecated */ protected $unserialize; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param Context $context * @param ForwardFactory $resultForwardFactory - * @param Download $download - * @param Unserialize $unserialize + * @param \Magento\Sales\Model\Download $download + * @param \Magento\Framework\Unserialize\Unserialize $unserialize + * @param \Magento\Framework\Serialize\Serializer\Json $serializer */ public function __construct( Context $context, ForwardFactory $resultForwardFactory, - Download $download, - Unserialize $unserialize + \Magento\Sales\Model\Download $download, + \Magento\Framework\Unserialize\Unserialize $unserialize, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { parent::__construct($context); $this->resultForwardFactory = $resultForwardFactory; $this->download = $download; $this->unserialize = $unserialize; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get( + \Magento\Framework\Serialize\Serializer\Json::class + ); } /** @@ -88,7 +102,7 @@ class DownloadCustomOption extends \Magento\Framework\App\Action\Action } try { - $info = $this->unserialize->unserialize($option->getValue()); + $info = $this->serializer->unserialize($option->getValue()); if ($this->getRequest()->getParam('key') != $info['secret_key']) { return $resultForward->forward('noroute'); } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 3715c02357a964bef3502a461db30574d6655ba1..a64d081ffd16041003bc73b78dc473610e4a55c1 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -224,6 +224,13 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ protected $quoteFactory; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -253,6 +260,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ * @param \Magento\Sales\Api\OrderManagementInterface $orderManagement * @param \Magento\Quote\Model\QuoteFactory $quoteFactory * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -283,7 +291,8 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Sales\Api\OrderManagementInterface $orderManagement, \Magento\Quote\Model\QuoteFactory $quoteFactory, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_objectManager = $objectManager; $this->_eventManager = $eventManager; @@ -312,6 +321,8 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ $this->dataObjectHelper = $dataObjectHelper; $this->orderManagement = $orderManagement; $this->quoteFactory = $quoteFactory; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($data); } @@ -632,7 +643,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ [ 'product' => $item->getProduct(), 'code' => 'additional_options', - 'value' => serialize($additionalOptions) + 'value' => $this->serializer->serialize($additionalOptions) ] ) ); @@ -794,7 +805,9 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ $info = $item->getOptionByCode('info_buyRequest'); if ($info) { - $info = new \Magento\Framework\DataObject(unserialize($info->getValue())); + $info = new \Magento\Framework\DataObject( + $this->serializer->unserialize($info->getValue()) + ); $info->setQty($qty); $info->setOptions($this->_prepareOptionsForRequest($item)); } else { @@ -1102,6 +1115,8 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ * @param string $additionalOptions * @return array * @throws \Magento\Framework\Exception\LocalizedException + * + * @deprecated */ protected function _parseOptions(\Magento\Quote\Model\Quote\Item $item, $additionalOptions) { @@ -1166,6 +1181,8 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ * @param \Magento\Quote\Model\Quote\Item $item * @param array $options * @return $this + * + * @deprecated */ protected function _assignOptionsToItem(\Magento\Quote\Model\Quote\Item $item, $options) { @@ -1209,7 +1226,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ [ 'product' => $item->getProduct(), 'code' => 'additional_options', - 'value' => serialize($options['additional_options']) + 'value' => $this->serializer->serialize($options['additional_options']) ] ) ); @@ -1844,7 +1861,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ } $addOptions = $item->getOptionByCode('additional_options'); if ($addOptions) { - $options['additional_options'] = unserialize($addOptions->getValue()); + $options['additional_options'] = $this->serializer->unserialize($addOptions->getValue()); } $item->setProductOrderOptions($options); } diff --git a/app/code/Magento/Sales/Model/Order/Config.php b/app/code/Magento/Sales/Model/Order/Config.php index 535e2975ee13da8458cba83666ba49abb942a89a..48021fedc03f8f3422af2bdcba4ffa15920861d5 100644 --- a/app/code/Magento/Sales/Model/Order/Config.php +++ b/app/code/Magento/Sales/Model/Order/Config.php @@ -192,7 +192,7 @@ class Config */ public function getStateStatuses($state, $addLabels = true) { - $key = md5(serialize([$state, $addLabels])); + $key = sha1(json_encode([$state, $addLabels])); if (isset($this->stateStatuses[$key])) { return $this->stateStatuses[$key]; } diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php index ff687074e4a66cc50c457c685fc1757d1529dcb6..75f100a93e4a315efd4a1d0baa89c9daffa85e4c 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php @@ -24,21 +24,32 @@ class CreditmemoFactory /** * @var \Magento\Framework\Unserialize\Unserialize + * @deprecated */ protected $unserialize; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * Factory constructor * * @param \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory * @param \Magento\Tax\Model\Config $taxConfig + * @param \Magento\Framework\Serialize\Serializer\Json $serializer */ public function __construct( \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory, - \Magento\Tax\Model\Config $taxConfig + \Magento\Tax\Model\Config $taxConfig, + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->convertor = $convertOrderFactory->create(); $this->taxConfig = $taxConfig; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\Serialize\Serializer\Json::class + ); } /** @@ -262,7 +273,7 @@ class CreditmemoFactory /** * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem - * @param array $qtys + * @param int $parentQty * @return int */ private function calculateProductOptions(\Magento\Sales\Api\Data\OrderItemInterface $orderItem, $parentQty) @@ -270,7 +281,7 @@ class CreditmemoFactory $qty = $parentQty; $productOptions = $orderItem->getProductOptions(); if (isset($productOptions['bundle_selection_attributes'])) { - $bundleSelectionAttributes = $this->getUnserialize() + $bundleSelectionAttributes = $this->serializer ->unserialize($productOptions['bundle_selection_attributes']); if ($bundleSelectionAttributes) { $qty = $bundleSelectionAttributes['qty'] * $parentQty; @@ -278,19 +289,4 @@ class CreditmemoFactory } return $qty; } - - /** - * Get Unserialize - * - * @return \Magento\Framework\Unserialize\Unserialize - * @deprecated - */ - private function getUnserialize() - { - if (!$this->unserialize) { - $this->unserialize = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Unserialize\Unserialize::class); - } - return $this->unserialize; - } } diff --git a/app/code/Magento/Sales/Model/Order/Item.php b/app/code/Magento/Sales/Model/Order/Item.php index 10ba1e16621448ddee118c16d7debbf58ca04e06..caf3352de0ee018fcc9c2dd475ce627761f15d2b 100644 --- a/app/code/Magento/Sales/Model/Order/Item.php +++ b/app/code/Magento/Sales/Model/Order/Item.php @@ -95,6 +95,13 @@ class Item extends AbstractModel implements OrderItemInterface */ protected $_storeManager; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * Initialize dependencies. * @@ -108,6 +115,7 @@ class Item extends AbstractModel implements OrderItemInterface * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -120,7 +128,8 @@ class Item extends AbstractModel implements OrderItemInterface \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { parent::__construct( $context, @@ -131,6 +140,8 @@ class Item extends AbstractModel implements OrderItemInterface $resourceCollection, $data ); + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); $this->_orderFactory = $orderFactory; $this->_storeManager = $storeManager; $this->productRepository = $productRepository; @@ -466,7 +477,7 @@ class Item extends AbstractModel implements OrderItemInterface public function getProductOptions() { $data = $this->_getData('product_options'); - return is_string($data) ? unserialize($data) : $data; + return is_string($data) ? $this->serializer->unserialize($data) : $data; } /** diff --git a/app/code/Magento/Sales/Model/Order/ShipmentFactory.php b/app/code/Magento/Sales/Model/Order/ShipmentFactory.php index a8839c75375870ce03294ca3d585635fdff84f14..cde0efcb8e0fef361a328ed1317973332a264db3 100644 --- a/app/code/Magento/Sales/Model/Order/ShipmentFactory.php +++ b/app/code/Magento/Sales/Model/Order/ShipmentFactory.php @@ -8,6 +8,7 @@ namespace Magento\Sales\Model\Order; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface; +use Magento\Framework\Serialize\Serializer\Json; /** * Factory class for @see \Magento\Sales\Api\Data\ShipmentInterface @@ -35,19 +36,30 @@ class ShipmentFactory */ protected $instanceName; + /** + * Serializer + * + * @var Json + */ + private $serializer; + /** * Factory constructor. * * @param \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory * @param \Magento\Sales\Model\Order\Shipment\TrackFactory $trackFactory + * @param \Magento\Framework\Serialize\Serializer\Json $serializer */ public function __construct( \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory, - \Magento\Sales\Model\Order\Shipment\TrackFactory $trackFactory + \Magento\Sales\Model\Order\Shipment\TrackFactory $trackFactory, + Json $serializer = null ) { $this->converter = $convertOrderFactory->create(); $this->trackFactory = $trackFactory; $this->instanceName = \Magento\Sales\Api\Data\ShipmentInterface::class; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Json::class); } /** @@ -100,7 +112,7 @@ class ShipmentFactory $productOptions = $orderItem->getProductOptions(); if (isset($productOptions['bundle_selection_attributes'])) { - $bundleSelectionAttributes = unserialize( + $bundleSelectionAttributes = $this->serializer->unserialize( $productOptions['bundle_selection_attributes'] ); diff --git a/app/code/Magento/Sales/Setup/ConvertSerializedDataToJson.php b/app/code/Magento/Sales/Setup/ConvertSerializedDataToJson.php new file mode 100644 index 0000000000000000000000000000000000000000..7fe2e2743e132244d62f98a38ed91cc4f0ffd337 --- /dev/null +++ b/app/code/Magento/Sales/Setup/ConvertSerializedDataToJson.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Setup; + +use Magento\Framework\DB\FieldDataConverterFactory; +use Magento\Framework\DB\DataConverter\SerializedToJson; +use Magento\Framework\DB\FieldDataConverter; + +/** + * Convert serialized data in sales tables to JSON + */ +class ConvertSerializedDataToJson +{ + /** + * @var SalesSetup + */ + private $salesSetup; + + /** + * @var FieldDataConverterFactory + */ + private $fieldDataConverterFactory; + + /** + * @var array + */ + private $fieldDataConverters = []; + + /** + * @var array + */ + private $fieldsToUpdate = [ + [ + 'table' => 'sales_order_item', + 'identifier' => 'item_id', + 'title' => 'product_options', + 'data_converter' => SerializedDataConverter::class + ], + [ + 'table' => 'sales_shipment', + 'identifier' => 'entity_id', + 'title' => 'packages', + 'data_converter' => SerializedToJson::class + ], + [ + 'table' => 'sales_order_payment', + 'identifier' => 'entity_id', + 'title' => 'additional_information', + 'data_converter' => SerializedToJson::class + ], + [ + 'table' => 'sales_payment_transaction', + 'identifier' => 'transaction_id', + 'title' => 'additional_information', + 'data_converter' => SerializedToJson::class + ] + ]; + + /** + * Constructor + * + * @param SalesSetup $salesSetup + * @param FieldDataConverterFactory $fieldDataConverterFactory + */ + public function __construct( + SalesSetup $salesSetup, + FieldDataConverterFactory $fieldDataConverterFactory + ) { + $this->salesSetup = $salesSetup; + $this->fieldDataConverterFactory = $fieldDataConverterFactory; + } + + /** + * Convert data for the following fields from serialized to JSON format: + * sales_order_item.product_options + * sales_shipment.packages + * sales_order_payment.additional_information + * sales_payment_transaction.additional_information + * + * @return void + */ + public function convert() + { + foreach ($this->fieldsToUpdate as $field) { + $fieldDataConverter = $this->getFieldDataConverter($field['data_converter']); + $fieldDataConverter->convert( + $this->salesSetup->getConnection(), + $this->salesSetup->getTable($field['table']), + $field['identifier'], + $field['title'] + ); + } + } + + /** + * Get field data converter + * + * @param string $dataConverterClassName + * @return FieldDataConverter + */ + private function getFieldDataConverter($dataConverterClassName) + { + if (!isset($this->fieldDataConverters[$dataConverterClassName])) { + $this->fieldDataConverters[$dataConverterClassName] = $this->fieldDataConverterFactory->create( + $dataConverterClassName + ); + } + return $this->fieldDataConverters[$dataConverterClassName]; + } +} diff --git a/app/code/Magento/Sales/Setup/SalesSetup.php b/app/code/Magento/Sales/Setup/SalesSetup.php index 3a2c999678a03e4115c867620092438cf27628b3..80b1ec4e8dc5fd2c171edc2e3ce8d5da8ebb9cff 100644 --- a/app/code/Magento/Sales/Setup/SalesSetup.php +++ b/app/code/Magento/Sales/Setup/SalesSetup.php @@ -11,13 +11,15 @@ use Magento\Framework\App\CacheInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Eav\Setup\EavSetup; /** - * Setup Model of Sales Module - * @codeCoverageIgnore + * Sales module setup class + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @codeCoverageIgnore */ -class SalesSetup extends \Magento\Eav\Setup\EavSetup +class SalesSetup extends EavSetup { /** * This should be set explicitly @@ -55,6 +57,8 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup private static $connectionName = 'sales'; /** + * Constructor + * * @param ModuleDataSetupInterface $setup * @param Context $context * @param CacheInterface $cache @@ -111,9 +115,10 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup */ protected function _flatTableExist($table) { - $tablesList = $this->getSetup()->getConnection(self::$connectionName)->listTables(); + $tablesList = $this->getConnection() + ->listTables(); return in_array( - strtoupper($this->getSetup()->getTable($table, self::$connectionName)), + strtoupper($this->getTable($table)), array_map('strtoupper', $tablesList) ); } @@ -152,15 +157,14 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup */ protected function _addFlatAttribute($table, $attribute, $attr) { - $tableInfo = $this->getSetup() - ->getConnection(self::$connectionName) - ->describeTable($this->getSetup()->getTable($table, self::$connectionName)); + $tableInfo = $this->getConnection() + ->describeTable($this->getTable($table)); if (isset($tableInfo[$attribute])) { return $this; } $columnDefinition = $this->_getAttributeColumnDefinition($attribute, $attr); - $this->getSetup()->getConnection(self::$connectionName)->addColumn( - $this->getSetup()->getTable($table, self::$connectionName), + $this->getConnection()->addColumn( + $this->getTable($table), $attribute, $columnDefinition ); @@ -180,8 +184,8 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup { if (in_array($entityTypeId, $this->_flatEntitiesGrid) && !empty($attr['grid'])) { $columnDefinition = $this->_getAttributeColumnDefinition($attribute, $attr); - $this->getSetup()->getConnection(self::$connectionName)->addColumn( - $this->getSetup()->getTable($table . '_grid', self::$connectionName), + $this->getConnection()->addColumn( + $this->getTable($table . '_grid'), $attribute, $columnDefinition ); @@ -297,4 +301,25 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup { return $this->encryptor; } + + /** + * Get sales connection + * + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + public function getConnection() + { + return $this->getSetup()->getConnection(self::$connectionName); + } + + /** + * Get table name + * + * @param string $table + * @return string + */ + public function getTable($table) + { + return $this->getSetup()->getTable($table, self::$connectionName); + } } diff --git a/app/code/Magento/Sales/Setup/SerializedDataConverter.php b/app/code/Magento/Sales/Setup/SerializedDataConverter.php new file mode 100644 index 0000000000000000000000000000000000000000..507713b910f59c84dad6e5414d745d368bbadba1 --- /dev/null +++ b/app/code/Magento/Sales/Setup/SerializedDataConverter.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Setup; + +use Magento\Framework\Serialize\Serializer\Serialize; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Serializer used to update nested serialized data in product_options field. + */ +class SerializedDataConverter implements \Magento\Framework\DB\DataConverter\DataConverterInterface +{ + /** + * @var Serialize + */ + private $serialize; + + /** + * @var Json + */ + private $json; + + /** + * SerializedDataConverter constructor. + * + * @param Serialize $serialize + * @param Json $json + */ + public function __construct( + Serialize $serialize, + Json $json + ) { + $this->serialize = $serialize; + $this->json = $json; + } + + /** + * Convert from serialized to JSON format. + * + * @param string $value + * @return string + */ + public function convert($value) + { + $valueUnserialized = $this->serialize->unserialize($value); + if (isset($valueUnserialized['options'])) { + foreach ($valueUnserialized['options'] as $key => $option) { + if ($option['option_type'] === 'file') { + $valueUnserialized['options'][$key]['option_value'] = $this->json->serialize( + $this->serialize->unserialize( + $option['option_value'] + ) + ); + } + } + } + if (isset($valueUnserialized['bundle_selection_attributes'])) { + $valueUnserialized['bundle_selection_attributes'] = $this->json->serialize( + $this->serialize->unserialize( + $valueUnserialized['bundle_selection_attributes'] + ) + ); + } + return $this->json->serialize($valueUnserialized); + } +} diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index 9580dd8a667a0477b3bbab6ffe2fac8698f85e57..67b43fee29d08b95d6a6ca5c2f8465cadc83f4ed 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -3,93 +3,112 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Sales\Setup; -use Magento\Framework\Setup\UpgradeDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; - -class UpgradeData implements UpgradeDataInterface +/** + * Data upgrade script + */ +class UpgradeData implements \Magento\Framework\Setup\UpgradeDataInterface { /** * Sales setup factory * - * @var SalesSetupFactory + * @var \Magento\Sales\Setup\SalesSetupFactory */ - protected $salesSetupFactory; + private $salesSetupFactory; /** * @var \Magento\Eav\Model\Config */ - protected $eavConfig; + private $eavConfig; + + /** + * @var \Magento\Sales\Setup\ConvertSerializedDataToJsonFactory + */ + private $convertSerializedDataToJsonFactory; /** - * @param SalesSetupFactory $salesSetupFactory + * Constructor + * + * @param \Magento\Sales\Setup\SalesSetupFactory $salesSetupFactory + * @param \Magento\Sales\Setup\ConvertSerializedDataToJsonFactory $convertSerializedDataToJsonFactory * @param \Magento\Eav\Model\Config $eavConfig */ public function __construct( - SalesSetupFactory $salesSetupFactory, + \Magento\Sales\Setup\SalesSetupFactory $salesSetupFactory, + \Magento\Sales\Setup\ConvertSerializedDataToJsonFactory $convertSerializedDataToJsonFactory, \Magento\Eav\Model\Config $eavConfig ) { $this->salesSetupFactory = $salesSetupFactory; + $this->convertSerializedDataToJsonFactory = $convertSerializedDataToJsonFactory; $this->eavConfig = $eavConfig; } /** * {@inheritdoc} - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - $setup->startSetup(); - - /** @var SalesSetup $salesSetup */ + public function upgrade( + \Magento\Framework\Setup\ModuleDataSetupInterface $setup, + \Magento\Framework\Setup\ModuleContextInterface $context + ) { $salesSetup = $this->salesSetupFactory->create(['setup' => $setup]); - if (version_compare($context->getVersion(), '2.0.1', '<')) { - $salesSetup->updateEntityType( - \Magento\Sales\Model\Order::ENTITY, - 'entity_model', - \Magento\Sales\Model\ResourceModel\Order::class - ); - $salesSetup->updateEntityType( - \Magento\Sales\Model\Order::ENTITY, - 'increment_model', - \Magento\Eav\Model\Entity\Increment\NumericValue::class - ); - $salesSetup->updateEntityType( - 'invoice', - 'entity_model', - \Magento\Sales\Model\ResourceModel\Order::class - ); - $salesSetup->updateEntityType( - 'invoice', - 'increment_model', - \Magento\Eav\Model\Entity\Increment\NumericValue::class - ); - $salesSetup->updateEntityType( - 'creditmemo', - 'entity_model', - \Magento\Sales\Model\ResourceModel\Order\Creditmemo::class - ); - $salesSetup->updateEntityType( - 'creditmemo', - 'increment_model', - \Magento\Eav\Model\Entity\Increment\NumericValue::class - ); - $salesSetup->updateEntityType( - 'shipment', - 'entity_model', - \Magento\Sales\Model\ResourceModel\Order\Shipment::class - ); - $salesSetup->updateEntityType( - 'shipment', - 'increment_model', - \Magento\Eav\Model\Entity\Increment\NumericValue::class - ); + $this->upgradeToTwoZeroOne($salesSetup); + } + if (version_compare($context->getVersion(), '2.0.5', '<')) { + $this->convertSerializedDataToJsonFactory->create(['salesSetup' => $salesSetup]) + ->convert(); } $this->eavConfig->clear(); - $setup->endSetup(); + } + + /** + * Upgrade to version 2.0.1 + * + * @param \Magento\Sales\Setup\SalesSetup $setup + * @return void + */ + private function upgradeToTwoZeroOne(\Magento\Sales\Setup\SalesSetup $setup) + { + $setup->updateEntityType( + \Magento\Sales\Model\Order::ENTITY, + 'entity_model', + \Magento\Sales\Model\ResourceModel\Order::class + ); + $setup->updateEntityType( + \Magento\Sales\Model\Order::ENTITY, + 'increment_model', + \Magento\Eav\Model\Entity\Increment\NumericValue::class + ); + $setup->updateEntityType( + 'invoice', + 'entity_model', + \Magento\Sales\Model\ResourceModel\Order::class + ); + $setup->updateEntityType( + 'invoice', + 'increment_model', + \Magento\Eav\Model\Entity\Increment\NumericValue::class + ); + $setup->updateEntityType( + 'creditmemo', + 'entity_model', + \Magento\Sales\Model\ResourceModel\Order\Creditmemo::class + ); + $setup->updateEntityType( + 'creditmemo', + 'increment_model', + \Magento\Eav\Model\Entity\Increment\NumericValue::class + ); + $setup->updateEntityType( + 'shipment', + 'entity_model', + \Magento\Sales\Model\ResourceModel\Order\Shipment::class + ); + $setup->updateEntityType( + 'shipment', + 'increment_model', + \Magento\Eav\Model\Entity\Increment\NumericValue::class + ); } } diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Download/DownloadCustomOptionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Download/DownloadCustomOptionTest.php index 387fc3b782b10a17f969975a1c67197c5cd9dfac..b7ceaf727edb069e283fa2d5a2b99b3aeebe9687 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Download/DownloadCustomOptionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Download/DownloadCustomOptionTest.php @@ -6,6 +6,9 @@ namespace Magento\Sales\Test\Unit\Controller\Download; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Unserialize\Unserialize; + /** * Class DownloadCustomOptionTest * @package Magento\Sales\Controller\Adminhtml\Order @@ -55,7 +58,7 @@ class DownloadCustomOptionTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Framework\Unserialize\Unserialize|\PHPUnit_Framework_MockObject_MockObject */ - protected $unserializeMock; + protected $serializerMock; /** * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject @@ -89,9 +92,9 @@ class DownloadCustomOptionTest extends \PHPUnit_Framework_TestCase ->setMethods(['downloadFile']) ->getMock(); - $this->unserializeMock = $this->getMockBuilder(\Magento\Framework\Unserialize\Unserialize::class) + $this->serializerMock = $this->getMockBuilder(Json::class) ->disableOriginalConstructor() - ->setMethods(['unserialize']) + ->setMethods(['serialize', 'unserialize']) ->getMock(); $requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) @@ -151,7 +154,8 @@ class DownloadCustomOptionTest extends \PHPUnit_Framework_TestCase 'context' => $contextMock, 'resultForwardFactory' => $resultForwardFactoryMock, 'download' => $this->downloadMock, - 'unserialize' => $this->unserializeMock + 'unserialize' => $this->getMock(Unserialize::class, [], [], '', false), + 'serializer' => $this->serializerMock ] ) ->getMock(); @@ -197,7 +201,7 @@ class DownloadCustomOptionTest extends \PHPUnit_Framework_TestCase } else { $unserializeResult = [self::SECRET_KEY => self::SECRET_KEY]; - $this->unserializeMock->expects($this->once()) + $this->serializerMock->expects($this->once()) ->method('unserialize') ->with($itemOptionValues[self::OPTION_VALUE]) ->willReturn($unserializeResult); @@ -321,7 +325,7 @@ class DownloadCustomOptionTest extends \PHPUnit_Framework_TestCase $this->productOptionMock->expects($this->any())->method('getProductId')->willReturn(self::OPTION_PRODUCT_ID); $this->productOptionMock->expects($this->any())->method('getType')->willReturn(self::OPTION_TYPE); - $this->unserializeMock->expects($this->once()) + $this->serializerMock->expects($this->once()) ->method('unserialize') ->with(self::OPTION_VALUE) ->willReturn([self::SECRET_KEY => 'bad_test_secret_key']); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php index 7ee4f745cde8f75de23147c956ca0bf039ab2e3a..e0aae72555c038e603d0f2d13e1a15d7eeb76737 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Sales\Test\Unit\Model\Order; +use Magento\Framework\DataObject; +use Magento\Sales\Model\ResourceModel\Order\Status\Collection; + /** * Class ConfigTest */ @@ -44,28 +47,28 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetInvisibleOnFrontStatuses() { $statuses = [ - new \Magento\Framework\DataObject( + new DataObject( [ 'status' => 'canceled', 'is_default' => 1, 'visible_on_front' => 1, ] ), - new \Magento\Framework\DataObject( + new DataObject( [ 'status' => 'complete', 'is_default' => 1, 'visible_on_front' => 0, ] ), - new \Magento\Framework\DataObject( + new DataObject( [ 'status' => 'processing', 'is_default' => 1, 'visible_on_front' => 1, ] ), - new \Magento\Framework\DataObject( + new DataObject( [ 'status' => 'pending_payment', 'is_default' => 1, @@ -76,7 +79,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $expectedResult = ['complete', 'pending_payment']; $collectionMock = $this->getMock( - \Magento\Sales\Model\ResourceModel\Order\Status\Collection::class, + Collection::class, ['create', 'joinStates'], [], '', @@ -97,14 +100,14 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetStateLabelByStateAndStatus() { $statuses = [ - new \Magento\Framework\DataObject( + new DataObject( [ 'status' => 'fraud', 'state' => 'processing', 'label' => 'Suspected Fraud', ] ), - new \Magento\Framework\DataObject( + new DataObject( [ 'status' => 'processing', 'state' => 'processing', @@ -113,7 +116,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase ) ]; $collectionMock = $this->getMock( - \Magento\Sales\Model\ResourceModel\Order\Status\Collection::class, + Collection::class, ['create', 'joinStates'], [], '', @@ -129,4 +132,98 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $result = $this->salesConfig->getStateLabelByStateAndStatus('processing', 'fraud'); $this->assertSame('Suspected Fraud', $result->getText()); } + + /** + * Test get statuses + * + * @dataProvider getStatusesDataProvider + * + * @param string $state + * @param bool $joinLabels + * @param DataObject[] $collectionData + * @param array $expectedResult + */ + public function testGetStatuses($state, $joinLabels, $collectionData, $expectedResult) + { + $collectionMock = $this->getMock( + Collection::class, + ['create', 'joinStates', 'addStateFilter', 'orderByLabel'], + [], + '', + false, + false + ); + $this->orderStatusCollectionFactoryMock->expects($this->any()) + ->method('create') + ->will($this->returnValue($collectionMock)); + + $collectionMock->expects($this->once()) + ->method('addStateFilter') + ->will($this->returnSelf()); + + $collectionMock->expects($this->once()) + ->method('orderByLabel') + ->will($this->returnValue($collectionData)); + + $collectionMock->expects($this->once()) + ->method('joinStates') + ->will($this->returnValue($collectionData)); + + $result = $this->salesConfig->getStateStatuses($state, $joinLabels); + $this->assertSame($expectedResult, $result); + + // checking data cached in private property + $this->assertSame($result, $this->salesConfig->getStateStatuses($state, $joinLabels)); + } + + /** + * Data provider for testGetStatuses + * + * @return array + */ + public function getStatusesDataProvider() + { + return [ + 'processing state' => [ + 'state' => 'processing', + 'joinLabels' => false, + 'collectionData' => [ + new DataObject( + [ + 'status' => 'fraud', + 'state' => 'processing', + 'store_label' => 'Suspected Fraud', + ] + ), + new DataObject( + [ + 'status' => 'processing', + 'state' => 'processing', + 'store_label' => 'Processing', + ] + ), + ], + 'expectedResult' => [ + 0 => 'fraud', + 1 => 'processing' + ], + ], + 'pending state' => [ + 'state' => 'pending', + 'joinLabels' => true, + 'collectionData' => [ + new DataObject( + [ + 'status' => 'pending_status', + 'state' => 'pending', + 'store_label' => 'Pending label', + ] + ), + ], + 'expectedResult' => [ + 'pending_status' => 'Pending label' + ], + ], + ]; + } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php index 3d6ba772618c70a710809bb986ec9f55efe3c90b..b40fc6c7f5916952c403734eb94b2564936f2aaf 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Test\Unit\Model\Order; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Sales\Model\ResourceModel\OrderFactory; use \Magento\Sales\Model\Order; @@ -31,14 +32,22 @@ class ItemTest extends \PHPUnit_Framework_TestCase */ protected $orderFactory; + /** + * @var Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializerMock; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->orderFactory = $this->getMock(\Magento\Sales\Model\OrderFactory::class, ['create'], [], '', false); + $this->serializerMock = $this->getMock(Json::class, [], ['unserialize'], '', false); + $arguments = [ 'orderFactory' => $this->orderFactory, + 'serializer' => $this->serializerMock ]; $this->model = $this->objectManager->getObject(\Magento\Sales\Model\Order\Item::class, $arguments); } @@ -173,4 +182,55 @@ class ItemTest extends \PHPUnit_Framework_TestCase $this->model->setData(\Magento\Sales\Api\Data\OrderItemInterface::ORIGINAL_PRICE, $originalPrice); $this->assertEquals($originalPrice, $this->model->getOriginalPrice()); } + + /** + * Test get product options with serialization + * + * @param array|string $options + * @param array $expectedResult + * + * @dataProvider getProductOptionsDataProvider + */ + public function testGetProductOptions($options, $expectedResult) + { + if (is_string($options)) { + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->will($this->returnValue($expectedResult)); + } + $this->model->setData('product_options', $options); + $result = $this->model->getProductOptions(); + $this->assertSame($result, $expectedResult); + } + + /** + * Data provider for testGetProductOptions + * + * @return array + */ + public function getProductOptionsDataProvider() + { + return [ + 'array' => [ + 'options' => [ + 'option1' => 'option 1 value', + 'option2' => 'option 2 value', + ], + 'expectedResult' => [ + 'option1' => 'option 1 value', + 'option2' => 'option 2 value', + ] + ], + 'serialized' => [ + 'options' => json_encode([ + 'option1' => 'option 1 value', + 'option2' => 'option 2 value', + ]), + 'expectedResult' => [ + 'option1' => 'option 1 value', + 'option2' => 'option 2 value', + ] + ] + ]; + } } diff --git a/app/code/Magento/Sales/Test/Unit/Setup/SerializedDataConverterTest.php b/app/code/Magento/Sales/Test/Unit/Setup/SerializedDataConverterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e5f7e162a77c71941eac6898c0cfac3b4c854ab2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Setup/SerializedDataConverterTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Test\Unit\Setup; + +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\Serializer\Serialize; + +/** + * Unit test for serialized data converter test. + */ +class SerializedDataConverterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Sales\Setup\SerializedDataConverter + */ + protected $model; + + /** + * @var Serialize|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializeMock; + + /** + * @var Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $jsonMock; + + public function setUp() + { + $this->serializeMock = $this->getMock(Serialize::class, ['unserialize'], [], '', false); + $this->serializeMock->expects($this->any()) + ->method('unserialize') + ->will( + $this->returnCallback( + function ($value) { + return unserialize($value); + } + ) + ); + $this->jsonMock = $this->getMock(Json::class, ['serialize'], [], '', false); + $this->jsonMock->expects($this->any()) + ->method('serialize') + ->will( + $this->returnCallback( + function ($value) { + return json_encode($value); + } + ) + ); + + $this->model = new \Magento\Sales\Setup\SerializedDataConverter($this->serializeMock, $this->jsonMock); + + } + + /** + * @param string $serialized + * @param string $expectedJson + * + * @dataProvider convertDataProvider + */ + public function testConvert($serialized, $expectedJson) + { + $this->assertEquals($expectedJson, $this->model->convert($serialized)); + } + + /** + * Data provider for convert method test + * + * Slashes in PHP string are implicitly escaped so they MUST be escaped manually to correspond real expected data + * + * @return array + */ + public function convertDataProvider() + { + // @codingStandardsIgnoreStart + return [ + 'dataset_1' => [ + 'serialized' => 'a:2:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:52:"aHR0cDovL20yLmxvYy9zaW1wbGUuaHRtbD9vcHRpb25zPWNhcnQ,";s:7:"product";s:1:"1";s:28:"selected_configurable_option";s:0:"";s:15:"related_product";s:0:"";s:7:"options";a:3:{i:1;s:4:"test";i:3;s:1:"2";i:2;a:9:{s:4:"type";s:10:"image/jpeg";s:5:"title";s:7:"476.jpg";s:10:"quote_path";s:61:"custom_options/quote/4/7/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg";s:10:"order_path";s:61:"custom_options/order/4/7/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg";s:8:"fullpath";s:89:"C:/www/magento/ce/pub/media/custom_options/quote/4/7/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg";s:4:"size";s:6:"122340";s:5:"width";i:666;s:6:"height";i:940;s:10:"secret_key";s:20:"bc61f16f0cc3a8c5abd7";}}s:3:"qty";s:1:"1";}s:7:"options";a:3:{i:0;a:7:{s:5:"label";s:11:"testoption1";s:5:"value";s:4:"test";s:11:"print_value";s:4:"test";s:9:"option_id";s:1:"1";s:11:"option_type";s:5:"field";s:12:"option_value";s:4:"test";s:11:"custom_view";b:0;}i:1;a:7:{s:5:"label";s:11:"testoption2";s:5:"value";s:132:"<a href="http://m2.loc/sales/download/downloadCustomOption/id/9/key/bc61f16f0cc3a8c5abd7/" target="_blank">476.jpg</a> 666 x 940 px.";s:11:"print_value";s:21:"476.jpg 666 x 940 px.";s:9:"option_id";s:1:"2";s:11:"option_type";s:4:"file";s:12:"option_value";s:600:"a:10:{s:4:"type";s:10:"image/jpeg";s:5:"title";s:7:"476.jpg";s:10:"quote_path";s:61:"custom_options/quote/4/7/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg";s:10:"order_path";s:61:"custom_options/order/4/7/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg";s:8:"fullpath";s:89:"C:/www/magento/ce/pub/media/custom_options/quote/4/7/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg";s:4:"size";s:6:"122340";s:5:"width";i:666;s:6:"height";i:940;s:10:"secret_key";s:20:"bc61f16f0cc3a8c5abd7";s:3:"url";a:2:{s:5:"route";s:35:"sales/download/downloadCustomOption";s:6:"params";a:2:{s:2:"id";s:1:"9";s:3:"key";s:20:"bc61f16f0cc3a8c5abd7";}}}";s:11:"custom_view";b:1;}i:2;a:7:{s:5:"label";s:8:"testopt3";s:5:"value";s:3:"222";s:11:"print_value";s:3:"222";s:9:"option_id";s:1:"3";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:1:"2";s:11:"custom_view";b:0;}}}', + 'expectedJson' => '{"info_buyRequest":{"uenc":"aHR0cDovL20yLmxvYy9zaW1wbGUuaHRtbD9vcHRpb25zPWNhcnQ,","product":"1","selected_configurable_option":"","related_product":"","options":{"1":"test","3":"2","2":{"type":"image\/jpeg","title":"476.jpg","quote_path":"custom_options\/quote\/4\/7\/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg","order_path":"custom_options\/order\/4\/7\/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg","fullpath":"C:\/www\/magento\/ce\/pub\/media\/custom_options\/quote\/4\/7\/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg","size":"122340","width":666,"height":940,"secret_key":"bc61f16f0cc3a8c5abd7"}},"qty":"1"},"options":[{"label":"testoption1","value":"test","print_value":"test","option_id":"1","option_type":"field","option_value":"test","custom_view":false},{"label":"testoption2","value":"<a href=\"http:\/\/m2.loc\/sales\/download\/downloadCustomOption\/id\/9\/key\/bc61f16f0cc3a8c5abd7\/\" target=\"_blank\">476.jpg<\/a> 666 x 940 px.","print_value":"476.jpg 666 x 940 px.","option_id":"2","option_type":"file","option_value":"{\"type\":\"image\\\\\/jpeg\",\"title\":\"476.jpg\",\"quote_path\":\"custom_options\\\\\/quote\\\\\/4\\\\\/7\\\\\/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg\",\"order_path\":\"custom_options\\\\\/order\\\\\/4\\\\\/7\\\\\/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg\",\"fullpath\":\"C:\\\\\/www\\\\\/magento\\\\\/ce\\\\\/pub\\\\\/media\\\\\/custom_options\\\\\/quote\\\\\/4\\\\\/7\\\\\/bc61f16f0cc3a8c5abd7ebbaad4cc139.jpg\",\"size\":\"122340\",\"width\":666,\"height\":940,\"secret_key\":\"bc61f16f0cc3a8c5abd7\",\"url\":{\"route\":\"sales\\\\\/download\\\\\/downloadCustomOption\",\"params\":{\"id\":\"9\",\"key\":\"bc61f16f0cc3a8c5abd7\"}}}","custom_view":true},{"label":"testopt3","value":"222","print_value":"222","option_id":"3","option_type":"drop_down","option_value":"2","custom_view":false}]}', + ], + 'dataset_2' => [ + 'serialized' => 'a:2:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:36:"aHR0cDovL20yLmxvYy9idW5kbGUuaHRtbA,,";s:7:"product";s:1:"4";s:28:"selected_configurable_option";s:0:"";s:15:"related_product";s:0:"";s:13:"bundle_option";a:2:{i:1;s:1:"1";i:2;s:1:"2";}s:3:"qty";s:1:"3";}s:27:"bundle_selection_attributes";s:97:"a:4:{s:5:"price";d:100;s:3:"qty";d:1;s:12:"option_label";s:8:"option 1";s:9:"option_id";s:1:"1";}";}', + 'expectedJson' => '{"info_buyRequest":{"uenc":"aHR0cDovL20yLmxvYy9idW5kbGUuaHRtbA,,","product":"4","selected_configurable_option":"","related_product":"","bundle_option":{"1":"1","2":"2"},"qty":"3"},"bundle_selection_attributes":"{\"price\":100,\"qty\":1,\"option_label\":\"option 1\",\"option_id\":\"1\"}"}', + ], + ]; + // @codingStandardsIgnoreEnd + } +} diff --git a/app/code/Magento/Sales/etc/module.xml b/app/code/Magento/Sales/etc/module.xml index 980395e965e0899695206b7094b72f7f5b396ece..c0bef637833a1b1b52f9d628689f9aa512f64072 100644 --- a/app/code/Magento/Sales/etc/module.xml +++ b/app/code/Magento/Sales/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Sales" setup_version="2.0.4"> + <module name="Magento_Sales" setup_version="2.0.5"> <sequence> <module name="Magento_Rule"/> <module name="Magento_Catalog"/> diff --git a/app/code/Magento/Tax/Helper/Data.php b/app/code/Magento/Tax/Helper/Data.php index 3f9d9648b4bb487a2911f608ade732c340fadb3a..0626b336b33214c46c5da0ddca47a9fd39f290ec 100644 --- a/app/code/Magento/Tax/Helper/Data.php +++ b/app/code/Magento/Tax/Helper/Data.php @@ -3,27 +3,27 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Tax\Helper; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Store\Model\Store; use Magento\Customer\Model\Address; use Magento\Tax\Model\Config; -use Magento\Tax\Api\TaxCalculationInterface; use Magento\Customer\Model\Session as CustomerSession; use Magento\Tax\Api\OrderTaxManagementInterface; use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\Order\Creditmemo; use Magento\Tax\Api\Data\OrderTaxDetailsItemInterface; use Magento\Sales\Model\EntityInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; /** - * Catalog data helper + * Tax helper + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @codingStandardsIgnoreFile */ class Data extends \Magento\Framework\App\Helper\AbstractHelper { @@ -79,9 +79,7 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper protected $_localeResolver; /** - * \Magento\Catalog\Helper\Data - * - * @var CatalogHelper + * @var \Magento\Catalog\Helper\Data */ protected $catalogHelper; @@ -96,16 +94,24 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper protected $priceCurrency; /** - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Framework\Json\Helper\Data $jsonHelper - * @param Config $taxConfig - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Locale\FormatInterface $localeFormat + * @var Json + */ + private $serializer; + + /** + * Constructor + * + * @param \Magento\Framework\App\Helper\Context $context + * @param \Magento\Framework\Json\Helper\Data $jsonHelper + * @param Config $taxConfig + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Locale\FormatInterface $localeFormat * @param \Magento\Tax\Model\ResourceModel\Sales\Order\Tax\CollectionFactory $orderTaxCollectionFactory - * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param \Magento\Catalog\Helper\Data $catalogHelper - * @param OrderTaxManagementInterface $orderTaxManagement - * @param PriceCurrencyInterface $priceCurrency + * @param \Magento\Framework\Locale\ResolverInterface $localeResolver + * @param \Magento\Catalog\Helper\Data $catalogHelper + * @param OrderTaxManagementInterface $orderTaxManagement + * @param PriceCurrencyInterface $priceCurrency + * @param Json $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -118,7 +124,8 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper \Magento\Framework\Locale\ResolverInterface $localeResolver, \Magento\Catalog\Helper\Data $catalogHelper, OrderTaxManagementInterface $orderTaxManagement, - PriceCurrencyInterface $priceCurrency + PriceCurrencyInterface $priceCurrency, + Json $serializer = null ) { parent::__construct($context); $this->priceCurrency = $priceCurrency; @@ -130,6 +137,7 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper $this->_localeResolver = $localeResolver; $this->catalogHelper = $catalogHelper; $this->orderTaxManagement = $orderTaxManagement; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); } /** @@ -738,7 +746,7 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper $taxableItemType = $itemTaxDetail->getType(); $ratio = $itemRatio; if ($item->getTaxRatio()) { - $taxRatio = unserialize($item->getTaxRatio()); + $taxRatio = $this->serializer->unserialize($item->getTaxRatio()); if (isset($taxRatio[$taxableItemType])) { $ratio = $taxRatio[$taxableItemType]; } diff --git a/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php b/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php index a727beef10b0648934dbb21b89945ff2d1462193..6340c697d1009ca602daeb19e5f12c897f4e1642 100644 --- a/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php +++ b/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php @@ -6,50 +6,62 @@ namespace Magento\Tax\Model\Quote; use Magento\Quote\Api\Data\TotalSegmentExtensionFactory; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; class GrandTotalDetailsPlugin { /** * @var \Magento\Tax\Api\Data\GrandTotalDetailsInterfaceFactory */ - protected $detailsFactory; + private $detailsFactory; /** * @var \Magento\Tax\Api\Data\GrandTotalRatesInterfaceFactory */ - protected $ratesFactory; + private $ratesFactory; /** * @var TotalSegmentExtensionFactory */ - protected $totalSegmentExtensionFactory; + private $totalSegmentExtensionFactory; /** * @var \Magento\Tax\Model\Config */ - protected $taxConfig; + private $taxConfig; /** * @var string */ - protected $code; + private $code; /** + * @var Json + */ + private $serializer; + + /** + * Constructor + * * @param \Magento\Tax\Api\Data\GrandTotalDetailsInterfaceFactory $detailsFactory * @param \Magento\Tax\Api\Data\GrandTotalRatesInterfaceFactory $ratesFactory * @param TotalSegmentExtensionFactory $totalSegmentExtensionFactory * @param \Magento\Tax\Model\Config $taxConfig + * @param Json $serializer */ public function __construct( \Magento\Tax\Api\Data\GrandTotalDetailsInterfaceFactory $detailsFactory, \Magento\Tax\Api\Data\GrandTotalRatesInterfaceFactory $ratesFactory, TotalSegmentExtensionFactory $totalSegmentExtensionFactory, - \Magento\Tax\Model\Config $taxConfig + \Magento\Tax\Model\Config $taxConfig, + Json $serializer ) { $this->detailsFactory = $detailsFactory; $this->ratesFactory = $ratesFactory; $this->totalSegmentExtensionFactory = $totalSegmentExtensionFactory; $this->taxConfig = $taxConfig; + $this->serializer = $serializer; $this->code = 'tax'; } @@ -73,7 +85,6 @@ class GrandTotalDetailsPlugin * @param \Magento\Quote\Model\Cart\TotalsConverter $subject * @param \Magento\Quote\Api\Data\TotalSegmentInterface[] $totalSegments * @param \Magento\Quote\Model\Quote\Address\Total[] $addressTotals - * * @return \Magento\Quote\Api\Data\TotalSegmentInterface[] * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -97,7 +108,7 @@ class GrandTotalDetailsPlugin $finalData = []; $fullInfo = $taxes['full_info']; if (is_string($fullInfo)) { - $fullInfo = unserialize($fullInfo); + $fullInfo = $this->serializer->unserialize($fullInfo); } foreach ($fullInfo as $info) { if ((array_key_exists('hidden', $info) && $info['hidden']) diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php index da3b86958eb4b13493c8e624eb556688899936ae..819f5a06349eab445cf86428e5fe0fc19fa74446 100755 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php @@ -11,6 +11,8 @@ use Magento\Quote\Model\Quote\Address; use Magento\Tax\Api\Data\TaxClassKeyInterface; use Magento\Tax\Model\Calculation; use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; /** * Tax totals calculation model @@ -46,6 +48,11 @@ class Tax extends CommonTaxCollector */ protected $_discountTaxCompensationes = []; + /** + * @var Json + */ + private $serializer; + /** * Class constructor * @@ -57,6 +64,7 @@ class Tax extends CommonTaxCollector * @param CustomerAddressFactory $customerAddressFactory * @param CustomerAddressRegionFactory $customerAddressRegionFactory * @param \Magento\Tax\Helper\Data $taxData + * @param Json $serializer */ public function __construct( \Magento\Tax\Model\Config $taxConfig, @@ -66,10 +74,12 @@ class Tax extends CommonTaxCollector \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory, CustomerAddressFactory $customerAddressFactory, CustomerAddressRegionFactory $customerAddressRegionFactory, - \Magento\Tax\Helper\Data $taxData + \Magento\Tax\Helper\Data $taxData, + Json $serializer = null ) { $this->setCode('tax'); $this->_taxData = $taxData; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); parent::__construct( $taxConfig, $taxCalculationService, @@ -300,7 +310,7 @@ class Tax extends CommonTaxCollector $store = $quote->getStore(); $applied = $total->getAppliedTaxes(); if (is_string($applied)) { - $applied = unserialize($applied); + $applied = $this->serializer->unserialize($applied); } $amount = $total->getTaxAmount(); if ($amount === null) { diff --git a/app/code/Magento/Tax/Test/Unit/Helper/DataTest.php b/app/code/Magento/Tax/Test/Unit/Helper/DataTest.php index ea716a0c474b05572edab18014fa5856079b240a..7a292fdaedb549cccf0365ed968ba5e38b9dc5df 100644 --- a/app/code/Magento/Tax/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Tax/Test/Unit/Helper/DataTest.php @@ -12,6 +12,8 @@ use Magento\Framework\DataObject as MagentoObject; /** * Class DataTest + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataTest extends \PHPUnit_Framework_TestCase { @@ -29,6 +31,9 @@ class DataTest extends \PHPUnit_Framework_TestCase /** @var \PHPUnit_Framework_MockObject_MockObject */ protected $taxConfigMock; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $serializer; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -42,13 +47,31 @@ class DataTest extends \PHPUnit_Framework_TestCase $this->taxConfigMock = $this->getMockBuilder(\Magento\Tax\Model\Config::class) ->disableOriginalConstructor() ->getMock(); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); - $this->helper = $objectManager->getObject( + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $this->helper = $objectManager->getObject( \Magento\Tax\Helper\Data::class, [ 'orderTaxManagement' => $this->orderTaxManagementMock, 'priceCurrency' => $this->priceCurrencyMock, - 'taxConfig' => $this->taxConfigMock + 'taxConfig' => $this->taxConfigMock, + 'serializer' => $this->serializer ] ); } @@ -147,7 +170,7 @@ class DataTest extends \PHPUnit_Framework_TestCase $appliedTaxesData = $orderTaxDetailsItemData['applied_taxes']; $appliedTaxesMocks = []; foreach ($appliedTaxesData as $appliedTaxData) { - $appliedTaxesMock = $this->getMockBuilder( + $appliedTaxesMock = $this->getMockBuilder( \Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface::class) ->getMock(); $appliedTaxesMock->expects($this->any()) @@ -363,7 +386,7 @@ class DataTest extends \PHPUnit_Framework_TestCase ), 'tax_amount' => 5.0, //half of weee tax is invoiced - 'tax_ratio' => serialize(['weee' => 0.5]), + 'tax_ratio' => json_encode(['weee' => 0.5]), ] ), ], diff --git a/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php b/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php index 693b0d437afc45853b5ab224d04c535f1af1ca92..1b2e269e7add46edefdeedabfcb422f86538eed1 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php @@ -75,6 +75,26 @@ class GrandTotalDetailsPluginTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + + $serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $this->objectManagerHelper = new ObjectManager($this); $this->model = $this->objectManagerHelper->getObject( \Magento\Tax\Model\Quote\GrandTotalDetailsPlugin::class, @@ -83,6 +103,7 @@ class GrandTotalDetailsPluginTest extends \PHPUnit_Framework_TestCase 'ratesFactory' => $this->ratesFactoryMock, 'detailsFactory' => $this->detailsFactoryMock, 'taxConfig' => $this->taxConfigMock, + 'serializer' => $serializer ] ); } @@ -166,12 +187,12 @@ class GrandTotalDetailsPluginTest extends \PHPUnit_Framework_TestCase ); $taxTotalData = [ - 'full_info' => [ + 'full_info' => json_encode([ [ 'amount' => $taxAmount, 'rates' => [$taxRate], ], - ], + ]), ]; $taxTotalMock = $this->setupTaxTotal($taxTotalData); $addressTotals = [ diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php index a74b5fe13ec784a1dbb1d5f41a837a74c034df08..749ee1424d5031e905b1b7a7b270e1b61f7672fb 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php @@ -38,7 +38,7 @@ class TaxTest extends \PHPUnit_Framework_TestCase * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function testCollect($itemData, $appliedRatesData, $taxDetailsData, $quoteDetailsData, - $addressData, $verifyData + $addressData, $verifyData ) { $this->markTestIncomplete('Source code is not testable. Need to be refactored before unit testing'); $shippingAssignmentMock = $this->getMock(\Magento\Quote\Api\Data\ShippingAssignmentInterface::class); @@ -247,8 +247,8 @@ class TaxTest extends \PHPUnit_Framework_TestCase $address = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address::class) ->disableOriginalConstructor() ->setMethods(['getAssociatedTaxables', - 'getQuote', 'getBillingAddress', 'getRegionId', - '__wakeup', 'getCustomAttributesCodes']) + 'getQuote', 'getBillingAddress', 'getRegionId', + '__wakeup', 'getCustomAttributesCodes']) ->getMock(); $item ->expects($this->any()) @@ -613,13 +613,36 @@ class TaxTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(true)); $objectManager = new ObjectManager($this); + + $serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + + $serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + /** @var \Magento\Tax\Model\Sales\Total\Quote\Tax $taxTotalsCalcModel */ $taxTotalsCalcModel = $objectManager->getObject( \Magento\Tax\Model\Sales\Total\Quote\Tax::class, - ['taxConfig' => $taxConfig] + [ + 'taxConfig' => $taxConfig, + 'serializer' => $serializer + ] ); - $appliedTaxes = unserialize($appliedTaxesData); $store = $this->getMockBuilder(\Magento\Store\Model\Store::class) ->disableOriginalConstructor() ->setMethods(['convertPrice', '__wakeup']) @@ -641,7 +664,7 @@ class TaxTest extends \PHPUnit_Framework_TestCase $totalsMock ->expects($this->once()) ->method('getAppliedTaxes') - ->will($this->returnValue($appliedTaxes)); + ->will($this->returnValue($appliedTaxesData)); $totalsMock ->expects($this->any()) ->method('getGrandTotal') @@ -675,6 +698,7 @@ class TaxTest extends \PHPUnit_Framework_TestCase $totalsArray = $taxTotalsCalcModel->fetch($quote, $totalsMock); $this->assertArrayHasKey('value', $totalsArray[0]); $this->assertEquals($taxAmount, $totalsArray[0]['value']); + $this->assertEquals(json_decode($appliedTaxesData, true), $totalsArray[0]['full_info']); } /** @@ -685,10 +709,26 @@ class TaxTest extends \PHPUnit_Framework_TestCase */ public function dataProviderFetchArray() { - $appliedDataString = 'a:1:{s:7:"TX Rate";a:9:{s:6:"amount";d:80;s:11:"base_amount";d:80;s:7:"percent";'; - $appliedDataString .= 'd:10;s:2:"id";s:7:"TX Rate";s:5:"rates";a:1:{i:0;a:3:{s:7:"percent";d:10;s:4:"code";'; - $appliedDataString .= 's:7:"TX Rate";s:5:"title";s:7:"TX Rate";}}s:7:"item_id";s:1:"1";s:9:"item_type";'; - $appliedDataString .= 's:7:"product";s:18:"associated_item_id";N;s:7:"process";i:0;}}'; + $appliedDataString = [ + 'amount' => 80.0, + 'base_amount' => 80.0, + 'percent' => 10.0, + 'id' => 'TX Rate', + 'rates' => [ + 0 => [ + 'percent' => 10.0, + 'code' => 'TX Rate', + 'title' => 'TX Rate', + ], + ], + 'item_id' => '1', + 'item_type' => 'product', + 'associated_item_id' => NULL, + 'process' => 0, + ]; + + $appliedDataString = json_encode($appliedDataString); + $data = [ 'default' => [ 'appliedTaxesData' => $appliedDataString, diff --git a/app/code/Magento/Ui/Model/Manager.php b/app/code/Magento/Ui/Model/Manager.php index 348b7d398800d71bc763eecb719313b89f44252e..a2a4f05eb8bad7b2a4e379b552e245df9c00e13c 100644 --- a/app/code/Magento/Ui/Model/Manager.php +++ b/app/code/Magento/Ui/Model/Manager.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Ui\Model; @@ -17,6 +17,8 @@ use Magento\Framework\View\Element\UiComponent\Config\ManagerInterface; use Magento\Framework\View\Element\UiComponent\Config\Provider\Component\Definition as ComponentDefinition; use Magento\Framework\View\Element\UiComponent\Config\ReaderFactory; use Magento\Framework\View\Element\UiComponent\Config\UiReaderInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\App\ObjectManager; /** * Class Manager @@ -94,6 +96,11 @@ class Manager implements ManagerInterface */ protected $uiReader; + /** + * @var SerializerInterface + */ + private $serializer; + /** * @param ComponentDefinition $componentConfigProvider * @param DomMergerInterface $domMerger @@ -102,6 +109,7 @@ class Manager implements ManagerInterface * @param AggregatedFileCollectorFactory $aggregatedFileCollectorFactory * @param CacheInterface $cache * @param InterpreterInterface $argumentInterpreter + * @param SerializerInterface|null $serializer */ public function __construct( ComponentDefinition $componentConfigProvider, @@ -110,7 +118,8 @@ class Manager implements ManagerInterface ArrayObjectFactory $arrayObjectFactory, AggregatedFileCollectorFactory $aggregatedFileCollectorFactory, CacheInterface $cache, - InterpreterInterface $argumentInterpreter + InterpreterInterface $argumentInterpreter, + SerializerInterface $serializer = null ) { $this->componentConfigProvider = $componentConfigProvider; $this->domMerger = $domMerger; @@ -120,6 +129,7 @@ class Manager implements ManagerInterface $this->aggregatedFileCollectorFactory = $aggregatedFileCollectorFactory; $this->cache = $cache; $this->argumentInterpreter = $argumentInterpreter; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); } /** @@ -164,9 +174,14 @@ class Manager implements ManagerInterface $cachedPool = $this->cache->load($cacheID); if ($cachedPool === false) { $this->prepare($name); - $this->cache->save($this->componentsPool->serialize(), $cacheID); + $this->cache->save( + $this->serializer->serialize($this->componentsPool->getArrayCopy()), + $cacheID + ); } else { - $this->componentsPool->unserialize($cachedPool); + $this->componentsPool->exchangeArray( + $this->serializer->unserialize($cachedPool) + ); } $this->componentsData->offsetSet($name, $this->componentsPool); $this->componentsData->offsetSet($name, $this->evaluateComponentArguments($this->getData($name))); diff --git a/app/code/Magento/Ui/Test/Unit/Model/ManagerTest.php b/app/code/Magento/Ui/Test/Unit/Model/ManagerTest.php index 37f97af4597ac069312dbb1e1fb404e36cbdf12f..0e50950cc953eb4a24a2b36ff114a588b700d573 100644 --- a/app/code/Magento/Ui/Test/Unit/Model/ManagerTest.php +++ b/app/code/Magento/Ui/Test/Unit/Model/ManagerTest.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ @@ -75,6 +75,9 @@ class ManagerTest extends \PHPUnit_Framework_TestCase */ protected $aggregatedFileCollectorFactory; + /** @var \Magento\Framework\Serialize\SerializerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $serializer; + protected function setUp() { $this->componentConfigProvider = $this->getMockBuilder( @@ -105,6 +108,24 @@ class ManagerTest extends \PHPUnit_Framework_TestCase ->getMockForAbstractClass(); $this->argumentInterpreter = $this->getMockBuilder(\Magento\Framework\Data\Argument\InterpreterInterface::class) ->getMockForAbstractClass(); + $this->serializer = $this->getMockBuilder( + \Magento\Framework\Serialize\SerializerInterface::class + )->getMockForAbstractClass(); + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $this->manager = new Manager( $this->componentConfigProvider, $this->domMerger, @@ -112,7 +133,8 @@ class ManagerTest extends \PHPUnit_Framework_TestCase $this->arrayObjectFactory, $this->aggregatedFileCollectorFactory, $this->cacheConfig, - $this->argumentInterpreter + $this->argumentInterpreter, + $this->serializer ); } @@ -192,7 +214,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase [ 'test_component1', new \ArrayObject(), - $cachedData->serialize(), + json_encode($cachedData->getArrayCopy()), [], [ 'test_component1' => [ diff --git a/app/code/Magento/Wishlist/Model/Item.php b/app/code/Magento/Wishlist/Model/Item.php index c68eb5572cf59a9a1451a2a966479ed96ce37f0f..2db7ff7ac20322d3cef0983a9db6d7076b98ddf0 100644 --- a/app/code/Magento/Wishlist/Model/Item.php +++ b/app/code/Magento/Wishlist/Model/Item.php @@ -120,6 +120,13 @@ class Item extends AbstractModel implements ItemInterface */ protected $productRepository; + /** + * Serializer interface instance. + * + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -133,6 +140,7 @@ class Item extends AbstractModel implements ItemInterface * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -147,7 +155,8 @@ class Item extends AbstractModel implements ItemInterface ProductRepositoryInterface $productRepository, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->productTypeConfig = $productTypeConfig; $this->_storeManager = $storeManager; @@ -155,6 +164,8 @@ class Item extends AbstractModel implements ItemInterface $this->_catalogUrl = $catalogUrl; $this->_wishlistOptFactory = $wishlistOptFactory; $this->_wishlOptionCollectionFactory = $wishlOptionCollectionFactory; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); $this->productRepository = $productRepository; } @@ -472,7 +483,7 @@ class Item extends AbstractModel implements ItemInterface public function getBuyRequest() { $option = $this->getOptionByCode('info_buyRequest'); - $initialData = $option ? unserialize($option->getValue()) : null; + $initialData = $option ? $this->serializer->unserialize($option->getValue()) : null; if ($initialData instanceof \Magento\Framework\DataObject) { $initialData = $initialData->getData(); @@ -500,7 +511,7 @@ class Item extends AbstractModel implements ItemInterface } $oldBuyRequest = $this->getBuyRequest()->getData(); - $sBuyRequest = serialize($buyRequest + $oldBuyRequest); + $sBuyRequest = $this->serializer->serialize($buyRequest + $oldBuyRequest); $option = $this->getOptionByCode('info_buyRequest'); if ($option) { @@ -523,7 +534,7 @@ class Item extends AbstractModel implements ItemInterface { $buyRequest->setId($this->getId()); - $_buyRequest = serialize($buyRequest->getData()); + $_buyRequest = $this->serializer->serialize($buyRequest->getData()); $this->setData('buy_request', $_buyRequest); return $this; } diff --git a/app/code/Magento/Wishlist/Setup/UpgradeData.php b/app/code/Magento/Wishlist/Setup/UpgradeData.php new file mode 100644 index 0000000000000000000000000000000000000000..f18ca29acf333d7ebfc873b973427c6dfa54f0cc --- /dev/null +++ b/app/code/Magento/Wishlist/Setup/UpgradeData.php @@ -0,0 +1,126 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Wishlist\Setup; + +use Magento\Framework\Setup\UpgradeDataInterface; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\DB\FieldDataConverterFactory; +use Magento\Framework\DB\DataConverter\SerializedToJson; +use Magento\Framework\DB\Select\QueryModifierFactory; +use Magento\Framework\DB\Select\InQueryModifier; +use Magento\Framework\DB\Query\Generator; + +class UpgradeData implements UpgradeDataInterface +{ + /** + * @var FieldDataConverterFactory + */ + private $fieldDataConverterFactory; + + /** + * @var QueryModifierFactory + */ + private $queryModifierFactory; + + /** + * @var Generator + */ + private $queryGenerator; + + /** + * Constructor + * + * @param FieldDataConverterFactory $fieldDataConverterFactory + * @param QueryModifierFactory $queryModifierFactory + * @param Generator $queryGenerator + */ + public function __construct( + FieldDataConverterFactory $fieldDataConverterFactory, + QueryModifierFactory $queryModifierFactory, + Generator $queryGenerator + ) { + $this->fieldDataConverterFactory = $fieldDataConverterFactory; + $this->queryModifierFactory = $queryModifierFactory; + $this->queryGenerator = $queryGenerator; + } + + /** + * {@inheritdoc} + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + if (version_compare($context->getVersion(), '2.0.1', '<')) { + $this->upgradeToVersionTwoZeroOne($setup); + } + } + + /** + * Upgrade to version 2.0.1, convert data for `value` field in `wishlist_item_option table` + * from php-serialized to JSON format + * + * @param ModuleDataSetupInterface $setup + * @return void + */ + private function upgradeToVersionTwoZeroOne(ModuleDataSetupInterface $setup) + { + $fieldDataConverter = $this->fieldDataConverterFactory->create(SerializedToJson::class); + $queryModifier = $this->queryModifierFactory->create( + 'in', + [ + 'values' => [ + 'code' => [ + 'parameters', + 'info_buyRequest', + 'bundle_option_ids', + 'bundle_selection_ids', + 'attributes', + 'bundle_selection_attributes', + ] + ] + ] + ); + $fieldDataConverter->convert( + $setup->getConnection(), + $setup->getTable('wishlist_item_option'), + 'option_id', + 'value', + $queryModifier + ); + $select = $setup->getConnection() + ->select() + ->from( + $setup->getTable('catalog_product_option'), + ['option_id'] + ) + ->where('type = ?', 'file'); + $iterator = $this->queryGenerator->generate('option_id', $select); + foreach ($iterator as $selectByRange) { + $codes = $setup->getConnection()->fetchCol($selectByRange); + $codes = array_map( + function ($id) { + return 'option_' . $id; + }, + $codes + ); + $queryModifier = $this->queryModifierFactory->create( + 'in', + [ + 'values' => [ + 'code' => $codes + ] + ] + ); + $fieldDataConverter->convert( + $setup->getConnection(), + $setup->getTable('wishlist_item_option'), + 'option_id', + 'value', + $queryModifier + ); + } + } +} diff --git a/app/code/Magento/Wishlist/etc/module.xml b/app/code/Magento/Wishlist/etc/module.xml index a8b0fa21edee3e7e5222d290f686ed04c3c2e286..5643b2dc285a9e8158e41bafadc843e4de23005d 100644 --- a/app/code/Magento/Wishlist/etc/module.xml +++ b/app/code/Magento/Wishlist/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Wishlist" setup_version="2.0.0"> + <module name="Magento_Wishlist" setup_version="2.0.1"> <sequence> <module name="Magento_Customer"/> <module name="Magento_Catalog"/> diff --git a/app/etc/di.xml b/app/etc/di.xml index 1b347574f4b2a2d3f010cb9a712da6cd6c7dbec2..5e58d5887678d91fdcfcccc1f28f751fa3502177 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1214,4 +1214,11 @@ </argument> </arguments> </type> + <type name="Magento\Framework\DB\Select\QueryModifierFactory"> + <arguments> + <argument name="queryModifiers" xsi:type="array"> + <item name="in" xsi:type="string">Magento\Framework\DB\Select\InQueryModifier</item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml index 96e78f58d78c8402a133184b633614e4e4fd20c7..43b4a251edd1b5a97d68d458b7203bd4435d69e0 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml @@ -32,7 +32,7 @@ <constraint name="Magento\Newsletter\Test\Constraint\AssertCustomerIsSubscribedToNewsletter" /> </variation> <variation name="RegisterCustomerFrontendEntityTestVariation3" summary="Register Customer" ticketId="MAGETWO-12394"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, stable:no</data> <data name="customer/data/firstname" xsi:type="string">john</data> <data name="customer/data/lastname" xsi:type="string">doe</data> <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data> diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php index f36c17889aa30145019690c3f2ad302780aa4dd1..c3988187c686d939206c045c4dd9a78d2422de6c 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/MenuTest.php @@ -28,6 +28,11 @@ class MenuTest extends \PHPUnit_Framework_TestCase */ protected $backupRegistrar; + /** + * @var \Magento\Backend\Model\Menu\Config + */ + private $menuConfig; + protected function setUp() { $this->configCacheType = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -35,15 +40,18 @@ class MenuTest extends \PHPUnit_Framework_TestCase ); $this->configCacheType->save('', \Magento\Backend\Model\Menu\Config::CACHE_MENU_OBJECT); - $this->blockMenu = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Backend\Block\Menu::class - ); - $reflection = new \ReflectionClass(\Magento\Framework\Component\ComponentRegistrar::class); $paths = $reflection->getProperty('paths'); $paths->setAccessible(true); $this->backupRegistrar = $paths->getValue(); $paths->setAccessible(false); + + $this->menuConfig = $this->prepareMenuConfig(); + + $this->blockMenu = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Backend\Block\Menu::class, + ['menuConfig' => $this->menuConfig] + ); } /** @@ -51,8 +59,7 @@ class MenuTest extends \PHPUnit_Framework_TestCase */ public function testRenderNavigation() { - $menuConfig = $this->prepareMenuConfig(); - $menuHtml = $this->blockMenu->renderNavigation($menuConfig->getMenu()); + $menuHtml = $this->blockMenu->renderNavigation($this->menuConfig->getMenu()); $menu = new \SimpleXMLElement($menuHtml); $item = $menu->xpath('/ul/li/a/span')[0]; diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/MenuTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/MenuTest.php index f6fad14d210e9a2b2602014e353451b8f049c213..6c51e061f9db7af18ebf5629742c4edeb03771ca 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/MenuTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/MenuTest.php @@ -15,30 +15,28 @@ class MenuTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Backend\Model\Menu */ - protected $_model; + private $model; + + /** @var \Magento\Framework\ObjectManagerInterface */ + private $objectManager; protected function setUp() { parent::setUp(); \Magento\TestFramework\Helper\Bootstrap::getInstance() ->loadArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Backend\Model\Auth::class); - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\Config\ScopeInterface::class - )->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create(\Magento\Backend\Model\Auth::class); + $this->objectManager->get(\Magento\Framework\Config\ScopeInterface::class) + ->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); } public function testMenuItemManipulation() { /* @var $menu \Magento\Backend\Model\Menu */ - $menu = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Backend\Model\Menu\Config::class - )->getMenu(); + $menu = $this->objectManager->create(\Magento\Backend\Model\Menu\Config::class)->getMenu(); /* @var $itemFactory \Magento\Backend\Model\Menu\Item\Factory */ - $itemFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Backend\Model\Menu\Item\Factory::class - ); + $itemFactory = $this->objectManager->create(\Magento\Backend\Model\Menu\Item\Factory::class); // Add new item in top level $menu->add( @@ -52,7 +50,7 @@ class MenuTest extends \PHPUnit_Framework_TestCase ) ); - //Add submenu + // Add submenu $menu->add( $itemFactory->create( [ @@ -79,4 +77,100 @@ class MenuTest extends \PHPUnit_Framework_TestCase // Move menu item $menu->move('Magento_Catalog::catalog_products', 'Magento_Backend::system2'); } + + /** + * @magentoAppIsolation enabled + */ + public function testSerialize() + { + /** @var Menu $menu */ + $menu = $this->objectManager->get(\Magento\Backend\Model\MenuFactory::class)->create(); + /* @var \Magento\Backend\Model\Menu\Item\Factory $itemFactory */ + $itemFactory = $this->objectManager->create(\Magento\Backend\Model\Menu\Item\Factory::class); + + // Add new item in top level + $menu->add( + $itemFactory->create( + [ + 'id' => 'Magento_Backend::system3', + 'title' => 'Extended System', + 'module' => 'Magento_Backend', + 'resource' => 'Magento_Backend::system3', + ] + ) + ); + + // Add submenu + $menu->add( + $itemFactory->create( + [ + 'id' => 'Magento_Backend::system3_acl', + 'title' => 'Acl', + 'module' => 'Magento_Backend', + 'action' => 'admin/backend/acl/index', + 'resource' => 'Magento_Backend::system3_acl', + ] + ), + 'Magento_Backend::system3' + ); + $serializedString = $menu->serialize(); + $expected = '[{"parent_id":null,"module_name":"Magento_Backend","sort_index":null,"depends_on_config":null,' + . '"id":"Magento_Backend::system3","resource":"Magento_Backend::system3","path":"","action":null,' + . '"depends_on_module":null,"tooltip":"","title":"Extended System",' + . '"sub_menu":[{"parent_id":null,"module_name":"Magento_Backend","sort_index":null,' + . '"depends_on_config":null,"id":"Magento_Backend::system3_acl","resource":"Magento_Backend::system3_acl",' + . '"path":"","action":"admin\/backend\/acl\/index","depends_on_module":null,"tooltip":"","title":"Acl",' + . '"sub_menu":null}]}]'; + $this->assertEquals($expected, $serializedString); + } + + /** + * @magentoAppIsolation enabled + */ + public function testUnserialize() + { + $serializedMenu = '[{"parent_id":null,"module_name":"Magento_Backend","sort_index":null,' + . '"depends_on_config":null,"id":"Magento_Backend::system3","resource":"Magento_Backend::system3",' + . '"path":"","action":null,"depends_on_module":null,"tooltip":"","title":"Extended System",' + . '"sub_menu":[{"parent_id":null,"module_name":"Magento_Backend","sort_index":null,' + . '"depends_on_config":null,"id":"Magento_Backend::system3_acl","resource":"Magento_Backend::system3_acl",' + . '"path":"","action":"admin\/backend\/acl\/index","depends_on_module":null,"tooltip":"","title":"Acl",' + . '"sub_menu":null}]}]'; + /** @var Menu $menu */ + $menu = $this->objectManager->get(\Magento\Backend\Model\MenuFactory::class)->create(); + $menu->unserialize($serializedMenu); + $expected = [ + [ + 'parent_id' => null, + 'module_name' => 'Magento_Backend', + 'sort_index' => null, + 'depends_on_config' => null, + 'id' => 'Magento_Backend::system3', + 'resource' => 'Magento_Backend::system3', + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => 'Extended System', + 'sub_menu' => + [ + [ + 'parent_id' => null, + 'module_name' => 'Magento_Backend', + 'sort_index' => null, + 'depends_on_config' => null, + 'id' => 'Magento_Backend::system3_acl', + 'resource' => 'Magento_Backend::system3_acl', + 'path' => '', + 'action' => 'admin/backend/acl/index', + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => 'Acl', + 'sub_menu' => null, + ], + ], + ], + ]; + $this->assertEquals($expected, $menu->toArray()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/DateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/DateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..280996a81d7f609e9ee7cd5351de50ea6ccab0f1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/DateTest.php @@ -0,0 +1,112 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Option\Type; + +/** + * Test for \Magento\Catalog\Model\Product\Option\Type\Date + */ +class DateTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Model\Product\Option\Type\Date + */ + protected $model; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create( + \Magento\Catalog\Model\Product\Option\Type\Date::class + ); + } + + /** + * @covers \Magento\Catalog\Model\Product\Option\Type\Date::prepareOptionValueForRequest() + * @dataProvider prepareOptionValueForRequestDataProvider + * @param array $optionValue + * @param array $infoBuyRequest + * @param array $expectedOptionValueForRequest + * @param array $productOptionData + */ + public function testPrepareOptionValueForRequest( + array $optionValue, + array $infoBuyRequest, + array $productOptionData, + array $expectedOptionValueForRequest + ) { + /** @var \Magento\Quote\Model\Quote\Item\Option $option */ + $option = $this->objectManager->create( + \Magento\Quote\Model\Quote\Item\Option::class, + ['data' => $infoBuyRequest] + ); + /** @var \Magento\Quote\Model\Quote\Item $item */ + $item = $this->objectManager->create(\Magento\Quote\Model\Quote\Item::class); + $item->addOption($option); + /** @var \Magento\Catalog\Model\Product\Option|null $productOption */ + $productOption = $productOptionData + ? $this->objectManager->create( + \Magento\Catalog\Model\Product\Option::class, + ['data' => $productOptionData] + ) + : null; + $this->model->setData('quote_item', $item); + $this->model->setOption($productOption); + + $actualOptionValueForRequest = $this->model->prepareOptionValueForRequest($optionValue); + $this->assertSame($expectedOptionValueForRequest, $actualOptionValueForRequest); + } + + /** + * @return array + */ + public function prepareOptionValueForRequestDataProvider() + { + return [ + // Variation 1 + [ + // $optionValue + ['field1' => 'value1', 'field2' => 'value2'], + // $infoBuyRequest + ['code' => 'info_buyRequest', 'value' => '{"qty":23}'], + // $productOptionData + ['id' => '11', 'value' => '{"qty":12}'], + // $expectedOptionValueForRequest + ['date_internal' => ['field1' => 'value1', 'field2' => 'value2']] + ], + // Variation 2 + [ + // $optionValue + ['field1' => 'value1', 'field2' => 'value2'], + // $infoBuyRequest + ['code' => 'info_buyRequest', 'value' => '{"options":{"11":{"qty":23}}}'], + // $productOptionData + ['id' => '11', 'value' => '{"qty":12}'], + // $expectedOptionValueForRequest + ['qty' => 23] + ], + // Variation 3 + [ + // $optionValue + ['field1' => 'value1', 'field2' => 'value2'], + // $infoBuyRequest + ['code' => 'info_buyRequest', 'value' => '{"options":{"11":{"qty":23}}}'], + // $productOptionData + [], + // $expectedOptionValueForRequest + ['date_internal' => ['field1' => 'value1', 'field2' => 'value2']] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php index 6f98f0f413bf2e3bcd336ca1c392b39462a48f82..17c29a4e82ad345871c49cc0ee12f9ae5005d74a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php @@ -35,6 +35,9 @@ class AbstractTypeTest extends \PHPUnit_Framework_TestCase $filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); $registry = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); $logger = $this->getMock(\Psr\Log\LoggerInterface::class, [], [], '', false); + $serializer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Framework\Serialize\Serializer\Json::class + ); $this->_model = $this->getMockForAbstractClass( \Magento\Catalog\Model\Product\Type\AbstractType::class, [ @@ -46,7 +49,8 @@ class AbstractTypeTest extends \PHPUnit_Framework_TestCase $filesystem, $registry, $logger, - $productRepository + $productRepository, + $serializer ] ); } @@ -186,7 +190,7 @@ class AbstractTypeTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf(\Magento\Framework\DataObject::class, $buyRequest); $this->assertEquals($product->getId(), $buyRequest->getProductId()); $this->assertSame($product, $buyRequest->getProduct()); - $this->assertEquals(serialize($requestData), $buyRequest->getValue()); + $this->assertEquals(json_encode($requestData), $buyRequest->getValue()); } /** diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_payment_saved.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_payment_saved.php index c3dbc4baaf3f77b38c9e18948798e814cc545cc7..f1759a9eb0c4ba12edaaceb569d788c4129496e1 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_payment_saved.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_payment_saved.php @@ -6,6 +6,9 @@ require 'quote_with_address.php'; +/** @var \Magento\Framework\Serialize\Serializer\Json $serializer */ +$serializer = $objectManager->create(\Magento\Framework\Serialize\Serializer\Json::class); + $quote->setReservedOrderId( 'test_order_1_with_payment' ); @@ -22,7 +25,7 @@ $quote->getPayment() ->setCcType('visa') ->setCcExpYear(2014) ->setCcExpMonth(1) - ->setAdditionalData(serialize($paymentDetails)); + ->setAdditionalData($serializer->serialize($paymentDetails)); $quote->collectTotals()->save(); 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 59d828baa2d5ce381e3e2076d51bad0c1408a1bf..69179686d3ee47e0e0600d264fb4e21aef2a102b 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 @@ -11,7 +11,6 @@ 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; /** @@ -294,29 +293,42 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase */ public function testGetSelectedAttributesInfo() { + /** @var $serializer \Magento\Framework\Serialize\Serializer\Json */ + $serializer = Bootstrap::getObjectManager()->create(\Magento\Framework\Serialize\Serializer\Json::class); + $product = $this->productRepository->getById(1, true); $attributes = $this->model->getConfigurableAttributesAsArray($product); $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; - $product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId])); + $product->addCustomOption('attributes', + $serializer->serialize([$attribute['attribute_id'] => $optionValueId]) + ); + $info = $this->model->getSelectedAttributesInfo($product); $this->assertEquals('Test Configurable', $info[0]['label']); $this->assertEquals('Option 1', $info[0]['value']); } /** + * @covers \Magento\ConfigurableProduct\Model\Product\Type\Configurable::getConfigurableAttributes() * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @magentoAppIsolation enabled */ public function testGetSelectedAttributesInfoForStore() { + /** @var $serializer \Magento\Framework\Serialize\Serializer\Json */ + $serializer = Bootstrap::getObjectManager()->create(\Magento\Framework\Serialize\Serializer\Json::class); + $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', + $serializer->serialize([$attribute['attribute_id'] => $optionValueId]) + ); $configurableAttr = $this->model->getConfigurableAttributes($this->product); $attribute = $configurableAttr->getFirstItem(); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Product/TypeTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Product/TypeTest.php index c9c9109ac95dfdef8d40ec1c69ff12547d201dd7..c3b6d668fdad11f7a154099ce404222b41aca303 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Product/TypeTest.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Product/TypeTest.php @@ -19,9 +19,15 @@ class TypeTest extends \PHPUnit_Framework_TestCase */ protected $_model; + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + protected function setUp() { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->_model = $this->objectManager->create( \Magento\Downloadable\Model\Product\Type::class ); } @@ -216,4 +222,37 @@ class TypeTest extends \PHPUnit_Framework_TestCase $this->assertEquals($value, $sample[$key]); } } + + /** + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Downloadable/_files/product_downloadable.php + * @covers \Magento\Downloadable\Model\Product\Type::checkProductBuyState() + */ + public function testCheckProductBuyState() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository =$this->objectManager->create( + \Magento\Catalog\Api\ProductRepositoryInterface::class + ); + $product = $productRepository->get('downloadable-product'); + $product->setLinksPurchasedSeparately(false); + $productRepository->save($product); + /** @var \Magento\Quote\Model\Quote\Item\Option $option */ + $option = $this->objectManager->create( + \Magento\Quote\Model\Quote\Item\Option::class, + ['data' => ['code' => 'info_buyRequest', 'value' => '{"qty":23}']] + ); + $option->setProduct($product); + $product->setCustomOptions(['info_buyRequest' => $option]); + + $this->_model->checkProductBuyState($product); + $linksFactory = $this->objectManager + ->get(\Magento\Downloadable\Model\ResourceModel\Link\CollectionFactory::class); + $allLinksIds = $linksFactory->create()->addProductToFilter($product->getEntityId())->getAllIds(); + $this->assertEquals( + '{"qty":23,"links":["' . implode('","', $allLinksIds). '"]}', + $product->getCustomOption('info_buyRequest')->getValue() + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/order_with_downloadable_product_with_additional_options.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/order_with_downloadable_product_with_additional_options.php new file mode 100644 index 0000000000000000000000000000000000000000..46caf347a098b8eddec390c6ec706696c3381ef8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/order_with_downloadable_product_with_additional_options.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +$billingAddress = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order\Address::class, + [ + 'data' => [ + 'firstname' => 'guest', + 'lastname' => 'guest', + 'email' => 'customer@example.com', + 'street' => 'street', + 'city' => 'Los Angeles', + 'region' => 'CA', + 'postcode' => '1', + 'country_id' => 'US', + 'telephone' => '1', + ] + ] +); +$billingAddress->setAddressType('billing'); + +$payment = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); + +$orderItem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId( + 1 +)->setProductType( + \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE +)->setBasePrice( + 100 +)->setQtyOrdered( + 1 +); +$orderItem->setProductOptions(['additional_options' => ['additional_option_key' => 'additional_option_value']]); + +$order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Sales\Model\Order::class); +$order->setCustomerEmail('mail@to.co') + ->addItem( + $orderItem +)->setIncrementId( + '100000001' +)->setCustomerIsGuest( + true +)->setStoreId( + 1 +)->setEmailSent( + 1 +)->setBillingAddress( + $billingAddress +)->setPayment( + $payment +); +$order->save(); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index dfff15995ecf4e9ccd4dc15b12630f55348c4999..97762b6c39c3c9ee305053100fd3f82cbb16f1c5 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -80,4 +80,51 @@ class GroupedTest extends \PHPUnit_Framework_TestCase $this->assertEquals($data[$productId]['qty'], $product->getQty()); $this->assertEquals($data[$productId]['position'], $product->getPosition()); } + + /** + * @magentoDataFixture Magento/GroupedProduct/_files/product_grouped.php + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ + public function testPrepareProduct() + { + $buyRequest = $this->objectManager->create( + \Magento\Framework\DataObject::class, + ['data' => ['value' => ['qty' => 2]]] + ); + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('grouped-product'); + + /** @var \Magento\GroupedProduct\Model\Product\Type\Grouped $type */ + $type = $this->objectManager->get(\Magento\GroupedProduct\Model\Product\Type\Grouped::class); + + $processModes = [ + \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_FULL, + \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_LITE + ]; + $expectedData = [ + \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_FULL => [ + 1 => '{"super_product_config":{"product_type":"grouped","product_id":"' + . $product->getId() . '"}}', + 21 => '{"super_product_config":{"product_type":"grouped","product_id":"' + . $product->getId() . '"}}', + ], + \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_LITE => [ + $product->getId() => '{"value":{"qty":2}}', + ] + ]; + + foreach ($processModes as $processMode) { + $products = $type->processConfiguration($buyRequest, $product, $processMode); + foreach ($products as $item) { + $productId = $item->getId(); + $this->assertEquals( + $expectedData[$processMode][$productId], + $item->getCustomOptions()['info_buyRequest']->getValue(), + "Wrong info_buyRequest data for product with id: $productId" + ); + } + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index d71f5c2df7e10d433042d807a4710e8ff7320498..ad7b87407608cc06db83ab6b801e7089dc3bfa7c 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -285,4 +285,28 @@ class AddressTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->_quote->getId(), $this->_address->getQuoteId()); $this->assertEquals($customerAddressId, $this->_address->getCustomerAddressId()); } + + /** + * Tests + * + * @covers \Magento\Quote\Model\Quote\Address::setAppliedTaxes() + * @covers \Magento\Quote\Model\Quote\Address::getAppliedTaxes() + * @dataProvider dataProvider + * @param $taxes + * @param $expected + */ + public function testAppliedTaxes($taxes, $expected) + { + $this->_address->setAppliedTaxes($taxes); + + $this->assertSame($expected, $this->_address->getAppliedTaxes()); + } + + public function dataProvider() + { + return [ + ['test', 'test'], + [[123, true], [123, true]] + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php index f8ca1929df91e1452ef6f66bd594ebd0e3a3d5fd..acbbbef1b1ed67f25d9f3bd5aa51cf4e120dac78 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php @@ -52,6 +52,63 @@ class CreateTest extends \PHPUnit_Framework_TestCase $this->assertNull($order->getShippingAddress()); } + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Downloadable/_files/product_downloadable.php + * @magentoDataFixture Magento/Downloadable/_files/order_with_downloadable_product_with_additional_options.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testInitFromOrderAndCreateOrderFromQuoteWithAdditionalOptions() + { + /** @var $serializer \Magento\Framework\Serialize\Serializer\Json */ + $serializer = Bootstrap::getObjectManager()->create(\Magento\Framework\Serialize\Serializer\Json::class); + + /** @var $order \Magento\Sales\Model\Order */ + $order = Bootstrap::getObjectManager()->create(\Magento\Sales\Model\Order::class); + $order->loadByIncrementId('100000001'); + + /** @var $orderCreate \Magento\Sales\Model\AdminOrder\Create */ + $orderCreate = $this->_model->initFromOrder($order); + + $quoteItems = $orderCreate->getQuote()->getItemsCollection(); + + $this->assertEquals(1, $quoteItems->count()); + + $quoteItem = $quoteItems->getFirstItem(); + $quoteItemOptions = $quoteItem->getOptionsByCode(); + + $this->assertEquals( + $serializer->serialize(['additional_option_key' => 'additional_option_value']), + $quoteItemOptions['additional_options']->getValue() + ); + + $session = Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session\Quote::class); + $session->setCustomerId(1); + + $customer = Bootstrap::getObjectManager()->create(\Magento\Customer\Model\Customer::class); + $customer->load(1)->setDefaultBilling(null)->setDefaultShipping(null)->save(); + + $rate = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote\Address\Rate::class); + $rate->setCode('freeshipping_freeshipping'); + + $this->_model->getQuote()->getShippingAddress()->addShippingRate($rate); + $this->_model->setShippingAsBilling(0); + $this->_model->setPaymentData(['method' => 'checkmo']); + + $newOrder = $this->_model->createOrder(); + $newOrderItems = $newOrder->getItemsCollection(); + + $this->assertEquals(1, $newOrderItems->count()); + + $newOrderItem = $newOrderItems->getFirstItem(); + + $this->assertEquals( + ['additional_option_key' => 'additional_option_value'], + $newOrderItem->getProductOptionByCode('additional_options') + ); + } + /** * @magentoDataFixture Magento/Downloadable/_files/product_downloadable.php * @magentoDataFixture Magento/Downloadable/_files/order_with_downloadable_product.php @@ -451,9 +508,9 @@ class CreateTest extends \PHPUnit_Framework_TestCase } /** - * @magentoAppIsolation enabled * @magentoDataFixture Magento/Sales/_files/quote.php * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoAppIsolation enabled */ public function testGetCustomerCartExistingCart() { @@ -477,6 +534,32 @@ class CreateTest extends \PHPUnit_Framework_TestCase $this->assertSame($customerQuote, $customerQuoteFromCache, 'Customer quote caching does not work correctly.'); } + /** + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoAppIsolation enabled + */ + public function testMoveQuoteItemToCart() + { + $fixtureCustomerId = 1; + + /** Preconditions */ + /** @var \Magento\Backend\Model\Session\Quote $session */ + $session = Bootstrap::getObjectManager()->create(\Magento\Backend\Model\Session\Quote::class); + $session->setCustomerId($fixtureCustomerId); + /** @var $quoteFixture \Magento\Quote\Model\Quote */ + $quoteFixture = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); + $quoteFixture->load('test01', 'reserved_order_id'); + $quoteFixture->setCustomerIsGuest(false)->setCustomerId($fixtureCustomerId)->save(); + + $customerQuote = $this->_model->getCustomerCart(); + $item = $customerQuote->getAllVisibleItems()[0]; + + $this->_model->moveQuoteItem($item, 'cart', 3); + $this->assertEquals(4, $item->getQty(), 'Number of Qty isn\'t correct for Quote item.'); + $this->assertEquals(3, $item->getQtyToAdd(), 'Number of added qty isn\'t correct for Quote item.'); + } + /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreditmemoFactoryTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreditmemoFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..64de21811dddf880fe79eab56feacbbda9f86891 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreditmemoFactoryTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order; + +/** + * Test for CreditmemoFactory class. + * @magentoDbIsolation enabled + */ +class CreditmemoFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * Placeholder for order item id field. + */ + const ORDER_ITEM_ID_PLACEHOLDER = 'id_item_'; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_dummy_item_and_invoiced.php + * @dataProvider createByOrderDataProvider + * @param array $creditmemoData + * @param int $expectedQty + */ + public function testCreateByOrder(array $creditmemoData, $expectedQty) + { + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); + $order->loadByIncrementId('100000001'); + /** @var \Magento\Sales\Model\Order\CreditmemoFactory $creditmemoFactory */ + $creditmemoFactory = $this->objectManager->create(\Magento\Sales\Model\Order\CreditmemoFactory::class); + $creditmemoData = $this->prepareCreditMemoData($order, $creditmemoData); + $creditmemo = $creditmemoFactory->createByOrder($order, $creditmemoData); + $this->assertEquals($expectedQty, $creditmemo->getTotalQty(), 'Creditmemo has wrong total qty.'); + } + + /** + * Prepare Creditmemo data. + * + * @param \Magento\Sales\Model\Order $order + * @param array $creditmemoData + * @return array + */ + private function prepareCreditMemoData(\Magento\Sales\Model\Order $order, array $creditmemoData) + { + $result = []; + $orderItems = $order->getAllItems(); + foreach ($creditmemoData['qtys'] as $key => $item) { + $result[$orderItems[$this->prepareOrderItemKey($key)]->getId()] = $item; + } + $creditmemoData['qtys'] = $result; + + return $creditmemoData; + } + + /** + * Prepare order item key. + * + * @param string $key + * @return int + */ + private function prepareOrderItemKey($key) + { + return str_replace(self::ORDER_ITEM_ID_PLACEHOLDER, '', $key) - 1; + } + + /** + * @return array + */ + public function createByOrderDataProvider() + { + return [ + // Variation #1 + [ + //$creditmemoData + [ + 'qtys' => [ + self::ORDER_ITEM_ID_PLACEHOLDER . '1' => 1, + self::ORDER_ITEM_ID_PLACEHOLDER . '2' => 1, + ] + ], + //$expectedQty + 4 + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ItemTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ItemTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2b0ec3bc7cac9f5cbceace9cdabefec30d98d980 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ItemTest.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Model\Order; + +/** + * Item test class. + */ +class ItemTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param string $options + * @param array $expectedData + * @dataProvider getProductOptionsDataProvider + */ + public function testGetProductOptions($options, $expectedData) + { + $model = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Sales\Model\Order\Item::class); + $model->setData('product_options', $options); + $this->assertEquals($expectedData, $model->getProductOptions()); + } + + /** + * @return array + */ + public function getProductOptionsDataProvider() + { + return [ + // Variation #1 + [ + // $options + '{"option1":1,"option2":2}', + //$expectedData + ["option1" => 1, "option2" => 2] + ], + // Variation #2 + [ + // $options + 'a:2:{s:7:"option1";i:1;s:7:"option2";i:2;}', + //$expectedData + null + ], + // Variation #3 + [ + // $options + ["option1" => 1, "option2" => 2], + //$expectedData + ["option1" => 1, "option2" => 2] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_dummy_item_and_invoiced.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_dummy_item_and_invoiced.php new file mode 100644 index 0000000000000000000000000000000000000000..49671242a6793299af729629affa5ccadb187923 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_dummy_item_and_invoiced.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +require 'order.php'; +/** @var \Magento\Sales\Model\Order $order */ + +$orderItems = [ + [ + \Magento\Sales\Api\Data\OrderItemInterface::PRODUCT_ID => 2, + \Magento\Sales\Api\Data\OrderItemInterface::BASE_PRICE => 100, + \Magento\Sales\Api\Data\OrderItemInterface::ORDER_ID => $order->getId(), + \Magento\Sales\Api\Data\OrderItemInterface::QTY_ORDERED => 2, + \Magento\Sales\Api\Data\OrderItemInterface::QTY_INVOICED => 2, + \Magento\Sales\Api\Data\OrderItemInterface::PRICE => 100, + \Magento\Sales\Api\Data\OrderItemInterface::ROW_TOTAL => 102, + \Magento\Sales\Api\Data\OrderItemInterface::PRODUCT_TYPE => 'bundle', + 'children' => [ + [ + \Magento\Sales\Api\Data\OrderItemInterface::PRODUCT_ID => 13, + \Magento\Sales\Api\Data\OrderItemInterface::ORDER_ID => $order->getId(), + \Magento\Sales\Api\Data\OrderItemInterface::QTY_ORDERED => 2, + \Magento\Sales\Api\Data\OrderItemInterface::QTY_INVOICED => 2, + \Magento\Sales\Api\Data\OrderItemInterface::BASE_PRICE => 90, + \Magento\Sales\Api\Data\OrderItemInterface::PRICE => 90, + \Magento\Sales\Api\Data\OrderItemInterface::ROW_TOTAL => 92, + \Magento\Sales\Api\Data\OrderItemInterface::PRODUCT_TYPE => 'simple', + 'product_options' => [ + 'bundle_selection_attributes' => '{"qty":2}', + ], + ] + ], + ] +]; + +// Invoiced all existing order items. +foreach ($order->getAllItems() as $item) { + $item->setQtyInvoiced(1); + $item->save(); +} + +saveOrderItems($orderItems); + + +/** + * Save Order Items. + * + * @param array $orderItems + * @param \Magento\Sales\Model\Order\Item|null $parentOrderItem [optional] + * @return void + */ +function saveOrderItems(array $orderItems, $parentOrderItem = null) +{ + /** @var array $orderItemData */ + foreach ($orderItems as $orderItemData) { + /** @var $orderItem \Magento\Sales\Model\Order\Item */ + $orderItem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order\Item::class + ); + if (null !== $parentOrderItem) { + $orderItemData['parent_item'] = $parentOrderItem; + } + $orderItem + ->setData($orderItemData) + ->save(); + + if (isset($orderItemData['children'])) { + saveOrderItems($orderItemData['children'], $orderItem); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php new file mode 100644 index 0000000000000000000000000000000000000000..004b605a7f1d7e6c355c4a6981961b347523b18c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Wishlist\Model; + +/** + * Item test class. + */ +class ItemTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\App\ObjectManager + */ + private $objectManager; + + /** + * @var \Magento\Wishlist\Model\Item + */ + private $model; + + /** + * {@inheritDoc} + */ + public function setUp() + { + $this->objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $this->model = $this->objectManager->get(\Magento\Wishlist\Model\Item::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testBuyRequest() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->getById(1); + + /** @var \Magento\Wishlist\Model\Item\Option $option */ + $option = $this->objectManager->create( + \Magento\Wishlist\Model\Item\Option::class, + ['data' => ['code' => 'info_buyRequest', 'value' => '{"qty":23}']] + ); + $option->setProduct($product); + $this->model->addOption($option); + + // Assert getBuyRequest method + $buyRequest = $this->model->getBuyRequest(); + $this->assertEquals($buyRequest->getOriginalQty(), 23); + + // Assert mergeBuyRequest method + $this->model->mergeBuyRequest(['qty' => 11, 'additional_data' => 'some value']); + $buyRequest = $this->model->getBuyRequest(); + $this->assertEquals( + ['additional_data' => 'some value', 'qty' => 0, 'original_qty' => 11], + $buyRequest->getData() + ); + } + + public function testSetBuyRequest() + { + $buyRequest = $this->objectManager->create( + \Magento\Framework\DataObject::class, + ['data' => ['field_1' => 'some data', 'field_2' => 234]] + ); + + $this->model->setBuyRequest($buyRequest); + + $this->assertEquals( + '{"field_1":"some data","field_2":234,"id":null}', + $this->model->getData('buy_request') + ); + } +} diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js index e5f90863ec63071f191b82b0df7fdbba0ac411fa..3e99c1c454cf4b2261d99e3623f365570684a1e3 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ @@ -18,21 +18,23 @@ define([ config; beforeEach(function () { - element = $('<input />'); + element = $('<input />'); observable = ko.observable(); config = { - options : { + options: { dateFormat: 'M/d/yy', - 'storeLocale': 'en_US', - 'timeFormat': 'h:mm: a' + storeLocale: 'en_US', + timeFormat: 'h:mm: a' }, - storage:ko.observable(moment().format('MM/DD/YYYY')) + storage: observable }; $(document.body).append(element); - ko.applyBindingsToNode(element[0], { datepicker: config }); + ko.applyBindingsToNode(element[0], { + datepicker: config + }); }); afterEach(function () { @@ -40,20 +42,16 @@ define([ }); it('writes picked date\'s value to assigned observable', function () { - var todayDate, - momentFormat, - result, - inputFormat; - - inputFormat = 'M/d/yy'; + var todayDate, momentFormat, result, + inputFormat = 'M/d/yy'; momentFormat = utils.convertToMomentFormat(inputFormat); + todayDate = moment().format(momentFormat); - todayDate = moment().format(momentFormat); - - result = $('input:last').val(); + element.datepicker('setTimezoneDate').blur().trigger('change'); + result = moment(observable()).format(momentFormat); expect(todayDate).toEqual(result); }); }); -}); \ No newline at end of file +}); diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php index ec670e2f2ea249c19c3ef2bd4001ce6a9d177fcc..2c3ee9649dc3ae37c82d41ad707fa0b25bf0853e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php @@ -196,10 +196,11 @@ class ObsoleteCodeTest extends \PHPUnit_Framework_TestCase */ protected function _testObsoleteClasses($content) { + /* avoid collision between obsolete class name and valid namespace and package tag */ + $content = preg_replace('/namespace[^;]+;/', '', $content); + $content = preg_replace('/\@package\s[a-zA-Z0-9\\\_]+/', '', $content); foreach (self::$_classes as $row) { list($class, , $replacement) = $row; - /* avoid collision between obsolete class name and valid namespace */ - $content = preg_replace('/namespace[^;]+;/', '', $content); $this->_assertNotRegExp( '/[^a-z\d_]' . preg_quote($class, '/') . '[^a-z\d_\\\\]/iS', $content, diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php index 683449d4e5e34b919ef476d93c26fe01d122b0a8..1c593657742f9f5605f94ce116ae89c05fb54962 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php @@ -45,11 +45,6 @@ return [ 'Magento\Framework\Serialize\Serializer\Serialize' => [ 'replacement' => 'Magento\Framework\Serialize\SerializerInterface', 'exclude' => [ - [ - 'type' => 'library', - 'name' => 'magento/framework', - 'path' => 'DB/Adapter/Pdo/Mysql.php' - ], [ 'type' => 'library', 'name' => 'magento/framework', @@ -69,6 +64,21 @@ return [ 'name' => 'magento/framework', 'path' => 'App/ObjectManager/ConfigLoader.php' ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/Adapter/Pdo/Mysql.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/DataConverter/SerializedToJson.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/Test/Unit/DataConverter/SerializedToJsonTest.php' + ], [ 'type' => 'library', 'name' => 'magento/framework', @@ -98,6 +108,16 @@ return [ 'type' => 'setup', 'path' => 'src/Magento/Setup/Module/Di/Compiler/Config/Writer/Filesystem.php' ], + [ + 'type' => 'module', + 'name' => 'Magento_Sales', + 'path' => 'Setup/SerializedDataConverter.php' + ], + [ + 'type' => 'module', + 'name' => 'Magento_Sales', + 'path' => 'Test/Unit/Setup/SerializedDataConverterTest.php' + ], ] ] ]; diff --git a/lib/internal/Magento/Framework/App/Http/Context.php b/lib/internal/Magento/Framework/App/Http/Context.php index 4146dac725f03a70971e85d6a29b27803e795412..59ab6f7602c3f4cc3369584ff5fa09f9629fae6e 100644 --- a/lib/internal/Magento/Framework/App/Http/Context.php +++ b/lib/internal/Magento/Framework/App/Http/Context.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\App\Http; @@ -27,6 +27,16 @@ class Context */ protected $default = []; + /** + * @param array $data + * @param array $default + */ + public function __construct(array $data = [], array $default = []) + { + $this->data = $data; + $this->default = $default; + } + /** * Data setter * @@ -99,4 +109,17 @@ class Context } return null; } + + /** + * Get data and default data in "key-value" format + * + * @return array + */ + public function toArray() + { + return [ + 'data' => $this->data, + 'default' => $this->default + ]; + } } diff --git a/lib/internal/Magento/Framework/App/PageCache/Kernel.php b/lib/internal/Magento/Framework/App/PageCache/Kernel.php index 8a83592bc322e86c046b68480f815878f3de8855..02d6646c67d0f76ee4a527bf29e7d0f165a8f822 100644 --- a/lib/internal/Magento/Framework/App/PageCache/Kernel.php +++ b/lib/internal/Magento/Framework/App/PageCache/Kernel.php @@ -1,12 +1,10 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\App\PageCache; -use Magento\Framework\App\ObjectManager; - /** * Builtin cache processor */ @@ -34,19 +32,76 @@ class Kernel */ private $fullPageCache; + /** + * @var \Magento\Framework\Serialize\SerializerInterface + */ + private $serializer; + + /** + * @var \Magento\Framework\App\Http\Context + */ + private $context; + + /** + * @var \Magento\Framework\App\Http\ContextFactory + */ + private $contextFactory; + + /** + * @var \Magento\Framework\App\Response\HttpFactory + */ + private $httpFactory; + /** * @param Cache $cache * @param Identifier $identifier * @param \Magento\Framework\App\Request\Http $request + * @param \Magento\Framework\App\Http\Context|null $context + * @param \Magento\Framework\App\Http\ContextFactory|null $contextFactory + * @param \Magento\Framework\App\Response\HttpFactory|null $httpFactory + * @param \Magento\Framework\Serialize\SerializerInterface|null $serializer */ public function __construct( \Magento\Framework\App\PageCache\Cache $cache, \Magento\Framework\App\PageCache\Identifier $identifier, - \Magento\Framework\App\Request\Http $request + \Magento\Framework\App\Request\Http $request, + \Magento\Framework\App\Http\Context $context = null, + \Magento\Framework\App\Http\ContextFactory $contextFactory = null, + \Magento\Framework\App\Response\HttpFactory $httpFactory = null, + \Magento\Framework\Serialize\SerializerInterface $serializer = null ) { $this->cache = $cache; $this->identifier = $identifier; $this->request = $request; + + if ($context) { + $this->context = $context; + } else { + $this->context = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Http\Context::class + ); + } + if ($contextFactory) { + $this->contextFactory = $contextFactory; + } else { + $this->contextFactory = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Http\ContextFactory::class + ); + } + if ($httpFactory) { + $this->httpFactory = $httpFactory; + } else { + $this->httpFactory = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Response\HttpFactory::class + ); + } + if ($serializer) { + $this->serializer = $serializer; + } else { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\Serialize\SerializerInterface::class + ); + } } /** @@ -57,7 +112,12 @@ class Kernel public function load() { if ($this->request->isGet() || $this->request->isHead()) { - return unserialize($this->getCache()->load($this->identifier->getValue())); + $responseData = $this->serializer->unserialize($this->getCache()->load($this->identifier->getValue())); + if (!$responseData) { + return false; + } + + return $this->buildResponse($responseData); } return false; } @@ -84,11 +144,63 @@ class Kernel if (!headers_sent()) { header_remove('Set-Cookie'); } - $this->getCache()->save(serialize($response), $this->identifier->getValue(), $tags, $maxAge); + + $this->getCache()->save( + $this->serializer->serialize($this->getPreparedData($response)), + $this->identifier->getValue(), + $tags, + $maxAge + ); } } } + /** + * Get prepared data for storage in the cache. + * + * @param \Magento\Framework\App\Response\Http $response + * @return array + */ + private function getPreparedData(\Magento\Framework\App\Response\Http $response) + { + return [ + 'content' => $response->getContent(), + 'status_code' => $response->getStatusCode(), + 'headers' => $response->getHeaders()->toArray(), + 'context' => $this->context->toArray() + ]; + + } + + /** + * Build response using response data. + * + * @param array $responseData + * @return \Magento\Framework\App\Response\Http + */ + private function buildResponse($responseData) + { + $context = $this->contextFactory->create( + [ + 'data' => $responseData['context']['data'], + 'default' => $responseData['context']['default'] + ] + ); + + $response = $this->httpFactory->create( + [ + 'context' => $context + ] + ); + $response->setStatusCode($responseData['status_code']); + $response->setContent($responseData['content']); + foreach ($responseData['headers'] as $headerKey => $headerValue) { + $response->setHeader($headerKey, $headerValue, true); + } + + return $response; + } + /** * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547 * @@ -97,7 +209,9 @@ class Kernel private function getCache() { if (!$this->fullPageCache) { - $this->fullPageCache = ObjectManager::getInstance()->get(\Magento\PageCache\Model\Cache\Type::class); + $this->fullPageCache = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\PageCache\Model\Cache\Type::class + ); } return $this->fullPageCache; } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Http/ContextTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Http/ContextTest.php index 05f589e0dc61a9299f717aeab0905f8b205fdafe..8ce4f006861e8d3cc518df48a0882cda22a15b14 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Http/ContextTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Http/ContextTest.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ @@ -64,4 +64,19 @@ class ContextTest extends \PHPUnit_Framework_TestCase ksort($data); $this->assertEquals(sha1(serialize($data)), $this->object->getVaryString()); } + + public function testToArray() + { + $newObject = new \Magento\Framework\App\Http\Context(['key' => 'value']); + + $newObject->setValue('key1', 'value1', 'default1'); + $newObject->setValue('key2', 'value2', 'default2'); + $this->assertEquals( + [ + 'data' => ['key' => 'value', 'key1' => 'value1', 'key2' => 'value2'], + 'default' => ['key1' => 'default1', 'key2' => 'default2'] + ], + $newObject->toArray() + ); + } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php index 81b828b3f938c45333a5ac8b096d8b23d013a81a..7e65b174abdd97a3adc231b415c96a62f634e194 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php @@ -1,12 +1,17 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\App\Test\Unit\PageCache; use \Magento\Framework\App\PageCache\Kernel; +use \Magento\Framework\App\Http\ContextFactory; +use \Magento\Framework\App\Response\HttpFactory; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class KernelTest extends \PHPUnit_Framework_TestCase { /** @var Kernel */ @@ -27,39 +32,136 @@ class KernelTest extends \PHPUnit_Framework_TestCase /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */ private $fullPageCacheMock; + /** @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject */ + private $httpResponseMock; + + /** @var ContextFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $contextFactoryMock; + + /** @var HttpFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $httpFactoryMock; + + /** @var \Magento\Framework\Serialize\SerializerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $serializer; + + /** @var \Magento\Framework\App\Http\Context|\PHPUnit_Framework_MockObject_MockObject */ + private $contextMock; + /** * Setup */ protected function setUp() { + $headersMock = $this->getMock(\Zend\Http\Headers::class, [], [], '', false); $this->cacheMock = $this->getMock(\Magento\Framework\App\PageCache\Cache::class, [], [], '', false); $this->fullPageCacheMock = $this->getMock(\Magento\PageCache\Model\Cache\Type::class, [], [], '', false); - $this->identifierMock = - $this->getMock(\Magento\Framework\App\PageCache\Identifier::class, [], [], '', false); + $this->contextMock = $this->getMock(\Magento\Framework\App\Http\Context::class, [], [], '', false); + $this->httpResponseMock = $this->getMock(\Magento\Framework\App\Response\Http::class, [], [], '', false); + $this->identifierMock = $this->getMock(\Magento\Framework\App\PageCache\Identifier::class, [], [], '', false); $this->requestMock = $this->getMock(\Magento\Framework\App\Request\Http::class, [], [], '', false); - $this->kernel = new Kernel($this->cacheMock, $this->identifierMock, $this->requestMock); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class, [], [], '', false); + $this->responseMock = $this->getMock(\Magento\Framework\App\Response\Http::class, [], [], '', false); + $this->contextFactoryMock = $this->getMock(ContextFactory::class, ['create'], [], '', false); + $this->httpFactoryMock = $this->getMock(HttpFactory::class, ['create'], [], '', false); + $this->responseMock->expects($this->any())->method('getHeaders')->willReturn($headersMock); + + $this->kernel = new Kernel( + $this->cacheMock, + $this->identifierMock, + $this->requestMock, + $this->contextMock, + $this->contextFactoryMock, + $this->httpFactoryMock, + $this->serializer + ); $reflection = new \ReflectionClass(\Magento\Framework\App\PageCache\Kernel::class); $reflectionProperty = $reflection->getProperty('fullPageCache'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->kernel, $this->fullPageCacheMock); + } - $this->responseMock = $this->getMockBuilder( - \Magento\Framework\App\Response\Http::class - )->setMethods( - ['getHeader', 'getHttpResponseCode', 'setNoCacheHeaders', 'clearHeader', '__wakeup'] - )->disableOriginalConstructor()->getMock(); + /** + * @dataProvider dataProviderForResultWithCachedData + * @param string $id + * @param mixed $cache + * @param bool $isGet + * @param bool $isHead + */ + public function testLoadWithCachedData($id, $cache, $isGet, $isHead) + { + $this->serializer->expects($this->once()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + + $this->contextFactoryMock + ->expects($this->once()) + ->method('create') + ->with( + [ + 'data' => ['context_data'], + 'default' => ['context_default_data'] + ] + ) + ->willReturn($this->contextMock); + + $this->httpFactoryMock + ->expects($this->once()) + ->method('create') + ->with(['context' => $this->contextMock]) + ->willReturn($this->httpResponseMock); + + $this->requestMock->expects($this->once())->method('isGet')->will($this->returnValue($isGet)); + $this->requestMock->expects($this->any())->method('isHead')->will($this->returnValue($isHead)); + $this->fullPageCacheMock->expects( + $this->any() + )->method( + 'load' + )->with( + $this->equalTo($id) + )->will( + $this->returnValue(json_encode($cache)) + ); + $this->httpResponseMock->expects($this->once())->method('setStatusCode')->with($cache['status_code']); + $this->httpResponseMock->expects($this->once())->method('setContent')->with($cache['content']); + $this->httpResponseMock->expects($this->once())->method('setHeader')->with(0, 'header', true); + $this->identifierMock->expects($this->any())->method('getValue')->will($this->returnValue($id)); + $this->assertEquals($this->httpResponseMock, $this->kernel->load()); + } + + /** + * @return array + */ + public function dataProviderForResultWithCachedData() + { + $data = [ + 'context' => [ + 'data' => ['context_data'], + 'default' => ['context_default_data'] + ], + 'status_code' => 'status_code', + 'content' => 'content', + 'headers' => ['header'] + ]; + + return [ + ['existing key', $data, true, false], + ['existing key', $data, false, true], + ]; } /** - * @dataProvider loadProvider - * @param mixed $expected + * @dataProvider dataProviderForResultWithoutCachedData * @param string $id * @param mixed $cache * @param bool $isGet * @param bool $isHead */ - public function testLoad($expected, $id, $cache, $isGet, $isHead) + public function testLoadWithoutCachedData($id, $cache, $isGet, $isHead) { $this->requestMock->expects($this->once())->method('isGet')->will($this->returnValue($isGet)); $this->requestMock->expects($this->any())->method('isHead')->will($this->returnValue($isHead)); @@ -70,31 +172,21 @@ class KernelTest extends \PHPUnit_Framework_TestCase )->with( $this->equalTo($id) )->will( - $this->returnValue(serialize($cache)) + $this->returnValue(json_encode($cache)) ); $this->identifierMock->expects($this->any())->method('getValue')->will($this->returnValue($id)); - $this->assertEquals($expected, $this->kernel->load()); + $this->assertEquals(false, $this->kernel->load()); } /** * @return array */ - public function loadProvider() + public function dataProviderForResultWithoutCachedData() { - $data = [1, 2, 3]; return [ - [$data, 'existing key', $data, true, false], - [$data, 'existing key', $data, false, true], - [ - new \Magento\Framework\DataObject($data), - 'existing key', - new \Magento\Framework\DataObject($data), - true, - false - ], - [false, 'existing key', $data, false, false], - [false, 'non existing key', false, true, false], - [false, 'non existing key', false, false, false] + ['existing key', [], false, false], + ['non existing key', false, true, false], + ['non existing key', false, false, false] ]; } @@ -104,6 +196,14 @@ class KernelTest extends \PHPUnit_Framework_TestCase */ public function testProcessSaveCache($httpCode, $at) { + $this->serializer->expects($this->once()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + $cacheControlHeader = \Zend\Http\Header\CacheControl::fromString( 'Cache-Control: public, max-age=100, s-maxage=100' ); diff --git a/lib/internal/Magento/Framework/DB/DataConverter/DataConverterInterface.php b/lib/internal/Magento/Framework/DB/DataConverter/DataConverterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..a2ca45ea638ded298e75b5115364abb77dad86b9 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/DataConverter/DataConverterInterface.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\DataConverter; + +/** + * Convert from one format to another + */ +interface DataConverterInterface +{ + /** + * Convert from one format to another + * + * @param string $value + * @return string + */ + public function convert($value); +} diff --git a/lib/internal/Magento/Framework/DB/DataConverter/SerializedToJson.php b/lib/internal/Magento/Framework/DB/DataConverter/SerializedToJson.php new file mode 100644 index 0000000000000000000000000000000000000000..ae675784021d4d5f3e0d89d54088dfd4f92d64ab --- /dev/null +++ b/lib/internal/Magento/Framework/DB/DataConverter/SerializedToJson.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\DataConverter; + +use Magento\Framework\Serialize\Serializer\Serialize; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Convert from serialized to JSON format + */ +class SerializedToJson implements DataConverterInterface +{ + /** + * @var Serialize + */ + private $serialize; + + /** + * @var Json + */ + private $json; + + /** + * Constructor + * + * @param Serialize $serialize + * @param Json $json + */ + public function __construct( + Serialize $serialize, + Json $json + ) { + $this->serialize = $serialize; + $this->json = $json; + } + + /** + * Convert from serialized to JSON format + * + * @param string $value + * @return string + */ + public function convert($value) + { + return $this->json->serialize($this->serialize->unserialize($value)); + } +} diff --git a/lib/internal/Magento/Framework/DB/FieldDataConverter.php b/lib/internal/Magento/Framework/DB/FieldDataConverter.php new file mode 100644 index 0000000000000000000000000000000000000000..39dcd3238c9b8a08cb6192f3ce2a4785528942a0 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/FieldDataConverter.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB; + +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\Generator; +use Magento\Framework\DB\DataConverter\DataConverterInterface; +use Magento\Framework\DB\Select\QueryModifierInterface; + +/** + * Convert field data from one representation to another + */ +class FieldDataConverter +{ + /** + * @var Generator + */ + private $queryGenerator; + + /** + * @var DataConverterInterface + */ + private $dataConverter; + + /** + * Constructor + * + * @param Generator $queryGenerator + * @param DataConverterInterface $dataConverter + */ + public function __construct( + Generator $queryGenerator, + DataConverterInterface $dataConverter + ) { + $this->queryGenerator = $queryGenerator; + $this->dataConverter = $dataConverter; + } + + /** + * Convert field data from one representation to another + * + * @param AdapterInterface $connection + * @param string $table + * @param string $identifier + * @param string $field + * @param QueryModifierInterface|null $queryModifier + * @return void + */ + public function convert( + AdapterInterface $connection, + $table, + $identifier, + $field, + QueryModifierInterface $queryModifier = null + ) { + $select = $connection->select() + ->from($table, [$identifier, $field]) + ->where($field . ' IS NOT NULL'); + if ($queryModifier) { + $queryModifier->modify($select); + } + $iterator = $this->queryGenerator->generate($identifier, $select); + foreach ($iterator as $selectByRange) { + $rows = $connection->fetchAll($selectByRange); + foreach ($rows as $row) { + $bind = [$field => $this->dataConverter->convert($row[$field])]; + $where = [$identifier . ' = ?' => (int) $row[$identifier]]; + $connection->update($table, $bind, $where); + } + } + } +} diff --git a/lib/internal/Magento/Framework/DB/FieldDataConverterFactory.php b/lib/internal/Magento/Framework/DB/FieldDataConverterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c5caca06b283a6f769351dba3dc68a225bb66fcb --- /dev/null +++ b/lib/internal/Magento/Framework/DB/FieldDataConverterFactory.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB; + +use Magento\Framework\ObjectManagerInterface; + +/** + * Create instance of FieldDataConverter with concrete implementation of DataConverterInterface + */ +class FieldDataConverterFactory +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * Constructor + * + * @param ObjectManagerInterface $objectManager + */ + public function __construct( + ObjectManagerInterface $objectManager + ) { + $this->objectManager = $objectManager; + } + + /** + * Create instance of FieldDataConverter + * + * @param string $dataConverterClassName + * @return FieldDataConverter + */ + public function create($dataConverterClassName) + { + return $this->objectManager->create( + FieldDataConverter::class, + [ + 'dataConverter' => $this->objectManager->get($dataConverterClassName) + ] + ); + } +} diff --git a/lib/internal/Magento/Framework/DB/Select/InQueryModifier.php b/lib/internal/Magento/Framework/DB/Select/InQueryModifier.php new file mode 100644 index 0000000000000000000000000000000000000000..e8a03f709568a67c2e34ca07b923c3ffbf16dafb --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Select/InQueryModifier.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Select; + +use Magento\Framework\DB\Select; + +/** + * Add IN condition to select + */ +class InQueryModifier implements QueryModifierInterface +{ + /** + * @var array + */ + private $values; + + /** + * Constructor + * + * @param array $values + */ + public function __construct( + $values = [] + ) { + $this->values = $values; + } + + /** + * {@inheritdoc} + */ + public function modify(Select $select) + { + foreach ($this->values as $field => $values) { + $select->where($field . ' IN (?)', $values); + } + } +} diff --git a/lib/internal/Magento/Framework/DB/Select/QueryModifierFactory.php b/lib/internal/Magento/Framework/DB/Select/QueryModifierFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..21eb484b43268b11373b1dcc9fbf726bdc0cf600 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Select/QueryModifierFactory.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Select; + +use Magento\Framework\ObjectManagerInterface; + +/** + * Create instance of QueryModifierInterface + */ +class QueryModifierFactory +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var array + */ + private $queryModifiers; + + /** + * Constructor + * + * @param ObjectManagerInterface $objectManager + * @param array $queryModifiers + */ + public function __construct( + ObjectManagerInterface $objectManager, + array $queryModifiers = [] + ) { + $this->objectManager = $objectManager; + $this->queryModifiers = $queryModifiers; + } + + /** + * Create instance of QueryModifierInterface + * + * @param string $type + * @param array $data + * @return QueryModifierInterface + * @throws \InvalidArgumentException + */ + public function create($type, array $data = []) + { + if (!isset($this->queryModifiers[$type])) { + throw new \InvalidArgumentException('Unknown query modifier type ' . $type); + } + $queryModifier = $this->objectManager->create($this->queryModifiers[$type], $data); + if (!($queryModifier instanceof QueryModifierInterface)) { + throw new \InvalidArgumentException( + $this->queryModifiers[$type] . ' must implement ' . QueryModifierInterface::class + ); + } + return $queryModifier; + } +} diff --git a/lib/internal/Magento/Framework/DB/Select/QueryModifierInterface.php b/lib/internal/Magento/Framework/DB/Select/QueryModifierInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..5cf676e088c440b626e352163ec350728c581fe1 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Select/QueryModifierInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Select; + +use Magento\Framework\DB\Select; + +/** + * Modify query, add custom conditions + */ +interface QueryModifierInterface +{ + /** + * Modify query + * + * @param Select $select + * @return void + */ + public function modify(Select $select); +} diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/DataConverter/SerializedToJsonTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/DataConverter/SerializedToJsonTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c84929f90c25c926c961906b2c1e5ab57afa77e9 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Test/Unit/DataConverter/SerializedToJsonTest.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Test\Unit\DataConverter; + +use Magento\Framework\Serialize\Serializer\Serialize; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\DB\DataConverter\SerializedToJson; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class SerializedToJsonTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Serialize|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializeMock; + + /** + * @var Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $jsonMock; + + /** + * @var SerializedToJson + */ + private $serializedToJson; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->serializeMock = $this->getMock(Serialize::class, [], [], '', false); + $this->jsonMock = $this->getMock(Json::class, [], [], '', false); + $this->serializedToJson = $objectManager->getObject( + SerializedToJson::class, + [ + 'serialize' => $this->serializeMock, + 'json' => $this->jsonMock + ] + ); + } + + public function testConvert() + { + $serializedData = 'serialized data'; + $jsonData = 'json data'; + $unserializedData = 'unserialized data'; + $this->serializeMock->expects($this->once()) + ->method('unserialize') + ->with($serializedData) + ->willReturn($unserializedData); + $this->jsonMock->expects($this->once()) + ->method('serialize') + ->with($unserializedData) + ->willReturn($jsonData); + $this->assertEquals($jsonData, $this->serializedToJson->convert($serializedData)); + } +} diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterFactoryTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..266f9e9ceceae2c89f0dc65fd27de66dfb27d166 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterFactoryTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Test\Unit; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\DB\FieldDataConverterFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\DB\FieldDataConverter; +use Magento\Framework\DB\DataConverter\DataConverterInterface; + +class FieldDataConverterFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $objectManagerMock; + + /** + * @var DataConverterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dataConverterMock; + + /** + * @var FieldDataConverterFactory + */ + private $fieldDataConverterFactory; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->objectManagerMock = $this->getMock(ObjectManagerInterface::class); + $this->dataConverterMock = $this->getMock(DataConverterInterface::class); + $this->fieldDataConverterFactory = $objectManager->getObject( + FieldDataConverterFactory::class, + [ + 'objectManager' => $this->objectManagerMock + ] + ); + } + + public function testCreate() + { + $dataConverterClassName = 'ClassName'; + $fieldDataConverterInstance = 'field data converter instance'; + $this->objectManagerMock->expects($this->once()) + ->method('get') + ->with($dataConverterClassName) + ->willReturn($this->dataConverterMock); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with( + FieldDataConverter::class, + [ + 'dataConverter' => $this->dataConverterMock + ] + ) + ->willReturn($fieldDataConverterInstance); + $this->assertEquals( + $fieldDataConverterInstance, + $this->fieldDataConverterFactory->create($dataConverterClassName) + ); + } +} diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f0005de98dcda2b66aaab355fc0a07cd9d0531d7 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Test\Unit; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\DB\Query\Generator; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\FieldDataConverter; +use Magento\Framework\DB\DataConverter\DataConverterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Select\QueryModifierInterface; + +class FieldDataConverterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionMock; + + /** + * @var Generator|\PHPUnit_Framework_MockObject_MockObject + */ + private $queryGeneratorMock; + + /** + * @var DataConverterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dataConverterMock; + + /** + * @var Select|\PHPUnit_Framework_MockObject_MockObject + */ + private $selectMock; + + /** + * @var QueryModifierInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $queryModifierMock; + + /** + * @var FieldDataConverter + */ + private $fieldDataConverter; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->connectionMock = $this->getMock(AdapterInterface::class); + $this->queryGeneratorMock = $this->getMock(Generator::class, [], [], '', false); + $this->dataConverterMock = $this->getMock(DataConverterInterface::class); + $this->selectMock = $this->getMock(Select::class, [], [], '', false); + $this->queryModifierMock = $this->getMock(QueryModifierInterface::class); + $this->fieldDataConverter = $objectManager->getObject( + FieldDataConverter::class, + [ + 'queryGenerator' => $this->queryGeneratorMock, + 'dataConverter' => $this->dataConverterMock + ] + ); + } + + /** + * @param boolean $useQueryModifier + * @param int $numQueryModifierCalls + * @dataProvider convertDataProvider + */ + public function testConvert($useQueryModifier, $numQueryModifierCalls) + { + $table = 'table'; + $identifier = 'id'; + $field = 'field'; + $where = $field . ' IS NOT NULL'; + $iterator = ['query 1']; + $rows = [ + [ + $identifier => 1, + $field => 'value' + ] + ]; + $convertedValue = 'converted value'; + $this->connectionMock->expects($this->once()) + ->method('select') + ->willReturn($this->selectMock); + $this->selectMock->expects($this->once()) + ->method('from') + ->with( + $table, + [$identifier, $field] + ) + ->willReturnSelf(); + $this->selectMock->expects($this->once()) + ->method('where') + ->with($where) + ->willReturnSelf(); + $this->queryModifierMock->expects($this->exactly($numQueryModifierCalls)) + ->method('modify') + ->with($this->selectMock); + $this->queryGeneratorMock->expects($this->once()) + ->method('generate') + ->with($identifier, $this->selectMock) + ->willReturn($iterator); + $this->connectionMock->expects($this->once()) + ->method('fetchAll') + ->with($iterator[0]) + ->willReturn($rows); + $this->dataConverterMock->expects($this->once()) + ->method('convert') + ->with($rows[0][$field]) + ->willReturn($convertedValue); + $this->connectionMock->expects($this->once()) + ->method('update') + ->with( + $table, + [$field => $convertedValue], + [$identifier . ' = ?' => $rows[0][$identifier]] + ); + $this->fieldDataConverter->convert( + $this->connectionMock, + $table, + $identifier, + $field, + $useQueryModifier ? $this->queryModifierMock : null + ); + } + + /** + * @return array + */ + public function convertDataProvider() + { + return [ + [false, 0], + [true, 1] + ]; + } +} diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Select/QueryModifierFactoryTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Select/QueryModifierFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b083b730bbdbaea4d36bb094eb47ca2361ae265b --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Select/QueryModifierFactoryTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DB\Test\Unit\Select; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\DB\Select\QueryModifierFactory; +use Magento\Framework\DB\Select\InQueryModifier; +use Magento\Framework\ObjectManagerInterface; + +class QueryModifierFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var QueryModifierFactory + */ + private $queryModifierFactory; + + /** + * @var ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $objectManagerMock; + + /** + * @var InQueryModifier|\PHPUnit_Framework_MockObject_MockObject + */ + private $inQueryModifierMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->objectManagerMock = $this->getMock(ObjectManagerInterface::class); + $this->inQueryModifierMock = $this->getMock(InQueryModifier::class, [], [], '', false); + } + + public function testCreate() + { + $params = ['foo' => 'bar']; + $this->queryModifierFactory = $this->objectManager->getObject( + QueryModifierFactory::class, + [ + 'objectManager' => $this->objectManagerMock, + 'queryModifiers' => [ + 'in' => InQueryModifier::class + ] + ] + ); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with( + InQueryModifier::class, + $params + ) + ->willReturn($this->inQueryModifierMock); + $this->queryModifierFactory->create('in', $params); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreateUnknownQueryModifierType() + { + $params = ['foo' => 'bar']; + $this->queryModifierFactory = $this->objectManager->getObject( + QueryModifierFactory::class, + [ + 'objectManager' => $this->objectManagerMock, + 'queryModifiers' => [] + ] + ); + $this->objectManagerMock->expects($this->never()) + ->method('create'); + $this->queryModifierFactory->create('in', $params); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreateDoesNotImplementInterface() + { + $params = ['foo' => 'bar']; + $this->queryModifierFactory = $this->objectManager->getObject( + QueryModifierFactory::class, + [ + 'objectManager' => $this->objectManagerMock, + 'queryModifiers' => [ + 'in' => \stdClass::class + ] + ] + ); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with( + \stdClass::class, + $params + ) + ->willReturn(new \stdClass()); + $this->queryModifierFactory->create('in', $params); + } +} diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php b/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php index 3d90fc781bf3a976d6ffb6dafb4306190ecbebfd..c87a07eaab37ed5b4c58a1c7d8162c901abc7742 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php @@ -7,6 +7,8 @@ namespace Magento\Framework\Model\ResourceModel; use Magento\Framework\DataObject; use Magento\Framework\Model\CallbackPool; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ObjectManager; /** * Abstract resource model @@ -14,7 +16,12 @@ use Magento\Framework\Model\CallbackPool; abstract class AbstractResource { /** - * Main constructor + * @var Json + */ + protected $serializer; + + /** + * Constructor */ public function __construct() { @@ -116,7 +123,7 @@ abstract class AbstractResource if (empty($value) && $unsetEmpty) { $object->unsetData($field); } else { - $object->setData($field, serialize($value ?: $defaultValue)); + $object->setData($field, $this->getSerializer()->serialize($value ?: $defaultValue)); } return $this; @@ -132,13 +139,7 @@ abstract class AbstractResource */ protected function _unserializeField(DataObject $object, $field, $defaultValue = null) { - $value = $object->getData($field); - - if ($value) { - $unserializedValue = @unserialize($value); - $value = $unserializedValue !== false || $value === 'b:0;' ? $unserializedValue : $value; - } - + $value = $this->getSerializer()->unserialize($object->getData($field)); if (empty($value)) { $object->setData($field, $defaultValue); } else { @@ -228,4 +229,18 @@ abstract class AbstractResource } return $columns; } + + /** + * Get serializer + * + * @return Json + * @deprecated + */ + protected function getSerializer() + { + if (null === $this->serializer) { + $this->serializer = ObjectManager::getInstance()->get(Json::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php index 4cd88e356e9cad79a6f0209bb2bab685ffe5f73c..12e2da6d4ebd4b7a5c6edf123ac15865e28b4f7c 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\Model\ResourceModel\Db; use Magento\Framework\App\ResourceConnection; @@ -14,7 +13,8 @@ use Magento\Framework\DB\Adapter\DuplicateException; use Magento\Framework\Phrase; /** - * Abstract resource model class + * Abstract resource model + * * @SuppressWarnings(PHPMD.NumberOfChildren) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) @@ -133,13 +133,15 @@ abstract class AbstractDb extends AbstractResource protected $objectRelationProcessor; /** - * Class constructor + * Constructor * * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param string $connectionName */ - public function __construct(\Magento\Framework\Model\ResourceModel\Db\Context $context, $connectionName = null) - { + public function __construct( + \Magento\Framework\Model\ResourceModel\Db\Context $context, + $connectionName = null + ) { $this->transactionManager = $context->getTransactionManager(); $this->_resources = $context->getResources(); $this->objectRelationProcessor = $context->getObjectRelationProcessor(); diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php index 217f5c8252378206881dacd8085bd4025e072821..85696221beeaf46ffe33b763ba505d26d44ad675 100644 --- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php +++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php @@ -7,103 +7,161 @@ namespace Magento\Framework\Model\Test\Unit\ResourceModel; use Magento\Framework\DataObject; use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; class AbstractResourceTest extends \PHPUnit_Framework_TestCase { /** - * @param array $arguments - * @param string $expectation - * @dataProvider serializableFieldsDataProvider + * @var AbstractResourceStub */ - public function testSerializeFields(array $arguments, $expectation) - { - /** @var DataObject $dataObject */ - list($dataObject, $field, $defaultValue, $unsetEmpty) = $arguments; + private $abstractResource; - $abstractResource = new AbstractResourceStub(); + /** + * @var Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializerMock; - $abstractResource->_serializeField($dataObject, $field, $defaultValue, $unsetEmpty); + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->serializerMock = $this->getMock(Json::class); + $this->abstractResource = $objectManager->getObject(AbstractResourceStub::class); + $objectManager->setBackwardCompatibleProperty( + $this->abstractResource, + 'serializer', + $this->serializerMock + ); + } - static::assertEquals($expectation, $dataObject->getDataByKey($field)); + /** + * @param array $arguments + * @param string $expected + * @param array|string|int $serializeCalledWith + * @param int $numSerializeCalled + * @dataProvider serializeFieldsDataProvider + */ + public function testSerializeFields( + array $arguments, + $expected, + $serializeCalledWith, + $numSerializeCalled = 1 + ) { + /** @var DataObject $dataObject */ + list($dataObject, $field, $defaultValue, $unsetEmpty) = $arguments; + $this->serializerMock->expects($this->exactly($numSerializeCalled)) + ->method('serialize') + ->with($serializeCalledWith) + ->willReturn($expected); + $this->abstractResource->_serializeField($dataObject, $field, $defaultValue, $unsetEmpty); + $this->assertEquals($expected, $dataObject->getData($field)); } /** * @return array */ - public function serializableFieldsDataProvider() + public function serializeFieldsDataProvider() { + $array = ['a', 'b', 'c']; + $string = 'i am string'; + $integer = 969; + $empty = ''; $dataObject = new DataObject( [ - 'object' => new \stdClass(), - 'array' => ['a', 'b', 'c'], - 'string' => 'i am string', - 'int' => 969, - 'serialized_object' => 'O:8:"stdClass":0:{}', - 'empty_value' => '', - 'empty_value_with_default' => '' + 'array' => $array, + 'string' => $string, + 'integer' => $integer, + 'empty' => $empty, + 'empty_with_default' => '' ] ); - return [ - [[$dataObject, 'object', null, false], serialize($dataObject->getDataByKey('object'))], - [[$dataObject, 'array', null, false], serialize($dataObject->getDataByKey('array'))], - [[$dataObject, 'string', null, false], serialize($dataObject->getDataByKey('string'))], - [[$dataObject, 'int', null, false], serialize($dataObject->getDataByKey('int'))], [ - [$dataObject, 'serialized_object', null, false], - serialize($dataObject->getDataByKey('serialized_object')) + [$dataObject, 'array', null, false], + '["a","b","c"]', + $array + ], + [ + [$dataObject, 'string', null, false], + '"i am string"', + $string + ], + [ + [$dataObject, 'integer', null, false], + '969', + $integer + ], + [ + [$dataObject, 'empty', null, true], + null, + $empty, + 0 ], - [[$dataObject, 'empty_value', null, true], null], - [[$dataObject, 'empty_value_with_default', new \stdClass(), false], 'O:8:"stdClass":0:{}'], + [ + [$dataObject, 'empty_with_default', 'default', false], + '"default"', + 'default' + ] ]; } /** * @param array $arguments - * @param mixed $expectation - * @dataProvider unserializableFieldsDataProvider + * @param array|string|int|boolean $expected + * @dataProvider unserializeFieldsDataProvider */ - public function testUnserializeFields(array $arguments, $expectation) + public function testUnserializeFields(array $arguments, $expected) { /** @var DataObject $dataObject */ list($dataObject, $field, $defaultValue) = $arguments; - - $abstractResource = new AbstractResourceStub(); - - $abstractResource->_unserializeField($dataObject, $field, $defaultValue); - - static::assertEquals($expectation, $dataObject->getDataByKey($field)); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with($dataObject->getData($field)) + ->willReturn($expected); + $this->abstractResource->_unserializeField($dataObject, $field, $defaultValue); + $this->assertEquals($expected, $dataObject->getData($field)); } /** * @return array */ - public function unserializableFieldsDataProvider() + public function unserializeFieldsDataProvider() { $dataObject = new DataObject( [ - 'object' => serialize(new \stdClass()), - 'array' => serialize(['a', 'b', 'c']), - 'string' => serialize('i am string'), - 'int' => serialize(969), - 'serialized_object' => serialize('O:8:"stdClass":0:{}'), - 'empty_value_with_default' => serialize(''), + 'array' => '["a","b","c"]', + 'string' => '"i am string"', + 'integer' => '969', + 'empty_with_default' => '""', 'not_serialized_string' => 'i am string', - 'serialized_boolean_false' => serialize(false) + 'serialized_boolean_false' => 'false' ] ); - - $defaultValue = new \stdClass(); - return [ - [[$dataObject, 'object', null], unserialize($dataObject->getDataByKey('object'))], - [[$dataObject, 'array', null], unserialize($dataObject->getDataByKey('array'))], - [[$dataObject, 'string', null], unserialize($dataObject->getDataByKey('string'))], - [[$dataObject, 'int', null], unserialize($dataObject->getDataByKey('int'))], - [[$dataObject, 'serialized_object', null], unserialize($dataObject->getDataByKey('serialized_object'))], - [[$dataObject, 'empty_value_with_default', $defaultValue], $defaultValue], - [[$dataObject, 'not_serialized_string', null], 'i am string'], - [[$dataObject, 'serialized_boolean_false', null], false] + [ + [$dataObject, 'array', null], + ['a', 'b', 'c'] + ], + [ + [$dataObject, 'string', null], + 'i am string' + ], + [ + [$dataObject, 'integer', null], + 969 + ], + [ + [$dataObject, 'empty_with_default', 'default', false], + 'default' + ], + [ + [$dataObject, 'not_serialized_string', null], + 'i am string' + ], + [ + [$dataObject, 'serialized_boolean_false', null], + false, + ] ]; } @@ -116,32 +174,31 @@ class AbstractResourceTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $abstractResource = new AbstractResourceStub(); - $abstractResource->setConnection($connection); - $abstractResource->addCommitCallback( + $this->abstractResource->setConnection($connection); + $this->abstractResource->addCommitCallback( function () use ($closureExpectation) { $closureExpectation->setData(1); } ); - $abstractResource->addCommitCallback( + $this->abstractResource->addCommitCallback( function () use ($closureExpectation) { $closureExpectation->getData(); } ); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('commit'); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('getTransactionLevel') ->willReturn(0); - $closureExpectation->expects(static::once()) + $closureExpectation->expects($this->once()) ->method('setData') ->with(1); - $closureExpectation->expects(static::once()) + $closureExpectation->expects($this->once()) ->method('getData'); - $abstractResource->commit(); + $this->abstractResource->commit(); } /** @@ -152,23 +209,22 @@ class AbstractResourceTest extends \PHPUnit_Framework_TestCase /** @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject $connection */ $connection = $this->getMock(AdapterInterface::class); - $abstractResource = new AbstractResourceStub(); - $abstractResource->setConnection($connection); - $abstractResource->addCommitCallback( + $this->abstractResource->setConnection($connection); + $this->abstractResource->addCommitCallback( function () { throw new \Exception(); } ); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('commit'); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('getTransactionLevel') ->willReturn(0); - $abstractResource->commit(); + $this->abstractResource->commit(); } - + public function testCommitNotCompletedTransaction() { /** @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject $connection */ @@ -178,24 +234,23 @@ class AbstractResourceTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $abstractResource = new AbstractResourceStub(); - $abstractResource->setConnection($connection); - $abstractResource->addCommitCallback( + $this->abstractResource->setConnection($connection); + $this->abstractResource->addCommitCallback( function () use ($closureExpectation) { $closureExpectation->setData(1); } ); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('commit'); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('getTransactionLevel') ->willReturn(1); - $closureExpectation->expects(static::never()) + $closureExpectation->expects($this->never()) ->method('setData') ->with(1); - $abstractResource->commit(); + $this->abstractResource->commit(); } } diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index 3334967c8da8327cb1de7eb396fb46978fc0c08c..ac3938c94c5c7432ba8095ea98c29162afb8b954 100755 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\View; @@ -8,11 +8,13 @@ namespace Magento\Framework\View; use Magento\Framework\Cache\FrontendInterface; use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Message\ManagerInterface as MessageManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\Layout\Element; use Magento\Framework\View\Layout\ScheduledStructure; use Magento\Framework\App\State as AppState; use Psr\Log\LoggerInterface as Logger; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\App\ObjectManager; /** * Layout model @@ -165,6 +167,11 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra */ protected $logger; + /** + * @var SerializerInterface + */ + private $serializer; + /** * @param Layout\ProcessorFactory $processorFactory * @param ManagerInterface $eventManager @@ -179,6 +186,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra * @param \Magento\Framework\App\State $appState * @param \Psr\Log\LoggerInterface $logger * @param bool $cacheable + * @param SerializerInterface|null $serializer */ public function __construct( Layout\ProcessorFactory $processorFactory, @@ -193,10 +201,12 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra Layout\Generator\ContextFactory $generatorContextFactory, AppState $appState, Logger $logger, - $cacheable = true + $cacheable = true, + SerializerInterface $serializer = null ) { $this->_elementClass = \Magento\Framework\View\Layout\Element::class; $this->_renderingOutput = new \Magento\Framework\DataObject(); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); $this->_processorFactory = $processorFactory; $this->_eventManager = $eventManager; @@ -308,12 +318,19 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra $cacheId = 'structure_' . $this->getUpdate()->getCacheId(); $result = $this->cache->load($cacheId); if ($result) { - $this->readerContext = unserialize($result); + $data = $this->serializer->unserialize($result); + $this->getReaderContext()->getPageConfigStructure()->populateWithArray($data['pageConfigStructure']); + $this->getReaderContext()->getScheduledStructure()->populateWithArray($data['scheduledStructure']); } else { \Magento\Framework\Profiler::start('build_structure'); $this->readerPool->interpret($this->getReaderContext(), $this->getNode()); \Magento\Framework\Profiler::stop('build_structure'); - $this->cache->save(serialize($this->getReaderContext()), $cacheId, $this->getUpdate()->getHandles()); + + $data = [ + 'pageConfigStructure' => $this->getReaderContext()->getPageConfigStructure()->__toArray(), + 'scheduledStructure' => $this->getReaderContext()->getScheduledStructure()->__toArray(), + ]; + $this->cache->save($this->serializer->serialize($data), $cacheId, $this->getUpdate()->getHandles()); } $generatorContext = $this->generatorContextFactory->create( diff --git a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php index cf1981335d6be9a6b12b38944623d727abe6fdeb..117fb8278145f89f22674b7ab25d21fab57946ff 100644 --- a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php +++ b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\View\Layout; @@ -19,6 +19,23 @@ class ScheduledStructure const ELEMENT_IS_AFTER = 'isAfter'; /**#@-*/ + /** + * Map of class properties. + * + * @var array + */ + private $serializableProperties = [ + 'scheduledStructure', + 'scheduledData', + 'scheduledElements', + 'scheduledMoves', + 'scheduledRemoves', + 'scheduledIfconfig', + 'scheduledPaths', + 'elementsToSort', + 'brokenParent', + ]; + /** * Information about structural elements, scheduled for creation * @@ -84,18 +101,10 @@ class ScheduledStructure /** * @param array $data - * - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function __construct(array $data = []) { - $this->scheduledStructure = isset($data['scheduledStructure']) ? $data['scheduledStructure'] : []; - $this->scheduledData = isset($data['scheduledData']) ? $data['scheduledData'] : []; - $this->scheduledElements = isset($data['scheduledElements']) ? $data['scheduledElements'] : []; - $this->scheduledMoves = isset($data['scheduledMoves']) ? $data['scheduledMoves'] : []; - $this->scheduledRemoves = isset($data['scheduledRemoves']) ? $data['scheduledRemoves'] : []; - $this->scheduledIfconfig = isset($data['scheduledIfconfig']) ? $data['scheduledIfconfig'] : []; - $this->scheduledPaths = isset($data['scheduledPaths']) ? $data['scheduledPaths'] : []; + $this->populateWithArray($data); } /** @@ -531,4 +540,44 @@ class ScheduledStructure $this->scheduledElements = []; $this->scheduledStructure = []; } + + /** + * Reformat 'Layout scheduled structure' to array. + * + * @return array + */ + public function __toArray() + { + $result = []; + foreach ($this->serializableProperties as $property) { + $result[$property] = $this->{$property}; + } + + return $result; + } + + /** + * Update 'Layout scheduled structure' data. + * + * @param array $data + * @return void + */ + public function populateWithArray(array $data) + { + foreach ($this->serializableProperties as $property) { + $this->{$property} = $this->getArrayValueByKey($property, $data); + } + } + + /** + * Get value from array by key. + * + * @param string $key + * @param array $array + * @return array + */ + private function getArrayValueByKey($key, array $array) + { + return isset($array[$key]) ? $array[$key] : []; + } } diff --git a/lib/internal/Magento/Framework/View/Page/Config/Structure.php b/lib/internal/Magento/Framework/View/Page/Config/Structure.php index 4ab89319f6d82aeeaad6e637076ed7e181fab2d4..d7fe402044e8ffd589e2669eb0c0611cdb4b7f61 100644 --- a/lib/internal/Magento/Framework/View/Page/Config/Structure.php +++ b/lib/internal/Magento/Framework/View/Page/Config/Structure.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ @@ -12,6 +12,22 @@ namespace Magento\Framework\View\Page\Config; */ class Structure { + /** + * Map of class properties. + * + * @var array + */ + private $serializableProperties = [ + 'assets', + 'removeAssets', + 'title', + 'metadata', + 'elementAttributes', + 'removeElementAttributes', + 'bodyClasses', + 'isBodyClassesDeleted', + ]; + /** * Information assets elements on page * @@ -194,4 +210,44 @@ class Structure { return $this->assets; } + + /** + * Reformat 'Page config structure' to array. + * + * @return array + */ + public function __toArray() + { + $result = []; + foreach ($this->serializableProperties as $property) { + $result[$property] = $this->{$property}; + } + + return $result; + } + + /** + * Update 'Page config structure' data. + * + * @param array $data + * @return void + */ + public function populateWithArray(array $data) + { + foreach ($this->serializableProperties as $property) { + $this->{$property} = $this->getArrayValueByKey($property, $data); + } + } + + /** + * Get value from array by key. + * + * @param string $key + * @param array $array + * @return array + */ + private function getArrayValueByKey($key, array $array) + { + return isset($array[$key]) ? $array[$key] : []; + } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php index 413b1e4cd1ac83008600dbf062d038796a408642..640ece658edaa0620c3c470206914b431cf403bc 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php @@ -1,10 +1,12 @@ <?php /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2017 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\View\Test\Unit; +use Magento\Framework\Serialize\SerializerInterface; + /** * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -81,6 +83,16 @@ class LayoutTest extends \PHPUnit_Framework_TestCase */ protected $readerContextMock; + /** + * @var \Magento\Framework\View\Page\Config\Structure|\PHPUnit_Framework_MockObject_MockObject + */ + private $pageConfigStructure; + + /** + * @var \Magento\Framework\View\Layout\ScheduledStructure|\PHPUnit_Framework_MockObject_MockObject + */ + private $layoutScheduledSructure; + /** * @var \Magento\Framework\View\Layout\Generator\ContextFactory|\PHPUnit_Framework_MockObject_MockObject */ @@ -96,6 +108,11 @@ class LayoutTest extends \PHPUnit_Framework_TestCase */ protected $loggerMock; + /** + * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + protected function setUp() { $this->structureMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Data\Structure::class) @@ -141,9 +158,22 @@ class LayoutTest extends \PHPUnit_Framework_TestCase $this->readerContextFactoryMock = $this->getMockBuilder( \Magento\Framework\View\Layout\Reader\ContextFactory::class )->disableOriginalConstructor()->getMock(); + + $this->pageConfigStructure = $this->getMockBuilder(\Magento\Framework\View\Page\Config\Structure::class) + ->setMethods(['__toArray', 'populateWithArray']) + ->getMock(); + $this->layoutScheduledSructure = $this->getMockBuilder(\Magento\Framework\View\Layout\ScheduledStructure::class) + ->setMethods(['__toArray', 'populateWithArray']) + ->getMock(); $this->readerContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Reader\Context::class) + ->setMethods(['getPageConfigStructure', 'getScheduledStructure']) ->disableOriginalConstructor() ->getMock(); + $this->readerContextMock->expects($this->any())->method('getPageConfigStructure') + ->willReturn($this->pageConfigStructure); + $this->readerContextMock->expects($this->any())->method('getScheduledStructure') + ->willReturn($this->layoutScheduledSructure); + $this->generatorContextFactoryMock = $this->getMockBuilder( \Magento\Framework\View\Layout\Generator\ContextFactory::class ) @@ -154,6 +184,15 @@ class LayoutTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) ->getMock(); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class); + $this->serializer->expects($this->any())->method('serialize') + ->willReturnCallback(function ($value) { + return json_encode($value); + }); + $this->serializer->expects($this->any())->method('unserialize') + ->willReturnCallback(function ($value) { + return json_decode($value, true); + }); $this->model = new \Magento\Framework\View\Layout( $this->processorFactoryMock, @@ -168,7 +207,8 @@ class LayoutTest extends \PHPUnit_Framework_TestCase $this->generatorContextFactoryMock, $this->appStateMock, $this->loggerMock, - true + true, + $this->serializer ); } @@ -735,9 +775,32 @@ class LayoutTest extends \PHPUnit_Framework_TestCase ->with($this->readerContextMock, $xml) ->willReturnSelf(); + $pageConfigStructureData = [ + 'field_1' => 123, + 'field_2' => 'text', + 'field_3' => [ + 'field_3_1' => '1244', + 'field_3_2' => null, + 'field_3_3' => false, + ] + ]; + $this->pageConfigStructure->expects($this->any())->method('__toArray') + ->willReturn($pageConfigStructureData); + + $layoutScheduledStructureData = [ + 'field_1' => 1283, + 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + ]; + $this->layoutScheduledSructure->expects($this->any())->method('__toArray') + ->willReturn($layoutScheduledStructureData); + $data = [ + 'pageConfigStructure' => $pageConfigStructureData, + 'scheduledStructure' => $layoutScheduledStructureData + ]; + $this->cacheMock->expects($this->once()) ->method('save') - ->with(serialize($this->readerContextMock), 'structure_' . $layoutCacheId, $handles) + ->with(json_encode($data), 'structure_' . $layoutCacheId, $handles) ->willReturn(true); $generatorContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Context::class) @@ -774,6 +837,9 @@ class LayoutTest extends \PHPUnit_Framework_TestCase $xml = simplexml_load_string('<layout/>', \Magento\Framework\View\Layout\Element::class); $this->model->setXml($xml); + $this->readerContextFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->readerContextMock); $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') @@ -787,14 +853,33 @@ class LayoutTest extends \PHPUnit_Framework_TestCase ->method('getCacheId') ->willReturn($layoutCacheId); - $readerContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Reader\Context::class) - ->disableOriginalConstructor() - ->getMock(); + $pageConfigStructureData = [ + 'field_1' => 123, + 'field_2' => 'text', + 'field_3' => [ + 'field_3_1' => '1244', + 'field_3_2' => null, + 'field_3_3' => false, + ] + ]; + $this->pageConfigStructure->expects($this->once())->method('populateWithArray') + ->with($pageConfigStructureData); + + $layoutScheduledStructureData = [ + 'field_1' => 1283, + 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + ]; + $this->layoutScheduledSructure->expects($this->once())->method('populateWithArray') + ->with($layoutScheduledStructureData); + $data = [ + 'pageConfigStructure' => $pageConfigStructureData, + 'scheduledStructure' => $layoutScheduledStructureData + ]; $this->cacheMock->expects($this->once()) ->method('load') ->with('structure_' . $layoutCacheId) - ->willReturn(serialize($readerContextMock)); + ->willReturn(json_encode($data)); $this->readerPoolMock->expects($this->never()) ->method('interpret'); @@ -811,7 +896,7 @@ class LayoutTest extends \PHPUnit_Framework_TestCase $this->generatorPoolMock->expects($this->once()) ->method('process') - ->with($readerContextMock, $generatorContextMock) + ->with($this->readerContextMock, $generatorContextMock) ->willReturn(true); $elements = [