diff --git a/app/code/Magento/Backend/App/Config.php b/app/code/Magento/Backend/App/Config.php index d8eaa7af14254bd16dd3c8f7ae3cc9a549e75cde..0ac5211b41d9f58308d4f57977e0c8054c15e4c7 100644 --- a/app/code/Magento/Backend/App/Config.php +++ b/app/code/Magento/Backend/App/Config.php @@ -10,6 +10,8 @@ namespace Magento\Backend\App; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * Backend config accessor */ @@ -36,7 +38,7 @@ class Config implements ConfigInterface */ public function getValue($path) { - return $this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, null)->getValue($path); + return $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->getValue($path); } /** @@ -48,7 +50,7 @@ class Config implements ConfigInterface */ public function setValue($path, $value) { - $this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, null)->setValue($path, $value); + $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->setValue($path, $value); } /** @@ -59,6 +61,6 @@ class Config implements ConfigInterface */ public function isSetFlag($path) { - return !!$this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, null)->getValue($path); + return !!$this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->getValue($path); } } diff --git a/app/code/Magento/Backend/Block/Menu.php b/app/code/Magento/Backend/Block/Menu.php index 55ad3f2cbca00cec0f08004c0a0db5867727d769..087fd1fa351709778cc536f519c4b84206d97134 100644 --- a/app/code/Magento/Backend/Block/Menu.php +++ b/app/code/Magento/Backend/Block/Menu.php @@ -436,6 +436,7 @@ class Menu extends \Magento\Backend\Block\Template * @param array $colBrakes * @return string HTML * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function renderNavigation($menu, $level = 0, $limit = 0, $colBrakes = []) { @@ -454,21 +455,30 @@ class Menu extends \Magento\Backend\Block\Template } $id = $this->getJsId($menuItem->getId()); - $output .= '<li ' . $this->getUiId( - $menuItem->getId() - ) . ' class="item-' . $itemClass . ' ' . $this->_renderItemCssClass( - $menuItem, - $level - ) . ($level == 0 ? '" id="' . $id . '" aria-haspopup="true' : '') - . '" role="menu-item">' . $this->_renderAnchor( - $menuItem, - $level - ) . $this->_addSubMenu( - $menuItem, - $level, - $limit, - $id - ) . '</li>'; + if (count($menu) > 1 || $level != 1) { + $output .= '<li ' . $this->getUiId( + $menuItem->getId() + ) . ' class="item-' . $itemClass . ' ' . $this->_renderItemCssClass( + $menuItem, + $level + ) . ($level == 0 ? '" id="' . $id . '" aria-haspopup="true' : '') + . '" role="menu-item">' . $this->_renderAnchor( + $menuItem, + $level + ) . $this->_addSubMenu( + $menuItem, + $level, + $limit, + $id + ) . '</li>'; + } else { + $output .= $this->_addSubMenu( + $menuItem, + $level, + $limit, + $id); + } + $itemPosition++; } diff --git a/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php b/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php index d32e99a00d814b5a134e9e3060526e7586f9043e..827ae18b0b8e1f908653597bc1816a6c73d9b148 100644 --- a/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php +++ b/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php @@ -8,6 +8,8 @@ namespace Magento\Backend\Block\Page\System\Config\Robots; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * "Reset to Defaults" button renderer * @@ -50,7 +52,7 @@ class Reset extends \Magento\Config\Block\System\Config\Form\Field public function getRobotsDefaultCustomInstructions() { return trim((string)$this->_scopeConfig->getValue( - self::XML_PATH_ROBOTS_DEFAULT_CUSTOM_INSTRUCTIONS, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + self::XML_PATH_ROBOTS_DEFAULT_CUSTOM_INSTRUCTIONS, ScopeConfigInterface::SCOPE_TYPE_DEFAULT )); } diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php index fec438f70c744d7b7cc9999d8574f3e4deef862c..d5cf7e86c4b8ae8c301a2993893a9d31d615e7e5 100644 --- a/app/code/Magento/Backend/Model/Auth/Session.php +++ b/app/code/Magento/Backend/Model/Auth/Session.php @@ -61,9 +61,11 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage * @param \Magento\Framework\Session\StorageInterface $storage * @param CookieManagerInterface $cookieManager * @param CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\App\State $appState * @param \Magento\Framework\Acl\Builder $aclBuilder * @param \Magento\Backend\Model\UrlInterface $backendUrl * @param \Magento\Backend\App\ConfigInterface $config + * @throws \Magento\Framework\Exception\SessionException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -75,6 +77,7 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage \Magento\Framework\Session\StorageInterface $storage, CookieManagerInterface $cookieManager, CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\App\State $appState, \Magento\Framework\Acl\Builder $aclBuilder, \Magento\Backend\Model\UrlInterface $backendUrl, \Magento\Backend\App\ConfigInterface $config @@ -90,9 +93,9 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage $validator, $storage, $cookieManager, - $cookieMetadataFactory + $cookieMetadataFactory, + $appState ); - $this->start(); } /** diff --git a/app/code/Magento/Backend/Model/Locale/Manager.php b/app/code/Magento/Backend/Model/Locale/Manager.php index 76a327f1364d1b42a05f3ea000474049d5b87068..4f9584c96a81706b0eaefafd3137a27d75291fc3 100644 --- a/app/code/Magento/Backend/Model/Locale/Manager.php +++ b/app/code/Magento/Backend/Model/Locale/Manager.php @@ -68,7 +68,7 @@ class Manager */ public function getUserInterfaceLocale() { - $interfaceLocale = \Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE; + $interfaceLocale = \Magento\Framework\Locale\Resolver::DEFAULT_LOCALE; $userData = $this->_authSession->getUser(); if ($userData && $userData->getInterfaceLocale()) { diff --git a/app/code/Magento/Backend/Model/Session.php b/app/code/Magento/Backend/Model/Session.php index c0719bb2ecf9ef98084a0ecba7e11630732eddbf..6dd5af4a3c54830029f8372e214009a4e3b04bef 100644 --- a/app/code/Magento/Backend/Model/Session.php +++ b/app/code/Magento/Backend/Model/Session.php @@ -9,39 +9,6 @@ namespace Magento\Backend\Model; class Session extends \Magento\Framework\Session\SessionManager { - /** - * @param \Magento\Framework\App\Request\Http $request - * @param \Magento\Framework\Session\SidResolverInterface $sidResolver - * @param \Magento\Framework\Session\Config\ConfigInterface $sessionConfig - * @param \Magento\Framework\Session\SaveHandlerInterface $saveHandler - * @param \Magento\Framework\Session\ValidatorInterface $validator - * @param \Magento\Framework\Session\StorageInterface $storage - * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager - * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory - */ - public function __construct( - \Magento\Framework\App\Request\Http $request, - \Magento\Framework\Session\SidResolverInterface $sidResolver, - \Magento\Framework\Session\Config\ConfigInterface $sessionConfig, - \Magento\Framework\Session\SaveHandlerInterface $saveHandler, - \Magento\Framework\Session\ValidatorInterface $validator, - \Magento\Framework\Session\StorageInterface $storage, - \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, - \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory - ) { - parent::__construct( - $request, - $sidResolver, - $sessionConfig, - $saveHandler, - $validator, - $storage, - $cookieManager, - $cookieMetadataFactory - ); - $this->start(); - } - /** * Skip path validation in backend area * diff --git a/app/code/Magento/Backend/Model/Session/Quote.php b/app/code/Magento/Backend/Model/Session/Quote.php index 2f1f75ab07e9201d03ec0706fc3a0fb20f42ddc1..1427e1ad5ad570d5ea93c0f5cef3c5cdfd71c4cb 100644 --- a/app/code/Magento/Backend/Model/Session/Quote.php +++ b/app/code/Magento/Backend/Model/Session/Quote.php @@ -83,11 +83,13 @@ class Quote extends \Magento\Framework\Session\SessionManager * @param \Magento\Framework\Session\StorageInterface $storage * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\App\State $appState * @param CustomerRepositoryInterface $customerRepository * @param \Magento\Quote\Model\QuoteRepository $quoteRepository * @param \Magento\Sales\Model\OrderFactory $orderFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param GroupManagementInterface $groupManagement + * @throws \Magento\Framework\Exception\SessionException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -99,6 +101,7 @@ class Quote extends \Magento\Framework\Session\SessionManager \Magento\Framework\Session\StorageInterface $storage, \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\App\State $appState, CustomerRepositoryInterface $customerRepository, \Magento\Quote\Model\QuoteRepository $quoteRepository, \Magento\Sales\Model\OrderFactory $orderFactory, @@ -118,9 +121,9 @@ class Quote extends \Magento\Framework\Session\SessionManager $validator, $storage, $cookieManager, - $cookieMetadataFactory + $cookieMetadataFactory, + $appState ); - $this->start(); if ($this->_storeManager->hasSingleStore()) { $this->setStoreId($this->_storeManager->getStore(true)->getId()); } @@ -147,7 +150,7 @@ class Quote extends \Magento\Framework\Session\SessionManager $this->_quote->setStoreId($this->getStoreId()); } - if ($this->getCustomerId()) { + if ($this->getCustomerId() && $this->getCustomerId() != $this->_quote->getCustomerId()) { $customer = $this->customerRepository->getById($this->getCustomerId()); $this->_quote->assignCustomer($customer); } diff --git a/app/code/Magento/Backend/Model/Url.php b/app/code/Magento/Backend/Model/Url.php index 265a7237ed37e1713fb17d97a3674c633d406341..36fbfafe66974d236c0bc90c2d7133fc3bcf128b 100644 --- a/app/code/Magento/Backend/Model/Url.php +++ b/app/code/Magento/Backend/Model/Url.php @@ -83,7 +83,7 @@ class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlIn * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver * @param \Magento\Framework\Session\Generic $session * @param \Magento\Framework\Session\SidResolverInterface $sidResolver - * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver + * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param string $scopeType @@ -105,7 +105,7 @@ class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlIn \Magento\Framework\Url\ScopeResolverInterface $scopeResolver, \Magento\Framework\Session\Generic $session, \Magento\Framework\Session\SidResolverInterface $sidResolver, - \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver, + \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory, \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, $scopeType, @@ -126,7 +126,7 @@ class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlIn $scopeResolver, $session, $sidResolver, - $routeParamsResolver, + $routeParamsResolverFactory, $queryParamsResolver, $scopeConfig, $scopeType, diff --git a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php index 000c861f11d8eba4c098f8427df867287de61c2d..a624a526d5dbffd7dcd06460a11872fb1f4328c2 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Backend\Test\Unit\Model\Locale; +use Magento\Framework\Locale\Resolver; + class ManagerTest extends \PHPUnit_Framework_TestCase { /** @@ -87,7 +89,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase { $locale = $this->_model->getUserInterfaceLocale(); - $this->assertEquals($locale, \Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE); + $this->assertEquals($locale, Resolver::DEFAULT_LOCALE); } /** diff --git a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php index 13f906bee346b45c02399d3f2cdc59602f9dec3f..fd3af3380023396c31f53ac97a41a1a631154993 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php @@ -182,6 +182,13 @@ class QuoteTest extends \PHPUnit_Framework_TestCase '', false ); + $appStateMock = $this->getMock( + 'Magento\Framework\App\State', + [], + [], + '', + false + ); $this->storeManagerMock = $this->getMockForAbstractClass( 'Magento\Store\Model\StoreManagerInterface', [], @@ -201,11 +208,12 @@ class QuoteTest extends \PHPUnit_Framework_TestCase 'storage' => $this->storageMock, 'cookieManager' => $this->cookieManagerMock, 'cookieMetadataFactory' => $this->cookieMetadataFactoryMock, + 'appState' => $appStateMock, 'customerRepository' => $this->customerRepositoryMock, 'quoteRepository' => $this->quoteRepositoryMock, 'orderFactory' => $this->orderFactoryMock, 'storeManager' => $this->storeManagerMock, - 'groupManagement' => $this->groupManagementMock + 'groupManagement' => $this->groupManagementMock, ], '', true @@ -217,12 +225,42 @@ class QuoteTest extends \PHPUnit_Framework_TestCase * * @return void */ - public function testGetQuote() + public function testGetQuoteWithoutQuoteId() { - $storeId = 10; $quoteId = 22; - $customerGroupId = 77; + $storeId = 10; $customerId = 66; + $customerGroupId = 77; + + $this->quote->expects($this->any()) + ->method('getQuoteId') + ->will($this->returnValue(null)); + $this->quote->expects($this->any()) + ->method('setQuoteId') + ->with($quoteId); + $this->quote->expects($this->any()) + ->method('getStoreId') + ->will($this->returnValue($storeId)); + $this->quote->expects($this->any()) + ->method('getCustomerId') + ->will($this->returnValue($customerId)); + + $defaultGroup = $this->getMockBuilder('Magento\Customer\Api\Data\GroupInterface') + ->getMock(); + $defaultGroup->expects($this->any()) + ->method('getId') + ->will($this->returnValue($customerGroupId)); + $this->groupManagementMock->expects($this->any()) + ->method('getDefaultGroup') + ->will($this->returnValue($defaultGroup)); + + $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->customerRepositoryMock->expects($this->once()) + ->method('getById') + ->with($customerId) + ->willReturn($dataCustomerMock); $quoteMock = $this->getMock( 'Magento\Quote\Model\Quote', @@ -240,28 +278,9 @@ class QuoteTest extends \PHPUnit_Framework_TestCase '', false ); - - $defaultGroup = $this->getMockBuilder('Magento\Customer\Api\Data\GroupInterface') - ->getMock(); - $defaultGroup->expects($this->any()) - ->method('getId') - ->will($this->returnValue($customerGroupId)); - $this->groupManagementMock->expects($this->any()) - ->method('getDefaultGroup') - ->will($this->returnValue($defaultGroup)); - - $this->quoteRepositoryMock->expects($this->once()) - ->method('create') - ->will($this->returnValue($quoteMock)); - $this->quote->expects($this->any()) - ->method('getStoreId') - ->will($this->returnValue($storeId)); $quoteMock->expects($this->once()) ->method('setStoreId') ->with($storeId); - $this->quote->expects($this->any()) - ->method('getQuoteId') - ->will($this->returnValue(null)); $quoteMock->expects($this->once()) ->method('setCustomerGroupId') ->with($customerGroupId) @@ -270,25 +289,9 @@ class QuoteTest extends \PHPUnit_Framework_TestCase ->method('setIsActive') ->with(false) ->will($this->returnSelf()); - $this->quoteRepositoryMock->expects($this->once()) - ->method('save') - ->with($quoteMock); $quoteMock->expects($this->once()) ->method('getId') ->will($this->returnValue($quoteId)); - $this->quote->expects($this->any()) - ->method('setQuoteId') - ->with($quoteId); - $this->quote->expects($this->any()) - ->method('getCustomerId') - ->will($this->returnValue($customerId)); - $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') - ->disableOriginalConstructor() - ->getMock(); - $this->customerRepositoryMock->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($dataCustomerMock); $quoteMock->expects($this->once()) ->method('assignCustomer') ->with($dataCustomerMock); @@ -299,6 +302,13 @@ class QuoteTest extends \PHPUnit_Framework_TestCase ->method('setIsSuperMode') ->with(true); + $this->quoteRepositoryMock->expects($this->once()) + ->method('create') + ->will($this->returnValue($quoteMock)); + $this->quoteRepositoryMock->expects($this->once()) + ->method('save') + ->with($quoteMock); + $this->assertEquals($quoteMock, $this->quote->getQuote()); } @@ -306,12 +316,33 @@ class QuoteTest extends \PHPUnit_Framework_TestCase * Run test getQuote method * * @return void + * @dataProvider getQuoteDataProvider */ - public function testGetQuoteGet() + public function testGetQuoteWithQuoteId($customerId, $quoteCustomerId, $expectedNumberOfInvokes) { - $storeId = 10; $quoteId = 22; - $customerId = 66; + $storeId = 10; + + $this->quote->expects($this->any()) + ->method('getQuoteId') + ->will($this->returnValue($quoteId)); + $this->quote->expects($this->any()) + ->method('setQuoteId') + ->with($quoteId); + $this->quote->expects($this->any()) + ->method('getStoreId') + ->will($this->returnValue($storeId)); + $this->quote->expects($this->any()) + ->method('getCustomerId') + ->will($this->returnValue($customerId)); + + $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->customerRepositoryMock->expects($this->$expectedNumberOfInvokes()) + ->method('getById') + ->with($customerId) + ->willReturn($dataCustomerMock); $quoteMock = $this->getMock( 'Magento\Quote\Model\Quote', @@ -323,43 +354,17 @@ class QuoteTest extends \PHPUnit_Framework_TestCase 'assignCustomer', 'setIgnoreOldQty', 'setIsSuperMode', + 'getCustomerId', '__wakeup' ], [], '', false ); - - $this->quoteRepositoryMock->expects($this->once()) - ->method('create') - ->will($this->returnValue($quoteMock)); - $this->quote->expects($this->any()) - ->method('getStoreId') - ->will($this->returnValue($storeId)); $quoteMock->expects($this->once()) ->method('setStoreId') ->with($storeId); - $this->quote->expects($this->any()) - ->method('getQuoteId') - ->will($this->returnValue($quoteId)); - $this->quoteRepositoryMock->expects($this->once()) - ->method('get') - ->with($quoteId) - ->willReturn($quoteMock); - $this->quote->expects($this->any()) - ->method('setQuoteId') - ->with($quoteId); - $this->quote->expects($this->any()) - ->method('getCustomerId') - ->will($this->returnValue($customerId)); - $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') - ->disableOriginalConstructor() - ->getMock(); - $this->customerRepositoryMock->expects($this->once()) - ->method('getById') - ->with($customerId) - ->willReturn($dataCustomerMock); - $quoteMock->expects($this->once()) + $quoteMock->expects($this->$expectedNumberOfInvokes()) ->method('assignCustomer') ->with($dataCustomerMock); $quoteMock->expects($this->once()) @@ -368,7 +373,29 @@ class QuoteTest extends \PHPUnit_Framework_TestCase $quoteMock->expects($this->once()) ->method('setIsSuperMode') ->with(true); + $quoteMock->expects($this->once()) + ->method('getCustomerId') + ->will($this->returnValue($quoteCustomerId)); + + $this->quoteRepositoryMock->expects($this->once()) + ->method('create') + ->will($this->returnValue($quoteMock)); + $this->quoteRepositoryMock->expects($this->once()) + ->method('get') + ->with($quoteId) + ->willReturn($quoteMock); $this->assertEquals($quoteMock, $this->quote->getQuote()); } + + /** + * @return array + */ + public function getQuoteDataProvider() + { + return [ + 'customer ids different' => [66, null, 'once'], + 'customer ids same' => [66, 66, 'never'], + ]; + } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php index a53b891a17be5e100f0916cf04e6b2a4f3469c71..b833fe25752a09ecd9af87a7147f4debf493affe 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php @@ -165,7 +165,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase 'menuConfig' => $this->_menuConfigMock, 'authSession' => $this->_authSessionMock, 'encryptor' => $this->_encryptor, - 'routeParamsResolver' => $this->_paramsResolverMock + 'routeParamsResolverFactory' => $this->_paramsResolverMock ] ); $this->_paramsResolverMock->expects( @@ -186,7 +186,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase 'menuConfig' => $this->_menuConfigMock, 'authSession' => $this->_authSessionMock, 'encryptor' => $this->_encryptor, - 'routeParamsResolver' => $this->_paramsResolverMock + 'routeParamsResolverFactory' => $this->_paramsResolverMock ] ); @@ -259,7 +259,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase [ 'backendHelper' => $helperMock, 'authSession' => $this->_authSessionMock, - 'routeParamsResolver' => $this->_paramsResolverMock + 'routeParamsResolverFactory' => $this->_paramsResolverMock ] ); $urlModel->getAreaFrontName(); diff --git a/app/code/Magento/Backup/README.md b/app/code/Magento/Backup/README.md index 661f8a5d6ddd86f23772c9f595526420ae51dd9e..59688ea3e716e162e8efa9421de0ffe004ec3cee 100644 --- a/app/code/Magento/Backup/README.md +++ b/app/code/Magento/Backup/README.md @@ -1,3 +1,3 @@ The Backup module allows administrators to perform backups and rollbacks. Types of backups include system, database and media backups. This module relies on the Cron module to schedule backups. -This module does not effect the storefront. \ No newline at end of file +This module does not affect the storefront. diff --git a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml index 861bc2050e44a61e3cf1daf950dcad8fc05d0997..1553a783f6356dca8510044813cd919312ff5fdb 100644 --- a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml +++ b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml @@ -46,7 +46,6 @@ <arguments> <argument name="header" xsi:type="string" translate="true">Name</argument> <argument name="index" xsi:type="string">display_name</argument> - <argument name="filter" xsi:type="string">0</argument> <argument name="sortable" xsi:type="string">1</argument> <argument name="column_css_class" xsi:type="string">col-name</argument> <argument name="header_css_class" xsi:type="string">col-name</argument> diff --git a/app/code/Magento/Bundle/etc/data_object.xml b/app/code/Magento/Bundle/etc/service_data_attributes.xml similarity index 70% rename from app/code/Magento/Bundle/etc/data_object.xml rename to app/code/Magento/Bundle/etc/service_data_attributes.xml index 2b3da013978f971bb20baca3a5e0ca95f2b501fc..8150a2dceeddd7d70d4e16ffa3d1b6996f316d5d 100644 --- a/app/code/Magento/Bundle/etc/data_object.xml +++ b/app/code/Magento/Bundle/etc/service_data_attributes.xml @@ -5,8 +5,8 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="bundle_product_options" type="Magento\Bundle\Api\Data\OptionInterface[]" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index a79466c6007bfb13a619d59a922c29d99d947968..29be0868558ee450d152d492091f5cb5b9bbaef8 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -29,7 +29,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template * * @var array */ - protected $_availableOrder = []; + protected $_availableOrder = null; /** * List of available view types @@ -146,19 +146,6 @@ class Toolbar extends \Magento\Framework\View\Element\Template parent::__construct($context, $data); } - /** - * Init Toolbar - * - * @return null - */ - protected function _construct() - { - parent::_construct(); - $this->_orderField = $this->_productListHelper->getDefaultSortField(); - $this->_availableOrder = $this->_catalogConfig->getAttributeUsedForSortByArray(); - $this->_availableMode = $this->_productListHelper->getAvailableViewMode(); - } - /** * Disable list state params memorizing * @@ -241,7 +228,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template } $orders = $this->getAvailableOrders(); - $defaultOrder = $this->_orderField; + $defaultOrder = $this->getOrderField(); if (!isset($orders[$defaultOrder])) { $keys = array_keys($orders); @@ -295,6 +282,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ public function setDefaultOrder($field) { + $this->loadAvailableOrders(); if (isset($this->_availableOrder[$field])) { $this->_orderField = $field; } @@ -322,6 +310,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ public function getAvailableOrders() { + $this->loadAvailableOrders(); return $this->_availableOrder; } @@ -346,6 +335,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ public function addOrderToAvailableOrders($order, $value) { + $this->loadAvailableOrders(); $this->_availableOrder[$order] = $value; return $this; } @@ -358,6 +348,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ public function removeOrderFromAvailableOrders($order) { + $this->loadAvailableOrders(); if (isset($this->_availableOrder[$order])) { unset($this->_availableOrder[$order]); } @@ -411,7 +402,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template if ($mode) { return $mode; } - $defaultMode = $this->_productListHelper->getDefaultViewMode($this->_availableMode); + $defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes()); $mode = $this->_toolbarModel->getMode(); if (!$mode || !isset($this->_availableMode[$mode])) { $mode = $defaultMode; @@ -439,6 +430,9 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ public function getModes() { + if ($this->_availableMode === []) { + $this->_availableMode = $this->_productListHelper->getAvailableViewMode(); + } return $this->_availableMode; } @@ -450,6 +444,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ public function setModes($modes) { + $this->getModes(); if (!isset($this->_availableMode)) { $this->_availableMode = $modes; } @@ -691,4 +686,30 @@ class Toolbar extends \Magento\Framework\View\Element\Template $options = array_replace_recursive($options, $customOptions); return json_encode(['productListToolbarForm' => $options]); } + + /** + * Get order field + * + * @return null|string + */ + protected function getOrderField() + { + if ($this->_orderField === null) { + $this->_orderField = $this->_productListHelper->getDefaultSortField(); + } + return $this->_orderField; + } + + /** + * Load Available Orders + * + * @return $this + */ + private function loadAvailableOrders() + { + if ($this->_availableOrder === null) { + $this->_availableOrder = $this->_catalogConfig->getAttributeUsedForSortByArray(); + } + return $this; + } } diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php index f80bdede71af34f4440d8cce9ff14da2ebfdf9f0..db3d2a911038295fbcb8942d7c61f0041a2637a5 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options.php @@ -206,19 +206,30 @@ class Options extends \Magento\Framework\View\Element\Template /* @var $option \Magento\Catalog\Model\Product\Option */ $priceValue = 0; if ($option->getGroupByType() == \Magento\Catalog\Model\Product\Option::OPTION_GROUP_SELECT) { - $_tmpPriceValues = []; + $tmpPriceValues = []; foreach ($option->getValues() as $value) { /* @var $value \Magento\Catalog\Model\Product\Option\Value */ $id = $value->getId(); - $_tmpPriceValues[$id] = $this->_getPriceConfiguration($value); + $tmpPriceValues[$id] = $this->_getPriceConfiguration($value); } - $priceValue = $_tmpPriceValues; + $priceValue = $tmpPriceValues; } else { $priceValue = $this->_getPriceConfiguration($option); } $config[$option->getId()] = $priceValue; } + $configObj = new \Magento\Framework\Object( + [ + 'config' => $config, + ] + ); + + //pass the return array encapsulated in an object for the other modules to be able to alter it eg: weee + $this->_eventManager->dispatch('catalog_product_option_price_configuration_after', ['configObj' => $configObj]); + + $config=$configObj->getConfig(); + return $this->_jsonEncoder->encode($config); } diff --git a/app/code/Magento/Catalog/Model/Observer.php b/app/code/Magento/Catalog/Model/Observer.php index 39e72883f68add9d3672497199eb7c744480a7db..417a4f61811c99663538fd72e3c790ddc0753f3c 100644 --- a/app/code/Magento/Catalog/Model/Observer.php +++ b/app/code/Magento/Catalog/Model/Observer.php @@ -34,7 +34,14 @@ class Observer * * @var \Magento\Catalog\Model\Layer */ - protected $_catalogLayer; + private $_catalogLayer = null; + + /** + * Catalog layer resolver + * + * @var \Magento\Catalog\Model\Layer\Resolver + */ + protected $layerResolver; /** * Store manager @@ -95,7 +102,7 @@ class Observer $this->_categoryResource = $categoryResource; $this->_catalogProduct = $catalogProduct; $this->_storeManager = $storeManager; - $this->_catalogLayer = $layerResolver->get(); + $this->layerResolver = $layerResolver; $this->_catalogCategory = $catalogCategory; $this->_catalogData = $catalogData; $this->categoryFlatConfig = $categoryFlatState; @@ -184,11 +191,12 @@ class Observer */ protected function hasActive($category) { - if (!$this->_catalogLayer) { + $catalogLayer = $this->getCatalogLayer(); + if (!$catalogLayer) { return false; } - $currentCategory = $this->_catalogLayer->getCurrentCategory(); + $currentCategory = $catalogLayer->getCurrentCategory(); if (!$currentCategory) { return false; } @@ -196,4 +204,16 @@ class Observer $categoryPathIds = explode(',', $currentCategory->getPathInStore()); return in_array($category->getId(), $categoryPathIds); } + + /** + * Get catalog layer + * @return \Magento\Catalog\Model\Layer + */ + private function getCatalogLayer() + { + if ($this->_catalogLayer === null) { + $this->_catalogLayer = $this->layerResolver->get(); + } + return $this->_catalogLayer; + } } diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index ebd9494fe1f842f7e42eba21cdb0c3da4cbd1325..d012f23de3df22b5e130abe2454fe2dc8f030c25 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -250,15 +250,25 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements protected $metadataService; /* - * @param \Magento\Catalog\Model\ProductLink\ProductLinkManagementInterface + * @param \Magento\Catalog\Model\ProductLink\CollectionProvider */ - protected $linkManagement; + protected $entityCollectionProvider; /* - * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory + * @param \Magento\Catalog\Model\Product\LinkTypeProvider + */ + protected $linkProvider; + + /* + * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory */ protected $productLinkFactory; + /* + * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory + */ + protected $productLinkExtensionFactory; + /** * @var \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory */ @@ -318,8 +328,10 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements * @param Indexer\Product\Eav\Processor $productEavIndexerProcessor * @param CategoryRepositoryInterface $categoryRepository * @param Product\Image\CacheFactory $imageCacheFactory - * @param \Magento\Catalog\Model\ProductLink\Management $linkManagement - * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory, + * @param \Magento\Catalog\Model\ProductLink\CollectionProvider $entityCollectionProvider + * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider + * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory + * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory * @param \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param array $data @@ -354,8 +366,10 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements \Magento\Catalog\Model\Indexer\Product\Eav\Processor $productEavIndexerProcessor, CategoryRepositoryInterface $categoryRepository, Product\Image\CacheFactory $imageCacheFactory, - \Magento\Catalog\Model\ProductLink\Management $linkManagement, + \Magento\Catalog\Model\ProductLink\CollectionProvider $entityCollectionProvider, + \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider, \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory, + \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory, \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, array $data = [] @@ -380,8 +394,10 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements $this->_productEavIndexerProcessor = $productEavIndexerProcessor; $this->categoryRepository = $categoryRepository; $this->imageCacheFactory = $imageCacheFactory; - $this->linkManagement = $linkManagement; + $this->entityCollectionProvider = $entityCollectionProvider; + $this->linkTypeProvider = $linkTypeProvider; $this->productLinkFactory = $productLinkFactory; + $this->productLinkExtensionFactory = $productLinkExtensionFactory; $this->mediaGalleryEntryFactory = $mediaGalleryEntryFactory; $this->dataObjectHelper = $dataObjectHelper; parent::__construct( @@ -1352,23 +1368,34 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements public function getProductLinks() { if (empty($this->_links)) { - $productLinks = []; - - $productLinks['related'] = $this->getRelatedProducts(); - $productLinks['upsell'] = $this->getUpSellProducts(); - $productLinks['crosssell'] = $this->getCrossSellProducts(); - $output = []; - foreach ($productLinks as $type => $linkTypeArray) { - foreach ($linkTypeArray as $link) { + $linkTypes = $this->linkTypeProvider->getLinkTypes(); + foreach (array_keys($linkTypes) as $linkTypeName) { + $collection = $this->entityCollectionProvider->getCollection($this, $linkTypeName); + foreach ($collection as $item) { /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ $productLink = $this->productLinkFactory->create(); $productLink->setProductSku($this->getSku()) - ->setLinkType($type) - ->setLinkedProductSku($link['sku']) - ->setLinkedProductType($link['type_id']) - ->setPosition($link['position']); - + ->setLinkType($linkTypeName) + ->setLinkedProductSku($item['sku']) + ->setLinkedProductType($item['type']) + ->setPosition($item['position']); + if (isset($item['custom_attributes'])) { + $productLinkExtension = $productLink->getExtensionAttributes(); + if ($productLinkExtension === null) { + $productLinkExtension = $this->productLinkExtensionFactory->create(); + } + foreach ($item['custom_attributes'] as $option) { + $name = $option['attribute_code']; + $value = $option['value']; + $setterName = 'set' . ucfirst($name); + // Check if setter exists + if (method_exists($productLinkExtension, $setterName)) { + call_user_func([$productLinkExtension, $setterName], $value); + } + } + $productLink->setExtensionAttributes($productLinkExtension); + } $output[] = $productLink; } } diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 94612dbf200a5f95fc003342e0c114d273366be1..b40b91d5b08b8efa59ff0534e04600c1bfcc33d7 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -100,6 +100,13 @@ abstract class AbstractType */ protected $_fileStorageDb; + /** + * Cache key for Product Attributes + * + * @var string + */ + protected $_cacheProductSetAttributes = '_cache_instance_product_set_attributes'; + /** * Delete data specific for this product type * @@ -249,9 +256,13 @@ abstract class AbstractType */ public function getSetAttributes($product) { - return $product->getResource() - ->loadAllAttributes($product) - ->getSortedAttributes($product->getAttributeSetId()); + if (!$product->hasData($this->_cacheProductSetAttributes)) { + $setAttributes = $product->getResource() + ->loadAllAttributes($product) + ->getSortedAttributes($product->getAttributeSetId()); + $product->setData($this->_cacheProductSetAttributes, $setAttributes); + } + return $product->getData($this->_cacheProductSetAttributes); } /** diff --git a/app/code/Magento/Catalog/Model/ProductLink/Management.php b/app/code/Magento/Catalog/Model/ProductLink/Management.php index 558561d22de50af6f402f9201a6138aaba441496..e702a6cde0e41f1a8e3316d5a30be821c620fa0b 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Management.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Management.php @@ -7,7 +7,6 @@ namespace Magento\Catalog\Model\ProductLink; use Magento\Catalog\Api\Data; -use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks as LinksInitializer; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; @@ -18,26 +17,6 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface */ protected $productRepository; - /** - * @var CollectionProvider - */ - protected $entityCollectionProvider; - - /** - * @var LinksInitializer - */ - protected $linkInitializer; - - /** - * @var \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory - */ - protected $productLinkFactory; - - /** - * @var \Magento\Catalog\Model\Resource\Product - */ - protected $productResource; - /** * @var \Magento\Catalog\Model\Product\LinkTypeProvider */ @@ -45,25 +24,13 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface /** * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository - * @param CollectionProvider $collectionProvider - * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory - * @param LinksInitializer $linkInitializer - * @param \Magento\Catalog\Model\Resource\Product $productResource * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider */ public function __construct( \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, - CollectionProvider $collectionProvider, - \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory, - LinksInitializer $linkInitializer, - \Magento\Catalog\Model\Resource\Product $productResource, \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider ) { $this->productRepository = $productRepository; - $this->entityCollectionProvider = $collectionProvider; - $this->productLinkFactory = $productLinkFactory; - $this->productResource = $productResource; - $this->linkInitializer = $linkInitializer; $this->linkTypeProvider = $linkTypeProvider; } @@ -73,27 +40,22 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface public function getLinkedItemsByType($sku, $type) { $output = []; - $product = $this->productRepository->get($sku); - try { - $collection = $this->entityCollectionProvider->getCollection($product, $type); - } catch (NoSuchEntityException $e) { + + $linkTypes = $this->linkTypeProvider->getLinkTypes(); + + if (!isset($linkTypes[$type])) { throw new NoSuchEntityException(__('Unknown link type: %1', (string)$type)); } - foreach ($collection as $item) { - /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ - $productLink = $this->productLinkFactory->create(); - $productLink->setProductSku($product->getSku()) - ->setLinkType($type) - ->setLinkedProductSku($item['sku']) - ->setLinkedProductType($item['type']) - ->setPosition($item['position']); - if (isset($item['custom_attributes'])) { - foreach ($item['custom_attributes'] as $option) { - $productLink->getExtensionAttributes()->setQty($option['value']); - } + $product = $this->productRepository->get($sku); + $links = $product->getProductLinks(); + + // Only return the links of type specified + foreach ($links as $link) { + if ($link->getLinkType() == $type) { + $output[] = $link; } - $output[] = $productLink; } + return $output; } @@ -111,32 +73,27 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface } $product = $this->productRepository->get($sku); - $assignedSkuList = []; - /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $link */ - foreach ($items as $link) { - $assignedSkuList[] = $link->getLinkedProductSku(); - } - $linkedProductIds = $this->productResource->getProductsIdsBySkus($assignedSkuList); - $links = []; - /** @var \Magento\Catalog\Api\Data\ProductLinkInterface[] $items*/ - foreach ($items as $link) { - $data = $link->__toArray(); - $linkedSku = $link->getLinkedProductSku(); - if (!isset($linkedProductIds[$linkedSku])) { - throw new NoSuchEntityException( - __('Product with SKU "%1" does not exist', $linkedSku) - ); + // Replace only links of the specified type + $existingLinks = $product->getProductLinks(); + $newLinks = []; + if (!empty($existingLinks)) { + foreach ($existingLinks as $link) { + if ($link->getLinkType() != $type) { + $newLinks[] = $link; + } } - $data['product_id'] = $linkedProductIds[$linkedSku]; - $links[$linkedProductIds[$linkedSku]] = $data; + $newLinks = array_merge($newLinks, $items); + } else { + $newLinks = $items; } - $this->linkInitializer->initializeLinks($product, [$type => $links]); + $product->setProductLinks($newLinks); try { - $product->save(); + $this->productRepository->save($product); } catch (\Exception $exception) { throw new CouldNotSaveException(__('Invalid data provided for linked products')); } + return true; } } diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 40ecc5d2cb3419101374a9d563b52ada19cd9967..8f2e7664c4e3df2c3ebf963cd4122e22a0e730cb 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -75,6 +75,16 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa */ protected $linkInitializer; + /* + * @param \Magento\Catalog\Model\Product\LinkTypeProvider + */ + protected $linkTypeProvider; + + /* + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + */ + protected $storeManager; + /** * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface */ @@ -129,6 +139,8 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository * @param Resource\Product $resourceModel * @param \Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $linkInitializer + * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider + * @param \Magento\Store\Model\StoreManagerInterface $storeManager, * @param \Magento\Framework\Api\FilterBuilder $filterBuilder * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter @@ -149,6 +161,8 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository, \Magento\Catalog\Model\Resource\Product $resourceModel, \Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $linkInitializer, + \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider, + \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Api\FilterBuilder $filterBuilder, \Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface, \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, @@ -166,6 +180,8 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->resourceModel = $resourceModel; $this->linkInitializer = $linkInitializer; + $this->linkTypeProvider = $linkTypeProvider; + $this->storeManager = $storeManager; $this->attributeRepository = $attributeRepository; $this->filterBuilder = $filterBuilder; $this->metadataService = $metadataServiceInterface; @@ -260,6 +276,9 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa { if ($createNew) { $product = $this->productFactory->create(); + if ($this->storeManager->hasSingleStore()) { + $product->setWebsiteIds([$this->storeManager->getStore(true)->getWebsite()->getId()]); + } } else { unset($this->instances[$productData['sku']]); $product = $this->get($productData['sku']); @@ -353,11 +372,12 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa } // Clear all existing product links and then set the ones we want - $this->linkInitializer->initializeLinks($product, ['related' => []]); - $this->linkInitializer->initializeLinks($product, ['upsell' => []]); - $this->linkInitializer->initializeLinks($product, ['crosssell' => []]); + $linkTypes = $this->linkTypeProvider->getLinkTypes(); + foreach (array_keys($linkTypes) as $typeName) { + $this->linkInitializer->initializeLinks($product, [$typeName => []]); + } - // Gather each linktype info + // Set each linktype info if (!empty($newLinks)) { $productLinks = []; foreach ($newLinks as $link) { diff --git a/app/code/Magento/Catalog/Model/Resource/Category.php b/app/code/Magento/Catalog/Model/Resource/Category.php index f99d1b9ee9d2c15cdca6c883cc7ded953973afc7..27d7f8b801df56c735259f20150e53aa1a5d7af4 100644 --- a/app/code/Magento/Catalog/Model/Resource/Category.php +++ b/app/code/Magento/Catalog/Model/Resource/Category.php @@ -92,13 +92,36 @@ class Category extends AbstractResource $this->_categoryTreeFactory = $categoryTreeFactory; $this->_categoryCollectionFactory = $categoryCollectionFactory; $this->_eventManager = $eventManager; - $this->setType( - \Magento\Catalog\Model\Category::ENTITY - )->setConnection( - $this->_resource->getConnection('catalog_read'), - $this->_resource->getConnection('catalog_write') - ); - $this->_categoryProductTable = $this->getTable('catalog_category_product'); + + $this->_read = 'catalog_read'; + $this->_write = 'catalog_write'; + } + + /** + * Entity type getter and lazy loader + * + * @return \Magento\Eav\Model\Entity\Type + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getEntityType() + { + if (empty($this->_type)) { + $this->setType(\Magento\Catalog\Model\Category::ENTITY); + } + return parent::getEntityType(); + } + + /** + * Category product table name getter + * + * @return string + */ + public function getCategoryProductTable() + { + if (!$this->_categoryProductTable) { + $this->_categoryProductTable = $this->getTable('catalog_category_product'); + } + return $this->_categoryProductTable; } /** @@ -359,7 +382,7 @@ class Category extends AbstractResource */ if (!empty($delete)) { $cond = ['product_id IN(?)' => array_keys($delete), 'category_id=?' => $id]; - $adapter->delete($this->_categoryProductTable, $cond); + $adapter->delete($this->getCategoryProductTable(), $cond); } /** @@ -374,7 +397,7 @@ class Category extends AbstractResource 'position' => (int)$position, ]; } - $adapter->insertMultiple($this->_categoryProductTable, $data); + $adapter->insertMultiple($this->getCategoryProductTable(), $data); } /** @@ -384,7 +407,7 @@ class Category extends AbstractResource foreach ($update as $productId => $position) { $where = ['category_id = ?' => (int)$id, 'product_id = ?' => (int)$productId]; $bind = ['position' => (int)$position]; - $adapter->update($this->_categoryProductTable, $bind, $where); + $adapter->update($this->getCategoryProductTable(), $bind, $where); } } @@ -417,7 +440,7 @@ class Category extends AbstractResource public function getProductsPosition($category) { $select = $this->_getWriteAdapter()->select()->from( - $this->_categoryProductTable, + $this->getCategoryProductTable(), ['product_id', 'position'] )->where( 'category_id = :category_id' diff --git a/app/code/Magento/Catalog/Model/Resource/Category/Collection.php b/app/code/Magento/Catalog/Model/Resource/Category/Collection.php index 2abc203eccbcada147503ce7f5be4400deb898e8..f21ce9f363b51f01a7555d79e3ba39aa89598b53 100644 --- a/app/code/Magento/Catalog/Model/Resource/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/Resource/Category/Collection.php @@ -33,7 +33,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl * * @var string */ - protected $_productTable; + private $_productTable; /** * Store id, that we should count products on @@ -47,7 +47,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl * * @var string */ - protected $_productWebsiteTable; + private $_productWebsiteTable; /** * Load with product count flag @@ -64,9 +64,6 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl protected function _construct() { $this->_init('Magento\Catalog\Model\Category', 'Magento\Catalog\Model\Resource\Category'); - - $this->_productWebsiteTable = $this->getTable('catalog_product_website'); - $this->_productTable = $this->getTable('catalog_category_product'); } /** @@ -224,7 +221,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl if (!empty($regularIds)) { $select = $this->_conn->select(); $select->from( - ['main_table' => $this->_productTable], + ['main_table' => $this->getProductTable()], ['category_id', new \Zend_Db_Expr('COUNT(main_table.product_id)')] )->where( $this->_conn->quoteInto('main_table.category_id IN(?)', $regularIds) @@ -233,7 +230,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl ); if ($websiteId) { $select->join( - ['w' => $this->_productWebsiteTable], + ['w' => $this->getProductWebsiteTable()], 'main_table.product_id = w.product_id', [] )->where( @@ -259,7 +256,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; $select = $this->_conn->select(); $select->from( - ['main_table' => $this->_productTable], + ['main_table' => $this->getProductTable()], new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') )->joinInner( ['e' => $this->getTable('catalog_category_entity')], @@ -272,7 +269,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl ); if ($websiteId) { $select->join( - ['w' => $this->_productWebsiteTable], + ['w' => $this->getProductWebsiteTable()], 'main_table.product_id = w.product_id', [] )->where( @@ -416,4 +413,30 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl $this->setOrder($field, self::SORT_ORDER_ASC); return $this; } + + /** + * Getter for _productWebsiteTable + * + * @return string + */ + public function getProductWebsiteTable() + { + if (empty($this->_productWebsiteTable)) { + $this->_productWebsiteTable = $this->getTable('catalog_product_website'); + } + return $this->_productWebsiteTable; + } + + /** + * Getter for _productTable + * + * @return string + */ + public function getProductTable() + { + if (empty($this->_productTable)) { + $this->_productTable = $this->getTable('catalog_category_product'); + } + return $this->_productTable; + } } diff --git a/app/code/Magento/Catalog/Model/Resource/Product.php b/app/code/Magento/Catalog/Model/Resource/Product.php index 336d8f796577ecabad0284932cc213cabcfb5f28..6f02829d2bc2a7dff4e95b39b7cd20181d5d1903 100644 --- a/app/code/Magento/Catalog/Model/Resource/Product.php +++ b/app/code/Magento/Catalog/Model/Resource/Product.php @@ -91,9 +91,48 @@ class Product extends AbstractResource $modelFactory, $data ); - $this->setType(\Magento\Catalog\Model\Product::ENTITY)->setConnection('catalog_read', 'catalog_write'); - $this->_productWebsiteTable = $this->getTable('catalog_product_website'); - $this->_productCategoryTable = $this->getTable('catalog_category_product'); + $this->_read = 'catalog_read'; + $this->_write = 'catalog_write'; + } + + /** + * Entity type getter and lazy loader + * + * @return \Magento\Eav\Model\Entity\Type + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getEntityType() + { + if (empty($this->_type)) { + $this->setType(\Magento\Catalog\Model\Product::ENTITY); + } + return parent::getEntityType(); + } + + /** + * Product Website table name getter + * + * @return string + */ + public function getProductWebsiteTable() + { + if (!$this->_productWebsiteTable) { + $this->_productWebsiteTable = $this->getTable('catalog_product_website'); + } + return $this->_productWebsiteTable; + } + + /** + * Product Category table name getter + * + * @return string + */ + public function getProductCategoryTable() + { + if (!$this->_productCategoryTable) { + $this->_productCategoryTable = $this->getTable('catalog_category_product'); + } + return $this->_productCategoryTable; } /** @@ -123,7 +162,7 @@ class Product extends AbstractResource } $select = $adapter->select()->from( - $this->_productWebsiteTable, + $this->getProductWebsiteTable(), 'website_id' )->where( 'product_id = ?', @@ -142,7 +181,7 @@ class Product extends AbstractResource public function getWebsiteIdsByProductIds($productIds) { $select = $this->_getWriteAdapter()->select()->from( - $this->_productWebsiteTable, + $this->getProductWebsiteTable(), ['product_id', 'website_id'] )->where( 'product_id IN (?)', @@ -171,7 +210,7 @@ class Product extends AbstractResource $adapter = $this->_getReadAdapter(); $select = $adapter->select()->from( - $this->_productCategoryTable, + $this->getProductCategoryTable(), 'category_id' )->where( 'product_id = ?', @@ -274,14 +313,14 @@ class Product extends AbstractResource foreach ($insert as $websiteId) { $data[] = ['product_id' => (int)$product->getId(), 'website_id' => (int)$websiteId]; } - $adapter->insertMultiple($this->_productWebsiteTable, $data); + $adapter->insertMultiple($this->getProductWebsiteTable(), $data); } if (!empty($delete)) { foreach ($delete as $websiteId) { $condition = ['product_id = ?' => (int)$product->getId(), 'website_id = ?' => (int)$websiteId]; - $adapter->delete($this->_productWebsiteTable, $condition); + $adapter->delete($this->getProductWebsiteTable(), $condition); } } @@ -329,7 +368,7 @@ class Product extends AbstractResource ]; } if ($data) { - $write->insertMultiple($this->_productCategoryTable, $data); + $write->insertMultiple($this->getProductCategoryTable(), $data); } } @@ -337,7 +376,7 @@ class Product extends AbstractResource foreach ($delete as $categoryId) { $where = ['product_id = ?' => (int)$object->getId(), 'category_id = ?' => (int)$categoryId]; - $write->delete($this->_productCategoryTable, $where); + $write->delete($this->getProductCategoryTable(), $where); } } diff --git a/app/code/Magento/Catalog/Model/Session.php b/app/code/Magento/Catalog/Model/Session.php index b61175b11e7da3ff879834074f303590b664b72b..d7a3748c2810bd82a6b856e4d7dfe814cd0401ab 100644 --- a/app/code/Magento/Catalog/Model/Session.php +++ b/app/code/Magento/Catalog/Model/Session.php @@ -8,6 +8,6 @@ namespace Magento\Catalog\Model; /** * Catalog session model */ -class Session extends \Magento\Framework\Session\Generic +class Session extends \Magento\Framework\Session\SessionManager { } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php index df66448c160372d82452ebe45b9ac68b7bb08749..d4501bd7d04393062a693c3491482132a7565d7d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php @@ -106,9 +106,6 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase '', false ); - $this->catalogConfig->expects($this->any()) - ->method('getAttributeUsedForSortByArray') - ->will($this->returnValue(['name' => [], 'price' => []])); $context = $this->getMock( 'Magento\Framework\View\Element\Template\Context', @@ -133,9 +130,6 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase '', false ); - $this->productListHelper->expects($this->any()) - ->method('getAvailableViewMode') - ->will($this->returnValue(['list' => 'List'])); $this->urlEncoder = $this->getMock('Magento\Framework\Url\EncoderInterface', ['encode'], [], '', false); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -187,6 +181,9 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase $this->model->expects($this->once()) ->method('getOrder') ->will($this->returnValue($order)); + $this->catalogConfig->expects($this->once()) + ->method('getAttributeUsedForSortByArray') + ->will($this->returnValue(['name' => [], 'price' => []])); $this->assertEquals($order, $this->block->getCurrentOrder()); } @@ -206,6 +203,9 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase { $mode = 'list'; + $this->productListHelper->expects($this->once()) + ->method('getAvailableViewMode') + ->will($this->returnValue(['list' => 'List'])); $this->model->expects($this->once()) ->method('getMode') ->will($this->returnValue($mode)); @@ -213,6 +213,40 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase $this->assertEquals($mode, $this->block->getCurrentMode()); } + public function testGetModes() + { + $mode = ['list' => 'List']; + $this->productListHelper->expects($this->once()) + ->method('getAvailableViewMode') + ->will($this->returnValue($mode)); + + $this->assertEquals($mode, $this->block->getModes()); + $this->assertEquals($mode, $this->block->getModes()); + } + + /** + * @param string[] $mode + * @param string[] $expected + * @dataProvider setModesDataProvider + */ + public function testSetModes($mode, $expected) + { + $this->productListHelper->expects($this->once()) + ->method('getAvailableViewMode') + ->will($this->returnValue($mode)); + + $block = $this->block->setModes(['mode' => 'mode']); + $this->assertEquals($expected, $block->getModes()); + } + + public function setModesDataProvider() + { + return [ + [['list' => 'List'], ['list' => 'List']], + [null, ['mode' => 'mode']], + ]; + } + public function testGetLimit() { $mode = 'list'; @@ -232,6 +266,9 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase ->method('getDefaultLimitPerPageValue') ->with($this->equalTo('list')) ->will($this->returnValue(10)); + $this->productListHelper->expects($this->any()) + ->method('getAvailableViewMode') + ->will($this->returnValue(['list' => 'List'])); $this->assertEquals($limit, $this->block->getLimit()); } @@ -280,4 +317,48 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase $this->assertTrue($this->block->getPagerHtml()); } + + public function testSetDefaultOrder() + { + $this->catalogConfig->expects($this->atLeastOnce()) + ->method('getAttributeUsedForSortByArray') + ->will($this->returnValue(['name' => [], 'price' => []])); + + $this->block->setDefaultOrder('field'); + } + + public function testGetAvailableOrders() + { + $data = ['name' => [], 'price' => []]; + $this->catalogConfig->expects($this->once()) + ->method('getAttributeUsedForSortByArray') + ->will($this->returnValue($data)); + + $this->assertEquals($data, $this->block->getAvailableOrders()); + $this->assertEquals($data, $this->block->getAvailableOrders()); + } + + public function testAddOrderToAvailableOrders() + { + $data = ['name' => [], 'price' => []]; + $this->catalogConfig->expects($this->once()) + ->method('getAttributeUsedForSortByArray') + ->will($this->returnValue($data)); + $expected = $data; + $expected['order'] = 'value'; + $toolbar = $this->block->addOrderToAvailableOrders('order', 'value'); + $this->assertEquals($expected, $toolbar->getAvailableOrders()); + } + + public function testRemoveOrderFromAvailableOrders() + { + $data = ['name' => [], 'price' => []]; + $this->catalogConfig->expects($this->once()) + ->method('getAttributeUsedForSortByArray') + ->will($this->returnValue($data)); + $toolbar = $this->block->removeOrderFromAvailableOrders('order', 'value'); + $this->assertEquals($data, $toolbar->getAvailableOrders()); + $toolbar2 = $this->block->removeOrderFromAvailableOrders('name'); + $this->assertEquals(['price' => []], $toolbar2->getAvailableOrders()); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php index 75149c26c8d001cf51956b2ca893ecb326f801d4..0ce39e12fbdc93ee8f2e1d38eff837c5a9b25514 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php @@ -119,11 +119,12 @@ class AbstractTypeTest extends \PHPUnit_Framework_TestCase public function testGetSetAttributes() { - $this->productResource->expects($this->any())->method('loadAllAttributes')->will( + $this->productResource->expects($this->once())->method('loadAllAttributes')->will( $this->returnValue($this->productResource) ); - $this->productResource->expects($this->any())->method('getSortedAttributes')->will($this->returnValue(5)); - $this->model->getSetAttributes($this->product); + $this->productResource->expects($this->once())->method('getSortedAttributes')->will($this->returnValue(5)); + $this->assertEquals(5, $this->model->getSetAttributes($this->product)); + //Call the method for a second time, the cached copy should be used $this->assertEquals(5, $this->model->getSetAttributes($this->product)); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php index ed28dc6fda78135af0e50f4e57d7db7c656dac67..92c2026f8961c258516299ba309da2cb811c145c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php @@ -26,58 +26,21 @@ class ManagementTest extends \PHPUnit_Framework_TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $collectionProviderMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $linkInitializerMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $productLinkFactoryMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $productResourceMock; + protected $productMock; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $productMock; + protected $linkTypeProviderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\ObjectManager */ - protected $linkTypeProviderMock; + protected $objectManager; protected function setUp() { $this->productRepositoryMock = $this->getMock('\Magento\Catalog\Model\ProductRepository', [], [], '', false); - $this->productResourceMock = $this->getMock('\Magento\Catalog\Model\Resource\Product', [], [], '', false); - $this->collectionProviderMock = $this->getMock( - '\Magento\Catalog\Model\ProductLink\CollectionProvider', - [], - [], - '', - false - ); - $this->linkInitializerMock = $this->getMock( - '\Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks', - [], - [], - '', - false - ); - $this->productLinkFactoryMock = $this->getMock( - '\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory', - ['create'], - [], - '', - false - ); $this->productMock = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false); $this->linkTypeProviderMock = $this->getMock( @@ -88,15 +51,11 @@ class ManagementTest extends \PHPUnit_Framework_TestCase false ); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->model = $objectManager->getObject( + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $this->objectManager->getObject( 'Magento\Catalog\Model\ProductLink\Management', [ 'productRepository' => $this->productRepositoryMock, - 'collectionProvider' => $this->collectionProviderMock, - 'productLinkFactory' => $this->productLinkFactoryMock, - 'linkInitializer' => $this->linkInitializerMock, - 'productResource' => $this->productResourceMock, 'linkTypeProvider' => $this->linkTypeProviderMock ] ); @@ -104,163 +63,135 @@ class ManagementTest extends \PHPUnit_Framework_TestCase public function testGetLinkedItemsByType() { - $productSku = 'product'; - $linkType = 'link'; + $productSku = 'Simple Product 1'; + $linkType = 'related'; $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) ->willReturn($this->productMock); - $item = [ - 'sku' => 'product1', - 'type' => 'type1', - 'position' => 'pos1', - ]; - $itemCollection = [$item]; - $this->collectionProviderMock->expects($this->once()) - ->method('getCollection') - ->with($this->productMock, $linkType) - ->willReturn($itemCollection); - $this->productMock->expects($this->once())->method('getSku')->willReturn($productSku); - $productLinkMock = $this->getMock('\Magento\Catalog\Api\Data\ProductLinkInterface'); - $productLinkMock->expects($this->once()) - ->method('setProductSku') - ->with($productSku) - ->willReturnSelf(); - $productLinkMock->expects($this->once()) - ->method('setLinkType') - ->with($linkType) - ->willReturnSelf(); - $productLinkMock->expects($this->once()) - ->method('setLinkedProductSku') - ->with($item['sku']) - ->willReturnSelf(); - $productLinkMock->expects($this->once()) - ->method('setLinkedProductType') - ->with($item['type']) - ->willReturnSelf(); - $productLinkMock->expects($this->once()) - ->method('setPosition') - ->with($item['position']) - ->willReturnSelf(); - $this->productLinkFactoryMock->expects($this->once())->method('create')->willReturn($productLinkMock); - $this->assertEquals([$productLinkMock], $this->model->getLinkedItemsByType($productSku, $linkType)); + + $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku($productSku); + $inputRelatedLink->setLinkType($linkType); + $inputRelatedLink->setData("sku", "Simple Product 2"); + $inputRelatedLink->setData("type_id", "simple"); + $inputRelatedLink->setPosition(0); + $links = [$inputRelatedLink]; + + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $this->productMock->expects($this->once())->method('getProductLinks')->willReturn($links); + $this->assertEquals($links, $this->model->getLinkedItemsByType($productSku, $linkType)); } /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Unknown link type: wrong_type + * @expectedExceptionMessage Unknown link type: bad type */ public function testGetLinkedItemsByTypeWithWrongType() { - $productSku = 'product'; - $linkType = 'wrong_type'; - - $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) + $productSku = 'Simple Product 1'; + $linkType = 'bad type'; + $this->productRepositoryMock->expects($this->never())->method('get')->with($productSku) ->willReturn($this->productMock); - $this->collectionProviderMock->expects($this->once()) - ->method('getCollection') - ->with($this->productMock, $linkType) - ->willThrowException(new NoSuchEntityException(__('Collection provider is not registered'))); + $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku($productSku); + $inputRelatedLink->setLinkType($linkType); + $inputRelatedLink->setData("sku", "Simple Product 2"); + $inputRelatedLink->setData("type_id", "simple"); + $inputRelatedLink->setPosition(0); + $links = [$inputRelatedLink]; + + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $this->productMock->expects($this->never())->method('getProductLinks')->willReturn($links); $this->model->getLinkedItemsByType($productSku, $linkType); } public function testSetProductLinks() { - $type = 'type'; - $linkedProductsMock = []; - $linksData = []; - - $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']); - for ($i = 0; $i < 2; $i++) { - $linkMock = $this->getMockForAbstractClass( - '\Magento\Catalog\Api\Data\ProductLinkInterface', - [], - '', - false, - false, - true, - ['getLinkedProductSku', '__toArray'] - ); - $linkMock->expects($this->exactly(2)) - ->method('getLinkedProductSku') - ->willReturn('linkedProduct' . $i . 'Sku'); - $linkMock->expects($this->once())->method('__toArray'); - $linkedProductsMock[$i] = $linkMock; - $linksData['productSku']['link'][] = $linkMock; - } - $linkedSkuList = ['linkedProduct0Sku', 'linkedProduct1Sku']; - $linkedProductIds = ['linkedProduct0Sku' => 1, 'linkedProduct1Sku' => 2]; - - $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus')->with($linkedSkuList) - ->willReturn($linkedProductIds); - $this->productRepositoryMock->expects($this->once())->method('get')->willReturn($this->productMock); - $this->linkInitializerMock->expects($this->once())->method('initializeLinks') - ->with($this->productMock, [$type => [ - 1 => ['product_id' => 1], - 2 => ['product_id' => 2], - ]]); - $this->productMock->expects($this->once())->method('save'); - $this->assertTrue($this->model->setProductLinks('', $type, $linkedProductsMock)); + $productSku = 'Simple Product 1'; + $linkType = 'related'; + $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) + ->willReturn($this->productMock); + + $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku($productSku); + $inputRelatedLink->setLinkType($linkType); + $inputRelatedLink->setData("sku", "Simple Product 1"); + $inputRelatedLink->setData("type_id", "related"); + $inputRelatedLink->setPosition(0); + $links = [$inputRelatedLink]; + + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $this->productMock->expects($this->once())->method('getProductLinks')->willReturn([]); + $this->productMock->expects($this->once())->method('setProductLinks')->with($links); + $this->assertTrue($this->model->setProductLinks($productSku, $linkType, $links)); } /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Provided link type "type2" does not exist + * @expectedExceptionMessage Provided link type "bad type" does not exist */ public function testSetProductLinksThrowExceptionIfProductLinkTypeDoesNotExist() { - $type = 'type'; - $linkedProductsMock = []; + $productSku = 'Simple Product 1'; + $linkType = 'bad type'; + $this->productRepositoryMock->expects($this->never())->method('get')->with($productSku) + ->willReturn($this->productMock); + + $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku($productSku); + $inputRelatedLink->setLinkType($linkType); + $inputRelatedLink->setData("sku", "Simple Product 2"); + $inputRelatedLink->setData("type_id", "simple"); + $inputRelatedLink->setPosition(0); + $links = [$inputRelatedLink]; - $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']); - $this->assertTrue($this->model->setProductLinks('', 'type2', $linkedProductsMock)); + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $this->assertTrue($this->model->setProductLinks('', $linkType, $links)); } /** - * @dataProvider setProductLinksNoProductExceptionDataProvider + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Requested product doesn't exist */ - public function testSetProductLinksNoProductException($exceptionName, $exceptionMessage, $linkedProductIds) + public function testSetProductLinksNoProductException() { - $this->setExpectedException($exceptionName, $exceptionMessage); - $linkedProductsMock = []; - $type = 'type'; - $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']); - for ($i = 0; $i < 2; $i++) { - $productLinkMock = $this->getMock( - '\Magento\Catalog\Api\Data\ProductLinkInterface', - [ - 'getLinkedProductSku', 'getProductSku', 'getLinkType', - '__toArray', 'getLinkedProductType', 'getPosition', 'getCustomAttribute', 'getCustomAttributes', - 'setCustomAttribute', 'setCustomAttributes', 'getMetadataServiceInterface', - 'getExtensionAttributes', 'setExtensionAttributes', - 'setLinkedProductSku', 'setProductSku', 'setLinkType', 'setLinkedProductType', 'setPosition' - ] - ); - $productLinkMock->expects($this->any()) - ->method('getLinkedProductSku') - ->willReturn('linkedProduct' . $i . 'Sku'); - $productLinkMock->expects($this->any())->method('getProductSku')->willReturn('productSku'); - $productLinkMock->expects($this->any())->method('getLinkType')->willReturn('link'); - $linkedProductsMock[$i] = $productLinkMock; - } - $linkedSkuList = ['linkedProduct0Sku', 'linkedProduct1Sku']; - $this->productResourceMock->expects($this->any())->method('getProductsIdsBySkus')->with($linkedSkuList) - ->willReturn($linkedProductIds); - $this->model->setProductLinks('', $type, $linkedProductsMock); - } - - public function setProductLinksNoProductExceptionDataProvider() - { - return [ - [ - '\Magento\Framework\Exception\NoSuchEntityException', - 'Product with SKU "linkedProduct0Sku" does not exist', - ['linkedProduct1Sku' => 2], - ], [ - '\Magento\Framework\Exception\NoSuchEntityException', - 'Product with SKU "linkedProduct1Sku" does not exist', - ['linkedProduct0Sku' => 1] - ] - ]; + $productSku = 'Simple Product 1'; + $linkType = 'related'; + + $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku($productSku); + $inputRelatedLink->setLinkType($linkType); + $inputRelatedLink->setData("sku", "Simple Product 2"); + $inputRelatedLink->setData("type_id", "simple"); + $inputRelatedLink->setPosition(0); + $links = [$inputRelatedLink]; + + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->will($this->throwException( + new \Magento\Framework\Exception\NoSuchEntityException(__('Requested product doesn\'t exist')))); + $this->model->setProductLinks($productSku, $linkType, $links); } /** @@ -269,39 +200,27 @@ class ManagementTest extends \PHPUnit_Framework_TestCase */ public function testSetProductLinksInvalidDataException() { - $type = 'type'; - $linkedProductsMock = []; - $linksData = []; - $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']); - for ($i = 0; $i < 2; $i++) { - $linkMock = $this->getMockForAbstractClass( - '\Magento\Catalog\Api\Data\ProductLinkInterface', - [], - '', - false, - false, - true, - ['getLinkedProductSku', '__toArray'] - ); - $linkMock->expects($this->exactly(2)) - ->method('getLinkedProductSku') - ->willReturn('linkedProduct' . $i . 'Sku'); - $linkMock->expects($this->once())->method('__toArray'); - $linkedProductsMock[$i] = $linkMock; - $linksData['productSku']['link'][] = $linkMock; - } - $linkedSkuList = ['linkedProduct0Sku', 'linkedProduct1Sku']; - $linkedProductIds = ['linkedProduct0Sku' => 1, 'linkedProduct1Sku' => 2]; - - $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus')->with($linkedSkuList) - ->willReturn($linkedProductIds); - $this->productRepositoryMock->expects($this->once())->method('get')->willReturn($this->productMock); - $this->linkInitializerMock->expects($this->once())->method('initializeLinks') - ->with($this->productMock, [$type => [ - 1 => ['product_id' => 1], - 2 => ['product_id' => 2], - ]]); - $this->productMock->expects($this->once())->method('save')->willThrowException(new \Exception()); - $this->model->setProductLinks('', $type, $linkedProductsMock); + $productSku = 'Simple Product 1'; + $linkType = 'related'; + $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) + ->willReturn($this->productMock); + + $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku($productSku); + $inputRelatedLink->setLinkType($linkType); + $inputRelatedLink->setData("sku", "bad sku"); + $inputRelatedLink->setData("type_id", "bad type"); + $inputRelatedLink->setPosition(0); + $links = [$inputRelatedLink]; + + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $this->productMock->expects($this->once())->method('getProductLinks')->willReturn([]); + + $this->productRepositoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + $this->model->setProductLinks($productSku, $linkType, $links); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index a8ba184519d797718c4613f9e5f4c4c8690764bf..35b260532553ee98240c049b12e1116449fbc9d9 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -111,11 +111,19 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase */ protected $contentValidatorMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $linkTypeProviderMock; + /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ protected $objectManager; + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ protected function setUp() { $this->productFactoryMock = $this->getMock('Magento\Catalog\Model\ProductFactory', ['create'], [], '', false); @@ -194,6 +202,8 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); $optionConverter = $this->objectManager->getObject('Magento\Catalog\Model\Product\Option\Converter'); + $this->linkTypeProviderMock = $this->getMock('Magento\Catalog\Model\Product\LinkTypeProvider', + ['getLinkTypes'], [], '', false); $this->model = $this->objectManager->getObject( 'Magento\Catalog\Model\ProductRepository', [ @@ -212,6 +222,7 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase 'fileSystem' => $this->fileSystemMock, 'contentFactory' => $this->contentFactoryMock, 'mimeTypeExtensionMap' => $this->mimeTypeExtensionMapMock, + 'linkTypeProvider' => $this->linkTypeProviderMock ] ); } @@ -847,6 +858,11 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase $this->initializedProductMock->setData("product_links", $existingLinks); if (!empty($newLinks)) { + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + $this->initializedProductMock->setData("ignore_links_flag", false); $this->resourceModelMock ->expects($this->any())->method('getProductsIdsBySkus') @@ -859,6 +875,10 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase $inputLink->setLinkedProductType($newLinks['linked_product_type']); $inputLink->setPosition($newLinks['position']); + if (isset($newLinks['qty'])) { + $inputLink->setQty($newLinks['qty']); + } + $this->productData['product_links'] = [$inputLink]; $this->initializedProductMock->expects($this->any()) @@ -898,6 +918,9 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase $outputLink->setLinkedProductSku($link['linked_product_sku']); $outputLink->setLinkedProductType($link['linked_product_type']); $outputLink->setPosition($link['position']); + if (isset($link['qty'])) { + $outputLink->setQty($link['qty']); + } $outputLinks[] = $outputLink; } @@ -913,11 +936,11 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase // Scenario 1 // No existing, new links $data['scenario_1'] = [ - 'newLinks' => ["product_sku" => "Simple Product 1", "link_type" => "related", "linked_product_sku" => - "Simple Product 2", "linked_product_type" => "simple", "position" => 0], + 'newLinks' => ["product_sku" => "Simple Product 1", "link_type" => "associated", "linked_product_sku" => + "Simple Product 2", "linked_product_type" => "simple", "position" => 0, "qty" => 1], 'existingLinks' => [], - 'expectedData' => [["product_sku" => "Simple Product 1", "link_type" => "related", "linked_product_sku" => - "Simple Product 2", "linked_product_type" => "simple", "position" => 0]] + 'expectedData' => [["product_sku" => "Simple Product 1", "link_type" => "associated", "linked_product_sku" => + "Simple Product 2", "linked_product_type" => "simple", "position" => 0, "qty" => 1]] ]; // Scenario 2 diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index 4dcd067721f5bc239c97b1aa0141e694fb8a7f07..4cc87c7011c705075f1cb321657748006efc6aa6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -146,6 +146,16 @@ class ProductTest extends \PHPUnit_Framework_TestCase */ protected $attributeValueFactory; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $linkTypeProviderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $entityCollectionProviderMock; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -283,6 +293,11 @@ class ProductTest extends \PHPUnit_Framework_TestCase $this->metadataServiceMock = $this->getMock('\Magento\Catalog\Api\ProductAttributeRepositoryInterface'); $this->attributeValueFactory = $this->getMockBuilder('Magento\Framework\Api\AttributeValueFactory') ->disableOriginalConstructor()->getMock(); + $this->linkTypeProviderMock = $this->getMock('Magento\Catalog\Model\Product\LinkTypeProvider', + ['getLinkTypes'], [], '', false); + $this->entityCollectionProviderMock = $this->getMock('Magento\Catalog\Model\ProductLink\CollectionProvider', + ['getCollection'], [], '', false); + $this->objectManagerHelper = new ObjectManagerHelper($this); $this->model = $this->objectManagerHelper->getObject( 'Magento\Catalog\Model\Product', @@ -306,6 +321,8 @@ class ProductTest extends \PHPUnit_Framework_TestCase 'mediaGalleryEntryFactory' => $this->mediaGalleryEntryFactoryMock, 'metadataService' => $this->metadataServiceMock, 'customAttributeFactory' => $this->attributeValueFactory, + 'entityCollectionProvider' => $this->entityCollectionProviderMock, + 'linkTypeProvider' => $this->linkTypeProviderMock, 'data' => ['id' => 1] ] ); @@ -737,37 +754,43 @@ class ProductTest extends \PHPUnit_Framework_TestCase */ public function testGetProductLinks() { - $inputLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); - $inputLink->setProductSku("Simple Product 1"); - $inputLink->setLinkType("related"); - $inputLink->setData("sku", "Simple Product 2"); - $inputLink->setData("type_id", "simple"); - $inputLink->setPosition(0); - - $outputLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); - $outputLink->setProductSku("Simple Product 1"); - $outputLink->setLinkType("related"); - $outputLink->setLinkedProductSku("Simple Product 2"); - $outputLink->setLinkedProductType("simple"); - $outputLink->setPosition(0); - - $productLinks = []; - $this->model->setData('related_products', [$inputLink]); - $productLinks[] = $outputLink; - $outputLink->setLinkType("upsell"); - $inputLink->setLinkType("upsell"); - $this->model->setData('up_sell_products', [$inputLink]); - $productLinks[] = $outputLink; - $outputLink->setLinkType("crosssell"); - $inputLink->setLinkType("crosssell"); - $this->model->setData('cross_sell_products', [$inputLink]); - $productLinks[] = $outputLink; - - $productLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); - $this->productLinkFactory->expects($this->atLeastOnce()) - ->method('create') - ->willReturn($productLink); - + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $inputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku("Simple Product 1"); + $inputRelatedLink->setLinkType("related"); + $inputRelatedLink->setData("sku", "Simple Product 2"); + $inputRelatedLink->setData("type", "simple"); + $inputRelatedLink->setPosition(0); + + $outputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $outputRelatedLink->setProductSku("Simple Product 1"); + $outputRelatedLink->setLinkType("related"); + $outputRelatedLink->setLinkedProductSku("Simple Product 2"); + $outputRelatedLink->setLinkedProductType("simple"); + $outputRelatedLink->setPosition(0); + + $this->entityCollectionProviderMock->expects($this->at(0)) + ->method('getCollection') + ->with($this->model, 'related') + ->willReturn([$inputRelatedLink]); + $this->entityCollectionProviderMock->expects($this->at(1)) + ->method('getCollection') + ->with($this->model, 'upsell') + ->willReturn([]); + $this->entityCollectionProviderMock->expects($this->at(2)) + ->method('getCollection') + ->with($this->model, 'crosssell') + ->willReturn([]); + $this->entityCollectionProviderMock->expects($this->at(3)) + ->method('getCollection') + ->with($this->model, 'associated') + ->willReturn([]); + + $expectedOutput = [$outputRelatedLink]; $typeInstanceMock = $this->getMock( 'Magento\ConfigurableProduct\Model\Product\Type\Simple', ["getSku"], [], '', false); $typeInstanceMock @@ -776,8 +799,13 @@ class ProductTest extends \PHPUnit_Framework_TestCase ->willReturn("Simple Product 1"); $this->model->setTypeInstance($typeInstanceMock); + $productLink1 = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $this->productLinkFactory->expects($this->at(0)) + ->method('create') + ->willReturn($productLink1); + $links = $this->model->getProductLinks(); - $this->assertEquals($links, $productLinks); + $this->assertEquals($links, $expectedOutput); } /** diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index acd741fdbbe66d5c107a3d24ce3d99a5ec716b52..8a4efe5a2b862b4be9e489a7a9c01b186ad08b0c 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -98,6 +98,7 @@ <type name="Magento\Catalog\Model\Product"> <arguments> <argument name="catalogProductStatus" xsi:type="object">Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy</argument> + <argument name="productLink" xsi:type="object">Magento\Catalog\Model\Product\Link\Proxy</argument> </arguments> </type> <type name="Magento\Catalog\Model\Resource\Product\Collection"> @@ -398,6 +399,11 @@ <argument name="validatorFile" xsi:type="object">Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile\Proxy</argument> </arguments> </type> + <type name="Magento\Catalog\Model\Attribute\Config"> + <arguments> + <argument name="dataStorage" xsi:type="object">Magento\Catalog\Model\Attribute\Config\Data\Proxy</argument> + </arguments> + </type> <virtualType name="Magento\Catalog\Model\Layer\Search\Context" type="Magento\Catalog\Model\Layer\Context"> <arguments> <argument name="collectionProvider" xsi:type="object">Magento\Catalog\Model\Layer\Search\ItemCollectionProvider</argument> @@ -464,4 +470,9 @@ <type name="Magento\Catalog\Api\ProductRepositoryInterface"> <plugin name="transactionWrapper" type="\Magento\Catalog\Model\Plugin\ProductRepository\TransactionWrapper" sortOrder="-1"/> </type> + <type name="Magento\Catalog\Model\CategoryRepository"> + <arguments> + <argument name="categoryResource" xsi:type="object">Magento\Catalog\Model\Resource\Category\Proxy</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/view/base/web/js/price-box.js b/app/code/Magento/Catalog/view/base/web/js/price-box.js index 667a30836e529533ce04c77b9f5605668852b04b..eed8ac4b9752c2eabcf80175699cc778d7d491c0 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-box.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-box.js @@ -190,7 +190,7 @@ define([ if (_.isEmpty(prices)) { priceHolders.each(function (index, element) { var type = $(element).data('priceType'), - amount = $(element).data('priceAmount'); + amount = parseFloat($(element).data('priceAmount')); if (type && amount) { prices[type] = { diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 15ab59aaab2b3b5e2b694f685e7d7523fcb5c565..72810dc82611f66c38e19f027215af9cd063721f 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -802,7 +802,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity } } } catch (\Exception $e) { - $this->_logger->logException($e); + $this->_logger->critical($e); } return $exportData; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php b/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php index a71fe9dcc496215b69d7573c094c6762a8be4c4d..0f68ad9e99503a7932d2407dd79bd15d55c870f5 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php @@ -13,23 +13,4 @@ namespace Magento\CatalogImportExport\Model\Import\Proxy\Product; class Resource extends \Magento\Catalog\Model\Resource\Product { - /** - * Product to category table. - * - * @return string - */ - public function getProductCategoryTable() - { - return $this->_productCategoryTable; - } - - /** - * Product to website table. - * - * @return string - */ - public function getProductWebsiteTable() - { - return $this->_productWebsiteTable; - } } diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php index 46163c26be6d3df33831a35e9344333ffeaef7ef..0a620ce1562d529959ce09af000bc2f3b9923b7a 100644 --- a/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php +++ b/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php @@ -55,7 +55,7 @@ interface StockItemInterface extends ExtensibleDataInterface const CUSTOMER_GROUP_ID = 'customer_group_id'; /** - * @return int + * @return int|null */ public function getItemId(); @@ -66,7 +66,7 @@ interface StockItemInterface extends ExtensibleDataInterface public function setItemId($itemId); /** - * @return int + * @return int|null */ public function getProductId(); @@ -79,7 +79,7 @@ interface StockItemInterface extends ExtensibleDataInterface /** * Retrieve Website Id * - * @return int + * @return int|null */ public function getWebsiteId(); @@ -94,7 +94,7 @@ interface StockItemInterface extends ExtensibleDataInterface /** * Retrieve stock identifier * - * @return int + * @return int|null */ public function getStockId(); diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AfterProductLoad.php b/app/code/Magento/CatalogInventory/Model/Plugin/AfterProductLoad.php new file mode 100644 index 0000000000000000000000000000000000000000..3d6c9d404afe0e0e4594ef8f467c630575c69fab --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Plugin/AfterProductLoad.php @@ -0,0 +1,51 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogInventory\Model\Plugin; + +class AfterProductLoad +{ + /** + * @var \Magento\CatalogInventory\Api\StockRegistryInterface + */ + protected $stockRegistry; + + /** + * @var \Magento\Catalog\Api\Data\ProductExtensionFactory + */ + protected $productExtensionFactory; + + /** + * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory + */ + public function __construct( + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, + \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory + ) { + $this->stockRegistry = $stockRegistry; + $this->productExtensionFactory = $productExtensionFactory; + } + + /** + * Add stock item information to the product's extension attributes + * + * @param \Magento\Catalog\Model\Product $product + * @return \Magento\Catalog\Model\Product + */ + public function afterLoad(\Magento\Catalog\Model\Product $product) + { + $productExtension = $product->getExtensionAttributes(); + if ($productExtension === null) { + $productExtension = $this->productExtensionFactory->create(); + } + // stockItem := \Magento\CatalogInventory\Api\Data\StockItemInterface + $productExtension->setStockItem($this->stockRegistry->getStockItem($product->getId())); + $product->setExtensionAttributes($productExtension); + return $product; + } +} diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php new file mode 100644 index 0000000000000000000000000000000000000000..0b222de117c9e84cb9aea4c009889f57ccbb8c80 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php @@ -0,0 +1,95 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogInventory\Model\Plugin; + +class AroundProductRepositorySave +{ + /** + * @var \Magento\CatalogInventory\Api\StockRegistryInterface + */ + protected $stockRegistry; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + protected $storeManager; + + /** + * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + */ + public function __construct( + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, + \Magento\Store\Model\StoreManagerInterface $storeManager + ) { + $this->stockRegistry = $stockRegistry; + $this->storeManager = $storeManager; + } + + /** + * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject + * @param callable $proceed + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param bool $saveOptions + * @return \Magento\Catalog\Api\Data\ProductInterface + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function aroundSave( + \Magento\Catalog\Api\ProductRepositoryInterface $subject, + \Closure $proceed, + \Magento\Catalog\Api\Data\ProductInterface $product, + $saveOptions = false + ) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $result */ + $result = $proceed($product, $saveOptions); + + /* @var \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem */ + $stockItem = $this->getStockItemToBeUpdated($product); + if ($stockItem == null) { + return $result; + } + + // set fields that the customer should not care about + $stockItem->setProductId($result->getId()); + $stockItem->setWebsiteId($this->storeManager->getStore($result->getStoreId())->getWebsiteId()); + + $this->stockRegistry->updateStockItemBySku($result->getSku(), $stockItem); + + // since we just saved a portion of the product, force a reload of it before returning it + return $subject->get($result->getSku(), false, $result->getStoreId(), true); + } + + /** + * Return the stock item that needs to be updated. + * If the stock item does not need to be updated, return null. + * + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null + */ + protected function getStockItemToBeUpdated($product) + { + // from the API, all the data we care about will exist as extension attributes of the original product + $extendedAttributes = $product->getExtensionAttributes(); + if ($extendedAttributes !== null) { + $stockItem = $extendedAttributes->getStockItem(); + if ($stockItem != null) { + return $stockItem; + } + } + + // we have no new stock item information to update, however we need to ensure that the product does have some + // stock item information present. On a newly created product, it will not have any stock item info. + $stockItem = $this->stockRegistry->getStockItem($product->getId()); + if ($stockItem->getItemId() != null) { + // we already have stock item info, so we return null since nothing more needs to be updated + return null; + } + + return $stockItem; + } +} diff --git a/app/code/Magento/CatalogInventory/Model/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/Stock/Item.php index cb622e904c6bbab1838275d66a5f9129e6640a24..15fb84875aafeac95108233e7f4ea482fc295a26 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/Item.php @@ -239,11 +239,11 @@ class Item extends AbstractExtensibleModel implements StockItemInterface } /** - * @return int Timestamp + * @return string Timestamp */ public function getLowStockDate() { - return (int) $this->_getData(static::LOW_STOCK_DATE); + return $this->_getData(static::LOW_STOCK_DATE); } /** diff --git a/app/code/Magento/CatalogInventory/Model/StockManagement.php b/app/code/Magento/CatalogInventory/Model/StockManagement.php index 1f50b8b5dceaa3b64ff14c83044ac6e62f7e85cb..3d5fee6d9cef90eb8bfcf98d78b6c45e68c0bca2 100644 --- a/app/code/Magento/CatalogInventory/Model/StockManagement.php +++ b/app/code/Magento/CatalogInventory/Model/StockManagement.php @@ -84,7 +84,7 @@ class StockManagement implements StockManagementInterface $orderedQty = $items[$productId]; $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId); $canSubtractQty = $stockItem->getItemId() && $this->canSubtractQty($stockItem); - if (!$canSubtractQty || !$this->stockConfiguration->isQty($this->getProductType($productId))) { + if (!$canSubtractQty || !$this->stockConfiguration->isQty($lockedItemRecord['type_id'])) { continue; } if (!$stockItem->hasAdminArea() diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistry.php b/app/code/Magento/CatalogInventory/Model/StockRegistry.php index fefbf9f34899f031a374d551e390b24235d3f9c0..f32aeb2104b686091e06120271c462ec33542f08 100644 --- a/app/code/Magento/CatalogInventory/Model/StockRegistry.php +++ b/app/code/Magento/CatalogInventory/Model/StockRegistry.php @@ -15,6 +15,8 @@ use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface; /** * Class StockRegistry + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class StockRegistry implements StockRegistryInterface { @@ -185,9 +187,7 @@ class StockRegistry implements StockRegistryInterface $origStockItem = $this->getStockItem($productId, $websiteId); $data = $stockItem->getData(); if ($origStockItem->getItemId()) { - if (isset($data['item_id'])) { - unset($data['item_id']); - } + unset($data['item_id']); } $origStockItem->addData($data); $origStockItem->setProductId($productId); diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AfterProductLoadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..77b8985cac84c3d21bb9eeb7a598ea9138c366d6 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AfterProductLoadTest.php @@ -0,0 +1,104 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +namespace Magento\CatalogInventory\Test\Unit\Model\Plugin; + +class AfterProductLoadTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\CatalogInventory\Model\Plugin\AfterProductLoad + */ + protected $plugin; + + /** + * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \Magento\Catalog\Api\Data\ProductExtensionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionFactoryMock; + + /** + * @var \Magento\Catalog\Api\Data\ProductExtension|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionMock; + + protected function setUp() + { + $stockRegistryMock = $this->getMock('\Magento\CatalogInventory\Api\StockRegistryInterface'); + $this->productExtensionFactoryMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->plugin = new \Magento\CatalogInventory\Model\Plugin\AfterProductLoad( + $stockRegistryMock, + $this->productExtensionFactoryMock + ); + + $productId = 5494; + $stockItemMock = $this->getMock('\Magento\CatalogInventory\Api\Data\StockItemInterface'); + + $stockRegistryMock->expects($this->once()) + ->method('getStockItem') + ->with($productId) + ->willReturn($stockItemMock); + + $this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['setStockItem']) + ->getMock(); + $this->productExtensionMock->expects($this->once()) + ->method('setStockItem') + ->with($stockItemMock) + ->willReturnSelf(); + + $this->productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') + ->disableOriginalConstructor() + ->getMock(); + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($this->productExtensionMock) + ->willReturnSelf(); + $this->productMock->expects(($this->once())) + ->method('getId') + ->will($this->returnValue($productId)); + } + + public function testAfterLoad() + { + // test when extension attributes are not (yet) present in the product + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + $this->productExtensionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->productExtensionMock); + + $this->assertEquals( + $this->productMock, + $this->plugin->afterLoad($this->productMock) + ); + } + + public function testAfterLoadWithExistingExtensionAttributes() + { + // test when extension attributes already exist + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionFactoryMock->expects($this->never()) + ->method('create'); + + $this->assertEquals( + $this->productMock, + $this->plugin->afterLoad($this->productMock) + ); + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php new file mode 100644 index 0000000000000000000000000000000000000000..95560e7ce7be3144937120e89eb40f41484b7586 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php @@ -0,0 +1,176 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +namespace Magento\CatalogInventory\Test\Unit\Model\Plugin; + +class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave + */ + protected $plugin; + + /** + * @var \Magento\CatalogInventory\Api\Data\StockItemInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $stockItemMock; + + /** + * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $savedProductMock; + + /** + * @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $stockRegistry; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var \Magento\Catalog\Api\Data\ProductExtension|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionMock; + + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productRepositoryMock; + + /** + * @var \Closure + */ + protected $closureMock; + + public function setUp() + { + $this->stockRegistry = $this->getMock('\Magento\CatalogInventory\Api\StockRegistryInterface'); + $this->storeManager = $this->getMock('\Magento\Store\Model\StoreManagerInterface'); + + $this->plugin = new \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave( + $this->stockRegistry, + $this->storeManager + ); + + $this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['getStockItem']) + ->getMock(); + $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface'); + $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $this->savedProductMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $this->closureMock = function () { + return $this->savedProductMock; + }; + $this->stockItemMock = $this->getMock('\Magento\CatalogInventory\Api\Data\StockItemInterface'); + } + + public function testAroundSaveWhenProductHasNoStockItemNeedingToBeUpdated() + { + // pretend we have no extension attributes at all + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + $this->productExtensionMock->expects($this->never())->method('getStockItem'); + + // pretend that the product already has existing stock item information + $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItemMock); + $this->stockItemMock->expects($this->once())->method('getItemId')->willReturn(1); + $this->stockItemMock->expects($this->never())->method('setProductId'); + $this->stockItemMock->expects($this->never())->method('setWebsiteId'); + + // expect that there are no changes to the existing stock item information + $this->assertEquals( + $this->savedProductMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + public function testAroundSaveWhenProductHasNoPersistentStockItemInfo() + { + // pretend we do have extension attributes, but none for 'stock_item' + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getStockItem') + ->willReturn(null); + + $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store') + ->disableOriginalConstructor()->getMock(); + $storeMock->expects($this->once())->method('getWebsiteId')->willReturn(1); + $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); + + $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItemMock); + $this->stockRegistry->expects($this->once())->method('updateStockItemBySku'); + + $this->stockItemMock->expects($this->once())->method('getItemId')->willReturn(null); + $this->stockItemMock->expects($this->once())->method('setProductId'); + $this->stockItemMock->expects($this->once())->method('setWebsiteId'); + + $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') + ->disableOriginalConstructor()->getMock(); + $this->productRepositoryMock->expects($this->once())->method('get')->willReturn($newProductMock); + + $this->assertEquals( + $newProductMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + public function testAroundSave() + { + $productId = 5494; + $websiteId = 1; + $storeId = 2; + $sku = 'my product that needs saving'; + + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getStockItem') + ->willReturn($this->stockItemMock); + + $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store') + ->disableOriginalConstructor()->getMock(); + $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); + + $this->savedProductMock->expects(($this->once()))->method('getId')->willReturn($productId); + $this->savedProductMock->expects(($this->atLeastOnce()))->method('getStoreId')->willReturn($storeId); + $this->savedProductMock->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); + + $this->stockItemMock->expects($this->once())->method('setProductId')->with($productId); + $this->stockItemMock->expects($this->once())->method('setWebsiteId')->with($websiteId); + + $this->stockRegistry->expects($this->once()) + ->method('updateStockItemBySku') + ->with($sku, $this->stockItemMock); + + $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') + ->disableOriginalConstructor()->getMock(); + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($sku, false, $storeId, true) + ->willReturn($newProductMock); + + $this->assertEquals( + $newProductMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php index 20de61fbf3ba19c6851e47bc016e3ee38e82d9eb..f6cfa8b4e7e6643d3b4df80ae83fe79bd57dd0fc 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php @@ -395,4 +395,12 @@ class ItemTest extends \PHPUnit_Framework_TestCase [0, $this->storeId], ]; } + + public function testGetLowStockDate() + { + // ensure we do *not* return '2015' due to casting to an int + $date = '2015-4-17'; + $this->item->setLowStockDate($date); + $this->assertEquals($date, $this->item->getLowStockDate()); + } } diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 11c98534f732718a84bb3cde28379c3f76137b61..b1ad4a03847bbd997e93dc255b0d7de325f11f9d 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -52,4 +52,15 @@ <type name="Magento\Catalog\Block\Product\View"> <plugin name="quantityValidators" type="Magento\CatalogInventory\Block\Plugin\ProductView" /> </type> + <type name="Magento\CatalogInventory\Model\Configuration"> + <arguments> + <argument name="config" xsi:type="object">Magento\Catalog\Model\ProductTypes\Config\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product"> + <plugin name="catalogInventoryAfterLoad" type="\Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/> + </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="catalogInventoryAroundSave" type="\Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave"/> + </type> </config> diff --git a/app/code/Magento/CatalogInventory/etc/service_data_attributes.xml b/app/code/Magento/CatalogInventory/etc/service_data_attributes.xml new file mode 100644 index 0000000000000000000000000000000000000000..587b98b401a0981d8ca14b75242f0adac67312a2 --- /dev/null +++ b/app/code/Magento/CatalogInventory/etc/service_data_attributes.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> + <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface"> + <resources> + <resource ref="Magento_CatalogInventory::cataloginventory"/> + </resources> + </attribute> + </extension_attributes> +</config> diff --git a/app/code/Magento/Checkout/Model/Session.php b/app/code/Magento/Checkout/Model/Session.php index 0f7d18e41355b5ffbae94f02f0f6272485d59cbd..da634e082756d1e8be81769a6af6dc32a6874fbe 100644 --- a/app/code/Magento/Checkout/Model/Session.php +++ b/app/code/Magento/Checkout/Model/Session.php @@ -90,6 +90,7 @@ class Session extends \Magento\Framework\Session\SessionManager * @param \Magento\Framework\Session\StorageInterface $storage * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\App\State $appState * @param \Magento\Sales\Model\OrderFactory $orderFactory * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Quote\Model\QuoteRepository $quoteRepository @@ -97,6 +98,7 @@ class Session extends \Magento\Framework\Session\SessionManager * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository + * @throws \Magento\Framework\Exception\SessionException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -108,6 +110,7 @@ class Session extends \Magento\Framework\Session\SessionManager \Magento\Framework\Session\StorageInterface $storage, \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\App\State $appState, \Magento\Sales\Model\OrderFactory $orderFactory, \Magento\Customer\Model\Session $customerSession, \Magento\Quote\Model\QuoteRepository $quoteRepository, @@ -131,9 +134,9 @@ class Session extends \Magento\Framework\Session\SessionManager $validator, $storage, $cookieManager, - $cookieMetadataFactory + $cookieMetadataFactory, + $appState ); - $this->start(); } /** diff --git a/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php b/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php index fed2f67da6f177806559e463deb8fda56a2fbb3f..11a83c181d64f504d267524440423a30c90b2970 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php @@ -81,7 +81,7 @@ class Chooser extends \Magento\Backend\Block\Widget\Grid\Extended if ($element->getValue()) { $block = $this->_blockFactory->create()->load($element->getValue()); if ($block->getId()) { - $chooser->setLabel($block->getTitle()); + $chooser->setLabel($this->escapeHtml($block->getTitle())); } } diff --git a/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php b/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php index addaf3f4926b8e05f48833b16e540eb73ad295ef..54c169c890a9b8f8901203bff0a9a3a6d4e69275 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php @@ -98,7 +98,7 @@ class Chooser extends \Magento\Backend\Block\Widget\Grid\Extended if ($element->getValue()) { $page = $this->_pageFactory->create()->load((int)$element->getValue()); if ($page->getId()) { - $chooser->setLabel($page->getTitle()); + $chooser->setLabel($this->escapeHtml($page->getTitle())); } } diff --git a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php index 0c075194e3330d099d7bb12b2427b971621f8a81..55761dae44ac94230eb126aa182e22394aaf1d32 100644 --- a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php +++ b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php @@ -35,6 +35,11 @@ class ChooserTest extends \PHPUnit_Framework_TestCase */ protected $urlBuilderMock; + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + protected $escaper; + /** * @var \Magento\Cms\Model\BlockFactory|\PHPUnit_Framework_MockObject_MockObject */ @@ -66,6 +71,14 @@ class ChooserTest extends \PHPUnit_Framework_TestCase $this->urlBuilderMock = $this->getMockBuilder('Magento\Framework\UrlInterface') ->disableOriginalConstructor() ->getMock(); + $this->escaper = $this->getMockBuilder('Magento\Framework\Escaper') + ->disableOriginalConstructor() + ->setMethods( + [ + 'escapeHtml', + ] + ) + ->getMock(); $this->blockFactoryMock = $this->getMockBuilder('Magento\Cms\Model\BlockFactory') ->setMethods( [ @@ -90,6 +103,7 @@ class ChooserTest extends \PHPUnit_Framework_TestCase [ 'getTitle', 'load', + 'getId', ] ) ->getMock(); @@ -112,15 +126,16 @@ class ChooserTest extends \PHPUnit_Framework_TestCase $this->context = $objectManager->getObject( 'Magento\Backend\Block\Template\Context', [ - 'layout' => $this->layoutMock, + 'layout' => $this->layoutMock, 'mathRandom' => $this->mathRandomMock, - 'urlBuilder' => $this->urlBuilderMock + 'urlBuilder' => $this->urlBuilderMock, + 'escaper' => $this->escaper, ] ); $this->this = $objectManager->getObject( 'Magento\Cms\Block\Adminhtml\Block\Widget\Chooser', [ - 'context' => $this->context, + 'context' => $this->context, 'blockFactory' => $this->blockFactoryMock ] ); @@ -135,13 +150,14 @@ class ChooserTest extends \PHPUnit_Framework_TestCase */ public function testPrepareElementHtml($elementValue, $modelBlockId) { - $elementId = 1; - $uniqId = '126hj4h3j73hk7b347jhkl37gb34'; - $sourceUrl = 'cms/block_widget/chooser/126hj4h3j73hk7b347jhkl37gb34'; - $config = ['key1' => 'value1']; - $fieldsetId = 2; - $html = 'some html'; - $title = 'some title'; + $elementId = 1; + $uniqId = '126hj4h3j73hk7b347jhkl37gb34'; + $sourceUrl = 'cms/block_widget/chooser/126hj4h3j73hk7b347jhkl37gb34'; + $config = ['key1' => 'value1']; + $fieldsetId = 2; + $html = 'some html'; + $title = 'some "><img src=y onerror=prompt(document.domain)>; title'; + $titleEscaped = 'some "><img src=y onerror=prompt(document.domain)>; title'; $this->this->setConfig($config); $this->this->setFieldsetId($fieldsetId); @@ -197,13 +213,18 @@ class ChooserTest extends \PHPUnit_Framework_TestCase $this->modelBlockMock->expects($this->any()) ->method('getTitle') ->willReturn($title); - $this->chooserMock->expects($this->any()) - ->method('setLabel') - ->with($title) - ->willReturnSelf(); $this->chooserMock->expects($this->atLeastOnce()) ->method('toHtml') ->willReturn($html); + if (!empty($elementValue) && !empty($modelBlockId)) { + $this->escaper->expects(($this->atLeastOnce())) + ->method('escapeHtml') + ->willReturn($titleEscaped); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setLabel') + ->with($titleEscaped) + ->willReturnSelf(); + } $this->elementMock->expects($this->atLeastOnce()) ->method('setData') ->with('after_element_html', $html) diff --git a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php new file mode 100644 index 0000000000000000000000000000000000000000..75107bcb42de11db287607e49595aa6fecfd637b --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php @@ -0,0 +1,271 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Cms\Test\Unit\Block\Adminhtml\Page\Widget; + +/** + * @covers \Magento\Cms\Block\Adminhtml\Page\Widget\Chooser + */ +class ChooserTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Cms\Block\Adminhtml\Page\Widget\Chooser + */ + protected $this; + + /** + * @var \Magento\Backend\Block\Template\Context + */ + protected $context; + + /** + * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject + */ + protected $mathRandomMock; + + /** + * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $urlBuilderMock; + + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + protected $escaper; + + /** + * @var \Magento\Cms\Model\Page|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cmsPageMock; + + /** + * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutMock; + + /** + * @var \Magento\Cms\Model\PageFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $pageFactoryMock; + + /** + * @var \Magento\Framework\Data\Form\Element\AbstractElement|\PHPUnit_Framework_MockObject_MockObject + */ + protected $elementMock; + + /** + * @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $chooserMock; + + protected function setUp() + { + $this->layoutMock = $this->getMockBuilder('Magento\Framework\View\LayoutInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->mathRandomMock = $this->getMockBuilder('Magento\Framework\Math\Random') + ->disableOriginalConstructor() + ->getMock(); + $this->urlBuilderMock = $this->getMockBuilder('Magento\Framework\UrlInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->escaper = $this->getMockBuilder('Magento\Framework\Escaper') + ->disableOriginalConstructor() + ->setMethods( + [ + 'escapeHtml', + ] + ) + ->getMock(); + $this->pageFactoryMock = $this->getMockBuilder('Magento\Cms\Model\PageFactory') + ->setMethods( + [ + 'create', + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $this->elementMock = $this->getMockBuilder('Magento\Framework\Data\Form\Element\AbstractElement') + ->disableOriginalConstructor() + ->setMethods( + [ + 'getId', + 'getValue', + 'setData', + ] + ) + ->getMock(); + $this->cmsPageMock = $this->getMockBuilder('Magento\Cms\Model\Page') + ->disableOriginalConstructor() + ->setMethods( + [ + 'getTitle', + 'load', + 'getId', + ] + ) + ->getMock(); + $this->chooserMock = $this->getMockBuilder('Magento\Framework\View\Element\BlockInterface') + ->disableOriginalConstructor() + ->setMethods( + [ + 'setElement', + 'setConfig', + 'setFieldsetId', + 'setSourceUrl', + 'setUniqId', + 'setLabel', + 'toHtml', + ] + ) + ->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->context = $objectManager->getObject( + 'Magento\Backend\Block\Template\Context', + [ + 'layout' => $this->layoutMock, + 'mathRandom' => $this->mathRandomMock, + 'urlBuilder' => $this->urlBuilderMock, + 'escaper' => $this->escaper, + ] + ); + $this->this = $objectManager->getObject( + 'Magento\Cms\Block\Adminhtml\Page\Widget\Chooser', + [ + 'context' => $this->context, + 'pageFactory' => $this->pageFactoryMock + ] + ); + } + + /** + * @covers \Magento\Cms\Block\Adminhtml\Block\Widget\Chooser::prepareElementHtml + * + * @param string $elementValue + * @param integer|null $cmsPageId + * + * @dataProvider prepareElementHtmlDataProvider + */ + public function testPrepareElementHtml($elementValue, $cmsPageId) + { + //$elementValue = 12345; + //$cmsPageId = 1; + $elementId = 1; + $uniqId = '126hj4h3j73hk7b347jhkl37gb34'; + $sourceUrl = 'cms/page_widget/chooser/126hj4h3j73hk7b347jhkl37gb34'; + $config = ['key1' => 'value1']; + $fieldsetId = 2; + $html = 'some html'; + $title = 'some "><img src=y onerror=prompt(document.domain)>; title'; + $titleEscaped = 'some "><img src=y onerror=prompt(document.domain)>; title'; + + $this->this->setConfig($config); + $this->this->setFieldsetId($fieldsetId); + + $this->elementMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn($elementId); + $this->mathRandomMock->expects($this->atLeastOnce()) + ->method('getUniqueHash') + ->with($elementId) + ->willReturn($uniqId); + $this->urlBuilderMock->expects($this->atLeastOnce()) + ->method('getUrl') + ->with('cms/page_widget/chooser', ['uniq_id' => $uniqId]) + ->willReturn($sourceUrl); + $this->layoutMock->expects($this->atLeastOnce()) + ->method('createBlock') + ->with('Magento\Widget\Block\Adminhtml\Widget\Chooser') + ->willReturn($this->chooserMock); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setElement') + ->with($this->elementMock) + ->willReturnSelf(); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setConfig') + ->with($config) + ->willReturnSelf(); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setFieldsetId') + ->with($fieldsetId) + ->willReturnSelf(); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setSourceUrl') + ->with($sourceUrl) + ->willReturnSelf(); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setUniqId') + ->with($uniqId) + ->willReturnSelf(); + $this->elementMock->expects($this->atLeastOnce()) + ->method('getValue') + ->willReturn($elementValue); + $this->pageFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->cmsPageMock); + $this->cmsPageMock->expects($this->any()) + ->method('load') + ->with((int)$elementValue) + ->willReturnSelf(); + $this->cmsPageMock->expects($this->any()) + ->method('getId') + ->willReturn($cmsPageId); + $this->cmsPageMock->expects($this->any()) + ->method('getTitle') + ->willReturn($title); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('toHtml') + ->willReturn($html); + if (!empty($elementValue) && !empty($cmsPageId)) { + $this->escaper->expects(($this->atLeastOnce())) + ->method('escapeHtml') + ->willReturn($titleEscaped); + $this->chooserMock->expects($this->atLeastOnce()) + ->method('setLabel') + ->with($titleEscaped) + ->willReturnSelf(); + } + $this->elementMock->expects($this->atLeastOnce()) + ->method('setData') + ->with('after_element_html', $html) + ->willReturnSelf(); + + $this->assertEquals($this->elementMock, $this->this->prepareElementHtml($this->elementMock)); + } + + public function prepareElementHtmlDataProvider() + { + return [ + 'elementValue NOT EMPTY, modelBlockId NOT EMPTY' => [ + 'elementValue' => 'some value', + 'cmsPageId' => 1, + ], + 'elementValue NOT EMPTY, modelBlockId IS EMPTY' => [ + 'elementValue' => 'some value', + 'cmsPageId' => null, + ], + 'elementValue IS EMPTY, modelBlockId NEVER REACHED' => [ + 'elementValue' => '', + 'cmsPageId' => 1, + ] + ]; + } + + /** + * @covers \Magento\Cms\Block\Adminhtml\Page\Widget\Chooser::getGridUrl + */ + public function testGetGridUrl() + { + $url = 'some url'; + + $this->urlBuilderMock->expects($this->atLeastOnce()) + ->method('getUrl') + ->with('cms/page_widget/chooser', ['_current' => true]) + ->willReturn($url); + + $this->assertEquals($url, $this->this->getGridUrl()); + } +} diff --git a/app/code/Magento/Config/Model/Config/Backend/File.php b/app/code/Magento/Config/Model/Config/Backend/File.php index e7a119f79af9f0bf78d8f459ff0d30875abd097b..a08ed7d4f23894640e6592728f9a2c6a8eb1432b 100644 --- a/app/code/Magento/Config/Model/Config/Backend/File.php +++ b/app/code/Magento/Config/Model/Config/Backend/File.php @@ -5,6 +5,7 @@ */ namespace Magento\Config\Model\Config\Backend; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; @@ -196,7 +197,7 @@ class File extends \Magento\Framework\App\Config\Value protected function _prependScopeInfo($path) { $scopeInfo = $this->getScope(); - if (\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT != $this->getScope()) { + if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT != $this->getScope()) { $scopeInfo .= '/' . $this->getScopeId(); } return $scopeInfo . '/' . $path; @@ -213,7 +214,7 @@ class File extends \Magento\Framework\App\Config\Value protected function _appendScopeInfo($path) { $path .= '/' . $this->getScope(); - if (\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT != $this->getScope()) { + if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT != $this->getScope()) { $path .= '/' . $this->getScopeId(); } return $path; diff --git a/app/code/Magento/Config/Model/Config/Backend/Locale.php b/app/code/Magento/Config/Model/Config/Backend/Locale.php index 0881afdebfc3839d52d0c27de0052ae6a8bd68b1..3d057fd6df864d23061ba4d1d762e265482eb595 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Locale.php +++ b/app/code/Magento/Config/Model/Config/Backend/Locale.php @@ -9,6 +9,8 @@ */ namespace Magento\Config\Model\Config\Backend; +use Magento\Framework\App\Config\ScopeConfigInterface; + class Locale extends \Magento\Framework\App\Config\Value { /** @@ -91,7 +93,7 @@ class Locale extends \Magento\Framework\App\Config\Value } switch ($data->getScope()) { - case \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT: + case ScopeConfigInterface::SCOPE_TYPE_DEFAULT: $scopeName = __('Default scope'); break; diff --git a/app/code/Magento/Config/Model/Config/ScopeDefiner.php b/app/code/Magento/Config/Model/Config/ScopeDefiner.php index 893496d874009b9834fc2f1821057283ba01e323..f69e9dd96d2eb820a87a9d1f5905445df9c576df 100644 --- a/app/code/Magento/Config/Model/Config/ScopeDefiner.php +++ b/app/code/Magento/Config/Model/Config/ScopeDefiner.php @@ -5,6 +5,7 @@ */ namespace Magento\Config\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\ScopeInterface as StoreScopeInterface; /** @@ -38,6 +39,6 @@ class ScopeDefiner 'store' ) ? StoreScopeInterface::SCOPE_STORE : ($this->_request->getParam( 'website' - ) ? StoreScopeInterface::SCOPE_WEBSITE : \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT); + ) ? StoreScopeInterface::SCOPE_WEBSITE : ScopeConfigInterface::SCOPE_TYPE_DEFAULT); } } diff --git a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php index e10a1a1c3e725333743bea341d6170865f17d119..03ed66f29abec3ebd79735f648aefcd633dc44cc 100644 --- a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php +++ b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php @@ -5,6 +5,7 @@ */ namespace Magento\Config\Model\Config\Structure; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\StoreManagerInterface; abstract class AbstractElement implements ElementInterface @@ -136,7 +137,7 @@ abstract class AbstractElement implements ElementInterface $showInScope = [ \Magento\Store\Model\ScopeInterface::SCOPE_STORE => $this->_hasVisibilityValue('showInStore'), \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE => $this->_hasVisibilityValue('showInWebsite'), - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT => $this->_hasVisibilityValue('showInDefault'), + ScopeConfigInterface::SCOPE_TYPE_DEFAULT => $this->_hasVisibilityValue('showInDefault'), ]; if ($this->_storeManager->isSingleStoreMode()) { diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php index 23069beec258fa8c6ab4e398c537a4874a9010b7..e0c64a35053ad44c88baa18861e84641a995d17d 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Config\Test\Unit\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class ScopeDefinerTest extends \PHPUnit_Framework_TestCase @@ -31,7 +32,7 @@ class ScopeDefinerTest extends \PHPUnit_Framework_TestCase public function testGetScopeReturnsDefaultScopeIfNoScopeDataIsSpecified() { - $this->assertEquals(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $this->_model->getScope()); + $this->assertEquals(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $this->_model->getScope()); } public function testGetScopeReturnsStoreScopeIfStoreIsSpecified() diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php index 03e9fd8d2266e5c9256c74a81716e59e4eb40ac0..778141ab2132096aa8d0e750f7175bebdcea935b 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Config\Test\Unit\Model\Config\Structure; +use Magento\Framework\App\Config\ScopeConfigInterface; + class AbstractElementTest extends \PHPUnit_Framework_TestCase { /** @@ -77,7 +79,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase $this->_storeManager->expects($this->once())->method('isSingleStoreMode')->will($this->returnValue(true)); $this->_model->setData( ['showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 0], - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); $this->assertTrue($this->_model->isVisible()); } @@ -87,7 +89,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase $this->_storeManager->expects($this->once())->method('isSingleStoreMode')->will($this->returnValue(true)); $this->_model->setData( ['hide_in_single_store_mode' => 1, 'showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 0], - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); $this->assertFalse($this->_model->isVisible()); } @@ -100,7 +102,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase $this->_storeManager->expects($this->once())->method('isSingleStoreMode')->will($this->returnValue(true)); $this->_model->setData( ['showInDefault' => 0, 'showInStore' => 0, 'showInWebsite' => 0], - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); $this->assertFalse($this->_model->isVisible()); } @@ -121,7 +123,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase return [ [ ['showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 0], - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, ], [ ['showInDefault' => 0, 'showInStore' => 1, 'showInWebsite' => 0], @@ -150,7 +152,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase return [ [ ['showInDefault' => 0, 'showInStore' => 1, 'showInWebsite' => 1], - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, ], [ ['showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 1], diff --git a/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php b/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php index 82e73fe8686e268e1b3701c216fe3bb8a2d4ee95..1c60ed2762efbb9c8e1a4fc6bd62c511dc98f7f1 100644 --- a/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php +++ b/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php @@ -41,17 +41,6 @@ interface OptionInterface extends \Magento\Framework\Api\ExtensibleDataInterface */ public function setLabel($label); - /** - * @return string|null - */ - public function getType(); - - /** - * @param string $type - * @return $this - */ - public function setType($type); - /** * @return int|null */ diff --git a/app/code/Magento/ConfigurableProduct/Api/OptionTypesListInterface.php b/app/code/Magento/ConfigurableProduct/Api/OptionTypesListInterface.php deleted file mode 100644 index 0978b9c705c4933d3590a69b9454432c4cf3f0d6..0000000000000000000000000000000000000000 --- a/app/code/Magento/ConfigurableProduct/Api/OptionTypesListInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ConfigurableProduct\Api; - -interface OptionTypesListInterface -{ - /** - * Get all available option types for configurable product - * - * @return string[] - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - */ - public function getItems(); -} diff --git a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php index 3e18c09648b9f9a6087e3daef9d67c1f4279b0e1..68cdb235bdeb03bd1e15e7b2146d92586236aac0 100644 --- a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php +++ b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php @@ -87,26 +87,17 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit public function get($sku, $id) { $product = $this->getProduct($sku); - $collection = $this->getConfigurableAttributesCollection($product); - $collection->addFieldToFilter($collection->getResource()->getIdFieldName(), $id); - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $configurableAttribute */ - $configurableAttribute = $collection->getFirstItem(); - if (!$configurableAttribute->getId()) { - throw new NoSuchEntityException(__('Requested option doesn\'t exist: %1', $id)); - } - $prices = $configurableAttribute->getPrices(); - if (is_array($prices)) { - foreach ($prices as $price) { - /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */ - $value = $this->optionValueFactory->create(); - $value->setValueIndex($price['value_index']) - ->setPricingValue($price['pricing_value']) - ->setIsPercent($price['is_percent']); - $values[] = $value; + + $extensionAttribute = $product->getExtensionAttributes(); + if ($extensionAttribute !== null) { + $options = $extensionAttribute->getConfigurableProductOptions(); + foreach ($options as $option) { + if ($option->getId() == $id) { + return $option; + } } } - $configurableAttribute->setValues($values); - return $configurableAttribute; + throw new NoSuchEntityException(__('Requested option doesn\'t exist: %1', $id)); } /** @@ -116,21 +107,10 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit { $options = []; $product = $this->getProduct($sku); - foreach ($this->getConfigurableAttributesCollection($product) as $option) { - $values = []; - $prices = $option->getPrices(); - if (is_array($prices)) { - foreach ($prices as $price) { - /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */ - $value = $this->optionValueFactory->create(); - $value->setValueIndex($price['value_index']) - ->setPricingValue($price['pricing_value']) - ->setIsPercent($price['is_percent']); - $values[] = $value; - } - } - $option->setValues($values); - $options[] = $option; + + $extensionAttribute = $product->getExtensionAttributes(); + if ($extensionAttribute !== null) { + $options = $extensionAttribute->getConfigurableProductOptions(); } return $options; } @@ -259,17 +239,6 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit return $product; } - /** - * Retrieve configurable attribute collection through product object - * - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Collection - */ - private function getConfigurableAttributesCollection(\Magento\Catalog\Model\Product $product) - { - return $this->configurableType->getConfigurableAttributeCollection($product); - } - /** * Ensure that all necessary data is available for a new option creation. * @@ -284,9 +253,6 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit if (!$option->getAttributeId()) { $inputException->addError(__('Option attribute ID is not specified.')); } - if (!$option->getType()) { - $inputException->addError(__('Option type is not specified.')); - } if (!$option->getLabel()) { $inputException->addError(__('Option label is not specified.')); } diff --git a/app/code/Magento/ConfigurableProduct/Model/OptionTypesList.php b/app/code/Magento/ConfigurableProduct/Model/OptionTypesList.php deleted file mode 100644 index 23b871b21248166e892ec10b45801cf1184dcf39..0000000000000000000000000000000000000000 --- a/app/code/Magento/ConfigurableProduct/Model/OptionTypesList.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\ConfigurableProduct\Model; - -class OptionTypesList implements \Magento\ConfigurableProduct\Api\OptionTypesListInterface -{ - /** - * @var \Magento\Catalog\Model\System\Config\Source\Inputtype - */ - protected $inputType; - - /** - * @param \Magento\Catalog\Model\System\Config\Source\Inputtype $inputType - */ - public function __construct(\Magento\Catalog\Model\System\Config\Source\Inputtype $inputType) - { - $this->inputType = $inputType; - } - - /** - * {@inheritdoc} - */ - public function getItems() - { - return array_map( - function ($inputType) { - return $inputType['value']; - }, - $this->inputType->toOptionArray() - ); - } -} diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php new file mode 100644 index 0000000000000000000000000000000000000000..6bf640f16735294afeb92a35513ebbbd58e7e5f0 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php @@ -0,0 +1,103 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Model\Plugin; + +class AfterProductLoad +{ + /** + * @var \Magento\Catalog\Api\Data\ProductExtensionFactory + */ + protected $productExtensionFactory; + + /** + * @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory + */ + protected $optionValueFactory; + + /** + * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory + * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory + */ + public function __construct( + \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory, + \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory + ) { + $this->productExtensionFactory = $productExtensionFactory; + $this->optionValueFactory = $optionValueFactory; + } + + /** + * @param \Magento\Catalog\Model\Product $subject + * @return \Magento\Catalog\Model\Product + */ + public function afterLoad(\Magento\Catalog\Model\Product $product) + { + if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) { + return $product; + } + + $productExtension = $product->getExtensionAttributes(); + if ($productExtension === null) { + $productExtension = $this->productExtensionFactory->create(); + } + + $productExtension->setConfigurableProductOptions($this->getOptions($product)); + $productExtension->setConfigurableProductLinks($this->getLinkedProducts($product)); + + $product->setExtensionAttributes($productExtension); + + return $product; + } + + /** + * @param \Magento\Catalog\Model\Product $product + * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[] + */ + protected function getOptions(\Magento\Catalog\Model\Product $product) + { + $options = []; + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */ + $typeInstance = $product->getTypeInstance(); + $attributeCollection = $typeInstance->getConfigurableAttributes($product); + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $option */ + foreach ($attributeCollection as $option) { + $values = []; + $prices = $option->getPrices(); + if (is_array($prices)) { + foreach ($prices as $price) { + /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */ + $value = $this->optionValueFactory->create(); + $value->setValueIndex($price['value_index']) + ->setPricingValue($price['pricing_value']) + ->setIsPercent($price['is_percent']); + $values[] = $value; + } + } + $option->setValues($values); + $options[] = $option; + } + return $options; + } + + /** + * @param \Magento\Catalog\Model\Product $product + * @return int[] + */ + protected function getLinkedProducts(\Magento\Catalog\Model\Product $product) + { + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */ + $typeInstance = $product->getTypeInstance(); + $childrenIds = $typeInstance->getChildrenIds($product->getId()); + + if (isset($childrenIds[0])) { + return $childrenIds[0]; + } else { + return []; + } + } +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php new file mode 100644 index 0000000000000000000000000000000000000000..f0a12b809dcd9c484b63f379c94f1749ff5d6555 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php @@ -0,0 +1,197 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Model\Plugin; + +use Magento\Framework\Exception\InputException; + +class AroundProductRepositorySave +{ + /** + * @var \Magento\ConfigurableProduct\Api\OptionRepositoryInterface + */ + protected $optionRepository; + + /** + * @var \Magento\Catalog\Model\ProductFactory + */ + protected $productFactory; + + /** + * Type configurable factory + * + * @var \Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory + */ + protected $typeConfigurableFactory; + + /* + * @var \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data + */ + protected $priceData; + + /** + * @param \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository + * @param \Magento\Catalog\Model\ProductFactory $productFactory + * @param \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data $priceData + * @param \Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory $typeConfigurableFactory + */ + public function __construct( + \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository, + \Magento\Catalog\Model\ProductFactory $productFactory, + \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data $priceData, + \Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory $typeConfigurableFactory + ) { + $this->optionRepository = $optionRepository; + $this->productFactory = $productFactory; + $this->priceData = $priceData; + $this->typeConfigurableFactory = $typeConfigurableFactory; + } + + /** + * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject + * @param callable $proceed + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param bool $saveOptions + * @return \Magento\Catalog\Api\Data\ProductInterface + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundSave( + \Magento\Catalog\Api\ProductRepositoryInterface $subject, + \Closure $proceed, + \Magento\Catalog\Api\Data\ProductInterface $product, + $saveOptions = false + ) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $result */ + $result = $proceed($product, $saveOptions); + + if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) { + return $result; + } + + $extendedAttributes = $product->getExtensionAttributes(); + if ($extendedAttributes === null) { + return $result; + } + $configurableProductOptions = $extendedAttributes->getConfigurableProductOptions(); + $configurableProductLinks = $extendedAttributes->getConfigurableProductLinks(); + if ($configurableProductOptions === null && $configurableProductLinks === null) { + return $result; + } + if ($configurableProductOptions !== null) { + $this->saveConfigurableProductOptions($result, $configurableProductOptions); + $result->getTypeInstance()->resetConfigurableAttributes($result); + } + if ($configurableProductLinks !== null) { + $this->saveConfigurableProductLinks($result, $configurableProductLinks); + } + $this->priceData->setProductPrice($result->getId(), null); + return $subject->get($result->getSku(), false, $result->getStoreId(), true); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $options + * @return $this + */ + protected function saveConfigurableProductOptions( + \Magento\Catalog\Api\Data\ProductInterface $product, + array $options + ) { + $existingOptionIds = []; + if ($product->getExtensionAttributes() !== null) { + $extensionAttributes = $product->getExtensionAttributes(); + if ($extensionAttributes->getConfigurableProductOptions() !== null) { + $existingOptions = $extensionAttributes->getConfigurableProductOptions(); + foreach ($existingOptions as $option) { + $existingOptionIds[] = $option->getId(); + } + } + } + + $updatedOptionIds = []; + foreach ($options as $option) { + if ($option->getId()) { + $updatedOptionIds[] = $option->getId(); + } + $this->optionRepository->save($product->getSku(), $option); + } + + $optionIdsToDelete = array_diff($existingOptionIds, $updatedOptionIds); + foreach ($optionIdsToDelete as $optionId) { + $this->optionRepository->deleteById($product->getSku(), $optionId); + } + return $this; + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param int[] $linkIds + * @return $this + */ + protected function saveConfigurableProductLinks( + \Magento\Catalog\Api\Data\ProductInterface $product, + array $linkIds + ) { + $configurableProductTypeResource = $this->typeConfigurableFactory->create(); + if (!empty($linkIds)) { + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableProductType */ + $configurableProductType = $product->getTypeInstance(); + $configurableAttributes = $configurableProductType->getConfigurableAttributes($product); + $attributeCodes = []; + foreach ($configurableAttributes as $configurableAttribute) { + /** @var \Magento\Catalog\Model\Resource\Eav\Attribute $productAttribute */ + $productAttribute = $configurableAttribute->getProductAttribute(); + $attributeCode = $productAttribute->getAttributeCode(); + $attributeCodes[] = $attributeCode; + } + $this->validateProductLinks($attributeCodes, $linkIds); + } + + $configurableProductTypeResource->saveProducts($product, $linkIds); + return $this; + } + + /** + * @param array $attributeCodes + * @param array $linkIds + * @throws InputException + * @return $this + */ + protected function validateProductLinks(array $attributeCodes, array $linkIds) + { + $valueMap = []; + if (empty($attributeCodes) && !empty($linkIds)) { + throw new InputException( + __('The configurable product does not have any variation attribute.') + ); + } + + foreach ($linkIds as $productId) { + $variation = $this->productFactory->create()->load($productId); + if (!$variation->getId()) { + throw new InputException(__('Product with id "%1" does not exist.', $productId)); + } + $valueKey = ''; + foreach ($attributeCodes as $attributeCode) { + if (!$variation->getData($attributeCode)) { + throw new InputException( + __('Product with id "%1" does not contain required attribute "%2".', $productId, $attributeCode) + ); + } + $valueKey = $valueKey . $attributeCode . ':' . $variation->getData($attributeCode) . ';'; + } + if (isset($valueMap[$valueKey])) { + throw new InputException( + __('Products "%1" and %2 have the same set of attribute values.', $productId, $valueMap[$valueKey]) + ); + } + $valueMap[$valueKey] = $productId; + } + return $this; + } +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 5922c01bad8a3868bd867b8bd1522d3b2593ef30..c1fb0b41ef427e758954ee9728084e67eb9c5bed 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -377,6 +377,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType return $product->getData($this->_configurableAttributes); } + /** + * Reset the cached configurable attributes of a product + * + * @param \Magento\Catalog\Model\Product $product + * @return $this + */ + public function resetConfigurableAttributes($product) + { + $product->unsetData($this->_configurableAttributes); + return $this; + } + /** * Retrieve Configurable Attributes as array * diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php index f02e39626d66a7571bea42e1c5b2b1af804caf9c..8cf22a9b6dc3b4127baa3b8e9c8e5a9c0ade4dce 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php @@ -24,7 +24,6 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme */ const KEY_ATTRIBUTE_ID = 'attribute_id'; const KEY_LABEL = 'label'; - const KEY_TYPE = 'type'; const KEY_POSITION = 'position'; const KEY_IS_USE_DEFAULT = 'is_use_default'; const KEY_VALUES = 'values'; @@ -119,15 +118,6 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme return $this->getData(self::KEY_ATTRIBUTE_ID); } - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getType() - { - return $this->getData(self::KEY_TYPE); - } - /** * {@inheritdoc} * @codeCoverageIgnore @@ -174,15 +164,6 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme return $this->setData(self::KEY_LABEL, $label); } - /** - * @param string $type - * @return $this - */ - public function setType($type) - { - return $this->setData(self::KEY_TYPE, $type); - } - /** * @param int $position * @return $this diff --git a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php index e556661f45b1a802fd4f9024cbcbbd4889653cd0..671aa6b136ab40b63ad67c9f7229dcdb7a8ae1f9 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php @@ -180,10 +180,6 @@ class Configurable extends \Magento\Framework\Model\Resource\Db\AbstractDb implode( ' AND ', [ - $this->_getReadAdapter()->quoteInto( - 'entity_value.entity_type_id = ?', - $product->getEntityTypeId() - ), 'entity_value.attribute_id = super_attribute.attribute_id', 'entity_value.store_id = 0', 'entity_value.entity_id = product_link.product_id' diff --git a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php index 9d2ca8b5414c7920d4fb9235e6c6eaaaf4882368..f10a02db96f2297d1050a322c9bb92eaaa28cb91 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php @@ -263,7 +263,12 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac $values = $this->getPriceValues(); foreach ($values as $data) { - $this->getItemById($data['product_super_attribute_id'])->addPrice($data); + $item = $this->getItemById($data['product_super_attribute_id']); + //the price values is cached, it could have gotten out of sync with current items + //when a filter is added, in that case, we just ignore the data from the cache + if ($item) { + $item->addPrice($data); + } } } return $this; diff --git a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php index 248ed3ede9f343883132711c056d9966431c763e..3e5c714e425e4d50f4eec11eb6f1a09879c1ec87 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php +++ b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php @@ -24,10 +24,10 @@ class Data /** * @param int $productId - * @param array $priceData + * @param array|null $priceData * @return void */ - public function setProductPrice($productId, array $priceData) + public function setProductPrice($productId, array $priceData = null) { $this->prices[$productId] = $priceData; } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..34b66ecb320dc65bed96338f0435a8253b7aca5b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php @@ -0,0 +1,210 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Test\Unit\Model; + +class OptionRepositoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\ConfigurableProduct\Model\OptionRepository + */ + protected $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + protected function setUp() + { + $this->productRepositoryMock = $this->getMock('\Magento\Catalog\Api\ProductRepositoryInterface'); + $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + '\Magento\ConfigurableProduct\Model\OptionRepository', + [ + 'productRepository' => $this->productRepositoryMock, + ] + ); + } + + public function testGet() + { + $productSku = "configurable"; + $optionId = 3; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); + $optionMock->expects($this->once()) + ->method('getId') + ->willReturn($optionId); + $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['getConfigurableProductOptions']) + ->getMock(); + $productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn([$optionMock]); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($productExtensionMock); + + $this->assertEquals($optionMock, $this->model->get($productSku, $optionId)); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Only implemented for configurable product: configurable + */ + public function testGetNotConfigurableProduct() + { + $productSku = "configurable"; + $optionId = 3; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn('simple'); + + $this->model->get($productSku, $optionId); + } + + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Requested option doesn't exist: 3 + */ + public function testGetEmptyExtensionAttribute() + { + $productSku = "configurable"; + $optionId = 3; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + + $this->model->get($productSku, $optionId); + } + + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Requested option doesn't exist: 3 + */ + public function testGetOptionIdNotFound() + { + $productSku = "configurable"; + $optionId = 3; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); + $optionMock->expects($this->once()) + ->method('getId') + ->willReturn(6); + $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['getConfigurableProductOptions']) + ->getMock(); + $productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn([$optionMock]); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($productExtensionMock); + + $this->model->get($productSku, $optionId); + } + + public function testGetList() + { + $productSku = "configurable"; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); + $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['getConfigurableProductOptions']) + ->getMock(); + $productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn([$optionMock]); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($productExtensionMock); + + $this->assertEquals([$optionMock], $this->model->getList($productSku)); + } + + public function testGetListNullExtensionAttribute() + { + $productSku = "configurable"; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + + $this->assertEquals([], $this->model->getList($productSku)); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Only implemented for configurable product: configurable + */ + public function testGetListNotConfigurableProduct() + { + $productSku = "configurable"; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn('simple'); + + $this->model->getList($productSku); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionTypesListTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionTypesListTest.php deleted file mode 100644 index 79867bb302e321ff1da0c4e887a1d2f754bb115b..0000000000000000000000000000000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionTypesListTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\ConfigurableProduct\Test\Unit\Model; - -class OptionTypesListTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var OptionTypesList - */ - protected $model; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $sourceMock; - - protected function setUp() - { - $this->sourceMock = $this->getMock('\Magento\Catalog\Model\System\Config\Source\Inputtype', [], [], '', false); - $this->model = new \Magento\ConfigurableProduct\Model\OptionTypesList($this->sourceMock); - } - - public function testGetItems() - { - $data = [ - ['value' => 'multiselect', 'label' => __('Multiple Select')], - ['value' => 'select', 'label' => __('Dropdown')] - ]; - $this->sourceMock->expects($this->once())->method('toOptionArray')->willReturn($data); - $expected = ['multiselect', 'select']; - $this->assertEquals($expected, $this->model->getItems()); - } -} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..af3e73f0b855eeddb16f965f7438d8a16cbcd601 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php @@ -0,0 +1,239 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin; + +use Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad; + +class AfterProductLoadTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AfterProductLoad + */ + protected $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $optionValueFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Magento\Catalog\Model\Product + */ + protected $productMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $configurableProductTypeInstanceMock; + + protected function setUp() + { + $this->optionValueFactory = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory' + )->disableOriginalConstructor()->getMock(); + $this->productMock = $this->getMockBuilder('Magento\Catalog\Model\Product') + ->disableOriginalConstructor() + ->getMock(); + $this->configurableProductTypeInstanceMock = $this->getMock( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable', + [], + [], + '', + false + ); + $this->productMock->expects($this->any()) + ->method('getTypeInstance') + ->willReturn($this->configurableProductTypeInstanceMock); + $this->productExtensionFactory = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new \Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad( + $this->productExtensionFactory, + $this->optionValueFactory + ); + } + + protected function setupOptions() + { + $optionValues = [ + [ + 'value_index' => 5, + 'pricing_value' => 10, + 'is_percent' => 0, + ], + [ + 'value_index' => 6, + 'pricing_value' => 5, + 'is_percent' => 1, + ], + ]; + $optionMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') + ->disableOriginalConstructor() + ->setMethods(['getPrices', 'setValues']) + ->getMock(); + $optionMock1->expects($this->once()) + ->method('getPrices') + ->willReturn($optionValues); + + $optionValueMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface') + ->disableOriginalConstructor() + ->getMock(); + $optionValueMock1->expects($this->once()) + ->method('setValueIndex') + ->with($optionValues[0]['value_index']) + ->willReturnSelf(); + $optionValueMock1->expects($this->once()) + ->method('setPricingValue') + ->with($optionValues[0]['pricing_value']) + ->willReturnSelf(); + $optionValueMock1->expects($this->once()) + ->method('setIsPercent') + ->with($optionValues[0]['is_percent']) + ->willReturnSelf(); + + $optionValueMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface') + ->disableOriginalConstructor() + ->getMock(); + $optionValueMock2->expects($this->once()) + ->method('setValueIndex') + ->with($optionValues[1]['value_index']) + ->willReturnSelf(); + $optionValueMock2->expects($this->once()) + ->method('setPricingValue') + ->with($optionValues[1]['pricing_value']) + ->willReturnSelf(); + $optionValueMock2->expects($this->once()) + ->method('setIsPercent') + ->with($optionValues[1]['is_percent']) + ->willReturnSelf(); + + $this->optionValueFactory->expects($this->at(0)) + ->method('create') + ->willReturn($optionValueMock1); + $optionMock1->expects($this->once()) + ->method('setValues') + ->with([$optionValueMock1, $optionValueMock2]); + + $optionMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute') + ->disableOriginalConstructor() + ->setMethods(['getPrices', 'setValues']) + ->getMock(); + $optionMock2->expects($this->once()) + ->method('getPrices') + ->willReturn([]); + $optionMock2->expects($this->once()) + ->method('setValues') + ->with([]); + $this->optionValueFactory->expects($this->at(1)) + ->method('create') + ->willReturn($optionValueMock2); + + $options = [$optionMock1, $optionMock2]; + + $this->configurableProductTypeInstanceMock->expects($this->once()) + ->method('getConfigurableAttributes') + ->with($this->productMock) + ->willReturn($options); + return $options; + } + + protected function setupLinks() + { + $id = 5; + $links = [6, 7]; + $this->productMock->expects($this->once()) + ->method('getId') + ->willReturn($id); + $this->configurableProductTypeInstanceMock->expects($this->once()) + ->method('getChildrenIds') + ->with($id) + ->willReturn([$links]); + return $links; + } + + public function testAfterLoad() + { + $options = $this->setupOptions(); + $links = $this->setupLinks(); + + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) + ->getMock(); + + $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions') + ->with($options) + ->willReturnSelf(); + $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks') + ->with($links) + ->willReturnSelf(); + + $this->productExtensionFactory->expects($this->once()) + ->method('create') + ->willReturn($extensionAttributeMock); + + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($extensionAttributeMock) + ->willReturnSelf(); + + $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock)); + } + + public function testAfterLoadNotConfigurableProduct() + { + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn('simple'); + + $this->productMock->expects($this->never()) + ->method('getExtensionAttributes'); + $this->productMock->expects($this->never()) + ->method('setExtensionAttributes'); + $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock)); + } + + public function testAfterLoadNoLinks() + { + $options = $this->setupOptions(); + + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + + $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks']) + ->getMock(); + + $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions') + ->with($options) + ->willReturnSelf(); + $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks') + ->with([]) + ->willReturnSelf(); + + $this->productExtensionFactory->expects($this->once()) + ->method('create') + ->willReturn($extensionAttributeMock); + + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($extensionAttributeMock) + ->willReturnSelf(); + + $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock)); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6e750f9e13fcee390a5ae1a4fab496a507816f6c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php @@ -0,0 +1,553 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin; + +use Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave; + +class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AroundProductRepositorySave + */ + protected $plugin; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productOptionRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productFactoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $configurableTypeFactoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $priceDataMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productInterfaceMock; + + /** + * @var \Closure + */ + protected $closureMock; + + protected function setUp() + { + $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface'); + $this->productOptionRepositoryMock = $this->getMock( + 'Magento\ConfigurableProduct\Api\OptionRepositoryInterface' + ); + $this->configurableTypeFactoryMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory' + )->disableOriginalConstructor()->getMock(); + $this->priceDataMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data' + )->disableOriginalConstructor()->getMock(); + $this->productInterfaceMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $this->productMock = $this->getMock( + 'Magento\Catalog\Model\Product', + ['getExtensionAttributes', 'getTypeId', 'getSku', 'getStoreId', 'getId', 'getTypeInstance'], + [], + '', + false + ); + $this->closureMock = function () { + return $this->productMock; + }; + + $this->productFactoryMock = $this->getMockBuilder('\Magento\Catalog\Model\ProductFactory') + ->disableOriginalConstructor() + ->getMock(); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->plugin = $objectManager->getObject( + 'Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave', + [ + 'optionRepository' => $this->productOptionRepositoryMock, + 'productFactory' => $this->productFactoryMock, + 'priceData' => $this->priceDataMock, + 'typeConfigurableFactory' => $this->configurableTypeFactoryMock + ] + ); + + $this->productExtensionMock = $this->getMock( + 'Magento\Catalog\Api\Data\ProductExtension', + [ + 'getConfigurableProductOptions', + 'getConfigurableProductLinks', + 'setConfigurableProductOptions', + 'setConfigurableProductLinks', + ], + [], + '', + false + ); + } + + public function testAroundSaveWhenProductIsSimple() + { + $this->productMock->expects($this->once())->method('getTypeId')->willReturn('simple'); + $this->productMock->expects($this->never())->method('getExtensionAttributes'); + + $this->assertEquals( + $this->productMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + public function testAroundSaveWithoutOptions() + { + $this->productInterfaceMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + $this->productInterfaceMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductLinks') + ->willReturn(null); + + $this->priceDataMock->expects($this->never()) + ->method('setProductPrice'); + + $this->assertEquals( + $this->productMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock) + ); + } + + protected function setupProducts($productIds, $attributeCode, $additionalProductId = null) + { + $count = 0; + $products = []; + foreach ($productIds as $productId) { + $productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') + ->disableOriginalConstructor() + ->getMock(); + $productMock->expects($this->once()) + ->method('load') + ->with($productId) + ->willReturnSelf(); + $productMock->expects($this->once()) + ->method('getId') + ->willReturn($productId); + $productMock->expects($this->any()) + ->method('getData') + ->with($attributeCode) + ->willReturn($productId); + $this->productFactoryMock->expects($this->at($count)) + ->method('create') + ->willReturn($productMock); + $products[] = $productMock; + $count++; + } + + if ($additionalProductId) { + $nonExistingProductMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') + ->disableOriginalConstructor() + ->getMock(); + $nonExistingProductMock->expects($this->once()) + ->method('load') + ->with($additionalProductId) + ->willReturnSelf(); + $this->productFactoryMock->expects($this->at($count)) + ->method('create') + ->willReturn($nonExistingProductMock); + $products[] = $nonExistingProductMock; + } + return $products; + } + + protected function setupConfigurableProductAttributes($attributeCodes) + { + $configurableProductTypeMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable' + )->disableOriginalConstructor()->getMock(); + + $this->productMock->expects($this->once()) + ->method('getTypeInstance') + ->willReturn($configurableProductTypeMock); + + $configurableAttributes = []; + foreach ($attributeCodes as $attributeCode) { + $configurableAttribute = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute' + )->setMethods(['getProductAttribute']) + ->disableOriginalConstructor() + ->getMock(); + $productAttributeMock = $this->getMockBuilder('\Magento\Catalog\Model\Resource\Eav\Attribute') + ->disableOriginalConstructor() + ->getMock(); + $productAttributeMock->expects($this->once()) + ->method('getAttributeCode') + ->willReturn($attributeCode); + $configurableAttribute->expects($this->once()) + ->method('getProductAttribute') + ->willReturn($productAttributeMock); + $configurableAttributes[] = $configurableAttribute; + } + + $configurableProductTypeMock->expects($this->once()) + ->method('getConfigurableAttributes') + ->with($this->productMock) + ->willReturn($configurableAttributes); + + return $this; + } + + public function testAroundSaveWithLinks() + { + $links = [4, 5]; + $configurableAttributeCode = 'color'; + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductLinks') + ->willReturn($links); + + $this->setupConfigurableProductAttributes([$configurableAttributeCode]); + $this->setupProducts($links, $configurableAttributeCode); + + $configurableTypeMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable' + )->disableOriginalConstructor()->getMock(); + $this->configurableTypeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($configurableTypeMock); + $configurableTypeMock->expects($this->once()) + ->method('saveProducts') + ->with($this->productMock, $links); + + $productId = 3; + $this->productMock->expects($this->any()) + ->method('getId') + ->willReturn($productId); + $this->priceDataMock->expects($this->once()) + ->method('setProductPrice') + ->with($productId, null); + + $productSku = 'configurable-sku'; + $this->productMock->expects($this->any()) + ->method('getSku') + ->willReturn($productSku); + $newProductMock = $this->setupReload($productSku); + + $this->assertEquals( + $newProductMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Product with id "6" does not exist. + */ + public function testAroundSaveWithNonExistingLinks() + { + $links = [4, 5]; + $nonExistingId = 6; + $configurableAttributeCode = 'color'; + + $this->setupConfigurableProductAttributes([$configurableAttributeCode]); + $productMocks = $this->setupProducts($links, $configurableAttributeCode, $nonExistingId); + $nonExistingProductMock = $productMocks[2]; + $nonExistingProductMock->expects($this->once()) + ->method('getId') + ->willReturn(null); + $links[] = $nonExistingId; + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductLinks') + ->willReturn($links); + + $configurableTypeMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable' + )->disableOriginalConstructor()->getMock(); + $this->configurableTypeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($configurableTypeMock); + $configurableTypeMock->expects($this->never()) + ->method('saveProducts') + ->with($this->productMock, $links); + + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Product with id "6" does not contain required attribute "color". + */ + public function testAroundSaveWithLinksWithMissingAttribute() + { + $links = [4, 5]; + $simpleProductId = 6; + $configurableAttributeCode = 'color'; + + $this->setupConfigurableProductAttributes([$configurableAttributeCode]); + $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId); + $simpleProductMock = $productMocks[2]; + $simpleProductMock->expects($this->once()) + ->method('getId') + ->willReturn($simpleProductId); + $simpleProductMock->expects($this->any()) + ->method('getData') + ->with($configurableAttributeCode) + ->willReturn(null); + + $links[] = $simpleProductId; + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductLinks') + ->willReturn($links); + + $configurableTypeMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable' + )->disableOriginalConstructor()->getMock(); + $this->configurableTypeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($configurableTypeMock); + $configurableTypeMock->expects($this->never()) + ->method('saveProducts') + ->with($this->productMock, $links); + + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Products "6" and 4 have the same set of attribute values. + */ + public function testAroundSaveWithLinksWithDuplicateAttributes() + { + $links = [4, 5]; + $simpleProductId = 6; + $configurableAttributeCode = 'color'; + + $this->setupConfigurableProductAttributes([$configurableAttributeCode]); + $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId); + $simpleProductMock = $productMocks[2]; + $simpleProductMock->expects($this->once()) + ->method('getId') + ->willReturn($simpleProductId); + $simpleProductMock->expects($this->any()) + ->method('getData') + ->with($configurableAttributeCode) + ->willReturn(4); + + $links[] = $simpleProductId; + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductLinks') + ->willReturn($links); + + $configurableTypeMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable' + )->disableOriginalConstructor()->getMock(); + $this->configurableTypeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($configurableTypeMock); + $configurableTypeMock->expects($this->never()) + ->method('saveProducts') + ->with($this->productMock, $links); + + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage The configurable product does not have any variation attribute. + */ + public function testAroundSaveWithLinksWithoutVariationAttributes() + { + $links = [4, 5]; + + $this->setupConfigurableProductAttributes([]); + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductOptions') + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getConfigurableProductLinks') + ->willReturn($links); + + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock); + } + + public function testAroundSaveWithOptions() + { + $productSku = "configurable_sku"; + $this->productInterfaceMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE); + //two options with id 5 and 6 + $options = $this->setupOptions([5, 6]); + //two existing options with id 4 and 5 + $this->setupExistingOptions([4, 5]); + + $this->productMock->expects($this->any())->method('getSku') + ->will($this->returnValue($productSku)); + + $this->productOptionRepositoryMock->expects($this->at(0)) + ->method('save') + ->with($productSku, $options[0]); + $this->productOptionRepositoryMock->expects($this->at(1)) + ->method('save') + ->with($productSku, $options[1]); + $this->productOptionRepositoryMock->expects($this->at(2)) + ->method('deleteById') + ->with($productSku, 4); + + $configurableProductTypeMock = $this->getMockBuilder( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable' + )->disableOriginalConstructor()->getMock(); + $configurableProductTypeMock->expects($this->once()) + ->method('resetConfigurableAttributes') + ->with($this->productMock) + ->willReturnSelf(); + $this->productMock->expects($this->any()) + ->method('getTypeInstance') + ->willReturn($configurableProductTypeMock); + + $productId = 3; + $this->productMock->expects($this->once()) + ->method('getId') + ->willReturn($productId); + $this->priceDataMock->expects($this->once()) + ->method('setProductPrice') + ->with($productId, null); + + $newProductMock = $this->setupReload($productSku); + + $this->assertEquals( + $newProductMock, + $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock) + ); + } + + protected function setupReload($productSku) + { + $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') + ->disableOriginalConstructor()->getMock(); + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku, false, null, true) + ->willReturn($newProductMock); + return $newProductMock; + } + + protected function setupExistingOptions(array $existingOptionIds) + { + $options = []; + foreach ($existingOptionIds as $existingOptionId) { + $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); + $optionMock->expects($this->any()) + ->method('getId') + ->willReturn($existingOptionId); + $options[] = $optionMock; + } + + $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension') + ->disableOriginalConstructor() + ->setMethods(['getConfigurableProductOptions']) + ->getMock(); + $productExtensionMock->expects($this->any()) + ->method('getConfigurableProductOptions') + ->willReturn($options); + + $this->productMock->expects($this->any()) + ->method('getExtensionAttributes') + ->willReturn($productExtensionMock); + } + + protected function setupOptions(array $optionIds) + { + $options = []; + foreach ($optionIds as $optionId) { + $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface'); + $optionMock->expects($this->any()) + ->method('getId') + ->willReturn($optionId); + $options[] = $optionMock; + } + + $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension') + ->disableOriginalConstructor() + ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks']) + ->getMock(); + $productExtensionMock->expects($this->any()) + ->method('getConfigurableProductOptions') + ->willReturn($options); + $productExtensionMock->expects($this->any()) + ->method('getConfigurableProductLinks') + ->willReturn(null); + + $this->productInterfaceMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($productExtensionMock); + return $options; + } +} 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 d0c5ae32ab1dc80a675f55233119de9eedcdc708..4ca090adef44b5e3786bd066067c7c6aaa03ac53 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 @@ -408,6 +408,19 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ]; } + public function testResetConfigurableAttributes() + { + $product = $this->getMockBuilder('\Magento\Catalog\Model\Product') + ->setMethods(['unsetData', '__wakeup', '__sleep']) + ->disableOriginalConstructor() + ->getMock(); + $product->expects($this->any())->method('unsetData') + ->with('_cache_instance_configurable_attributes') + ->will($this->returnSelf()); + + $this->assertEquals($this->_model, $this->_model->resetConfigurableAttributes($product)); + } + public function testHasOptions() { $productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') @@ -584,10 +597,6 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->setMethods(['getId', 'getAttributeCode']) ->disableOriginalConstructor() ->getMock(); - $productResource = $this->getMockBuilder('\Magento\Catalog\Model\Resource\Product') - ->setMethods(['__wakeup', 'loadAllAttributes', 'getSortedAttributes']) - ->disableOriginalConstructor() - ->getMock(); $productCollection = $this->getMockBuilder( 'Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Product\Collection' ) @@ -616,14 +625,12 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase ->method('getData') ->with('_cache_instance_store_filter') ->willReturn('some_filter'); - $productMock->expects($this->once())->method('hasData')->willReturn(true); - $productMock->expects($this->at(3))->method('getData')->willReturn([$usedProductMock]); - $productMock->expects($this->once())->method('getResource')->willReturn($productResource); - $productMock->expects($this->once())->method('getAttributeSetId')->willReturn(5); - $productResource->expects($this->once())->method('loadAllAttributes')->with($productMock)->willReturnSelf(); - $productResource->expects($this->once()) - ->method('getSortedAttributes') - ->with(5) + $productMock->expects($this->any())->method('hasData')->willReturn(true); + $productMock->expects($this->at(3))->method('getData') + ->with('_cache_instance_products') + ->willReturn([$usedProductMock]); + $productMock->expects($this->at(5))->method('getData') + ->with('_cache_instance_product_set_attributes') ->willReturn([$eavAttributeMock]); $eavAttributeMock->expects($this->once())->method('getId')->willReturn(1); $eavAttributeMock->expects($this->once())->method('getAttributeCode')->willReturn('attr_code'); diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index fcd5d77b81e4de8e9a5df180968909b02d290221..490c15a70a51fc150d80d3e8b42dd5c4ef8aa47e 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -6,7 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd"> - <preference for="Magento\ConfigurableProduct\Api\OptionTypesListInterface" type="Magento\ConfigurableProduct\Model\OptionTypesList" /> <preference for="Magento\ConfigurableProduct\Api\ConfigurableProductManagementInterface" type="Magento\ConfigurableProduct\Model\ConfigurableProductManagement" /> <preference for="Magento\ConfigurableProduct\Api\LinkManagementInterface" type="Magento\ConfigurableProduct\Model\LinkManagement" /> <preference for="Magento\ConfigurableProduct\Api\OptionRepositoryInterface" type="Magento\ConfigurableProduct\Model\OptionRepository" /> @@ -59,6 +58,12 @@ <type name="Magento\Catalog\Model\Product\Type"> <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" /> </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="configurableProductSaveOptions" type="\Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave"/> + </type> + <type name="Magento\Catalog\Model\Product"> + <plugin name="configurableProductLoadAfter" type="\Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad"/> + </type> <virtualType name="Magento\ConfigurableProduct\Pricing\Price\Pool" type="Magento\Framework\Pricing\Price\Pool"> <arguments> <argument name="prices" xsi:type="array"> diff --git a/app/code/Magento/ConfigurableProduct/etc/service_data_attributes.xml b/app/code/Magento/ConfigurableProduct/etc/service_data_attributes.xml new file mode 100644 index 0000000000000000000000000000000000000000..74edcdbf65a91e08cc384b50b416deff63e8780c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/etc/service_data_attributes.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> + <attribute code="configurable_product_options" type="Magento\ConfigurableProduct\Api\Data\OptionInterface[]" /> + <attribute code="configurable_product_links" type="int[]" /> + </extension_attributes> +</config> diff --git a/app/code/Magento/ConfigurableProduct/etc/webapi.xml b/app/code/Magento/ConfigurableProduct/etc/webapi.xml index 9c68aa71869f44782120dda382eb7de6c34e9f2d..158db3c3c5fa8313960e1735250f3f7cfb250388 100644 --- a/app/code/Magento/ConfigurableProduct/etc/webapi.xml +++ b/app/code/Magento/ConfigurableProduct/etc/webapi.xml @@ -43,12 +43,6 @@ <resource ref="Magento_Catalog::products"/> </resources> </route> - <route url="/V1/configurable-products/options/types" method="GET"> - <service class="Magento\ConfigurableProduct\Api\OptionTypesListInterface" method="getItems" /> - <resources> - <resource ref="Magento_Catalog::products" /> - </resources> - </route> <route url="/V1/configurable-products/:sku/options" method="POST"> <service class="Magento\ConfigurableProduct\Api\OptionRepositoryInterface" method="save" /> <resources> diff --git a/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php b/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php index 7e187a71e5c08bdad11cf84b9cfdc3c9ca85632c..b25f4c6d0b3a3cb83b3693f1d2d7c08fb4859247 100644 --- a/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php +++ b/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php @@ -5,6 +5,8 @@ */ namespace Magento\Customer\Model\Config\Backend\Address; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * Line count config model for customer address street attribute * @@ -64,7 +66,7 @@ class Street extends \Magento\Framework\App\Config\Value } break; - case \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT: + case ScopeConfigInterface::SCOPE_TYPE_DEFAULT: $attribute->setData('multiline_count', $value); break; } diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php index c65cda9e51bbae8fa77d7f89b0bb440a7fb121f7..776c8e74e1ace9aa8cc4205174c6fb0977032368 100644 --- a/app/code/Magento/Customer/Model/Customer.php +++ b/app/code/Magento/Customer/Model/Customer.php @@ -12,6 +12,7 @@ use Magento\Customer\Model\Resource\Address\CollectionFactory; use Magento\Customer\Model\Resource\Customer as ResourceCustomer; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Customer\Model\Data\Customer as CustomerData; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Exception\EmailNotConfirmedException; use Magento\Framework\Exception\InvalidEmailOrPasswordException; @@ -1310,7 +1311,7 @@ class Customer extends \Magento\Framework\Model\AbstractModel { return (int)$this->_scopeConfig->getValue( self::XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); } diff --git a/app/code/Magento/Customer/Model/Resource/Address.php b/app/code/Magento/Customer/Model/Resource/Address.php index 57a5c1cbcf3d9c28e975a999395d5ffd18600696..3b6ae0a28c8f9e812e762b72dbfe45d35681ebe9 100644 --- a/app/code/Magento/Customer/Model/Resource/Address.php +++ b/app/code/Magento/Customer/Model/Resource/Address.php @@ -45,13 +45,22 @@ class Address extends \Magento\Eav\Model\Entity\AbstractEntity */ protected function _construct() { - $resource = $this->_resource; - $this->setType( - 'customer_address' - )->setConnection( - $resource->getConnection('customer_read'), - $resource->getConnection('customer_write') - ); + $this->_read = 'customer_read'; + $this->_write = 'customer_write'; + } + + /** + * Getter and lazy loader for _type + * + * @throws \Magento\Framework\Exception\LocalizedException + * @return \Magento\Eav\Model\Entity\Type + */ + public function getEntityType() + { + if (empty($this->_type)) { + $this->setType('customer_address'); + } + return parent::getEntityType(); } /** diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index c01cd99d642cf7cb72c963fc20513f2e8e525235..4212be78b59114439b049203b03eca3a488991e4 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -102,6 +102,7 @@ class Session extends \Magento\Framework\Session\SessionManager * @param \Magento\Framework\Session\StorageInterface $storage * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\App\State $appState * @param Share $configShare * @param \Magento\Framework\Url\Helper\Data $coreUrl * @param \Magento\Customer\Model\Url $customerUrl @@ -113,6 +114,7 @@ class Session extends \Magento\Framework\Session\SessionManager * @param \Magento\Framework\App\Http\Context $httpContext * @param CustomerRepositoryInterface $customerRepository * @param GroupManagementInterface $groupManagement + * @throws \Magento\Framework\Exception\SessionException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -124,6 +126,7 @@ class Session extends \Magento\Framework\Session\SessionManager \Magento\Framework\Session\StorageInterface $storage, \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\App\State $appState, Config\Share $configShare, \Magento\Framework\Url\Helper\Data $coreUrl, \Magento\Customer\Model\Url $customerUrl, @@ -154,9 +157,9 @@ class Session extends \Magento\Framework\Session\SessionManager $validator, $storage, $cookieManager, - $cookieMetadataFactory + $cookieMetadataFactory, + $appState ); - $this->start(); $this->groupManagement = $groupManagement; $this->_eventManager->dispatch('customer_session_init', ['customer_session' => $this]); } diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index 5d1e91708b4fcd6da306011c551415bf3586d66a..834c64df6701f19605ccd05fb8298d1def87fa4a 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -10,6 +10,7 @@ namespace Magento\Customer\Test\Unit\Block\Widget; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Customer\Block\Widget\Dob; +use Magento\Framework\Locale\Resolver; class DobTest extends \PHPUnit_Framework_TestCase { @@ -69,7 +70,7 @@ class DobTest extends \PHPUnit_Framework_TestCase $localeResolver = $this->getMock('\Magento\Framework\Locale\ResolverInterface'); $localeResolver->expects($this->any()) ->method('getLocale') - ->willReturn(\Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE); + ->willReturn(Resolver::DEFAULT_LOCALE); $timezone = $objectManager->getObject( 'Magento\Framework\Stdlib\DateTime\Timezone', ['localeResolver' => $localeResolver] diff --git a/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php b/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php index fe17aa1aca70eb47723da99b6c9a8639944b76de..c15cf1596c8245fb30d8a79ba097923d545c26b0 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php @@ -18,6 +18,9 @@ class AddressTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $customerFactory; + /** @var \Magento\Eav\Model\Entity\Type */ + protected $eavConfigType; + protected function setUp() { $this->addressResource = (new ObjectManagerHelper($this))->getObject( @@ -178,16 +181,16 @@ class AddressTest extends \PHPUnit_Framework_TestCase ) ); - $eavConfigType = $this->getMock( + $this->eavConfigType = $this->getMock( 'Magento\Eav\Model\Entity\Type', ['getEntityIdField', 'getId', 'getEntityTable', '__wakeup'], [], '', false ); - $eavConfigType->expects($this->any())->method('getEntityIdField')->willReturn(false); - $eavConfigType->expects($this->any())->method('getId')->willReturn(false); - $eavConfigType->expects($this->any())->method('getEntityTable')->willReturn('customer_address_entity'); + $this->eavConfigType->expects($this->any())->method('getEntityIdField')->willReturn(false); + $this->eavConfigType->expects($this->any())->method('getId')->willReturn(false); + $this->eavConfigType->expects($this->any())->method('getEntityTable')->willReturn('customer_address_entity'); $eavConfig = $this->getMock( 'Magento\Eav\Model\Config', @@ -199,10 +202,10 @@ class AddressTest extends \PHPUnit_Framework_TestCase $eavConfig->expects($this->any()) ->method('getEntityType') ->with('customer_address') - ->willReturn($eavConfigType); + ->willReturn($this->eavConfigType); $eavConfig->expects($this->any()) ->method('getEntityAttributeCodes') - ->with($eavConfigType) + ->with($this->eavConfigType) ->willReturn( [ 'entity_type_id', @@ -217,13 +220,13 @@ class AddressTest extends \PHPUnit_Framework_TestCase $eavConfig->expects($this->any()) ->method('getAttribute') ->willReturnMap([ - [$eavConfigType, 'entity_type_id', $attributeMock], - [$eavConfigType, 'attribute_set_id', $attributeMock], - [$eavConfigType, 'created_at', $attributeMock], - [$eavConfigType, 'updated_at', $attributeMock], - [$eavConfigType, 'parent_id', $attributeMock], - [$eavConfigType, 'increment_id', $attributeMock], - [$eavConfigType, 'entity_id', $attributeMock], + [$this->eavConfigType, 'entity_type_id', $attributeMock], + [$this->eavConfigType, 'attribute_set_id', $attributeMock], + [$this->eavConfigType, 'created_at', $attributeMock], + [$this->eavConfigType, 'updated_at', $attributeMock], + [$this->eavConfigType, 'parent_id', $attributeMock], + [$this->eavConfigType, 'increment_id', $attributeMock], + [$this->eavConfigType, 'entity_id', $attributeMock], ]); return $eavConfig; @@ -261,4 +264,9 @@ class AddressTest extends \PHPUnit_Framework_TestCase $this->customerFactory = $this->getMock('Magento\Customer\Model\CustomerFactory', ['create'], [], '', false); return $this->customerFactory; } + + public function testGetType() + { + $this->assertSame($this->eavConfigType, $this->addressResource->getEntityType()); + } } diff --git a/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php b/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php index af413fcd477b0b044e36d1bc9ed4bfc3c6637eb5..cb54368e519392d63b842cd6227cce1644e51c7d 100644 --- a/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php +++ b/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php @@ -38,7 +38,7 @@ class NavigationMode extends \Magento\Framework\Url * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver * @param \Magento\Framework\Session\Generic $session * @param \Magento\Framework\Session\SidResolverInterface $sidResolver - * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver + * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param string $scopeType @@ -53,7 +53,7 @@ class NavigationMode extends \Magento\Framework\Url \Magento\Framework\Url\ScopeResolverInterface $scopeResolver, \Magento\Framework\Session\Generic $session, \Magento\Framework\Session\SidResolverInterface $sidResolver, - \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver, + \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory, \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, $scopeType, @@ -75,7 +75,7 @@ class NavigationMode extends \Magento\Framework\Url $scopeResolver, $session, $sidResolver, - $routeParamsResolver, + $routeParamsResolverFactory, $queryParamsResolver, $scopeConfig, $scopeType, diff --git a/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php b/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php index e6de82bcf54ee3798e263bafb0d3a5f77c5e1435..e4ee8ff76365954f2ec2b404f72515d4c2cc3249 100644 --- a/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php +++ b/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php @@ -90,7 +90,7 @@ class NavigationModeTest extends \PHPUnit_Framework_TestCase [ 'helper' => $this->_designHelperMock, 'data' => $this->_testData, - 'routeParamsResolver' => $this->_routeParamsMock, + 'routeParamsResolverFactory' => $this->_routeParamsMock, 'scopeResolver' => $this->_scopeResolverMock ] ); diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index f858f6b51bc275787d5b476b42a1a3f62277ec11..13ec5c3a13106fc5820440bb0b7754e6404a5074 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <section id="dev" translate="label"> <group id="front_end_development_workflow" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Front-end development workflow</label> - <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Workflow type</label> <comment>Not available in production mode</comment> <source_model>Magento\Developer\Model\Config\Source\WorkflowType</source_model> diff --git a/app/code/Magento/Directory/Model/Currency.php b/app/code/Magento/Directory/Model/Currency.php index 9e7d2b9abef186b2f221c94c7ef61007534c81eb..31ab3bf266dd24b8fb1b657731f5b8bcf2c1bda4 100644 --- a/app/code/Magento/Directory/Model/Currency.php +++ b/app/code/Magento/Directory/Model/Currency.php @@ -311,6 +311,16 @@ class Currency extends \Magento\Framework\Model\AbstractModel return $this->_localeCurrency->getCurrency($this->getCode())->toCurrency($price, $options); } + /** + * Return currency symbol for current locale and currency code + * + * @return string + */ + public function getCurrencySymbol() + { + return $this->_localeCurrency->getCurrency($this->getCode())->getSymbol(); + } + /** * @return string */ diff --git a/app/code/Magento/Directory/Model/PriceCurrency.php b/app/code/Magento/Directory/Model/PriceCurrency.php index 3aa697742a41687cf66d0e78517afd3549aaac1c..9cf7dabd2de746d5193abe8bbf7b1c1f5662e550 100644 --- a/app/code/Magento/Directory/Model/PriceCurrency.php +++ b/app/code/Magento/Directory/Model/PriceCurrency.php @@ -117,6 +117,16 @@ class PriceCurrency implements \Magento\Framework\Pricing\PriceCurrencyInterface return $currentCurrency; } + /** + * @param null|string|bool|int|\Magento\Framework\App\ScopeInterface $scope + * @param \Magento\Framework\Model\AbstractModel|string|null $currency + * @return string + */ + public function getCurrencySymbol($scope = null, $currency = null) + { + return $this->getCurrency($scope, $currency)->getCurrencySymbol(); + } + /** * Get store model * diff --git a/app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php b/app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..33a312880adc33b16f3c0f2327573b2f08d1ae32 --- /dev/null +++ b/app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Directory\Test\Unit\Model; + +use Magento\Directory\Model\Currency; + +class CurrencyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Currency + */ + protected $currency; + + protected $currencyCode = 'USD'; + + /** + * @var \Magento\Framework\Locale\CurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $localeCurrencyMock; + + public function setUp() + { + $this->localeCurrencyMock = $this->getMock('\Magento\Framework\Locale\CurrencyInterface'); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->currency = $objectManager->getObject('Magento\Directory\Model\Currency', [ + 'localeCurrency' => $this->localeCurrencyMock, + 'data' => [ + 'currency_code' => $this->currencyCode, + ] + ]); + } + + public function testGetCurrencySymbol() + { + $currencySymbol = '$'; + + $currencyMock = $this->getMockBuilder('\Magento\Framework\Currency') + ->disableOriginalConstructor() + ->getMock(); + $currencyMock->expects($this->once()) + ->method('getSymbol') + ->willReturn($currencySymbol); + + $this->localeCurrencyMock->expects($this->once()) + ->method('getCurrency') + ->with($this->currencyCode) + ->willReturn($currencyMock); + $this->assertEquals($currencySymbol, $this->currency->getCurrencySymbol()); + } +} diff --git a/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php b/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php index 205a63e7b0c33f3ffaa312cf9256ee2607f4aab8..b934435151a040f0f7156c532aecdb85f313e6e8 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php @@ -168,6 +168,18 @@ class PriceCurrencyTest extends \PHPUnit_Framework_TestCase )); } + public function testGetCurrencySymbol() + { + $storeId = 2; + $currencySymbol = '$'; + + $currencyMock = $this->getCurrentCurrencyMock(); + $currencyMock->expects($this->once()) + ->method('getCurrencySymbol') + ->willReturn($currencySymbol); + $this->assertEquals($currencySymbol, $this->priceCurrency->getCurrencySymbol($storeId, $currencyMock)); + } + protected function getCurrentCurrencyMock() { $currency = $this->getMockBuilder('Magento\Directory\Model\Currency') diff --git a/app/code/Magento/Downloadable/Api/Data/LinkContentInterface.php b/app/code/Magento/Downloadable/Api/Data/LinkContentInterface.php deleted file mode 100644 index 1972068f435512611b3d564f875ec666f6d20b56..0000000000000000000000000000000000000000 --- a/app/code/Magento/Downloadable/Api/Data/LinkContentInterface.php +++ /dev/null @@ -1,194 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Downloadable\Api\Data; - -/** - * @codeCoverageIgnore - */ -interface LinkContentInterface extends \Magento\Framework\Api\ExtensibleDataInterface -{ - /** - * Retrieve sample title - * - * @return string - */ - public function getTitle(); - - /** - * Set sample title - * - * @param string $title - * @return $this - */ - public function setTitle($title); - - /** - * Retrieve sample sort order - * - * @return int - */ - public function getSortOrder(); - - /** - * Set sample sort order - * - * @param int $sortOrder - * @return $this - */ - public function setSortOrder($sortOrder); - - /** - * Retrieve link price - * - * @return string - */ - public function getPrice(); - - /** - * Set link price - * - * @param string $price - * @return $this - */ - public function setPrice($price); - - /** - * Retrieve number of allowed downloads of the link - * - * @return int - */ - public function getNumberOfDownloads(); - - /** - * Set number of allowed downloads of the link - * - * @param int $numberOfDownloads - * @return $this - */ - public function setNumberOfDownloads($numberOfDownloads); - - /** - * Check if link is shareable - * - * @return bool - */ - public function isShareable(); - - /** - * Set whether link is shareable - * - * @param bool $shareable - * @return $this - */ - public function setShareable($shareable); - - /** - * Retrieve link file content - * - * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null - */ - public function getLinkFile(); - - /** - * Set link file content - * - * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFile - * @return $this - */ - public function setLinkFile(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFile = null); - - /** - * Retrieve link URL - * - * @return string|null - */ - public function getLinkUrl(); - - /** - * Set link URL - * - * @param string $linkUrl - * @return $this - */ - public function setLinkUrl($linkUrl); - - /** - * Retrieve link type ('url' or 'file') - * - * @return string|null - */ - public function getLinkType(); - - /** - * Set link type ('url' or 'file') - * - * @param string $linkType - * @return $this - */ - public function setLinkType($linkType); - - /** - * Retrieve sample file content - * - * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null - */ - public function getSampleFile(); - - /** - * Retrieve sample file content - * - * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile - * @return $this - */ - public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null); - - /** - * Retrieve sample URL - * - * @return string|null - */ - public function getSampleUrl(); - - /** - * Set sample URL - * - * @param string $sampleUrl - * @return $this - */ - public function setSampleUrl($sampleUrl); - - /** - * Retrieve sample type ('url' or 'file') - * - * @return string|null - */ - public function getSampleType(); - - /** - * Set sample type ('url' or 'file') - * - * @param string $sampleType - * @return $this - */ - public function setSampleType($sampleType); - - /** - * Retrieve existing extension attributes object or create a new one. - * - * @return \Magento\Downloadable\Api\Data\LinkContentExtensionInterface|null - */ - public function getExtensionAttributes(); - - /** - * Set an extension attributes object. - * - * @param \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes - * @return $this - */ - public function setExtensionAttributes( - \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes - ); -} diff --git a/app/code/Magento/Downloadable/Api/Data/LinkInterface.php b/app/code/Magento/Downloadable/Api/Data/LinkInterface.php index 0152a1a256540db9b0cab423e2804bd906475245..6fe62e070349e96c265a7d6d5e32da64de0eb976 100644 --- a/app/code/Magento/Downloadable/Api/Data/LinkInterface.php +++ b/app/code/Magento/Downloadable/Api/Data/LinkInterface.php @@ -119,9 +119,24 @@ interface LinkInterface extends \Magento\Framework\Api\ExtensibleDataInterface public function setLinkFile($linkFile); /** - * Return URL or NULL when type is 'file' + * Return file content * - * @return string|null file URL + * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null + */ + public function getLinkFileContent(); + + /** + * Set file content + * + * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent + * @return $this + */ + public function setLinkFileContent(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent = null); + + /** + * Return link url or null when type is 'file' + * + * @return string|null */ public function getLinkUrl(); @@ -159,6 +174,23 @@ interface LinkInterface extends \Magento\Framework\Api\ExtensibleDataInterface */ public function setSampleFile($sampleFile); + /** + * Return sample file content when type is 'file' + * + * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null relative file path + */ + public function getSampleFileContent(); + + /** + * Set sample file content + * + * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent + * @return $this + */ + public function setSampleFileContent( + \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent = null + ); + /** * Return URL or NULL when type is 'file' * diff --git a/app/code/Magento/Downloadable/Api/Data/SampleContentInterface.php b/app/code/Magento/Downloadable/Api/Data/SampleContentInterface.php deleted file mode 100644 index b4b0580bda1879b6aa75ab70371f6c522b7d1100..0000000000000000000000000000000000000000 --- a/app/code/Magento/Downloadable/Api/Data/SampleContentInterface.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Downloadable\Api\Data; - -/** - * @codeCoverageIgnore - */ -interface SampleContentInterface extends \Magento\Framework\Api\ExtensibleDataInterface -{ - /** - * Retrieve sample title - * - * @return string - */ - public function getTitle(); - - /** - * Set sample title - * - * @param string $title - * @return $this - */ - public function setTitle($title); - - /** - * Retrieve sample type ('url' or 'file') - * - * @return string|null - */ - public function getSampleType(); - - /** - * Set sample type ('url' or 'file') - * - * @param string $sampleType - * @return $this - */ - public function setSampleType($sampleType); - - /** - * Retrieve sample file content - * - * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null - */ - public function getSampleFile(); - - /** - * Set sample file content - * - * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile - * @return $this - */ - public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null); - - /** - * Retrieve sample sort order - * - * @return int - */ - public function getSortOrder(); - - /** - * Set sample sort order - * - * @param int $sortOrder - * @return $this - */ - public function setSortOrder($sortOrder); - - /** - * Retrieve sample URL - * - * @return string|null - */ - public function getSampleUrl(); - - /** - * Set sample URL - * - * @param string $sampleUrl - * @return $this - */ - public function setSampleUrl($sampleUrl); - - /** - * Retrieve existing extension attributes object or create a new one. - * - * @return \Magento\Downloadable\Api\Data\SampleContentExtensionInterface|null - */ - public function getExtensionAttributes(); - - /** - * Set an extension attributes object. - * - * @param \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes - * @return $this - */ - public function setExtensionAttributes( - \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes - ); -} diff --git a/app/code/Magento/Downloadable/Api/Data/SampleInterface.php b/app/code/Magento/Downloadable/Api/Data/SampleInterface.php index 3d6dfc937f3871a4a489e43c1f1912c5a7498082..8e30bf6f0e1ea0f3fc8bcf78b4261696e6119106 100644 --- a/app/code/Magento/Downloadable/Api/Data/SampleInterface.php +++ b/app/code/Magento/Downloadable/Api/Data/SampleInterface.php @@ -5,6 +5,8 @@ */ namespace Magento\Downloadable\Api\Data; +use \Magento\Downloadable\Api\Data\File\ContentInterface; + /** * @codeCoverageIgnore */ @@ -82,6 +84,21 @@ interface SampleInterface extends \Magento\Framework\Api\ExtensibleDataInterface */ public function setSampleFile($sampleFile); + /** + * Retrieve sample file content + * + * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null + */ + public function getSampleFileContent(); + + /** + * Set sample file content + * + * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent + * @return $this + */ + public function setSampleFileContent(ContentInterface $sampleFileContent = null); + /** * Return URL or NULL when type is 'file' * diff --git a/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php b/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php index a8bd6f83d9be190834e0e92d34c039010a4d870c..3a0d8053e4f8d599af652e83d830ad6c7ead7b65 100644 --- a/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php +++ b/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php @@ -5,7 +5,7 @@ */ namespace Magento\Downloadable\Api; -use Magento\Downloadable\Api\Data\LinkContentInterface; +use Magento\Downloadable\Api\Data\LinkInterface; interface LinkRepositoryInterface { @@ -17,6 +17,14 @@ interface LinkRepositoryInterface */ public function getSamples($sku); + /** + * List of samples for downloadable product + * + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return \Magento\Downloadable\Api\Data\SampleInterface[] + */ + public function getSamplesByProduct(\Magento\Catalog\Api\Data\ProductInterface $product); + /** * List of links with associated samples * @@ -25,22 +33,29 @@ interface LinkRepositoryInterface */ public function getLinks($sku); + /** + * List of links with associated samples + * + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return \Magento\Downloadable\Api\Data\LinkInterface[] + */ + public function getLinksByProduct(\Magento\Catalog\Api\Data\ProductInterface $product); + /** * Update downloadable link of the given product (link type and its resources cannot be changed) * * @param string $sku - * @param \Magento\Downloadable\Api\Data\LinkContentInterface $linkContent - * @param int $linkId + * @param \Magento\Downloadable\Api\Data\LinkInterface $link * @param bool $isGlobalScopeContent * @return int */ - public function save($sku, LinkContentInterface $linkContent, $linkId = null, $isGlobalScopeContent = false); + public function save($sku, LinkInterface $link, $isGlobalScopeContent = false); /** * Delete downloadable link * - * @param int $linkId + * @param int $id * @return bool */ - public function delete($linkId); + public function delete($id); } diff --git a/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php b/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php index cd72680b21bae5b75bd3b245d5a867ebd8e7f168..9a52cea8819f4e413e8c5a1964e5732e5936ed6f 100644 --- a/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php +++ b/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php @@ -5,31 +5,29 @@ */ namespace Magento\Downloadable\Api; -use Magento\Downloadable\Api\Data\SampleContentInterface; +use Magento\Downloadable\Api\Data\SampleInterface; interface SampleRepositoryInterface { /** - * Update downloadable sample of the given product (sample type and its resource cannot be changed) + * Update downloadable sample of the given product * - * @param string $productSku - * @param \Magento\Downloadable\Api\Data\SampleContentInterface $sampleContent - * @param int $sampleId + * @param string $sku + * @param \Magento\Downloadable\Api\Data\SampleInterface $sample * @param bool $isGlobalScopeContent * @return int */ public function save( - $productSku, - SampleContentInterface $sampleContent, - $sampleId = null, + $sku, + SampleInterface $sample, $isGlobalScopeContent = false ); /** * Delete downloadable sample * - * @param int $sampleId + * @param int $id * @return bool */ - public function delete($sampleId); + public function delete($id); } diff --git a/app/code/Magento/Downloadable/Model/Link.php b/app/code/Magento/Downloadable/Model/Link.php index 84de54915ab0edec47b492ac19a2fde4f5d131b4..66703a9dccb811ce294d5c4b39b053db7ec07cc2 100644 --- a/app/code/Magento/Downloadable/Model/Link.php +++ b/app/code/Magento/Downloadable/Model/Link.php @@ -43,9 +43,11 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C const KEY_NUMBER_OF_DOWNLOADS = 'number_of_downloads'; const KEY_LINK_TYPE = 'link_type'; const KEY_LINK_FILE = 'link_file'; + const KEY_LINK_FILE_CONTENT = 'link_file_content'; const KEY_LINK_URL = 'link_url'; const KEY_SAMPLE_TYPE = 'sample_type'; const KEY_SAMPLE_FILE = 'sample_file'; + const KEY_SAMPLE_FILE_CONTENT = 'sample_file_content'; const KEY_SAMPLE_URL = 'sample_url'; /**#@-*/ @@ -213,6 +215,16 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C return $this->getData(self::KEY_LINK_FILE); } + /** + * Return file content + * + * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null + */ + public function getLinkFileContent() + { + return $this->getData(self::KEY_LINK_FILE_CONTENT); + } + /** * {@inheritdoc} * @codeCoverageIgnore @@ -240,6 +252,16 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C return $this->getData(self::KEY_SAMPLE_FILE); } + /** + * Return sample file content when type is 'file' + * + * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null relative file path + */ + public function getSampleFileContent() + { + return $this->getData(self::KEY_SAMPLE_FILE_CONTENT); + } + /** * {@inheritdoc} * @codeCoverageIgnore @@ -320,6 +342,17 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C return $this->setData(self::KEY_LINK_FILE, $linkFile); } + /** + * Set file content + * + * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent + * @return $this + */ + public function setLinkFileContent(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent = null) + { + return $this->setData(self::KEY_LINK_FILE_CONTENT, $linkFileContent); + } + /** * Set URL * @@ -351,6 +384,19 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C return $this->setData(self::KEY_SAMPLE_FILE, $sampleFile); } + /** + * Set sample file content + * + * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent + * @return $this + */ + public function setSampleFileContent( + \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent = null + ) { + return $this->setData(self::KEY_SAMPLE_FILE_CONTENT, $sampleFileContent); + } + + /** * Set URL * diff --git a/app/code/Magento/Downloadable/Model/Link/Content.php b/app/code/Magento/Downloadable/Model/Link/Content.php deleted file mode 100644 index a40f76faf1cff5c353a8406943c9b51b4f95e428..0000000000000000000000000000000000000000 --- a/app/code/Magento/Downloadable/Model/Link/Content.php +++ /dev/null @@ -1,268 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Downloadable\Model\Link; - -use Magento\Downloadable\Api\Data\LinkContentInterface; - -/** - * @codeCoverageIgnore - */ -class Content extends \Magento\Framework\Model\AbstractExtensibleModel implements LinkContentInterface -{ - const TITLE = 'title'; - const PRICE = 'price'; - const NUMBER_OF_DOWNLOADS = 'number_of_downloads'; - const SHAREABLE = 'shareable'; - const SORT_ORDER = 'sort_order'; - const LINK_FILE = 'link_file'; - const LINK_URL = 'link_url'; - const LINK_TYPE = 'link_type'; - const SAMPLE_FILE = 'sample_file'; - const SAMPLE_URL = 'sample_url'; - const SAMPLE_TYPE = 'sample_type'; - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getTitle() - { - return $this->getData(self::TITLE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSortOrder() - { - return $this->getData(self::SORT_ORDER); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getPrice() - { - return $this->getData(self::PRICE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getNumberOfDownloads() - { - return $this->getData(self::NUMBER_OF_DOWNLOADS); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function isShareable() - { - return $this->getData(self::SHAREABLE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getLinkFile() - { - return $this->getData(self::LINK_FILE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getLinkUrl() - { - return $this->getData(self::LINK_URL); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getLinkType() - { - return $this->getData(self::LINK_TYPE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSampleFile() - { - return $this->getData(self::SAMPLE_FILE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSampleUrl() - { - return $this->getData(self::SAMPLE_URL); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSampleType() - { - return $this->getData(self::SAMPLE_TYPE); - } - - /** - * Set sample title - * - * @param string $title - * @return $this - */ - public function setTitle($title) - { - return $this->setData(self::TITLE, $title); - } - - /** - * Set sample sort order - * - * @param int $sortOrder - * @return $this - */ - public function setSortOrder($sortOrder) - { - return $this->setData(self::SORT_ORDER, $sortOrder); - } - - /** - * Set link price - * - * @param string $price - * @return $this - */ - public function setPrice($price) - { - return $this->setData(self::PRICE, $price); - } - - /** - * Set number of allowed downloads of the link - * - * @param int $numberOfDownloads - * @return $this - */ - public function setNumberOfDownloads($numberOfDownloads) - { - return $this->setData(self::NUMBER_OF_DOWNLOADS, $numberOfDownloads); - } - - /** - * Set whether link is shareable - * - * @param bool $shareable - * @return $this - */ - public function setShareable($shareable) - { - return $this->setData(self::SHAREABLE, $shareable); - } - - /** - * Set link file content - * - * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFile - * @return $this - */ - public function setLinkFile(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFile = null) - { - return $this->setData(self::LINK_FILE, $linkFile); - } - - /** - * Set link URL - * - * @param string $linkUrl - * @return $this - */ - public function setLinkUrl($linkUrl) - { - return $this->setData(self::LINK_URL, $linkUrl); - } - - /** - * Set link type ('url' or 'file') - * - * @param string $linkType - * @return $this - */ - public function setLinkType($linkType) - { - return $this->setData(self::LINK_TYPE, $linkType); - } - - /** - * Retrieve sample file content - * - * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile - * @return $this - */ - public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null) - { - return $this->setData(self::SAMPLE_FILE, $sampleFile); - } - - /** - * Set sample URL - * - * @param string $sampleUrl - * @return $this - */ - public function setSampleUrl($sampleUrl) - { - return $this->setData(self::SAMPLE_URL, $sampleUrl); - } - - /** - * Set sample type ('url' or 'file') - * - * @param string $sampleType - * @return $this - */ - public function setSampleType($sampleType) - { - return $this->setData(self::SAMPLE_TYPE, $sampleType); - } - - /** - * {@inheritdoc} - * - * @return \Magento\Downloadable\Api\Data\LinkContentExtensionInterface|null - */ - public function getExtensionAttributes() - { - return $this->_getExtensionAttributes(); - } - - /** - * {@inheritdoc} - * - * @param \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes - * @return $this - */ - public function setExtensionAttributes( - \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes - ) { - return $this->_setExtensionAttributes($extensionAttributes); - } -} diff --git a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php index 708302b7871c21c7bd1921b7a522019cbcd25aef..f1bf1d2dcbe85f819a0af7091d8806bee9cdcc12 100644 --- a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php @@ -5,7 +5,7 @@ */ namespace Magento\Downloadable\Model\Link; -use Magento\Downloadable\Api\Data\LinkContentInterface; +use Magento\Downloadable\Api\Data\LinkInterface; use Magento\Downloadable\Model\File\ContentValidator as FileContentValidator; use Magento\Framework\Exception\InputException; use Magento\Framework\Url\Validator as UrlValidator; @@ -37,43 +37,50 @@ class ContentValidator /** * Check if link content is valid * - * @param LinkContentInterface $linkContent + * @param LinkInterface $link + * @param bool $validateLinkContent + * @param bool $validateSampleContent * @return bool * @throws InputException */ - public function isValid(LinkContentInterface $linkContent) + public function isValid(LinkInterface $link, $validateLinkContent = true, $validateSampleContent = true) { - if (!is_numeric($linkContent->getPrice()) || $linkContent->getPrice() < 0) { + if (!is_numeric($link->getPrice()) || $link->getPrice() < 0) { throw new InputException(__('Link price must have numeric positive value.')); } - if (!is_int($linkContent->getNumberOfDownloads()) || $linkContent->getNumberOfDownloads() < 0) { + if (!is_int($link->getNumberOfDownloads()) || $link->getNumberOfDownloads() < 0) { throw new InputException(__('Number of downloads must be a positive integer.')); } - if (!is_int($linkContent->getSortOrder()) || $linkContent->getSortOrder() < 0) { + if (!is_int($link->getSortOrder()) || $link->getSortOrder() < 0) { throw new InputException(__('Sort order must be a positive integer.')); } - $this->validateLinkResource($linkContent); - $this->validateSampleResource($linkContent); + if ($validateLinkContent) { + $this->validateLinkResource($link); + } + if ($validateSampleContent) { + $this->validateSampleResource($link); + } return true; } /** * Validate link resource (file or URL) * - * @param LinkContentInterface $linkContent + * @param LinkInterface $link * @throws InputException * @return void */ - protected function validateLinkResource(LinkContentInterface $linkContent) + protected function validateLinkResource(LinkInterface $link) { - if ($linkContent->getLinkType() == 'url' - && !$this->urlValidator->isValid($linkContent->getLinkUrl()) + if ($link->getLinkType() == 'url' + && !$this->urlValidator->isValid($link->getLinkUrl()) ) { throw new InputException(__('Link URL must have valid format.')); } - if ($linkContent->getLinkType() == 'file' - && (!$linkContent->getLinkFile() || !$this->fileContentValidator->isValid($linkContent->getLinkFile())) + if ($link->getLinkType() == 'file' + && (!$link->getLinkFileContent() + || !$this->fileContentValidator->isValid($link->getLinkFileContent())) ) { throw new InputException(__('Provided file content must be valid base64 encoded data.')); } @@ -82,19 +89,20 @@ class ContentValidator /** * Validate sample resource (file or URL) * - * @param LinkContentInterface $linkContent + * @param LinkInterface $link * @throws InputException * @return void */ - protected function validateSampleResource(LinkContentInterface $linkContent) + protected function validateSampleResource(LinkInterface $link) { - if ($linkContent->getSampleType() == 'url' - && !$this->urlValidator->isValid($linkContent->getSampleUrl()) + if ($link->getSampleType() == 'url' + && !$this->urlValidator->isValid($link->getSampleUrl()) ) { throw new InputException(__('Sample URL must have valid format.')); } - if ($linkContent->getSampleType() == 'file' - && (!$linkContent->getSampleFile() || !$this->fileContentValidator->isValid($linkContent->getSampleFile())) + if ($link->getSampleType() == 'file' + && (!$link->getSampleFileContent() + || !$this->fileContentValidator->isValid($link->getSampleFileContent())) ) { throw new InputException(__('Provided file content must be valid base64 encoded data.')); } diff --git a/app/code/Magento/Downloadable/Model/LinkRepository.php b/app/code/Magento/Downloadable/Model/LinkRepository.php index 426e8bf63f21ed644dae4394470c8bf71d6482b4..bca06682ba718dee04cdd59e0edbfc275cc18efb 100644 --- a/app/code/Magento/Downloadable/Model/LinkRepository.php +++ b/app/code/Magento/Downloadable/Model/LinkRepository.php @@ -5,7 +5,7 @@ */ namespace Magento\Downloadable\Model; -use Magento\Downloadable\Api\Data\LinkContentInterface; +use Magento\Downloadable\Api\Data\LinkInterface; use Magento\Downloadable\Api\Data\File\ContentUploaderInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; @@ -92,9 +92,18 @@ class LinkRepository implements \Magento\Downloadable\Api\LinkRepositoryInterfac */ public function getLinks($sku) { - $linkList = []; /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($sku); + return $this->getLinksByProduct($product); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return array + */ + public function getLinksByProduct(\Magento\Catalog\Api\Data\ProductInterface $product) + { + $linkList = []; $links = $this->downloadableType->getLinks($product); /** @var \Magento\Downloadable\Model\Link $link */ foreach ($links as $link) { @@ -152,9 +161,17 @@ class LinkRepository implements \Magento\Downloadable\Api\LinkRepositoryInterfac */ public function getSamples($sku) { - $sampleList = []; - /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($sku); + return $this->getSamplesByProduct($product); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return array + */ + public function getSamplesByProduct(\Magento\Catalog\Api\Data\ProductInterface $product) + { + $sampleList = []; $samples = $this->downloadableType->getSamples($product); /** @var \Magento\Downloadable\Model\Sample $sample */ foreach ($samples as $sample) { @@ -181,113 +198,149 @@ class LinkRepository implements \Magento\Downloadable\Api\LinkRepositoryInterfac * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function save($sku, LinkContentInterface $linkContent, $linkId = null, $isGlobalScopeContent = false) + public function save($sku, LinkInterface $link, $isGlobalScopeContent = false) { $product = $this->productRepository->get($sku, true); - if ($linkId) { - - /** @var $link \Magento\Downloadable\Model\Link */ - $link = $this->linkFactory->create()->load($linkId); - if (!$link->getId()) { - throw new NoSuchEntityException(__('There is no downloadable link with provided ID.')); - } - if ($link->getProductId() != $product->getId()) { - throw new InputException(__('Provided downloadable link is not related to given product.')); - } - if (!$this->contentValidator->isValid($linkContent)) { - throw new InputException(__('Provided link information is invalid.')); - } - if ($isGlobalScopeContent) { - $product->setStoreId(0); - } - $title = $linkContent->getTitle(); - if (empty($title)) { - if ($isGlobalScopeContent) { - throw new InputException(__('Link title cannot be empty.')); - } - // use title from GLOBAL scope - $link->setTitle(null); - } else { - $link->setTitle($linkContent->getTitle()); - } - - $link->setProductId($product->getId()) - ->setStoreId($product->getStoreId()) - ->setWebsiteId($product->getStore()->getWebsiteId()) - ->setProductWebsiteIds($product->getWebsiteIds()) - ->setSortOrder($linkContent->getSortOrder()) - ->setPrice($linkContent->getPrice()) - ->setIsShareable($linkContent->isShareable()) - ->setNumberOfDownloads($linkContent->getNumberOfDownloads()) - ->save(); - return $link->getId(); + if ($link->getId() !== null) { + return $this->updateLink($product, $link, $isGlobalScopeContent); } else { - $product = $this->productRepository->get($sku, true); if ($product->getTypeId() !== \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { throw new InputException(__('Product type of the product must be \'downloadable\'.')); } - if (!$this->contentValidator->isValid($linkContent)) { + if (!$this->contentValidator->isValid($link)) { throw new InputException(__('Provided link information is invalid.')); } - if (!in_array($linkContent->getLinkType(), ['url', 'file'])) { + if (!in_array($link->getLinkType(), ['url', 'file'])) { throw new InputException(__('Invalid link type.')); } - $title = $linkContent->getTitle(); + $title = $link->getTitle(); if (empty($title)) { throw new InputException(__('Link title cannot be empty.')); } + return $this->saveLink($product, $link, $isGlobalScopeContent); + } + } - $linkData = [ - 'link_id' => 0, - 'is_delete' => 0, - 'type' => $linkContent->getLinkType(), - 'sort_order' => $linkContent->getSortOrder(), - 'title' => $linkContent->getTitle(), - 'price' => $linkContent->getPrice(), - 'number_of_downloads' => $linkContent->getNumberOfDownloads(), - 'is_shareable' => $linkContent->isShareable(), - ]; + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param LinkInterface $link + * @param bool $isGlobalScopeContent + * @return int + */ + protected function saveLink( + \Magento\Catalog\Api\Data\ProductInterface $product, + LinkInterface $link, + $isGlobalScopeContent + ) { + $linkData = [ + 'link_id' => $link->getid() === null ? 0 : $link->getid(), + 'is_delete' => 0, + 'type' => $link->getLinkType(), + 'sort_order' => $link->getSortOrder(), + 'title' => $link->getTitle(), + 'price' => $link->getPrice(), + 'number_of_downloads' => $link->getNumberOfDownloads(), + 'is_shareable' => $link->getIsShareable(), + ]; - if ($linkContent->getLinkType() == 'file') { - $linkData['file'] = $this->jsonEncoder->encode( + if ($link->getLinkType() == 'file' && $link->getLinkFile() === null) { + $linkData['file'] = $this->jsonEncoder->encode( + [ + $this->fileContentUploader->upload($link->getLinkFileContent(), 'link_file'), + ] + ); + } elseif ($link->getLinkType() === 'url') { + $linkData['link_url'] = $link->getLinkUrl(); + } else { + //existing link file + $linkData['file'] = $this->jsonEncoder->encode( + [ [ - $this->fileContentUploader->upload($linkContent->getLinkFile(), 'link_file'), + 'file' => $link->getLinkFile(), + 'status' => 'old', ] - ); - } else { - $linkData['link_url'] = $linkContent->getLinkUrl(); - } + ] + ); + } - if ($linkContent->getSampleType() == 'file') { - $linkData['sample']['type'] = 'file'; - $linkData['sample']['file'] = $this->jsonEncoder->encode( - [ - $this->fileContentUploader->upload($linkContent->getSampleFile(), 'link_sample_file'), - ] - ); - } elseif ($linkContent->getSampleType() == 'url') { - $linkData['sample']['type'] = 'url'; - $linkData['sample']['url'] = $linkContent->getSampleUrl(); - } + if ($link->getSampleType() == 'file' && $link->getSampleFile() === null) { + $linkData['sample']['type'] = 'file'; + $linkData['sample']['file'] = $this->jsonEncoder->encode( + [ + $this->fileContentUploader->upload($link->getSampleFileContent(), 'link_sample_file'), + ] + ); + } elseif ($link->getSampleType() == 'url') { + $linkData['sample']['type'] = 'url'; + $linkData['sample']['url'] = $link->getSampleUrl(); + } - $downloadableData = ['link' => [$linkData]]; - $product->setDownloadableData($downloadableData); + $downloadableData = ['link' => [$linkData]]; + $product->setDownloadableData($downloadableData); + if ($isGlobalScopeContent) { + $product->setStoreId(0); + } + $this->downloadableType->save($product); + return $product->getLastAddedLinkId(); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param LinkInterface $link + * @param bool $isGlobalScopeContent + * @return mixed + * @throws InputException + * @throws NoSuchEntityException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + protected function updateLink( + \Magento\Catalog\Api\Data\ProductInterface $product, + LinkInterface $link, + $isGlobalScopeContent + ) { + /** @var $existingLink \Magento\Downloadable\Model\Link */ + $existingLink = $this->linkFactory->create()->load($link->getId()); + if (!$existingLink->getId()) { + throw new NoSuchEntityException(__('There is no downloadable link with provided ID.')); + } + if ($existingLink->getProductId() != $product->getId()) { + throw new InputException(__('Provided downloadable link is not related to given product.')); + } + $validateLinkContent = $link->getLinkFileContent() === null ? false : true; + $validateSampleContent = $link->getSampleFileContent() === null ? false : true; + if (!$this->contentValidator->isValid($link, $validateLinkContent, $validateSampleContent)) { + throw new InputException(__('Provided link information is invalid.')); + } + if ($isGlobalScopeContent) { + $product->setStoreId(0); + } + $title = $link->getTitle(); + if (empty($title)) { if ($isGlobalScopeContent) { - $product->setStoreId(0); + throw new InputException(__('Link title cannot be empty.')); } - $product->save(); - return $product->getLastAddedLinkId(); } + + if ($link->getLinkType() == 'file' && $link->getLinkFileContent() === null) { + $link->setLinkFile($existingLink->getLinkFile()); + } + if ($link->getSampleType() == 'file' && $link->getSampleFileContent() === null) { + $link->setSampleFile($existingLink->getSampleFile()); + } + + $this->saveLink($product, $link, $isGlobalScopeContent); + return $existingLink->getId(); } /** * {@inheritdoc} */ - public function delete($linkId) + public function delete($id) { /** @var $link \Magento\Downloadable\Model\Link */ - $link = $this->linkFactory->create()->load($linkId); + $link = $this->linkFactory->create()->load($id); if (!$link->getId()) { throw new NoSuchEntityException(__('There is no downloadable link with provided ID.')); } diff --git a/app/code/Magento/Downloadable/Model/Observer.php b/app/code/Magento/Downloadable/Model/Observer.php index 0fd516bfa9fafc120ee467f52725eb0e974de334..68dc8f018d7f00aef5a9556f6622d518bb924142 100644 --- a/app/code/Magento/Downloadable/Model/Observer.php +++ b/app/code/Magento/Downloadable/Model/Observer.php @@ -114,10 +114,10 @@ class Observer //order not saved in the database return $this; } - $product = $orderItem->getProduct(); - if ($product && $product->getTypeId() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { + if ($orderItem->getProductType() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { return $this; } + $product = $orderItem->getProduct(); $purchasedLink = $this->_createPurchasedModel()->load($orderItem->getId(), 'order_item_id'); if ($purchasedLink->getId()) { return $this; diff --git a/app/code/Magento/Downloadable/Model/Plugin/AfterProductLoad.php b/app/code/Magento/Downloadable/Model/Plugin/AfterProductLoad.php new file mode 100644 index 0000000000000000000000000000000000000000..83466a0fa080f34d7aa3d38fbcfd0fd8905d1101 --- /dev/null +++ b/app/code/Magento/Downloadable/Model/Plugin/AfterProductLoad.php @@ -0,0 +1,62 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Downloadable\Model\Plugin; + +class AfterProductLoad +{ + /** + * @var \Magento\Downloadable\Api\LinkRepositoryInterface + */ + protected $linkRepository; + + /** + * @var \Magento\Catalog\Api\Data\ProductExtensionFactory + */ + protected $productExtensionFactory; + + /** + * @param \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository + * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory + */ + public function __construct( + \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository, + \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory + ) { + $this->linkRepository = $linkRepository; + $this->productExtensionFactory = $productExtensionFactory; + } + + /** + * @param \Magento\Catalog\Model\Product $product + * @return \Magento\Catalog\Model\Product + */ + public function afterLoad( + \Magento\Catalog\Model\Product $product + ) { + if ($product->getTypeId() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { + return $product; + } + + $productExtension = $product->getExtensionAttributes(); + if ($productExtension === null) { + $productExtension = $this->productExtensionFactory->create(); + } + $links = $this->linkRepository->getLinksByProduct($product); + if ($links !== null) { + $productExtension->setDownloadableProductLinks($links); + } + $samples = $this->linkRepository->getSamplesByProduct($product); + if ($samples !== null) { + $productExtension->setDownloadableProductSamples($samples); + } + + $product->setExtensionAttributes($productExtension); + + return $product; + } +} diff --git a/app/code/Magento/Downloadable/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/Downloadable/Model/Plugin/AroundProductRepositorySave.php new file mode 100644 index 0000000000000000000000000000000000000000..13dd932c3a7683109c4fcef022556abb0dbfa519 --- /dev/null +++ b/app/code/Magento/Downloadable/Model/Plugin/AroundProductRepositorySave.php @@ -0,0 +1,145 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Downloadable\Model\Plugin; + +class AroundProductRepositorySave +{ + /** + * @var \Magento\Downloadable\Api\LinkRepositoryInterface + */ + protected $linkRepository; + + /** + * @var \Magento\Downloadable\Api\SampleRepositoryInterface + */ + protected $sampleRepository; + + /** + * @param \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository + * @param \Magento\Downloadable\Api\SampleRepositoryInterface $sampleRepository + */ + public function __construct( + \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository, + \Magento\Downloadable\Api\SampleRepositoryInterface $sampleRepository + ) { + $this->linkRepository = $linkRepository; + $this->sampleRepository = $sampleRepository; + } + + /** + * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject + * @param callable $proceed + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param bool $saveOptions + * @return \Magento\Catalog\Api\Data\ProductInterface + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function aroundSave( + \Magento\Catalog\Api\ProductRepositoryInterface $subject, + \Closure $proceed, + \Magento\Catalog\Api\Data\ProductInterface $product, + $saveOptions = false + ) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $result */ + $result = $proceed($product, $saveOptions); + + if ($product->getTypeId() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { + return $result; + } + + /* @var \Magento\Catalog\Api\Data\ProductExtensionInterface $options */ + $extendedAttributes = $product->getExtensionAttributes(); + if ($extendedAttributes === null) { + return $result; + } + $links = $extendedAttributes->getDownloadableProductLinks(); + $samples = $extendedAttributes->getDownloadableProductSamples(); + + if ($links === null && $samples === null) { + return $result; + } + + if ($links !== null) { + $this->saveLinks($result, $links); + } + if ($samples !== null) { + $this->saveSamples($result, $samples); + } + + return $subject->get($result->getSku(), false, $result->getStoreId(), true); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param \Magento\Downloadable\Api\Data\LinkInterface[] $links + * @return $this + */ + protected function saveLinks(\Magento\Catalog\Api\Data\ProductInterface $product, array $links) + { + $existingLinkIds = []; + //get existing links from extension attribute + $extensionAttributes = $product->getExtensionAttributes(); + if ($extensionAttributes !== null) { + $existingLinks = $extensionAttributes->getDownloadableProductLinks(); + if ($existingLinks !== null) { + foreach ($existingLinks as $existingLink) { + $existingLinkIds[] = $existingLink->getId(); + } + } + } + + $updatedLinkIds = []; + foreach ($links as $link) { + $linkId = $link->getId(); + if ($linkId) { + $updatedLinkIds[] = $linkId; + } + $this->linkRepository->save($product->getSku(), $link); + } + $linkIdsToDelete = array_diff($existingLinkIds, $updatedLinkIds); + + foreach ($linkIdsToDelete as $linkId) { + $this->linkRepository->delete($linkId); + } + return $this; + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param \Magento\Downloadable\Api\Data\SampleInterface[] $samples + * @return $this + */ + protected function saveSamples(\Magento\Catalog\Api\Data\ProductInterface $product, array $samples) + { + $existingSampleIds = []; + $extensionAttributes = $product->getExtensionAttributes(); + if ($extensionAttributes !== null) { + $existingSamples = $extensionAttributes->getDownloadableProductSamples(); + if ($existingSamples !== null) { + foreach ($existingSamples as $existingSample) { + $existingSampleIds[] = $existingSample->getId(); + } + } + } + + $updatedSampleIds = []; + foreach ($samples as $sample) { + $sampleId = $sample->getId(); + if ($sampleId) { + $updatedSampleIds[] = $sampleId; + } + $this->sampleRepository->save($product->getSku(), $sample); + } + $sampleIdsToDelete = array_diff($existingSampleIds, $updatedSampleIds); + + foreach ($sampleIdsToDelete as $sampleId) { + $this->sampleRepository->delete($sampleId); + } + return $this; + } +} diff --git a/app/code/Magento/Downloadable/Model/Sample.php b/app/code/Magento/Downloadable/Model/Sample.php index 07dd821050aad1d60ae18abc38211afdd1fb4a55..4909fe6a39e649e071b47859b77a136eb31a1241 100644 --- a/app/code/Magento/Downloadable/Model/Sample.php +++ b/app/code/Magento/Downloadable/Model/Sample.php @@ -27,6 +27,7 @@ class Sample extends \Magento\Framework\Model\AbstractExtensibleModel implements const KEY_SORT_ORDER = 'sort_order'; const KEY_SAMPLE_TYPE = 'sample_type'; const KEY_SAMPLE_FILE = 'sample_file'; + const KEY_SAMPLE_FILE_CONTENT = 'sample_file_content'; const KEY_SAMPLE_URL = 'sample_url'; /**#@-*/ @@ -163,6 +164,15 @@ class Sample extends \Magento\Framework\Model\AbstractExtensibleModel implements return $this->getData(self::KEY_SAMPLE_FILE); } + /** + * {@inheritdoc} + * @codeCoverageIgnore + */ + public function getSampleFileContent() + { + return $this->getData(self::KEY_SAMPLE_FILE_CONTENT); + } + /** * {@inheritdoc} * @codeCoverageIgnore @@ -214,6 +224,17 @@ class Sample extends \Magento\Framework\Model\AbstractExtensibleModel implements return $this->setData(self::KEY_SAMPLE_FILE, $sampleFile); } + /** + * Set sample file content + * + * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent + * @return $this + */ + public function setSampleFileContent(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent = null) + { + return $this->setData(self::KEY_SAMPLE_FILE_CONTENT, $sampleFileContent); + } + /** * Set sample URL * diff --git a/app/code/Magento/Downloadable/Model/Sample/Content.php b/app/code/Magento/Downloadable/Model/Sample/Content.php deleted file mode 100644 index 0fcf613353966548cfbf6ec83e2ca47837ff9f54..0000000000000000000000000000000000000000 --- a/app/code/Magento/Downloadable/Model/Sample/Content.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Downloadable\Model\Sample; - -use Magento\Downloadable\Api\Data\SampleContentInterface; - -/** - * @codeCoverageIgnore - */ -class Content extends \Magento\Framework\Model\AbstractExtensibleModel implements SampleContentInterface -{ - const TITLE = 'title'; - const SORT_ORDER = 'sort_order'; - const SAMPLE_FILE = 'sample_file'; - const SAMPLE_URL = 'sample_url'; - const SAMPLE_TYPE = 'sample_type'; - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getTitle() - { - return $this->getData(self::TITLE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSampleType() - { - return $this->getData(self::SAMPLE_TYPE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSampleFile() - { - return $this->getData(self::SAMPLE_FILE); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSortOrder() - { - return $this->getData(self::SORT_ORDER); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function getSampleUrl() - { - return $this->getData(self::SAMPLE_URL); - } - - /** - * Set sample title - * - * @param string $title - * @return $this - */ - public function setTitle($title) - { - return $this->setData(self::TITLE, $title); - } - - /** - * Set sample type ('url' or 'file') - * - * @param string $sampleType - * @return $this - */ - public function setSampleType($sampleType) - { - return $this->setData(self::SAMPLE_TYPE, $sampleType); - } - - /** - * Set sample file content - * - * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile - * @return $this - */ - public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null) - { - return $this->setData(self::SAMPLE_FILE, $sampleFile); - } - - /** - * Set sample sort order - * - * @param int $sortOrder - * @return $this - */ - public function setSortOrder($sortOrder) - { - return $this->setData(self::SORT_ORDER, $sortOrder); - } - - /** - * Set sample URL - * - * @param string $sampleUrl - * @return $this - */ - public function setSampleUrl($sampleUrl) - { - return $this->setData(self::SAMPLE_URL, $sampleUrl); - } - - /** - * {@inheritdoc} - * - * @return \Magento\Downloadable\Api\Data\SampleContentExtensionInterface|null - */ - public function getExtensionAttributes() - { - return $this->_getExtensionAttributes(); - } - - /** - * {@inheritdoc} - * - * @param \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes - * @return $this - */ - public function setExtensionAttributes( - \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes - ) { - return $this->_setExtensionAttributes($extensionAttributes); - } -} diff --git a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php index 294bd783d463560a5846eada1cc400f5e6a97ce7..83f44d1a5c1b07c20229193db238f53d93cfad6f 100644 --- a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php @@ -5,7 +5,7 @@ */ namespace Magento\Downloadable\Model\Sample; -use Magento\Downloadable\Api\Data\SampleContentInterface; +use Magento\Downloadable\Api\Data\SampleInterface; use Magento\Downloadable\Model\File\ContentValidator as FileContentValidator; use Magento\Framework\Exception\InputException; use Magento\Framework\Url\Validator as UrlValidator; @@ -37,38 +37,41 @@ class ContentValidator /** * Check if sample content is valid * - * @param SampleContentInterface $sampleContent + * @param SampleInterface $sample + * @param bool $validateSampleContent * @return bool * @throws InputException */ - public function isValid(SampleContentInterface $sampleContent) + public function isValid(SampleInterface $sample, $validateSampleContent = true) { - if (!is_int($sampleContent->getSortOrder()) || $sampleContent->getSortOrder() < 0) { + if (!is_int($sample->getSortOrder()) || $sample->getSortOrder() < 0) { throw new InputException(__('Sort order must be a positive integer.')); } - $this->validateSampleResource($sampleContent); + if ($validateSampleContent) { + $this->validateSampleResource($sample); + } return true; } /** * Validate sample resource (file or URL) * - * @param SampleContentInterface $sampleContent + * @param SampleInterface $sample * @throws InputException * @return void */ - protected function validateSampleResource(SampleContentInterface $sampleContent) + protected function validateSampleResource(SampleInterface $sample) { - $sampleFile = $sampleContent->getSampleFile(); - if ($sampleContent->getSampleType() == 'file' + $sampleFile = $sample->getSampleFileContent(); + if ($sample->getSampleType() == 'file' && (!$sampleFile || !$this->fileContentValidator->isValid($sampleFile)) ) { throw new InputException(__('Provided file content must be valid base64 encoded data.')); } - if ($sampleContent->getSampleType() == 'url' - && !$this->urlValidator->isValid($sampleContent->getSampleUrl()) + if ($sample->getSampleType() == 'url' + && !$this->urlValidator->isValid($sample->getSampleUrl()) ) { throw new InputException(__('Sample URL must have valid format.')); } diff --git a/app/code/Magento/Downloadable/Model/SampleRepository.php b/app/code/Magento/Downloadable/Model/SampleRepository.php index b97702bd9adaf8c143dbf0f62823c460121fe02c..f357e6a5d8541ce450fab6ee85cdb782dd8fcc4e 100644 --- a/app/code/Magento/Downloadable/Model/SampleRepository.php +++ b/app/code/Magento/Downloadable/Model/SampleRepository.php @@ -8,12 +8,17 @@ namespace Magento\Downloadable\Model; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Downloadable\Model\SampleFactory; use Magento\Downloadable\Api\Data\File\ContentUploaderInterface; -use Magento\Downloadable\Api\Data\SampleContentInterface; +use Magento\Downloadable\Api\Data\SampleInterface; use Magento\Downloadable\Model\Sample\ContentValidator; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Json\EncoderInterface; +/** + * Class SampleRepository + * @package Magento\Downloadable\Model + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInterface { /** @@ -21,6 +26,11 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte */ protected $productRepository; + /** + * @var \Magento\Downloadable\Model\Product\Type + */ + protected $downloadableType; + /** * @var ContentValidator */ @@ -38,6 +48,7 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte /** * @param ProductRepositoryInterface $productRepository + * @param \Magento\Downloadable\Model\Product\Type $downloadableType * @param ContentValidator $contentValidator * @param ContentUploaderInterface $fileContentUploader * @param EncoderInterface $jsonEncoder @@ -45,12 +56,14 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte */ public function __construct( ProductRepositoryInterface $productRepository, + \Magento\Downloadable\Model\Product\Type $downloadableType, ContentValidator $contentValidator, ContentUploaderInterface $fileContentUploader, EncoderInterface $jsonEncoder, SampleFactory $sampleFactory ) { $this->productRepository = $productRepository; + $this->downloadableType = $downloadableType; $this->contentValidator = $contentValidator; $this->fileContentUploader = $fileContentUploader; $this->jsonEncoder = $jsonEncoder; @@ -58,106 +71,153 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte } /** - * {@inheritdoc} + * Update downloadable sample of the given product + * + * @param string $sku + * @param \Magento\Downloadable\Api\Data\SampleInterface $sample + * @param bool $isGlobalScopeContent + * @return int * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function save( - $productSku, - SampleContentInterface $sampleContent, - $sampleId = null, + $sku, + SampleInterface $sample, $isGlobalScopeContent = false ) { - $product = $this->productRepository->get($productSku, true); + $product = $this->productRepository->get($sku, true); + $sampleId = $sample->getId(); if ($sampleId) { - - /** @var $sample \Magento\Downloadable\Model\Sample */ - $sample = $this->sampleFactory->create()->load($sampleId); - - if (!$sample->getId()) { - throw new NoSuchEntityException(__('There is no downloadable sample with provided ID.')); - } - - if ($sample->getProductId() != $product->getId()) { - throw new InputException(__('Provided downloadable sample is not related to given product.')); - } - if (!$this->contentValidator->isValid($sampleContent)) { - throw new InputException(__('Provided sample information is invalid.')); - } - if ($isGlobalScopeContent) { - $product->setStoreId(0); - } - - $title = $sampleContent->getTitle(); - if (empty($title)) { - if ($isGlobalScopeContent) { - throw new InputException(__('Sample title cannot be empty.')); - } - // use title from GLOBAL scope - $sample->setTitle(null); - } else { - $sample->setTitle($sampleContent->getTitle()); - } - - $sample->setProductId($product->getId()) - ->setStoreId($product->getStoreId()) - ->setSortOrder($sampleContent->getSortOrder()) - ->save(); - - return $sample->getId(); + return $this->updateSample($product, $sample, $isGlobalScopeContent); } else { - if ($product->getTypeId() !== \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { throw new InputException(__('Product type of the product must be \'downloadable\'.')); } - if (!$this->contentValidator->isValid($sampleContent)) { + if (!$this->contentValidator->isValid($sample)) { throw new InputException(__('Provided sample information is invalid.')); } - if (!in_array($sampleContent->getSampleType(), ['url', 'file'])) { + if (!in_array($sample->getSampleType(), ['url', 'file'])) { throw new InputException(__('Invalid sample type.')); } - $title = $sampleContent->getTitle(); + $title = $sample->getTitle(); if (empty($title)) { throw new InputException(__('Sample title cannot be empty.')); } - $sampleData = [ - 'sample_id' => 0, - 'is_delete' => 0, - 'type' => $sampleContent->getSampleType(), - 'sort_order' => $sampleContent->getSortOrder(), - 'title' => $sampleContent->getTitle(), - ]; + return $this->saveSample($product, $sample, $isGlobalScopeContent); + } + } - if ($sampleContent->getSampleType() == 'file') { - $sampleData['file'] = $this->jsonEncoder->encode( + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param SampleInterface $sample + * @param bool $isGlobalScopeContent + * @return int + */ + protected function saveSample( + \Magento\Catalog\Api\Data\ProductInterface $product, + SampleInterface $sample, + $isGlobalScopeContent + ) { + $sampleData = [ + 'sample_id' => $sample->getid() === null ? 0 : $sample->getid(), + 'is_delete' => 0, + 'type' => $sample->getSampleType(), + 'sort_order' => $sample->getSortOrder(), + 'title' => $sample->getTitle(), + ]; + + if ($sample->getSampleType() == 'file' && $sample->getSampleFile() === null) { + $sampleData['file'] = $this->jsonEncoder->encode( + [ + $this->fileContentUploader->upload($sample->getSampleFileContent(), 'sample'), + ] + ); + } elseif ($sample->getSampleType() === 'url') { + $sampleData['sample_url'] = $sample->getSampleUrl(); + } else { + //existing file + $sampleData['file'] = $this->jsonEncoder->encode( + [ [ - $this->fileContentUploader->upload($sampleContent->getSampleFile(), 'sample'), - ] - ); - } else { - $sampleData['sample_url'] = $sampleContent->getSampleUrl(); - } + 'file' => $sample->getSampleFile(), + 'status' => 'old', + ], + ] + ); + } + + $downloadableData = ['sample' => [$sampleData]]; + $product->setDownloadableData($downloadableData); + if ($isGlobalScopeContent) { + $product->setStoreId(0); + } + $this->downloadableType->save($product); + return $product->getLastAddedSampleId(); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param SampleInterface $sample + * @param bool $isGlobalScopeContent + * @return int + * @throws InputException + * @throws NoSuchEntityException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + protected function updateSample( + \Magento\Catalog\Api\Data\ProductInterface $product, + SampleInterface $sample, + $isGlobalScopeContent + ) { + $sampleId = $sample->getId(); + /** @var $existingSample \Magento\Downloadable\Model\Sample */ + $existingSample = $this->sampleFactory->create()->load($sampleId); - $downloadableData = ['sample' => [$sampleData]]; - $product->setDownloadableData($downloadableData); + if (!$existingSample->getId()) { + throw new NoSuchEntityException(__('There is no downloadable sample with provided ID.')); + } + + if ($existingSample->getProductId() != $product->getId()) { + throw new InputException(__('Provided downloadable sample is not related to given product.')); + } + + $validateFileContent = $sample->getSampleFileContent() === null ? false : true; + if (!$this->contentValidator->isValid($sample, $validateFileContent)) { + throw new InputException(__('Provided sample information is invalid.')); + } + if ($isGlobalScopeContent) { + $product->setStoreId(0); + } + + $title = $sample->getTitle(); + if (empty($title)) { if ($isGlobalScopeContent) { - $product->setStoreId(0); + throw new InputException(__('Sample title cannot be empty.')); } - $product->save(); - return $product->getLastAddedSampleId(); + // use title from GLOBAL scope + $existingSample->setTitle(null); + } else { + $existingSample->setTitle($sample->getTitle()); + } + + if ($sample->getSampleType() === 'file' && $sample->getSampleFileContent() === null) { + $sample->setSampleFile($existingSample->getSampleFile()); } + $this->saveSample($product, $sample, $isGlobalScopeContent); + return $existingSample->getId(); } /** * {@inheritdoc} */ - public function delete($sampleId) + public function delete($id) { /** @var $sample \Magento\Downloadable\Model\Sample */ - $sample = $this->sampleFactory->create()->load($sampleId); + $sample = $this->sampleFactory->create()->load($id); if (!$sample->getId()) { throw new NoSuchEntityException(__('There is no downloadable sample with provided ID.')); } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php index 6e7a6555514b345535e784fc4b2e3abf8839c195..685d116b2b700cd10a286a22b604b435597ad3bb 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php @@ -52,12 +52,14 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase ); $this->linkFileMock = $this->getMock('\Magento\Downloadable\Api\Data\File\ContentInterface'); $this->sampleFileMock = $this->getMock('\Magento\Downloadable\Api\Data\File\ContentInterface'); - $this->validator = new \Magento\Downloadable\Model\Link\ContentValidator($this->fileValidatorMock, $this->urlValidatorMock); + $this->validator = new ContentValidator($this->fileValidatorMock, $this->urlValidatorMock); } public function testIsValid() { - $linkContentData = [ + $linkFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface'); + $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface'); + $linkData = [ 'title' => 'Title', 'sort_order' => 1, 'price' => 10.1, @@ -65,11 +67,53 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase 'number_of_downloads' => 100, 'link_type' => 'file', 'sample_type' => 'file', + 'link_file_content' => $linkFileContentMock, + 'sample_file_content' => $sampleFileContentMock, ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); - $contentMock = $this->getLinkContentMock($linkContentData); - $this->assertTrue($this->validator->isValid($contentMock)); + $linkMock = $this->getLinkMock($linkData); + $this->assertTrue($this->validator->isValid($linkMock)); + } + + public function testIsValidSkipLinkContent() + { + $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface'); + $linkData = [ + 'title' => 'Title', + 'sort_order' => 1, + 'price' => 10.1, + 'shareable' => true, + 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://example.com', + 'sample_type' => 'file', + 'sample_file_content' => $sampleFileContentMock, + ]; + $this->fileValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true)); + $this->urlValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true)); + $linkMock = $this->getLinkMock($linkData); + $this->assertTrue($this->validator->isValid($linkMock, false)); + } + + public function testIsValidSkipSampleContent() + { + $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface'); + $linkData = [ + 'title' => 'Title', + 'sort_order' => 1, + 'price' => 10.1, + 'shareable' => true, + 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://example.com', + 'sample_type' => 'file', + 'sample_file_content' => $sampleFileContentMock, + ]; + $this->fileValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true)); + $this->urlValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true)); + $linkMock = $this->getLinkMock($linkData); + $this->assertTrue($this->validator->isValid($linkMock, true, false)); } /** @@ -91,7 +135,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); - $contentMock = $this->getLinkContentMock($linkContentData); + $contentMock = $this->getLinkMock($linkContentData); $this->validator->isValid($contentMock); } @@ -126,7 +170,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); - $contentMock = $this->getLinkContentMock($linkContentData); + $contentMock = $this->getLinkMock($linkContentData); $this->validator->isValid($contentMock); } @@ -160,7 +204,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase ]; $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); - $contentMock = $this->getLinkContentMock($linkContentData); + $contentMock = $this->getLinkMock($linkContentData); $this->validator->isValid($contentMock); } @@ -177,51 +221,58 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase } /** - * @param array $linkContentData + * @param array $linkData * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getLinkContentMock(array $linkContentData) + protected function getLinkMock(array $linkData) { - $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkContentInterface'); - $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue( - $linkContentData['title'] + $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $linkMock->expects($this->any())->method('getTitle')->will($this->returnValue( + $linkData['title'] )); - $contentMock->expects($this->any())->method('getPrice')->will($this->returnValue( - $linkContentData['price'] + $linkMock->expects($this->any())->method('getPrice')->will($this->returnValue( + $linkData['price'] )); - $contentMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( - $linkContentData['sort_order'] + $linkMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( + $linkData['sort_order'] )); - $contentMock->expects($this->any())->method('isShareable')->will($this->returnValue( - $linkContentData['shareable'] + $linkMock->expects($this->any())->method('isShareable')->will($this->returnValue( + $linkData['shareable'] )); - $contentMock->expects($this->any())->method('getNumberOfDownloads')->will($this->returnValue( - $linkContentData['number_of_downloads'] + $linkMock->expects($this->any())->method('getNumberOfDownloads')->will($this->returnValue( + $linkData['number_of_downloads'] )); - $contentMock->expects($this->any())->method('getLinkType')->will($this->returnValue( - $linkContentData['link_type'] + $linkMock->expects($this->any())->method('getLinkType')->will($this->returnValue( + $linkData['link_type'] )); - $contentMock->expects($this->any())->method('getLinkFile')->will($this->returnValue( + $linkMock->expects($this->any())->method('getLinkFile')->will($this->returnValue( $this->linkFileMock )); - if (isset($linkContentData['link_url'])) { - $contentMock->expects($this->any())->method('getLinkUrl')->will($this->returnValue( - $linkContentData['link_url'] + if (isset($linkData['link_url'])) { + $linkMock->expects($this->any())->method('getLinkUrl')->will($this->returnValue( + $linkData['link_url'] )); } - if (isset($linkContentData['sample_url'])) { - $contentMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( - $linkContentData['sample_url'] + if (isset($linkData['sample_url'])) { + $linkMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( + $linkData['sample_url'] )); } - if (isset($linkContentData['sample_type'])) { - $contentMock->expects($this->any())->method('getSampleType')->will($this->returnValue( - $linkContentData['sample_type'] + if (isset($linkData['sample_type'])) { + $linkMock->expects($this->any())->method('getSampleType')->will($this->returnValue( + $linkData['sample_type'] )); } - $contentMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( + if (isset($linkData['link_file_content'])) { + $linkMock->expects($this->any())->method('getLinkFileContent')->willReturn($linkData['link_file_content']); + } + if (isset($linkData['sample_file_content'])) { + $linkMock->expects($this->any())->method('getSampleFileContent') + ->willReturn($linkData['sample_file_content']); + } + $linkMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( $this->sampleFileMock )); - return $contentMock; + return $linkMock; } } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php index 06c849f969cd47da2759747c05254ef143d2b759..d4ec184847edcbdbca8feb4e8a08c04f1b664766 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php @@ -128,69 +128,80 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase } /** - * @param array $linkContentData + * @param array $linkData * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getLinkContentMock(array $linkContentData) + protected function getLinkMock(array $linkData) { - $contentMock = $this->getMock( - '\Magento\Downloadable\Api\Data\LinkContentInterface', + $linkMock = $this->getMock( + '\Magento\Downloadable\Api\Data\LinkInterface', [], [], '', false ); - $contentMock->expects($this->any())->method('getPrice')->will( + if (isset($linkData['id'])) { + $linkMock->expects($this->any())->method('getId')->willReturn($linkData['id']); + } + + $linkMock->expects($this->any())->method('getPrice')->will( $this->returnValue( - $linkContentData['price'] + $linkData['price'] ) ); - $contentMock->expects($this->any())->method('getTitle')->will( + $linkMock->expects($this->any())->method('getTitle')->will( $this->returnValue( - $linkContentData['title'] + $linkData['title'] ) ); - $contentMock->expects($this->any())->method('getSortOrder')->will( + $linkMock->expects($this->any())->method('getSortOrder')->will( $this->returnValue( - $linkContentData['sort_order'] + $linkData['sort_order'] ) ); - $contentMock->expects($this->any())->method('getNumberOfDownloads')->will( + $linkMock->expects($this->any())->method('getNumberOfDownloads')->will( $this->returnValue( - $linkContentData['number_of_downloads'] + $linkData['number_of_downloads'] ) ); - $contentMock->expects($this->any())->method('isShareable')->will( + $linkMock->expects($this->any())->method('getIsShareable')->will( $this->returnValue( - $linkContentData['shareable'] + $linkData['is_shareable'] ) ); - if (isset($linkContentData['link_type'])) { - $contentMock->expects($this->any())->method('getLinkType')->will( + if (isset($linkData['link_type'])) { + $linkMock->expects($this->any())->method('getLinkType')->will( $this->returnValue( - $linkContentData['link_type'] + $linkData['link_type'] ) ); } - if (isset($linkContentData['link_url'])) { - $contentMock->expects($this->any())->method('getLinkUrl')->will( + if (isset($linkData['link_url'])) { + $linkMock->expects($this->any())->method('getLinkUrl')->will( $this->returnValue( - $linkContentData['link_url'] + $linkData['link_url'] ) ); } - return $contentMock; + if (isset($linkData['link_file'])) { + $linkMock->expects($this->any())->method('getLinkFile')->will( + $this->returnValue( + $linkData['link_file'] + ) + ); + } + return $linkMock; } public function testCreate() { $productSku = 'simple'; - $linkContentData = [ + $linkData = [ 'title' => 'Title', 'sort_order' => 1, 'price' => 10.1, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'url', 'link_url' => 'http://example.com/', @@ -198,8 +209,8 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable')); - $linkContentMock = $this->getLinkContentMock($linkContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock) + $linkMock = $this->getLinkMock($linkData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock) ->will($this->returnValue(true)); $this->productMock->expects($this->once())->method('setDownloadableData')->with( @@ -208,19 +219,20 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase [ 'link_id' => 0, 'is_delete' => 0, - 'type' => $linkContentData['link_type'], - 'sort_order' => $linkContentData['sort_order'], - 'title' => $linkContentData['title'], - 'price' => $linkContentData['price'], - 'number_of_downloads' => $linkContentData['number_of_downloads'], - 'is_shareable' => $linkContentData['shareable'], - 'link_url' => $linkContentData['link_url'], + 'type' => $linkData['link_type'], + 'sort_order' => $linkData['sort_order'], + 'title' => $linkData['title'], + 'price' => $linkData['price'], + 'number_of_downloads' => $linkData['number_of_downloads'], + 'is_shareable' => $linkData['is_shareable'], + 'link_url' => $linkData['link_url'], ], ], ] ); - $this->productMock->expects($this->once())->method('save'); - $this->service->save($productSku, $linkContentMock, null); + $this->productTypeMock->expects($this->once())->method('save') + ->with($this->productMock); + $this->service->save($productSku, $linkMock); } /** @@ -230,12 +242,12 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase public function testCreateThrowsExceptionIfTitleIsEmpty() { $productSku = 'simple'; - $linkContentData = [ + $linkData = [ 'title' => '', 'sort_order' => 1, 'price' => 10.1, 'number_of_downloads' => 100, - 'shareable' => true, + 'is_shareable' => true, 'link_type' => 'url', 'link_url' => 'http://example.com/', ]; @@ -243,13 +255,13 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable')); $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); - $linkContentMock = $this->getLinkContentMock($linkContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock) + $linkMock = $this->getLinkMock($linkData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock) ->will($this->returnValue(true)); $this->productMock->expects($this->never())->method('save'); - $this->service->save($productSku, $linkContentMock, null); + $this->service->save($productSku, $linkMock); } public function testUpdate() @@ -258,12 +270,15 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase $linkId = 1; $productSku = 'simple'; $productId = 1; - $linkContentData = [ + $linkData = [ + 'id' => $linkId, 'title' => 'Updated Title', 'sort_order' => 1, 'price' => 10.1, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://example.com/', ]; $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); @@ -271,54 +286,126 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase $storeMock = $this->getMock('\Magento\Store\Model\Store', [], [], '', false); $storeMock->expects($this->any())->method('getWebsiteId')->will($this->returnValue($websiteId)); $this->productMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); - $linkMock = $this->getMock( + $existingLinkMock = $this->getMock( '\Magento\Downloadable\Model\Link', [ '__wakeup', - 'setTitle', - 'setPrice', - 'setSortOrder', - 'setIsShareable', - 'setNumberOfDownloads', 'getId', - 'setProductId', - 'setStoreId', - 'setWebsiteId', - 'setProductWebsiteIds', 'load', - 'save', 'getProductId' ], [], '', false ); - $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($linkMock)); - $linkContentMock = $this->getLinkContentMock($linkContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock) + $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock)); + $linkMock = $this->getLinkMock($linkData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock) ->will($this->returnValue(true)); - $linkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId)); - $linkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); - $linkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setTitle')->with($linkContentData['title']) - ->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setSortOrder')->with($linkContentData['sort_order']) - ->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setPrice')->with($linkContentData['price']) - ->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setIsShareable')->with($linkContentData['shareable']) - ->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setNumberOfDownloads')->with($linkContentData['number_of_downloads']) - ->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setProductId')->with($productId) - ->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setStoreId')->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setWebsiteId')->with($websiteId)->will($this->returnSelf()); - $linkMock->expects($this->once())->method('setProductWebsiteIds')->will($this->returnSelf()); - $linkMock->expects($this->once())->method('save')->will($this->returnSelf()); - - $this->assertEquals($linkId, $this->service->save($productSku, $linkContentMock, $linkId)); + $existingLinkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId)); + $existingLinkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); + $existingLinkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf()); + + $this->productMock->expects($this->once())->method('setDownloadableData')->with( + [ + 'link' => [ + [ + 'link_id' => $linkId, + 'is_delete' => 0, + 'type' => $linkData['link_type'], + 'sort_order' => $linkData['sort_order'], + 'title' => $linkData['title'], + 'price' => $linkData['price'], + 'number_of_downloads' => $linkData['number_of_downloads'], + 'is_shareable' => $linkData['is_shareable'], + 'link_url' => $linkData['link_url'], + ], + ], + ] + ); + $this->productTypeMock->expects($this->once())->method('save') + ->with($this->productMock); + + $this->assertEquals($linkId, $this->service->save($productSku, $linkMock)); + } + + public function testUpdateWithExistingFile() + { + $websiteId = 1; + $linkId = 1; + $productSku = 'simple'; + $productId = 1; + $linkFile = '/l/i/link.jpg'; + $encodedFiles = "something"; + $linkData = [ + 'id' => $linkId, + 'title' => 'Updated Title', + 'sort_order' => 1, + 'price' => 10.1, + 'is_shareable' => true, + 'number_of_downloads' => 100, + 'link_type' => 'file', + 'link_file' => $linkFile, + ]; + $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) + ->will($this->returnValue($this->productMock)); + $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId)); + $storeMock = $this->getMock('\Magento\Store\Model\Store', [], [], '', false); + $storeMock->expects($this->any())->method('getWebsiteId')->will($this->returnValue($websiteId)); + $this->productMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); + $existingLinkMock = $this->getMock( + '\Magento\Downloadable\Model\Link', + [ + '__wakeup', + 'getId', + 'load', + 'getProductId' + ], + [], + '', + false + ); + $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock)); + $linkMock = $this->getLinkMock($linkData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock) + ->will($this->returnValue(true)); + + $existingLinkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId)); + $existingLinkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); + $existingLinkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf()); + + $this->jsonEncoderMock->expects($this->once()) + ->method('encode') + ->with( + [ + [ + 'file' => $linkFile, + 'status' => 'old' + ] + ] + )->willReturn($encodedFiles); + $this->productMock->expects($this->once())->method('setDownloadableData')->with( + [ + 'link' => [ + [ + 'link_id' => $linkId, + 'is_delete' => 0, + 'type' => $linkData['link_type'], + 'sort_order' => $linkData['sort_order'], + 'title' => $linkData['title'], + 'price' => $linkData['price'], + 'number_of_downloads' => $linkData['number_of_downloads'], + 'is_shareable' => $linkData['is_shareable'], + 'file' => $encodedFiles, + ], + ], + ] + ); + $this->productTypeMock->expects($this->once())->method('save') + ->with($this->productMock); + + $this->assertEquals($linkId, $this->service->save($productSku, $linkMock)); } /** @@ -330,34 +417,34 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase $linkId = 1; $productSku = 'simple'; $productId = 1; - $linkContentData = [ + $linkData = [ + 'id' => $linkId, 'title' => '', 'sort_order' => 1, 'price' => 10.1, 'number_of_downloads' => 100, - 'shareable' => true, + 'is_shareable' => true, ]; $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId)); - $linkMock = $this->getMock( + $existingLinkMock = $this->getMock( '\Magento\Downloadable\Model\Link', ['__wakeup', 'getId', 'load', 'save', 'getProductId'], [], '', false ); - $linkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId)); - $linkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); - $linkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf()); - $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($linkMock)); - $linkContentMock = $this->getLinkContentMock($linkContentData); + $existingLinkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId)); + $existingLinkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); + $existingLinkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf()); + $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock)); + $linkContentMock = $this->getLinkMock($linkData); $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock) ->will($this->returnValue(true)); - $linkMock->expects($this->never())->method('save'); - - $this->service->save($productSku, $linkContentMock, $linkId, true); + $this->productTypeMock->expects($this->never())->method('save'); + $this->service->save($productSku, $linkContentMock, true); } public function testDelete() diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php index 99fb7ed07618d23e136ce710af5ddb93010b97aa..f372aaa2451400888c72491ba1607e751d69dc0a 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php @@ -497,6 +497,33 @@ class ObserverTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('\Magento\Downloadable\Model\Observer', $result); } + public function testSaveDownloadableOrderItemNotDownloadableItem() + { + $itemId = 100; + $itemMock = $this->getMockBuilder('\Magento\Sales\Model\Order\Item') + ->disableOriginalConstructor() + ->getMock(); + $itemMock->expects($this->any()) + ->method('getId') + ->willReturn($itemId); + $itemMock->expects($this->any()) + ->method('getProductType') + ->willReturn('simple'); + $itemMock->expects($this->never()) + ->method('getProduct'); + $event = new \Magento\Framework\Object( + [ + 'item' => $itemMock, + ] + ); + $observer = new \Magento\Framework\Object( + [ + 'event' => $event + ] + ); + $this->observer->saveDownloadableOrderItem($observer); + } + /** * @param $id * @param int $statusId diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AfterProductLoadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..36f21e6fb5f28382cca4f4bec106d5772d125ec2 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AfterProductLoadTest.php @@ -0,0 +1,220 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +namespace Magento\Downloadable\Test\Unit\Model\Plugin; + +class AfterProductLoadTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Downloadable\Model\Plugin\AfterProductLoad + */ + protected $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $linkRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionFactory; + + protected function setUp() + { + $this->linkRepositoryMock = $this->getMock('\Magento\Downloadable\Api\LinkRepositoryInterface'); + $this->productExtensionFactory = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory') + ->disableOriginalConstructor() + ->getMock(); + $this->model = new \Magento\Downloadable\Model\Plugin\AfterProductLoad( + $this->linkRepositoryMock, + $this->productExtensionFactory + ); + $this->productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product') + ->disableOriginalConstructor() + ->getMock(); + $this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['setDownloadableProductLinks', 'setDownloadableProductSamples'])->getMock(); + } + + public function testAfterLoad() + { + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + + $this->productExtensionFactory->expects($this->once()) + ->method('create') + ->willReturn($this->productExtensionMock); + + $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $this->linkRepositoryMock->expects($this->once()) + ->method('getLinksByProduct') + ->with($this->productMock) + ->willReturn([$linkMock]); + $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $this->linkRepositoryMock->expects($this->once()) + ->method('getSamplesByProduct') + ->with($this->productMock) + ->willReturn([$sampleMock]); + $this->productExtensionMock->expects($this->once()) + ->method('setDownloadableProductLinks') + ->with([$linkMock]) + ->willReturnSelf(); + $this->productExtensionMock->expects($this->once()) + ->method('setDownloadableProductSamples') + ->with([$sampleMock]) + ->willReturnSelf(); + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($this->productExtensionMock) + ->willReturnSelf(); + + $this->assertEquals( + $this->productMock, + $this->model->afterLoad($this->productMock) + ); + } + + public function testAfterLoadWithExistingExtensionAttributes() + { + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + + $this->productExtensionFactory->expects($this->never()) + ->method('create'); + + $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $this->linkRepositoryMock->expects($this->once()) + ->method('getLinksByProduct') + ->with($this->productMock) + ->willReturn([$linkMock]); + $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $this->linkRepositoryMock->expects($this->once()) + ->method('getSamplesByProduct') + ->with($this->productMock) + ->willReturn([$sampleMock]); + $this->productExtensionMock->expects($this->once()) + ->method('setDownloadableProductLinks') + ->with([$linkMock]) + ->willReturnSelf(); + $this->productExtensionMock->expects($this->once()) + ->method('setDownloadableProductSamples') + ->with([$sampleMock]) + ->willReturnSelf(); + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($this->productExtensionMock) + ->willReturnSelf(); + + $this->assertEquals( + $this->productMock, + $this->model->afterLoad($this->productMock) + ); + } + + public function testAfterLoadOnlyLinks() + { + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + + $this->productExtensionFactory->expects($this->once()) + ->method('create') + ->willReturn($this->productExtensionMock); + + $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $this->linkRepositoryMock->expects($this->once()) + ->method('getLinksByProduct') + ->with($this->productMock) + ->willReturn([$linkMock]); + $this->linkRepositoryMock->expects($this->once()) + ->method('getSamplesByProduct') + ->with($this->productMock) + ->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('setDownloadableProductLinks') + ->with([$linkMock]) + ->willReturnSelf(); + $this->productExtensionMock->expects($this->never()) + ->method('setDownloadableProductSamples'); + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($this->productExtensionMock) + ->willReturnSelf(); + + $this->assertEquals( + $this->productMock, + $this->model->afterLoad($this->productMock) + ); + } + + public function testAfterLoadOnlySamples() + { + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + + $this->productExtensionFactory->expects($this->once()) + ->method('create') + ->willReturn($this->productExtensionMock); + + $this->linkRepositoryMock->expects($this->once()) + ->method('getLinksByProduct') + ->with($this->productMock) + ->willReturn(null); + $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $this->linkRepositoryMock->expects($this->once()) + ->method('getSamplesByProduct') + ->with($this->productMock) + ->willReturn([$sampleMock]); + $this->productExtensionMock->expects($this->never()) + ->method('setDownloadableProductLinks'); + $this->productExtensionMock->expects($this->once()) + ->method('setDownloadableProductSamples') + ->with([$sampleMock]) + ->willReturnSelf(); + $this->productMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($this->productExtensionMock) + ->willReturnSelf(); + + $this->assertEquals( + $this->productMock, + $this->model->afterLoad($this->productMock) + ); + } + + public function testAfterLoadIfProductTypeNotDownloadable() + { + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE); + $this->productMock->expects($this->never())->method('getExtensionAttributes'); + $this->productMock->expects($this->never())->method('setExtensionAttributes'); + $this->assertEquals( + $this->productMock, + $this->model->afterLoad($this->productMock) + ); + } +} diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e33241defa659ee03c309c4ca74fe85b237a9b07 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php @@ -0,0 +1,328 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +namespace Magento\Downloadable\Test\Unit\Model\Plugin; + +use \Magento\Downloadable\Model\Plugin\AroundProductRepositorySave; + +class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AroundProductRepositorySave + */ + protected $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $linkRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $sampleRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $savedProductMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productExtensionMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $existingProductExtensionMock; + + /** + * @var \Closure + */ + protected $closureMock; + + protected function setUp() + { + $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface'); + $this->linkRepositoryMock = $this->getMock('Magento\Downloadable\Api\LinkRepositoryInterface'); + $this->sampleRepositoryMock = $this->getMock('Magento\Downloadable\Api\SampleRepositoryInterface'); + $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $this->savedProductMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface'); + $this->closureMock = function () { + return $this->savedProductMock; + }; + $this->model = new AroundProductRepositorySave( + $this->linkRepositoryMock, + $this->sampleRepositoryMock ); + $this->productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['getDownloadableProductLinks', 'getDownloadableProductSamples'])->getMock(); + $this->existingProductExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension') + ->setMethods(['getDownloadableProductLinks', 'getDownloadableProductSamples']) + ->getMock(); + } + + public function testAroundSaveWhenProductIsSimple() + { + $this->productMock->expects($this->once())->method('getTypeId')->willReturn('simple'); + $this->productMock->expects($this->never())->method('getExtensionAttributes'); + + $this->assertEquals( + $this->savedProductMock, + $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + public function testAroundSaveWhenProductHasNoExtensionAttributes() + { + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + + $this->savedProductMock->expects($this->never())->method('getExtensionAttributes'); + $this->linkRepositoryMock->expects($this->never())->method('save'); + + $this->assertEquals( + $this->savedProductMock, + $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + /** + * Input has two links and two samples, one existing and one new + * Existing product has two links and two samples, one will be updated and one will be deleted + */ + public function testAroundSave() + { + $productSku = "downloadable_product"; + $existingLinkId = '2'; + $existingSampleId = '5'; + $toBeDeletedLinkId = '3'; + $toBeDeletedSampleId = '4'; + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $updateLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $updateLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId); + $newLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $newLinkMock->expects($this->once())->method('getId')->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getDownloadableProductLinks') + ->willReturn([$newLinkMock, $updateLinkMock]); + + $updateSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $updateSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId); + $newSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $newSampleMock->expects($this->once())->method('getId')->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getDownloadableProductSamples') + ->willReturn([$updateSampleMock, $newSampleMock]); + + $existingLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $existingLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId); + $toBeDeletedLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $toBeDeletedLinkMock->expects($this->once())->method('getId')->willReturn($toBeDeletedLinkId); + + $existingSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $existingSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId); + $toBeDeletedSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $toBeDeletedSampleMock->expects($this->once())->method('getId')->willReturn($toBeDeletedSampleId); + + $this->savedProductMock->expects($this->any())->method('getSku')->willReturn($productSku); + $this->savedProductMock->expects($this->exactly(2))->method('getExtensionAttributes') + ->willReturn($this->existingProductExtensionMock); + $this->existingProductExtensionMock->expects($this->once()) + ->method('getDownloadableProductLinks') + ->willReturn([$existingLinkMock, $toBeDeletedLinkMock]); + $this->existingProductExtensionMock->expects($this->once()) + ->method('getDownloadableProductSamples') + ->willReturn([$existingSampleMock, $toBeDeletedSampleMock]); + + $this->linkRepositoryMock->expects($this->at(0)) + ->method('save') + ->with($productSku, $updateLinkMock); + $this->linkRepositoryMock->expects($this->at(1)) + ->method('save') + ->with($productSku, $newLinkMock); + $this->linkRepositoryMock->expects($this->at(2)) + ->method('delete') + ->with($toBeDeletedLinkId); + + $this->sampleRepositoryMock->expects($this->at(0)) + ->method('save') + ->with($productSku, $updateSampleMock); + $this->sampleRepositoryMock->expects($this->at(1)) + ->method('save') + ->with($productSku, $newSampleMock); + $this->sampleRepositoryMock->expects($this->at(2)) + ->method('delete') + ->with($toBeDeletedSampleId); + + $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') + ->disableOriginalConstructor()->getMock(); + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku, false, null, true) + ->willReturn($newProductMock); + + $this->assertEquals( + $newProductMock, + $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + /** + * Input has two links and no samples, one existing and one new + * Existing product has two links, one will be updated and one will be deleted + */ + public function testAroundSaveWithOnlyLinks() + { + $productSku = "downloadable_product"; + $existingLinkId = '2'; + $toBeDeletedLinkId = '3'; + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $updateLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $updateLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId); + $newLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $newLinkMock->expects($this->once())->method('getId')->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getDownloadableProductLinks') + ->willReturn([$newLinkMock, $updateLinkMock]); + + $this->productExtensionMock->expects($this->once()) + ->method('getDownloadableProductSamples') + ->willReturn(null); + + $existingLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $existingLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId); + $toBeDeletedLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface'); + $toBeDeletedLinkMock->expects($this->once())->method('getId')->willReturn($toBeDeletedLinkId); + + $this->savedProductMock->expects($this->any())->method('getSku')->willReturn($productSku); + $this->savedProductMock->expects($this->once())->method('getExtensionAttributes') + ->willReturn($this->existingProductExtensionMock); + $this->existingProductExtensionMock->expects($this->once()) + ->method('getDownloadableProductLinks') + ->willReturn([$existingLinkMock, $toBeDeletedLinkMock]); + $this->existingProductExtensionMock->expects($this->never()) + ->method('getDownloadableProductSamples'); + + $this->linkRepositoryMock->expects($this->at(0)) + ->method('save') + ->with($productSku, $updateLinkMock); + $this->linkRepositoryMock->expects($this->at(1)) + ->method('save') + ->with($productSku, $newLinkMock); + $this->linkRepositoryMock->expects($this->at(2)) + ->method('delete') + ->with($toBeDeletedLinkId); + + $this->sampleRepositoryMock->expects($this->never()) + ->method('save'); + + $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') + ->disableOriginalConstructor()->getMock(); + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku, false, null, true) + ->willReturn($newProductMock); + + $this->assertEquals( + $newProductMock, + $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } + + /** + * Input has two samples, one existing and one new + * Existing product has two samples, one will be updated and one will be deleted + */ + public function testAroundSaveWithOnlySamples() + { + $productSku = "downloadable_product"; + $existingSampleId = '5'; + $toBeDeletedSampleId = '4'; + + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE); + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getDownloadableProductLinks') + ->willReturn(null); + + $updateSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $updateSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId); + $newSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $newSampleMock->expects($this->once())->method('getId')->willReturn(null); + $this->productExtensionMock->expects($this->once()) + ->method('getDownloadableProductSamples') + ->willReturn([$updateSampleMock, $newSampleMock]); + + $existingSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $existingSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId); + $toBeDeletedSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); + $toBeDeletedSampleMock->expects($this->once())->method('getId')->willReturn($toBeDeletedSampleId); + + $this->savedProductMock->expects($this->any())->method('getSku')->willReturn($productSku); + $this->savedProductMock->expects($this->once())->method('getExtensionAttributes') + ->willReturn($this->existingProductExtensionMock); + $this->existingProductExtensionMock->expects($this->never()) + ->method('getDownloadableProductLinks'); + $this->existingProductExtensionMock->expects($this->once()) + ->method('getDownloadableProductSamples') + ->willReturn([$existingSampleMock, $toBeDeletedSampleMock]); + + $this->linkRepositoryMock->expects($this->never()) + ->method('save'); + + $this->sampleRepositoryMock->expects($this->at(0)) + ->method('save') + ->with($productSku, $updateSampleMock); + $this->sampleRepositoryMock->expects($this->at(1)) + ->method('save') + ->with($productSku, $newSampleMock); + $this->sampleRepositoryMock->expects($this->at(2)) + ->method('delete') + ->with($toBeDeletedSampleId); + + $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface') + ->disableOriginalConstructor()->getMock(); + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku, false, null, true) + ->willReturn($newProductMock); + + $this->assertEquals( + $newProductMock, + $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock) + ); + } +} diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php index 8a94c4b8ba1279711a75d133a7697b5cf49738c5..517563a68cd8b58ee2baba39bf71ca479dd55005 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php @@ -51,15 +51,17 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase false ); $this->sampleFileMock = $this->getMock('\Magento\Downloadable\Api\Data\File\ContentInterface'); - $this->validator = new \Magento\Downloadable\Model\Sample\ContentValidator($this->fileValidatorMock, $this->urlValidatorMock); + $this->validator = new ContentValidator($this->fileValidatorMock, $this->urlValidatorMock); } public function testIsValid() { + $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface'); $sampleContentData = [ 'title' => 'Title', 'sort_order' => 1, 'sample_type' => 'file', + 'sample_file_content' => $sampleFileContentMock, ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); @@ -103,7 +105,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase */ protected function getSampleContentMock(array $sampleContentData) { - $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleContentInterface'); + $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue( $sampleContentData['title'] )); @@ -119,6 +121,10 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase $sampleContentData['sample_url'] )); } + if (isset($sampleContentData['sample_file_content'])) { + $contentMock->expects($this->any())->method('getSampleFileContent') + ->willReturn($sampleContentData['sample_file_content']); + } $contentMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( $this->sampleFileMock )); diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php index 4027405e14f5c27eaf84dc245574e21d4a8a156a..7e25af50c3f6b771308ffc0eed2f79b227bd35b6 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php @@ -15,6 +15,11 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase */ protected $repositoryMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productTypeMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -55,6 +60,7 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase false ); $this->repositoryMock = $this->getMock('\Magento\Catalog\Model\ProductRepository', [], [], '', false); + $this->productTypeMock = $this->getMock('\Magento\Downloadable\Model\Product\Type', [], [], '', false); $this->contentValidatorMock = $this->getMock( 'Magento\Downloadable\Model\Sample\ContentValidator', [], @@ -78,6 +84,7 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase $this->service = new \Magento\Downloadable\Model\SampleRepository( $this->repositoryMock, + $this->productTypeMock, $this->contentValidatorMock, $this->contentUploaderMock, $this->jsonEncoderMock, @@ -86,35 +93,43 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase } /** - * @param array $sampleContentData + * @param array $sampleData * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getSampleContentMock(array $sampleContentData) + protected function getSampleMock(array $sampleData) { - $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleContentInterface'); + $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface'); - $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue($sampleContentData['title'])); - $contentMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( - $sampleContentData['sort_order'] + if (isset($sampleData['id'])) { + $sampleMock->expects($this->any())->method('getId')->willReturn($sampleData['id']); + } + $sampleMock->expects($this->any())->method('getTitle')->will($this->returnValue($sampleData['title'])); + $sampleMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( + $sampleData['sort_order'] )); - if (isset($sampleContentData['sample_type'])) { - $contentMock->expects($this->any())->method('getSampleType')->will($this->returnValue( - $sampleContentData['sample_type'] + if (isset($sampleData['sample_type'])) { + $sampleMock->expects($this->any())->method('getSampleType')->will($this->returnValue( + $sampleData['sample_type'] + )); + } + if (isset($sampleData['sample_url'])) { + $sampleMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( + $sampleData['sample_url'] )); } - if (isset($sampleContentData['sample_url'])) { - $contentMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( - $sampleContentData['sample_url'] + if (isset($sampleData['sample_file'])) { + $sampleMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( + $sampleData['sample_file'] )); } - return $contentMock; + return $sampleMock; } public function testCreate() { $productSku = 'simple'; - $sampleContentData = [ + $sampleData = [ 'title' => 'Title', 'sort_order' => 1, 'sample_type' => 'url', @@ -123,8 +138,8 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable')); - $sampleContentMock = $this->getSampleContentMock($sampleContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock) + $sampleMock = $this->getSampleMock($sampleData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock) ->will($this->returnValue(true)); $this->productMock->expects($this->once())->method('setDownloadableData')->with([ @@ -132,15 +147,15 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase [ 'sample_id' => 0, 'is_delete' => 0, - 'type' => $sampleContentData['sample_type'], - 'sort_order' => $sampleContentData['sort_order'], - 'title' => $sampleContentData['title'], - 'sample_url' => $sampleContentData['sample_url'], + 'type' => $sampleData['sample_type'], + 'sort_order' => $sampleData['sort_order'], + 'title' => $sampleData['title'], + 'sample_url' => $sampleData['sample_url'], ], ], ]); - $this->productMock->expects($this->once())->method('save'); - $this->service->save($productSku, $sampleContentMock, null); + $this->productTypeMock->expects($this->once())->method('save')->with($this->productMock); + $this->service->save($productSku, $sampleMock); } /** @@ -150,7 +165,7 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase public function testCreateThrowsExceptionIfTitleIsEmpty() { $productSku = 'simple'; - $sampleContentData = [ + $sampleData = [ 'title' => '', 'sort_order' => 1, 'sample_type' => 'url', @@ -160,13 +175,13 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable')); - $sampleContentMock = $this->getSampleContentMock($sampleContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock) + $sampleMock = $this->getSampleMock($sampleData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock) ->will($this->returnValue(true)); - $this->productMock->expects($this->never())->method('save'); + $this->productTypeMock->expects($this->never())->method('save'); - $this->service->save($productSku, $sampleContentMock, null); + $this->service->save($productSku, $sampleMock); } public function testUpdate() @@ -174,39 +189,109 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase $sampleId = 1; $productId = 1; $productSku = 'simple'; - $sampleContentData = [ + $sampleData = [ + 'id' => $sampleId, 'title' => 'Updated Title', 'sort_order' => 1, + 'sample_type' => 'url', + 'sample_url' => 'http://example.com/', ]; $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId)); - $sampleMock = $this->getMock( + $existingSampleMock = $this->getMock( '\Magento\Downloadable\Model\Sample', - ['__wakeup', 'setTitle', 'setSortOrder', 'getId', 'setProductId', 'setStoreId', - 'load', 'save', 'getProductId'], + ['__wakeup', 'getId', 'load', 'getProductId'], [], '', false ); - $this->sampleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($sampleMock)); - $sampleContentMock = $this->getSampleContentMock($sampleContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock) + $this->sampleFactoryMock->expects($this->once())->method('create') + ->will($this->returnValue($existingSampleMock)); + $sampleMock = $this->getSampleMock($sampleData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock) ->will($this->returnValue(true)); - $sampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId)); - $sampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); - $sampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf()); - $sampleMock->expects($this->once())->method('setTitle')->with($sampleContentData['title']) - ->will($this->returnSelf()); - $sampleMock->expects($this->once())->method('setSortOrder')->with($sampleContentData['sort_order']) - ->will($this->returnSelf()); - $sampleMock->expects($this->once())->method('setProductId')->with($productId) - ->will($this->returnSelf()); - $sampleMock->expects($this->once())->method('setStoreId')->will($this->returnSelf()); - $sampleMock->expects($this->once())->method('save')->will($this->returnSelf()); - - $this->assertEquals($sampleId, $this->service->save($productSku, $sampleContentMock, $sampleId)); + $existingSampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId)); + $existingSampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); + $existingSampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf()); + + $this->productMock->expects($this->once())->method('setDownloadableData')->with([ + 'sample' => [ + [ + 'sample_id' => $sampleId, + 'is_delete' => 0, + 'type' => $sampleData['sample_type'], + 'sort_order' => $sampleData['sort_order'], + 'title' => $sampleData['title'], + 'sample_url' => $sampleData['sample_url'], + ], + ], + ]); + $this->productTypeMock->expects($this->once())->method('save')->with($this->productMock); + + $this->assertEquals($sampleId, $this->service->save($productSku, $sampleMock)); + } + + public function testUpdateWithExistingFile() + { + $sampleId = 1; + $productId = 1; + $productSku = 'simple'; + $sampleFile = '/s/a/sample.jpg'; + $encodedFile = 'something'; + $sampleData = [ + 'id' => $sampleId, + 'title' => 'Updated Title', + 'sort_order' => 1, + 'sample_type' => 'file', + 'sample_file' => $sampleFile, + ]; + $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) + ->will($this->returnValue($this->productMock)); + $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId)); + $existingSampleMock = $this->getMock( + '\Magento\Downloadable\Model\Sample', + ['__wakeup', 'getId', 'load', 'getProductId'], + [], + '', + false + ); + $this->sampleFactoryMock->expects($this->once())->method('create') + ->will($this->returnValue($existingSampleMock)); + $sampleMock = $this->getSampleMock($sampleData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock) + ->will($this->returnValue(true)); + + $existingSampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId)); + $existingSampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); + $existingSampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf()); + + $this->jsonEncoderMock->expects($this->once()) + ->method('encode') + ->with( + [ + [ + 'file' => $sampleFile, + 'status' => 'old', + ] + ] + )->willReturn($encodedFile); + $this->productMock->expects($this->once())->method('setDownloadableData')->with([ + 'sample' => [ + [ + 'sample_id' => $sampleId, + 'is_delete' => 0, + 'type' => $sampleData['sample_type'], + 'sort_order' => $sampleData['sort_order'], + 'title' => $sampleData['title'], + 'file' => $encodedFile, + ], + ], + ]); + $this->productTypeMock->expects($this->once())->method('save')->with($this->productMock); + + $this->assertEquals($sampleId, $this->service->save($productSku, $sampleMock)); } /** @@ -218,31 +303,33 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase $sampleId = 1; $productSku = 'simple'; $productId = 1; - $sampleContentData = [ + $sampleData = [ + 'id' => $sampleId, 'title' => '', 'sort_order' => 1, ]; $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId)); - $sampleMock = $this->getMock( + $existingSampleMock = $this->getMock( '\Magento\Downloadable\Model\Sample', ['__wakeup', 'getId', 'load', 'save', 'getProductId'], [], '', false ); - $sampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId)); - $sampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf()); - $sampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); - $this->sampleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($sampleMock)); - $sampleContentMock = $this->getSampleContentMock($sampleContentData); - $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock) + $existingSampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId)); + $existingSampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf()); + $existingSampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); + $this->sampleFactoryMock->expects($this->once())->method('create') + ->will($this->returnValue($existingSampleMock)); + $sampleMock = $this->getSampleMock($sampleData); + $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock) ->will($this->returnValue(true)); - $sampleMock->expects($this->never())->method('save'); + $this->productTypeMock->expects($this->never())->method('save'); - $this->service->save($productSku, $sampleContentMock, $sampleId, true); + $this->service->save($productSku, $sampleMock, true); } public function testDelete() diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml index 9b492b051acc779329be9e761c9ede86ce6752e7..acb63729a578108f11e7b5f9d59c60d3d1e00745 100644 --- a/app/code/Magento/Downloadable/etc/di.xml +++ b/app/code/Magento/Downloadable/etc/di.xml @@ -58,12 +58,16 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product"> + <plugin name="downloadableAfterLoad" type="\Magento\Downloadable\Model\Plugin\AfterProductLoad"/> + </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="downloadableAroundSave" type="\Magento\Downloadable\Model\Plugin\AroundProductRepositorySave"/> + </type> <preference for="\Magento\Downloadable\Api\LinkRepositoryInterface" type="\Magento\Downloadable\Model\LinkRepository" /> <preference for="\Magento\Downloadable\Api\SampleRepositoryInterface" type="\Magento\Downloadable\Model\SampleRepository" /> <preference for="\Magento\Downloadable\Api\Data\LinkInterface" type="\Magento\Downloadable\Model\Link" /> <preference for="\Magento\Downloadable\Api\Data\SampleInterface" type="\Magento\Downloadable\Model\Sample" /> - <preference for="\Magento\Downloadable\Api\Data\SampleContentInterface" type="\Magento\Downloadable\Model\Sample\Content" /> - <preference for="\Magento\Downloadable\Api\Data\LinkContentInterface" type="\Magento\Downloadable\Model\Link\Content" /> <preference for="\Magento\Downloadable\Api\Data\File\ContentInterface" type="\Magento\Downloadable\Model\File\Content" /> <preference for="\Magento\Downloadable\Api\Data\File\ContentUploaderInterface" type="\Magento\Downloadable\Model\File\ContentUploader" /> <preference for="\Magento\Downloadable\Model\Product\TypeHandler\TypeHandlerInterface" type="\Magento\Downloadable\Model\Product\TypeHandler\TypeHandler" /> diff --git a/app/code/Magento/Downloadable/etc/service_data_attributes.xml b/app/code/Magento/Downloadable/etc/service_data_attributes.xml new file mode 100644 index 0000000000000000000000000000000000000000..80cc1cb0abf9ff457b97d12eddc9b3b6ee519784 --- /dev/null +++ b/app/code/Magento/Downloadable/etc/service_data_attributes.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> + <attribute code="downloadable_product_links" type="Magento\Downloadable\Api\Data\LinkInterface[]" /> + <attribute code="downloadable_product_samples" type="Magento\Downloadable\Api\Data\SampleInterface[]" /> + </extension_attributes> +</config> diff --git a/app/code/Magento/Downloadable/etc/webapi.xml b/app/code/Magento/Downloadable/etc/webapi.xml index 0291fe5322f8328380ab63b58dfd7b8b8322dbe2..7f597119f9e74db7142d3baa866ec3c697c67e45 100644 --- a/app/code/Magento/Downloadable/etc/webapi.xml +++ b/app/code/Magento/Downloadable/etc/webapi.xml @@ -25,13 +25,13 @@ <resource ref="Magento_Downloadable::downloadable" /> </resources> </route> - <route url="/V1/products/:sku/downloadable-links/:linkId" method="PUT"> + <route url="/V1/products/:sku/downloadable-links/:id" method="PUT"> <service class="Magento\Downloadable\Api\LinkRepositoryInterface" method="save"/> <resources> <resource ref="Magento_Downloadable::downloadable" /> </resources> </route> - <route url="/V1/products/downloadable-links/:linkId" method="DELETE"> + <route url="/V1/products/downloadable-links/:id" method="DELETE"> <service class="Magento\Downloadable\Api\LinkRepositoryInterface" method="delete"/> <resources> <resource ref="Magento_Downloadable::downloadable" /> @@ -43,13 +43,13 @@ <resource ref="Magento_Downloadable::downloadable" /> </resources> </route> - <route url="/V1/products/:sku/downloadable-links/samples/:sampleId" method="PUT"> + <route url="/V1/products/:sku/downloadable-links/samples/:id" method="PUT"> <service class="Magento\Downloadable\Api\SampleRepositoryInterface" method="save"/> <resources> <resource ref="Magento_Downloadable::downloadable" /> </resources> </route> - <route url="/V1/products/downloadable-links/samples/:sampleId" method="DELETE"> + <route url="/V1/products/downloadable-links/samples/:id" method="DELETE"> <service class="Magento\Downloadable\Api\SampleRepositoryInterface" method="delete"/> <resources> <resource ref="Magento_Downloadable::downloadable" /> diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index ac8c5805f4b72f8727d09a4a4cbc71a0f05c2258..c8fa8214eab58921da003c19e9a5c55b4a3d66c1 100755 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -31,14 +31,14 @@ abstract class AbstractEntity extends \Magento\Framework\Model\Resource\Abstract /** * Read connection * - * @var \Magento\Framework\DB\Adapter\Pdo\Mysql + * @var \Magento\Framework\DB\Adapter\Pdo\Mysql | string */ protected $_read; /** * Write connection * - * @var \Magento\Framework\DB\Adapter\Pdo\Mysql + * @var \Magento\Framework\DB\Adapter\Pdo\Mysql | string */ protected $_write; diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index 3dfebf456c7ff4ae26f6f8a118431872c7c2f67e..00524114f784dd3bc859a8395a9c1d23012433cc 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -57,4 +57,9 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Entity\Attribute"> + <arguments> + <argument name="reservedAttributeList" xsi:type="object">Magento\Catalog\Model\Product\ReservedAttributeList\Proxy</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Email/Model/BackendTemplate.php b/app/code/Magento/Email/Model/BackendTemplate.php index be5b232564a67e139724e96f6d7a51a89702ed9c..c8bc6b34398e0c492f9123cfaeedc8182da23299 100644 --- a/app/code/Magento/Email/Model/BackendTemplate.php +++ b/app/code/Magento/Email/Model/BackendTemplate.php @@ -5,6 +5,8 @@ */ namespace Magento\Email\Model; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * Adminhtml email template model * @@ -78,7 +80,7 @@ class BackendTemplate extends Template return []; } - $configData = $this->_scopeConfig->getValue(null, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT); + $configData = $this->_scopeConfig->getValue(null, ScopeConfigInterface::SCOPE_TYPE_DEFAULT); $paths = $this->_findEmailTemplateUsages($templateCode, $configData, ''); return $paths; } diff --git a/app/code/Magento/GiftMessage/etc/data_object.xml b/app/code/Magento/GiftMessage/etc/service_data_attributes.xml similarity index 64% rename from app/code/Magento/GiftMessage/etc/data_object.xml rename to app/code/Magento/GiftMessage/etc/service_data_attributes.xml index a1255cc8a161a5a8d28090697088a87ede8920e7..f02c0a717e169224d0b7d450e861f38d7176dc7f 100644 --- a/app/code/Magento/GiftMessage/etc/data_object.xml +++ b/app/code/Magento/GiftMessage/etc/service_data_attributes.xml @@ -5,11 +5,11 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Sales\Api\Data\OrderInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Sales\Api\Data\OrderInterface"> <attribute code="gift_message" type="Magento\GiftMessage\Api\Data\MessageInterface" /> - </custom_attributes> - <custom_attributes for="Magento\Sales\Api\Data\OrderItemInterface"> + </extension_attributes> + <extension_attributes for="Magento\Sales\Api\Data\OrderItemInterface"> <attribute code="gift_message" type="Magento\GiftMessage\Api\Data\MessageInterface" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/app/code/Magento/GoogleAdwords/etc/di.xml b/app/code/Magento/GoogleAdwords/etc/di.xml new file mode 100644 index 0000000000000000000000000000000000000000..293db157d7c38cca198df46a541016f71c97dd54 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/etc/di.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd"> + <type name="Magento\GoogleAdwords\Model\Observer"> + <arguments> + <argument name="collection" xsi:type="object">Magento\Sales\Model\Resource\Order\Collection\Proxy</argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php b/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php index 089f14216bdadb05a1ee8c4b4530526b46ad892a..d20e9cc0cc88c9215df5aa15b0b69cd32ccb5061 100644 --- a/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php +++ b/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php @@ -8,6 +8,7 @@ namespace Magento\GoogleShopping\Model\Attribute; use Magento\Framework\Parse\Zip; use Magento\Store\Model\Store; use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Model\TaxClass\Key; /** * Tax attribute model @@ -179,8 +180,8 @@ class Tax extends \Magento\GoogleShopping\Model\Attribute\DefaultAttribute 'code' => $product->getSku(), 'type' => 'product', 'tax_class_key' => [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, - TaxClassKeyInterface::KEY_VALUE => $product->getTaxClassId(), + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, + Key::KEY_VALUE => $product->getTaxClassId(), ], 'unit_price' => $product->getPrice(), 'quantity' => 1, @@ -204,8 +205,8 @@ class Tax extends \Magento\GoogleShopping\Model\Attribute\DefaultAttribute 'billing_address' => $billingAddressDataArray, 'shipping_address' => $shippingAddressDataArray, 'customer_tax_class_key' => [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, - TaxClassKeyInterface::KEY_VALUE => $defaultCustomerTaxClassId, + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, + Key::KEY_VALUE => $defaultCustomerTaxClassId, ], 'items' => [ $quoteDetailsItemDataArray, diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index d856527779116c8c6795c79d704fc90a892bfcd2..5ee51f1ecbfb45622fd6f8c28955de99da4c2874 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -480,7 +480,7 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType */ public function beforeSave($product) { - if ($product->hasData('product_options')) { + if ($product->hasData('product_options') && !empty($product->getData('product_options'))) { throw new \Exception('Custom options for grouped product type are not supported'); } return parent::beforeSave($product); diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php new file mode 100644 index 0000000000000000000000000000000000000000..04d7f729f8c86ad5a0c85435bfa266584cf3fbb1 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php @@ -0,0 +1,430 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +namespace Magento\GroupedProduct\Test\Unit\Model; + +use \Magento\Catalog\Model\Product; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Product Test + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) + * + */ +class ProductTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $model; + + /** + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject + */ + protected $moduleManager; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $stockItemFactoryMock; + + /** + * @var \Magento\Indexer\Model\IndexerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $categoryIndexerMock; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productFlatProcessor; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productPriceProcessor; + + /** + * @var Product\Type|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productTypeInstanceMock; + + /** + * @var Product\Option|\PHPUnit_Framework_MockObject_MockObject + */ + protected $optionInstanceMock; + + /** + * @var \Magento\Framework\Pricing\PriceInfo\Base|\PHPUnit_Framework_MockObject_MockObject + */ + protected $_priceInfoMock; + + /** + * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + */ + private $store; + + /** + * @var \Magento\Catalog\Model\Resource\Product|\PHPUnit_Framework_MockObject_MockObject + */ + private $resource; + + /** + * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + */ + private $registry; + + /** + * @var \Magento\Catalog\Model\Category|\PHPUnit_Framework_MockObject_MockObject + */ + private $category; + + /** + * @var \Magento\Store\Model\Website|\PHPUnit_Framework_MockObject_MockObject + */ + private $website; + + /** + * @var \Magento\Indexer\Model\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject + */ + protected $indexerRegistryMock; + + /** + * @var \Magento\Catalog\Api\CategoryRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $categoryRepository; + + /** + * @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject + */ + private $_catalogProduct; + + /** + * @var \Magento\Catalog\Model\Product\Image\Cache|\PHPUnit_Framework_MockObject_MockObject + */ + protected $imageCache; + + /** + * @var \Magento\Catalog\Model\Product\Image\CacheFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $imageCacheFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $mediaGalleryEntryFactoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $productLinkFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $dataObjectHelperMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $metadataServiceMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $attributeValueFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $linkTypeProviderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $entityCollectionProviderMock; + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function setUp() + { + $this->categoryIndexerMock = $this->getMockForAbstractClass('\Magento\Indexer\Model\IndexerInterface'); + + $this->moduleManager = $this->getMock( + 'Magento\Framework\Module\Manager', + ['isEnabled'], + [], + '', + false + ); + $this->stockItemFactoryMock = $this->getMock( + 'Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory', + ['create'], + [], + '', + false + ); + $this->dataObjectHelperMock = $this->getMockBuilder('\Magento\Framework\Api\DataObjectHelper') + ->disableOriginalConstructor() + ->getMock(); + $this->productFlatProcessor = $this->getMock( + 'Magento\Catalog\Model\Indexer\Product\Flat\Processor', + [], + [], + '', + false + ); + + $this->_priceInfoMock = $this->getMock('Magento\Framework\Pricing\PriceInfo\Base', [], [], '', false); + $this->productTypeInstanceMock = $this->getMock('Magento\Catalog\Model\Product\Type', [], [], '', false); + $this->productPriceProcessor = $this->getMock( + 'Magento\Catalog\Model\Indexer\Product\Price\Processor', + [], + [], + '', + false + ); + + $stateMock = $this->getMock('Magento\FrameworkApp\State', ['getAreaCode'], [], '', false); + $stateMock->expects($this->any()) + ->method('getAreaCode') + ->will($this->returnValue(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE)); + + $eventManagerMock = $this->getMock('Magento\Framework\Event\ManagerInterface'); + $actionValidatorMock = $this->getMock( + '\Magento\Framework\Model\ActionValidator\RemoveAction', + [], + [], + '', + false + ); + $actionValidatorMock->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); + $cacheInterfaceMock = $this->getMock('Magento\Framework\App\CacheInterface'); + + $contextMock = $this->getMock( + '\Magento\Framework\Model\Context', + ['getEventDispatcher', 'getCacheManager', 'getAppState', 'getActionValidator'], [], '', false + ); + $contextMock->expects($this->any())->method('getAppState')->will($this->returnValue($stateMock)); + $contextMock->expects($this->any())->method('getEventDispatcher')->will($this->returnValue($eventManagerMock)); + $contextMock->expects($this->any()) + ->method('getCacheManager') + ->will($this->returnValue($cacheInterfaceMock)); + $contextMock->expects($this->any()) + ->method('getActionValidator') + ->will($this->returnValue($actionValidatorMock)); + + $this->optionInstanceMock = $this->getMockBuilder('Magento\Catalog\Model\Product\Option') + ->setMethods(['setProduct', 'saveOptions', '__wakeup', '__sleep']) + ->disableOriginalConstructor()->getMock(); + + $this->resource = $this->getMockBuilder('Magento\Catalog\Model\Resource\Product') + ->disableOriginalConstructor() + ->getMock(); + + $this->registry = $this->getMockBuilder('Magento\Framework\Registry') + ->disableOriginalConstructor() + ->getMock(); + + $this->category = $this->getMockBuilder('Magento\Catalog\Model\Category') + ->disableOriginalConstructor() + ->getMock(); + + $this->store = $this->getMockBuilder('Magento\Store\Model\Store') + ->disableOriginalConstructor() + ->getMock(); + + $this->website = $this->getMockBuilder('\Magento\Store\Model\Website') + ->disableOriginalConstructor() + ->getMock(); + + $storeManager = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface') + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $storeManager->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($this->store)); + $storeManager->expects($this->any()) + ->method('getWebsite') + ->will($this->returnValue($this->website)); + $this->indexerRegistryMock = $this->getMock('Magento\Indexer\Model\IndexerRegistry', ['get'], [], '', false); + $this->categoryRepository = $this->getMock('Magento\Catalog\Api\CategoryRepositoryInterface'); + + $this->_catalogProduct = $this->getMock( + 'Magento\Catalog\Helper\Product', + ['isDataForProductCategoryIndexerWasChanged'], + [], + '', + false + ); + + $this->imageCache = $this->getMockBuilder('Magento\Catalog\Model\Product\Image\Cache') + ->disableOriginalConstructor() + ->getMock(); + $this->imageCacheFactory = $this->getMockBuilder('Magento\Catalog\Model\Product\Image\CacheFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->productLinkFactory = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductLinkInterfaceFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->mediaGalleryEntryFactoryMock = + $this->getMockBuilder('Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory') + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->metadataServiceMock = $this->getMock('\Magento\Catalog\Api\ProductAttributeRepositoryInterface'); + $this->attributeValueFactory = $this->getMockBuilder('Magento\Framework\Api\AttributeValueFactory') + ->disableOriginalConstructor()->getMock(); + $this->linkTypeProviderMock = $this->getMock('Magento\Catalog\Model\Product\LinkTypeProvider', + ['getLinkTypes'], [], '', false); + $this->entityCollectionProviderMock = $this->getMock('Magento\Catalog\Model\ProductLink\CollectionProvider', + ['getCollection'], [], '', false); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + 'Magento\Catalog\Model\Product', + [ + 'context' => $contextMock, + 'catalogProductType' => $this->productTypeInstanceMock, + 'productFlatIndexerProcessor' => $this->productFlatProcessor, + 'productPriceIndexerProcessor' => $this->productPriceProcessor, + 'catalogProductOption' => $this->optionInstanceMock, + 'storeManager' => $storeManager, + 'resource' => $this->resource, + 'registry' => $this->registry, + 'moduleManager' => $this->moduleManager, + 'stockItemFactory' => $this->stockItemFactoryMock, + 'dataObjectHelper' => $this->dataObjectHelperMock, + 'indexerRegistry' => $this->indexerRegistryMock, + 'categoryRepository' => $this->categoryRepository, + 'catalogProduct' => $this->_catalogProduct, + 'imageCacheFactory' => $this->imageCacheFactory, + 'productLinkFactory' => $this->productLinkFactory, + 'mediaGalleryEntryFactory' => $this->mediaGalleryEntryFactoryMock, + 'metadataService' => $this->metadataServiceMock, + 'customAttributeFactory' => $this->attributeValueFactory, + 'entityCollectionProvider' => $this->entityCollectionProviderMock, + 'linkTypeProvider' => $this->linkTypeProviderMock, + 'data' => ['id' => 1] + ] + ); + + } + + /** + * Test for getProductLinks() with associated product links + */ + public function testGetProductLinks() + { + $this->markTestIncomplete('Skipped due to https://jira.corp.x.com/browse/MAGETWO-36926'); + $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; + $this->linkTypeProviderMock->expects($this->once()) + ->method('getLinkTypes') + ->willReturn($linkTypes); + + $inputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputRelatedLink->setProductSku("Simple Product 1"); + $inputRelatedLink->setLinkType("related"); + $inputRelatedLink->setData("sku", "Simple Product 2"); + $inputRelatedLink->setData("type", "simple"); + $inputRelatedLink->setPosition(0); + + $customData = ["attribute_code" => "qty", "value" => 1]; + $inputGroupLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $inputGroupLink->setProductSku("Simple Product 1"); + $inputGroupLink->setLinkType("associated"); + $inputGroupLink->setData("sku", "Simple Product 2"); + $inputGroupLink->setData("type", "simple"); + $inputGroupLink->setPosition(0); + $inputGroupLink["custom_attributes"] = [$customData]; + + $outputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $outputRelatedLink->setProductSku("Simple Product 1"); + $outputRelatedLink->setLinkType("related"); + $outputRelatedLink->setLinkedProductSku("Simple Product 2"); + $outputRelatedLink->setLinkedProductType("simple"); + $outputRelatedLink->setPosition(0); + + $groupExtension = $this->objectManagerHelper->getObject('Magento\Catalog\Api\Data\ProductLinkExtension'); + $reflectionOfExtension = new \ReflectionClass('Magento\Catalog\Api\Data\ProductLinkExtension'); + $method = $reflectionOfExtension->getMethod('setData'); + $method->setAccessible(true); + $method->invokeArgs($groupExtension, array('qty', 1)); + + $outputGroupLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $outputGroupLink->setProductSku("Simple Product 1"); + $outputGroupLink->setLinkType("associated"); + $outputGroupLink->setLinkedProductSku("Simple Product 2"); + $outputGroupLink->setLinkedProductType("simple"); + $outputGroupLink->setPosition(0); + $outputGroupLink->setExtensionAttributes($groupExtension); + + $this->entityCollectionProviderMock->expects($this->at(0)) + ->method('getCollection') + ->with($this->model, 'related') + ->willReturn([$inputRelatedLink]); + $this->entityCollectionProviderMock->expects($this->at(1)) + ->method('getCollection') + ->with($this->model, 'upsell') + ->willReturn([]); + $this->entityCollectionProviderMock->expects($this->at(2)) + ->method('getCollection') + ->with($this->model, 'crosssell') + ->willReturn([]); + $this->entityCollectionProviderMock->expects($this->at(3)) + ->method('getCollection') + ->with($this->model, 'associated') + ->willReturn([$inputGroupLink]); + + $expectedOutput = [$outputRelatedLink, $outputGroupLink]; + $typeInstanceMock = $this->getMock( + 'Magento\ConfigurableProduct\Model\Product\Type\Simple', ["getSku"], [], '', false); + $typeInstanceMock + ->expects($this->atLeastOnce()) + ->method('getSku') + ->willReturn("Simple Product 1"); + $this->model->setTypeInstance($typeInstanceMock); + + $productLink1 = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $productLink2 = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link'); + $this->productLinkFactory->expects($this->at(0)) + ->method('create') + ->willReturn($productLink1); + $this->productLinkFactory->expects($this->at(1)) + ->method('create') + ->willReturn($productLink2); + + $extension = $this->objectManagerHelper->getObject('Magento\Catalog\Api\Data\ProductLinkExtension'); + $productLink2->setExtensionAttributes($extension); + + $links = $this->model->getProductLinks(); + // Match the links + $matches = 0; + foreach ($links as $link) { + foreach ($expectedOutput as $expected) { + if ($expected->getData() == $link->getData()) { + $matches++; + } + } + } + $this->assertEquals($matches, 2); + } +} diff --git a/app/code/Magento/GroupedProduct/etc/data_object.xml b/app/code/Magento/GroupedProduct/etc/service_data_attributes.xml similarity index 66% rename from app/code/Magento/GroupedProduct/etc/data_object.xml rename to app/code/Magento/GroupedProduct/etc/service_data_attributes.xml index d6a76ebaa081f2e742c78e66d70dc504c460bdde..0618cc9286cfd8ccb0cab46f6a505b05a39c4ee2 100644 --- a/app/code/Magento/GroupedProduct/etc/data_object.xml +++ b/app/code/Magento/GroupedProduct/etc/service_data_attributes.xml @@ -5,8 +5,8 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Catalog\Api\Data\ProductLinkInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Catalog\Api\Data\ProductLinkInterface"> <attribute code="qty" type="float" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/app/code/Magento/Newsletter/Model/Session.php b/app/code/Magento/Newsletter/Model/Session.php index 18d680df142601fb39eab95181cee59a79cca4cb..2ffb0b2f25236066922a7510267f10135c1a518f 100644 --- a/app/code/Magento/Newsletter/Model/Session.php +++ b/app/code/Magento/Newsletter/Model/Session.php @@ -8,7 +8,7 @@ namespace Magento\Newsletter\Model; /** * Newsletter session model */ -class Session extends \Magento\Framework\Session\Generic +class Session extends \Magento\Framework\Session\SessionManager { /** * Set error message diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index f2885610281947d30f55abdccb526e6a270c6ac4..5fa2c3aa052638f3841cefcc5ddfe6bf1ef4358a 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -442,7 +442,6 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel $this->setStatusChanged(true); try { - $this->save(); if ($isConfirmNeed === true && $isOwnSubscribes === false ) { @@ -450,6 +449,7 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel } else { $this->sendConfirmationSuccessEmail(); } + $this->save(); return $this->getStatus(); } catch (\Exception $e) { throw new \Exception($e->getMessage()); diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bd8380299a52ee06f8d95a9ddea229ae2649160e --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Newsletter\Test\Unit\Model; + +class SubscriberTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Newsletter\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + */ + protected $newsletterData; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $scopeConfig; + + /** + * @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + protected $transportBuilder; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerSession; + + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerRepository; + + /** + * @var \Magento\Customer\Api\AccountManagementInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerAccountManagement; + + /** + * @var \Magento\Framework\Translate\Inline\StateInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $inlineTranslation; + + /** + * @var \Magento\Newsletter\Model\Resource\Subscriber|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resource; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** + * @var \Magento\Newsletter\Model\Subscriber + */ + protected $subscriber; + + public function setUp() + { + $this->newsletterData = $this->getMock('Magento\Newsletter\Helper\Data', [], [], '', false); + $this->scopeConfig = $this->getMock('Magento\Framework\App\Config\ScopeConfigInterface'); + $this->transportBuilder = $this->getMock( + 'Magento\Framework\Mail\Template\TransportBuilder', + [ + 'setTemplateIdentifier', + 'setTemplateOptions', + 'setTemplateVars', + 'setFrom', + 'addTo', + 'getTransport' + ], + [], + '', + false + ); + $this->storeManager = $this->getMock('Magento\Store\Model\StoreManagerInterface'); + $this->customerSession = $this->getMock( + 'Magento\Customer\Model\Session', + [ + 'isLoggedIn', + 'getCustomerDataObject', + 'getCustomerId' + ], + [], + '', + false + ); + $this->customerRepository = $this->getMock('Magento\Customer\Api\CustomerRepositoryInterface'); + $this->customerAccountManagement = $this->getMock('Magento\Customer\Api\AccountManagementInterface'); + $this->inlineTranslation = $this->getMock('Magento\Framework\Translate\Inline\StateInterface'); + $this->resource = $this->getMock( + 'Magento\Newsletter\Model\Resource\Subscriber', + [ + 'loadByEmail', + 'getIdFieldName', + 'save' + ], + [], + '', + false + ); + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subscriber = $this->objectManager->getObject( + 'Magento\Newsletter\Model\Subscriber', + [ + 'newsletterData' => $this->newsletterData, + 'scopeConfig' => $this->scopeConfig, + 'transportBuilder' => $this->transportBuilder, + 'storeManager' => $this->storeManager, + 'customerSession' => $this->customerSession, + 'customerRepository' => $this->customerRepository, + 'customerAccountManagement' => $this->customerAccountManagement, + 'inlineTranslation' => $this->inlineTranslation, + 'resource' => $this->resource + ] + ); + } + + public function testSubscribe() + { + $email = 'subscriber_email@magento.com'; + $this->resource->expects($this->any())->method('loadByEmail')->willReturn( + [ + 'subscriber_status' => 3, + 'subscriber_email' => $email, + 'name' => 'subscriber_name' + ] + ); + $this->resource->expects($this->any())->method('getIdFieldName')->willReturn('id_field'); + $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); + $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn(true); + $customerDataModel = $this->getMock('\Magento\Customer\Api\Data\CustomerInterface'); + $this->customerSession->expects($this->any())->method('getCustomerDataObject')->willReturn($customerDataModel); + $this->customerSession->expects($this->any())->method('getCustomerId')->willReturn(1); + $customerDataModel->expects($this->any())->method('getEmail')->willReturn($email); + $this->customerRepository->expects($this->any())->method('getById')->willReturn($customerDataModel); + $customerDataModel->expects($this->any())->method('getStoreId')->willReturn(1); + $customerDataModel->expects($this->any())->method('getId')->willReturn(1); + $this->transportBuilder->expects($this->any())->method('setTemplateIdentifier')->willReturnSelf(); + $this->transportBuilder->expects($this->any())->method('setTemplateOptions')->willReturnSelf(); + $this->transportBuilder->expects($this->any())->method('setTemplateVars')->willReturnSelf(); + $this->transportBuilder->expects($this->any())->method('setFrom')->willReturnSelf(); + $this->transportBuilder->expects($this->any())->method('addTo')->willReturnSelf(); + $storeModel = $this->getMock('\Magento\Store\Model\Store', ['getId'], [], '', false); + $this->scopeConfig->expects($this->any())->method('getValue')->willReturn('owner_email@magento.com'); + $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); + $storeModel->expects($this->any())->method('getId')->willReturn(1); + $transport = $this->getMock('\Magento\Framework\Mail\TransportInterface'); + $this->transportBuilder->expects($this->any())->method('getTransport')->willReturn($transport); + $transport->expects($this->any())->method('sendMessage')->willReturnSelf(); + $inlineTranslation = $this->getMock('Magento\Framework\Translate\Inline\StateInterface'); + $inlineTranslation->expects($this->any())->method('resume')->willReturnSelf(); + $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); + $this->assertEquals(1, $this->subscriber->subscribe($email)); + } +} diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index 4eeb1ff2b61a7e865d24c28af7411c599b7c3f05..0b46fc59fb7dcbead0e93d40563787ce7f6af5b7 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -25,7 +25,6 @@ <preference for="Magento\Quote\Api\Data\TotalsInterface" type="\Magento\Quote\Model\Cart\Totals" /> <preference for="Magento\Quote\Api\Data\TotalsItemInterface" type="\Magento\Quote\Model\Quote\Cart\Totals\Item" /> <preference for="Magento\Quote\Api\Data\CurrencyInterface" type="\Magento\Quote\Model\Cart\Currency" /> - <preference for="Magento\Quote\Api\GuestCartManagementInterface" type="Magento\Quote\Model\GuestCart\GuestCartManagement" /> <preference for="Magento\Quote\Api\GuestCartRepositoryInterface" type="Magento\Quote\Model\GuestCart\GuestCartRepository" /> <preference for="Magento\Quote\Api\GuestCartItemRepositoryInterface" type="Magento\Quote\Model\GuestCart\GuestCartItemRepository" /> @@ -35,7 +34,6 @@ <preference for="Magento\Quote\Api\GuestShippingAddressManagementInterface" type="Magento\Quote\Model\GuestCart\GuestShippingAddressManagement" /> <preference for="Magento\Quote\Api\GuestShippingMethodManagementInterface" type="Magento\Quote\Model\GuestCart\GuestShippingMethodManagement" /> <preference for="Magento\Quote\Api\GuestBillingAddressManagementInterface" type="Magento\Quote\Model\GuestCart\GuestBillingAddressManagement" /> - <type name="Magento\Webapi\Controller\Rest\ParamsOverrider"> <arguments> <argument name="paramOverriders" xsi:type="array"> @@ -43,4 +41,14 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\QuoteRepository"> + <arguments> + <argument name="quoteCollection" xsi:type="object">Magento\Quote\Model\Resource\Quote\Collection\Proxy</argument> + </arguments> + </type> + <type name="Magento\Quote\Model\Quote\Address"> + <arguments> + <argument name="addressConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Review/Block/Form.php b/app/code/Magento/Review/Block/Form.php index 9682f444c4100126155f6daa0ecde88b656ce156..cf506a09857d50ca75e60af14307933802b9678b 100644 --- a/app/code/Magento/Review/Block/Form.php +++ b/app/code/Magento/Review/Block/Form.php @@ -163,7 +163,11 @@ class Form extends \Magento\Framework\View\Element\Template */ public function getProductInfo() { - return $this->productRepository->getById($this->getProductId()); + return $this->productRepository->getById( + $this->getProductId(), + false, + $this->_storeManager->getStore()->getId() + ); } /** diff --git a/app/code/Magento/Review/Model/Resource/Review/Collection.php b/app/code/Magento/Review/Model/Resource/Review/Collection.php index 7bd2de244b7adb3bdff739d5782cb681e832c41b..d32a6b04ac03043b9fc8a2c596ece7d84af58eaf 100644 --- a/app/code/Magento/Review/Model/Resource/Review/Collection.php +++ b/app/code/Magento/Review/Model/Resource/Review/Collection.php @@ -17,35 +17,35 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac * * @var string */ - protected $_reviewTable; + protected $_reviewTable = null; /** * Review detail table * * @var string */ - protected $_reviewDetailTable; + protected $_reviewDetailTable = null; /** * Review status table * * @var string */ - protected $_reviewStatusTable; + protected $_reviewStatusTable = null; /** * Review entity table * * @var string */ - protected $_reviewEntityTable; + protected $_reviewEntityTable = null; /** * Review store table * * @var string */ - protected $_reviewStoreTable; + protected $_reviewStoreTable = null; /** * Add store data flag @@ -111,11 +111,6 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac protected function _construct() { $this->_init('Magento\Review\Model\Review', 'Magento\Review\Model\Resource\Review'); - $this->_reviewTable = $this->getTable('review'); - $this->_reviewDetailTable = $this->getTable('review_detail'); - $this->_reviewStatusTable = $this->getTable('review_status'); - $this->_reviewEntityTable = $this->getTable('review_entity'); - $this->_reviewStoreTable = $this->getTable('review_store'); } /** @@ -127,7 +122,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac { parent::_initSelect(); $this->getSelect()->join( - ['detail' => $this->_reviewDetailTable], + ['detail' => $this->getReviewDetailTable()], 'main_table.review_id = detail.review_id', ['detail_id', 'title', 'detail', 'nickname', 'customer_id'] ); @@ -156,7 +151,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac { $inCond = $this->getConnection()->prepareSqlCondition('store.store_id', ['in' => $storeId]); $this->getSelect()->join( - ['store' => $this->_reviewStoreTable], + ['store' => $this->getReviewStoreTable()], 'main_table.review_id=store.review_id', [] ); @@ -184,18 +179,19 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac */ public function addEntityFilter($entity, $pkValue) { + $reviewEntityTable = $this->getReviewEntityTable(); if (is_numeric($entity)) { $this->addFilter('entity', $this->getConnection()->quoteInto('main_table.entity_id=?', $entity), 'string'); } elseif (is_string($entity)) { $this->_select->join( - $this->_reviewEntityTable, - 'main_table.entity_id=' . $this->_reviewEntityTable . '.entity_id', + $reviewEntityTable, + 'main_table.entity_id=' . $reviewEntityTable . '.entity_id', ['entity_code'] ); $this->addFilter( 'entity', - $this->getConnection()->quoteInto($this->_reviewEntityTable . '.entity_code=?', $entity), + $this->getConnection()->quoteInto($reviewEntityTable . '.entity_code=?', $entity), 'string' ); } @@ -268,7 +264,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac public function addReviewsTotalCount() { $this->_select->joinLeft( - ['r' => $this->_reviewTable], + ['r' => $this->getReviewTable()], 'main_table.entity_pk_value = r.entity_pk_value', ['total_reviews' => new \Zend_Db_Expr('COUNT(r.review_id)')] )->group( @@ -311,7 +307,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac $storesToReviews = []; if (count($reviewsIds) > 0) { $inCond = $adapter->prepareSqlCondition('review_id', ['in' => $reviewsIds]); - $select = $adapter->select()->from($this->_reviewStoreTable)->where($inCond); + $select = $adapter->select()->from($this->getReviewStoreTable())->where($inCond); $result = $adapter->fetchAll($select); foreach ($result as $row) { if (!isset($storesToReviews[$row['review_id']])) { @@ -329,4 +325,69 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac } } } + + /** + * Get review table + * + * @return string + */ + protected function getReviewTable() + { + if ($this->_reviewTable === null) { + $this->_reviewTable = $this->getTable('review'); + } + return $this->_reviewTable; + } + + /** + * Get review detail table + * + * @return string + */ + protected function getReviewDetailTable() + { + if ($this->_reviewDetailTable === null) { + $this->_reviewDetailTable = $this->getTable('review_detail'); + } + return $this->_reviewDetailTable; + } + + /** + * Get review status table + * + * @return string + */ + protected function getReviewStatusTable() + { + if ($this->_reviewStatusTable === null) { + $this->_reviewStatusTable = $this->getTable('review_status'); + } + return $this->_reviewStatusTable; + } + + /** + * Get review entity table + * + * @return string + */ + protected function getReviewEntityTable() + { + if ($this->_reviewEntityTable === null) { + $this->_reviewEntityTable = $this->getTable('review_entity'); + } + return $this->_reviewEntityTable; + } + + /** + * Get review store table + * + * @return string + */ + protected function getReviewStoreTable() + { + if ($this->_reviewStoreTable === null) { + $this->_reviewStoreTable = $this->getTable('review_store'); + } + return $this->_reviewStoreTable; + } } diff --git a/app/code/Magento/Review/Test/Unit/Block/FormTest.php b/app/code/Magento/Review/Test/Unit/Block/FormTest.php new file mode 100644 index 0000000000000000000000000000000000000000..32a59cf8334c59766d49955916821c587a1bb32f --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Block/FormTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Review\Test\Unit\Block; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class FormTest extends \PHPUnit_Framework_TestCase +{ + /** @var \Magento\Review\Block\Form */ + protected $object; + + /** @var ObjectManagerHelper */ + protected $objectManagerHelper; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $requestMock; + + /** @var \Magento\Framework\View\Element\Template\Context|\PHPUnit_Framework_MockObject_MockObject */ + protected $context; + + /** + * @var \Magento\Review\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + */ + protected $reviewDataMock; + + /** @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $productRepository; + + /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $storeManager; + + protected function setUp() + { + $this->storeManager = $this->getMock('\Magento\Store\Model\StoreManagerInterface'); + $this->requestMock = $this->getMock('\Magento\Framework\App\RequestInterface'); + $this->reviewDataMock = $this->getMockBuilder('\Magento\Review\Helper\Data') + ->disableOriginalConstructor() + ->getMock(); + + $this->reviewDataMock->expects($this->once()) + ->method('getIsGuestAllowToWrite') + ->willReturn(true); + + $this->context = $this->getMock('Magento\Framework\View\Element\Template\Context', [], [], '', false); + $this->context->expects( + $this->any() + )->method( + 'getStoreManager' + )->will( + $this->returnValue($this->storeManager) + ); + $this->context->expects($this->any()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->productRepository = $this->getMock('\Magento\Catalog\Api\ProductRepositoryInterface'); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->object = $this->objectManagerHelper->getObject( + 'Magento\Review\Block\Form', + [ + 'context' => $this->context, + 'reviewData' => $this->reviewDataMock, + 'productRepository' => $this->productRepository, + ] + ); + } + + public function testGetProductInfo() + { + $productId = 3; + $storeId = 1; + + $this->storeManager->expects( + $this->any() + )->method( + 'getStore' + )->will( + $this->returnValue(new \Magento\Framework\Object(['id' => $storeId])) + ); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('id', false) + ->willReturn($productId); + + $productMock = $this->getMock('Magento\Catalog\Api\Data\ProductInterface'); + $this->productRepository->expects($this->once()) + ->method('getById') + ->with($productId, false, $storeId) + ->willReturn($productMock); + + $this->assertSame($productMock, $this->object->getProductInfo()); + } +} diff --git a/app/code/Magento/Review/Test/Unit/Model/Resource/Review/CollectionTest.php b/app/code/Magento/Review/Test/Unit/Model/Resource/Review/CollectionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..67e67cdf8381745aa356f7b85129945f2d563d33 --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Model/Resource/Review/CollectionTest.php @@ -0,0 +1,188 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Review\Test\Unit\Model\Resource\Review; + +class CollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Review\Model\Resource\Review\Collection + */ + protected $model; + + /** + * @var \Zend_Db_Select | \PHPUnit_Framework_MockObject_MockObject + */ + protected $selectMock; + + /** + * @var \Magento\Store\Model\StoreManagerInterface | \PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManagerMock; + + /** + * @var \Magento\Framework\Model\Resource\Db\AbstractDb | \PHPUnit_Framework_MockObject_MockObject + */ + protected $resourceMock; + + /** + * @var \Zend_Db_Adapter_Abstract | \PHPUnit_Framework_MockObject_MockObject + */ + protected $readerAdapterMock; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + public function setUp() + { + $store = $this->getMock('\Magento\Store\Model\Store', ['getId'], [], '', false); + $store->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->storeManagerMock = $this->getMock('Magento\Store\Model\StoreManagerInterface'); + $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); + $this->objectManager = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)); + $this->resourceMock = $this->getMockBuilder('Magento\Framework\Model\Resource\Db\AbstractDb') + ->disableOriginalConstructor() + ->setMethods(['getReadConnection', 'getMainTable', 'getTable']) + ->getMockForAbstractClass(); + $this->readerAdapterMock = $this->getMockBuilder('\Zend_Db_Adapter_Abstract') + ->disableOriginalConstructor() + ->setMethods(['select', 'prepareSqlCondition', 'quoteInto']) + ->getMockForAbstractClass(); + $this->selectMock = $this->getMockBuilder('\Zend_Db_Select') + ->disableOriginalConstructor() + ->getMock(); + $this->readerAdapterMock->expects($this->any()) + ->method('select') + ->willReturn($this->selectMock); + $this->resourceMock->expects($this->any()) + ->method('getReadConnection') + ->willReturn($this->readerAdapterMock); + $this->resourceMock->expects($this->any()) + ->method('getMainTable') + ->willReturn('maintable'); + $this->resourceMock->expects($this->any()) + ->method('getTable') + ->willReturnCallback(function ($table) { + return $table; + }); + $this->model = $this->objectManager->getObject( + '\Magento\Review\Model\Resource\Review\Collection', + [ + 'storeManager' => $this->storeManagerMock, + 'resource' => $this->resourceMock, + ] + ); + + } + + public function testInitSelect() + { + $this->selectMock->expects($this->once()) + ->method('join') + ->with( + ['detail' => 'review_detail'], + 'main_table.review_id = detail.review_id', + ['detail_id', 'title', 'detail', 'nickname', 'customer_id'] + ); + $this->objectManager->getObject( + '\Magento\Review\Model\Resource\Review\Collection', + [ + 'storeManager' => $this->storeManagerMock, + 'resource' => $this->resourceMock, + ] + ); + } + + public function testAddStoreFilter() + { + $this->readerAdapterMock->expects($this->once()) + ->method('prepareSqlCondition'); + $this->selectMock->expects($this->once()) + ->method('join') + ->with( + ['store' => 'review_store'], + 'main_table.review_id=store.review_id', + [] + ); + $this->model->addStoreFilter(1); + } + + /** + * @param int|string $entity + * @param int $pkValue + * @param string $quoteIntoArguments1 + * @param string $quoteIntoArguments2 + * @param string $quoteIntoReturn1 + * @param string $quoteIntoReturn2 + * @param int $callNum + * @dataProvider addEntityFilterDataProvider + */ + public function testAddEntityFilter( + $entity, + $pkValue, + $quoteIntoArguments1, + $quoteIntoArguments2, + $quoteIntoReturn1, + $quoteIntoReturn2, + $callNum + ) { + $this->readerAdapterMock->expects($this->at(0)) + ->method('quoteInto') + ->with($quoteIntoArguments1[0], $quoteIntoArguments1[1]) + ->willReturn($quoteIntoReturn1); + $this->readerAdapterMock->expects($this->at(1)) + ->method('quoteInto') + ->with($quoteIntoArguments2[0], $quoteIntoArguments2[1]) + ->willReturn($quoteIntoReturn2); + $this->selectMock->expects($this->exactly($callNum)) + ->method('join') + ->with( + 'review_entity', + 'main_table.entity_id=' . 'review_entity' . '.entity_id', + ['entity_code'] + ); + $this->model->addEntityFilter($entity, $pkValue); + } + + public function addEntityFilterDataProvider() + { + return [ + [ + 1, + 2, + ['main_table.entity_id=?', 1], + ['main_table.entity_pk_value=?', 2], + 'quoteIntoReturn1', + 'quoteIntoReturn2', + 0 + ], + [ + 'entity', + 2, + ['review_entity.entity_code=?', 'entity'], + ['main_table.entity_pk_value=?', 2], + 'quoteIntoReturn1', + 'quoteIntoReturn2', + 1 + ] + ]; + } + + public function testAddReviewsTotalCount() + { + $this->selectMock->expects($this->once()) + ->method('joinLeft') + ->with( + ['r' => 'review'], + 'main_table.entity_pk_value = r.entity_pk_value', + ['total_reviews' => new \Zend_Db_Expr('COUNT(r.review_id)')] + )->willReturnSelf(); + $this->selectMock->expects($this->once()) + ->method('group'); + $this->model->addReviewsTotalCount(); + } +} diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php index 28afae3c0f9528343770c22dfe283b82324a7c5a..3a4a5c23033f48a2b2fcc41eb519330cb3e955fd 100644 --- a/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php +++ b/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php @@ -57,6 +57,14 @@ interface CreditmemoCommentInterface extends \Magento\Framework\Api\ExtensibleDa */ public function getCreatedAt(); + /** + * Sets the credit memo created-at timestamp. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the credit memo ID. * diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php index 205d5c37b3f1033132c9679a240e036223a2e5e8..ce5a46b9838b447159bcf96b87bbd8753e46ed5e 100644 --- a/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php +++ b/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php @@ -383,6 +383,15 @@ interface CreditmemoInterface extends \Magento\Framework\Api\ExtensibleDataInter * @return string Credit memo created-at timestamp. */ public function getCreatedAt(); + + /** + * Sets the credit memo created-at timestamp. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the credit memo status. * diff --git a/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php index 10c86a870231cd4cd0c24a165c1cc5cf022bcdaf..ec6fce4016f6a0d397858b3ebf5848cacdc3e047 100644 --- a/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php +++ b/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php @@ -55,6 +55,14 @@ interface InvoiceCommentInterface extends \Magento\Framework\Api\ExtensibleDataI */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the invoice. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the ID for the invoice. * diff --git a/app/code/Magento/Sales/Api/Data/InvoiceInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceInterface.php index e17decc98e0bbd364313f43a2d7373cbfd7990cc..fcbe20b83f28c96b939277d524a04dec9f923b7d 100644 --- a/app/code/Magento/Sales/Api/Data/InvoiceInterface.php +++ b/app/code/Magento/Sales/Api/Data/InvoiceInterface.php @@ -319,6 +319,14 @@ interface InvoiceInterface extends \Magento\Framework\Api\ExtensibleDataInterfac */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the invoice. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the discount amount for the invoice. * diff --git a/app/code/Magento/Sales/Api/Data/OrderInterface.php b/app/code/Magento/Sales/Api/Data/OrderInterface.php index 7bd62fbe1c3228b2241690b981a7f0ab8c2f84eb..42ccf6aee23c151088f27b7d6e714237a75ce845 100644 --- a/app/code/Magento/Sales/Api/Data/OrderInterface.php +++ b/app/code/Magento/Sales/Api/Data/OrderInterface.php @@ -914,6 +914,14 @@ interface OrderInterface extends \Magento\Framework\Api\ExtensibleDataInterface */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the order. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the customer date-of-birth (DOB) for the order. * diff --git a/app/code/Magento/Sales/Api/Data/OrderItemInterface.php b/app/code/Magento/Sales/Api/Data/OrderItemInterface.php index 6e59b03a98370feb88a6d6522c9fb84560e12bf3..e10e196380670f54fd11963a281738c50226d20c 100644 --- a/app/code/Magento/Sales/Api/Data/OrderItemInterface.php +++ b/app/code/Magento/Sales/Api/Data/OrderItemInterface.php @@ -578,6 +578,14 @@ interface OrderItemInterface extends \Magento\Framework\Api\ExtensibleDataInterf */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the order item. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the description for the order item. * diff --git a/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php b/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php index 4ef7daf322d37f0ca1b26f0ab7501e7ad5a79fcf..3a27e326c3b88866c1cb03ec4c84227391e46416 100644 --- a/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php +++ b/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php @@ -64,6 +64,14 @@ interface OrderStatusHistoryInterface extends \Magento\Framework\Api\ExtensibleD */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the order status history. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the ID for the order status history. * diff --git a/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php index 213b7c2e5e4931bd9f9295aee3b537f817891a68..e6bcd71928463f0bd09446f60bc303a1a31f4864 100644 --- a/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php +++ b/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php @@ -55,6 +55,14 @@ interface ShipmentCommentInterface extends \Magento\Framework\Api\ExtensibleData */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the shipment comment. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the ID for the shipment comment. * diff --git a/app/code/Magento/Sales/Api/Data/ShipmentInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentInterface.php index 41c676ea2081219e35d7e7d6561206f4f5bf5c24..67b3b60b05266ef8febff35e6df0952414ebdea1 100644 --- a/app/code/Magento/Sales/Api/Data/ShipmentInterface.php +++ b/app/code/Magento/Sales/Api/Data/ShipmentInterface.php @@ -103,6 +103,14 @@ interface ShipmentInterface extends \Magento\Framework\Api\ExtensibleDataInterfa */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the shipment. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the customer ID for the shipment. * diff --git a/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php index 7ace1ead4114db6c8c81c9a3921d56f1e9195727..e9bfa3915c4074096f9d5023e5a5c13514a92267 100644 --- a/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php +++ b/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php @@ -76,6 +76,14 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the shipment package. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets the description for the shipment package. * diff --git a/app/code/Magento/Sales/Api/Data/TransactionInterface.php b/app/code/Magento/Sales/Api/Data/TransactionInterface.php index 224d9765582f0e3381846f159814103e72111351..92d431c6e0b5754ed8222c5deb9f8fb02f28ce05 100644 --- a/app/code/Magento/Sales/Api/Data/TransactionInterface.php +++ b/app/code/Magento/Sales/Api/Data/TransactionInterface.php @@ -75,6 +75,14 @@ interface TransactionInterface extends \Magento\Framework\Api\ExtensibleDataInte */ public function getTransactionId(); + /** + * Sets the transaction ID for the transaction. + * + * @param int $id + * @return $this + */ + public function setTransactionId($id); + /** * Gets the parent ID for the transaction. * @@ -138,6 +146,14 @@ interface TransactionInterface extends \Magento\Framework\Api\ExtensibleDataInte */ public function getCreatedAt(); + /** + * Sets the created-at timestamp for the transaction. + * + * @param string $createdAt timestamp + * @return $this + */ + public function setCreatedAt($createdAt); + /** * Gets an array of child transactions for the transaction. * diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/EditInterface.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/EditInterface.php deleted file mode 100644 index 79fefe2a18515d4f143f561aba1c9a2f9e3f4cde..0000000000000000000000000000000000000000 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/EditInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -/** - * - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -interface EditInterface extends \Magento\Framework\App\ActionInterface -{ -} diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 02077b3e30163a4794f260175e74ec667ed17592..43f4b95497720ed8df3e7f74a4cc03e31255b980 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -35,7 +35,6 @@ use Magento\Sales\Model\Resource\Order\Status\History\Collection as HistoryColle * @method \Magento\Sales\Model\Resource\Order getResource() * @method int getGiftMessageId() * @method \Magento\Sales\Model\Order setGiftMessageId(int $value) - * @method \Magento\Sales\Model\Order setCreatedAt(string $value) * @method bool hasBillingAddressId() * @method \Magento\Sales\Model\Order unsBillingAddressId() * @method bool hasShippingAddressId() @@ -2481,6 +2480,14 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface return $this->getData(OrderInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(OrderInterface::CREATED_AT, $createdAt); + } + /** * Returns customer_dob * diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo.php b/app/code/Magento/Sales/Model/Order/Creditmemo.php index 9e77f5afd8635e1b6421a1915684a4c0a858cbd0..7fbc58d77171b57ccf708e81a9a4ad77a827293b 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo.php @@ -20,7 +20,6 @@ use Magento\Sales\Model\EntityInterface; * * @method \Magento\Sales\Model\Resource\Order\Creditmemo _getResource() * @method \Magento\Sales\Model\Resource\Order\Creditmemo getResource() - * @method \Magento\Sales\Model\Order\Creditmemo setCreatedAt(string $value) * @method \Magento\Sales\Model\Order\Invoice setSendEmail(bool $value) * @method \Magento\Sales\Model\Order\Invoice setCustomerNote(string $value) * @method string getCustomerNote() @@ -1034,6 +1033,14 @@ class Creditmemo extends AbstractModel implements EntityInterface, CreditmemoInt return $this->getData(CreditmemoInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(CreditmemoInterface::CREATED_AT, $createdAt); + } + /** * Returns creditmemo_status * diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php index a488162c322611e892d33bc90cd60be5dac7cc6f..2dac94c26b818bc2eab23acc83aea3079166a9d5 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php @@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel; /** * @method \Magento\Sales\Model\Resource\Order\Creditmemo\Comment _getResource() * @method \Magento\Sales\Model\Resource\Order\Creditmemo\Comment getResource() - * @method \Magento\Sales\Model\Order\Creditmemo\Comment setCreatedAt(string $value) */ class Comment extends AbstractModel implements CreditmemoCommentInterface { @@ -126,6 +125,14 @@ class Comment extends AbstractModel implements CreditmemoCommentInterface return $this->getData(CreditmemoCommentInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(CreditmemoCommentInterface::CREATED_AT, $createdAt); + } + /** * Returns is_customer_notified * diff --git a/app/code/Magento/Sales/Model/Order/Invoice.php b/app/code/Magento/Sales/Model/Order/Invoice.php index 8317b7041d61f3e747af2b644050854b3b55174d..036aec685121162933181a05e6c66af57af6d58c 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice.php +++ b/app/code/Magento/Sales/Model/Order/Invoice.php @@ -11,7 +11,6 @@ use Magento\Sales\Model\AbstractModel; use Magento\Sales\Model\EntityInterface; /** - * @method \Magento\Sales\Model\Order\Invoice setCreatedAt(string $value) * @method \Magento\Sales\Model\Order\Invoice setSendEmail(bool $value) * @method \Magento\Sales\Model\Order\Invoice setCustomerNote(string $value) * @method string getCustomerNote() @@ -969,6 +968,14 @@ class Invoice extends AbstractModel implements EntityInterface, InvoiceInterface return $this->getData(InvoiceInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(InvoiceInterface::CREATED_AT, $createdAt); + } + /** * Returns discount_amount * diff --git a/app/code/Magento/Sales/Model/Order/Invoice/Comment.php b/app/code/Magento/Sales/Model/Order/Invoice/Comment.php index 074e77b3638e656906f3da22e3727cea61423e98..e7b99dea1593ed699d4f76e6f745256f780e8075 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice/Comment.php +++ b/app/code/Magento/Sales/Model/Order/Invoice/Comment.php @@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel; /** * @method \Magento\Sales\Model\Resource\Order\Invoice\Comment _getResource() * @method \Magento\Sales\Model\Resource\Order\Invoice\Comment getResource() - * @method \Magento\Sales\Model\Order\Invoice\Comment setCreatedAt(string $value) */ class Comment extends AbstractModel implements InvoiceCommentInterface { @@ -126,6 +125,14 @@ class Comment extends AbstractModel implements InvoiceCommentInterface return $this->getData(InvoiceCommentInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(InvoiceCommentInterface::CREATED_AT, $createdAt); + } + /** * Returns is_customer_notified * diff --git a/app/code/Magento/Sales/Model/Order/Item.php b/app/code/Magento/Sales/Model/Order/Item.php index b23a34296c690d5584dc2a6e314401669981bd3a..602cde0a32e3ccbbc03195aa4933e4fdb81b3079 100644 --- a/app/code/Magento/Sales/Model/Order/Item.php +++ b/app/code/Magento/Sales/Model/Order/Item.php @@ -14,7 +14,6 @@ use Magento\Sales\Api\Data\OrderItemInterface; * * @method \Magento\Sales\Model\Resource\Order\Item _getResource() * @method \Magento\Sales\Model\Resource\Order\Item getResource() - * @method \Magento\Sales\Model\Order\Item setCreatedAt(string $value) * @method int getGiftMessageId() * @method \Magento\Sales\Model\Order\Item setGiftMessageId(int $value) * @method int getGiftMessageAvailable() @@ -949,6 +948,14 @@ class Item extends AbstractModel implements OrderItemInterface return $this->getData(OrderItemInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(OrderItemInterface::CREATED_AT, $createdAt); + } + /** * Returns description * diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 3c0d281f3e27cd8630d0471e6e8f505c29f6b10c..16cc9b45365adec1da8225d0c7ef6cd27ba813f1 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -19,7 +19,6 @@ use Magento\Sales\Model\AbstractModel; * * @method \Magento\Sales\Model\Resource\Order\Payment\Transaction _getResource() * @method \Magento\Sales\Model\Resource\Order\Payment\Transaction getResource() - * @method \Magento\Sales\Model\Order\Payment\Transaction setCreatedAt(string $value) * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) @@ -922,6 +921,14 @@ class Transaction extends AbstractModel implements TransactionInterface return $this->getData(TransactionInterface::TRANSACTION_ID); } + /** + * {@inheritdoc} + */ + public function setTransactionId($id) + { + return $this->setData(TransactionInterface::TRANSACTION_ID, $id); + } + /** * Returns method * @@ -1002,8 +1009,9 @@ class Transaction extends AbstractModel implements TransactionInterface return $this->getData(TransactionInterface::IS_CLOSED); } + //@codeCoverageIgnoreStart /** - * Returns created_at + * Gets the created-at timestamp for the transaction. * * @return string */ @@ -1012,7 +1020,14 @@ class Transaction extends AbstractModel implements TransactionInterface return $this->getData(TransactionInterface::CREATED_AT); } - //@codeCoverageIgnoreStart + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(TransactionInterface::CREATED_AT, $createdAt); + } + /** * {@inheritdoc} */ diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php index 3472e9663ad337ceb64c7399b06a60e389883708..6763a4895dfe30891530cdeb2c25a61e47047c14 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment.php +++ b/app/code/Magento/Sales/Model/Order/Shipment.php @@ -15,7 +15,6 @@ use Magento\Sales\Model\EntityInterface; * * @method \Magento\Sales\Model\Resource\Order\Shipment _getResource() * @method \Magento\Sales\Model\Resource\Order\Shipment getResource() - * @method \Magento\Sales\Model\Order\Shipment setCreatedAt(string $value) * @method \Magento\Sales\Model\Order\Invoice setSendEmail(bool $value) * @method \Magento\Sales\Model\Order\Invoice setCustomerNote(string $value) * @method string getCustomerNote() @@ -626,6 +625,14 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa return $this->getData(ShipmentInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(ShipmentInterface::CREATED_AT, $createdAt); + } + /** * Returns customer_id * diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Comment.php b/app/code/Magento/Sales/Model/Order/Shipment/Comment.php index 43361d79d59c4a4b6e403a7b0c7677ba1dbc1784..d1e9a5ac880bce77a0cef548f077e6e09ce754e7 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/Comment.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/Comment.php @@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel; /** * @method \Magento\Sales\Model\Resource\Order\Shipment\Comment _getResource() * @method \Magento\Sales\Model\Resource\Order\Shipment\Comment getResource() - * @method \Magento\Sales\Model\Order\Shipment\Comment setCreatedAt(string $value) */ class Comment extends AbstractModel implements ShipmentCommentInterface { @@ -126,6 +125,14 @@ class Comment extends AbstractModel implements ShipmentCommentInterface return $this->getData(ShipmentCommentInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(ShipmentCommentInterface::CREATED_AT, $createdAt); + } + /** * Returns is_customer_notified * diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Track.php b/app/code/Magento/Sales/Model/Order/Shipment/Track.php index 325c9837bee51bc906c55afdc3d172c9da8966f3..f5b15181cb8fcb89a5ddb301b28a4290573010ec 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/Track.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/Track.php @@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel; /** * @method \Magento\Sales\Model\Resource\Order\Shipment\Track _getResource() * @method \Magento\Sales\Model\Resource\Order\Shipment\Track getResource() - * @method \Magento\Sales\Model\Order\Shipment\Track setCreatedAt(string $value) * * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -232,6 +231,14 @@ class Track extends AbstractModel implements ShipmentTrackInterface return $this->getData(ShipmentTrackInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(ShipmentTrackInterface::CREATED_AT, $createdAt); + } + /** * Returns description * diff --git a/app/code/Magento/Sales/Model/Order/Status/History.php b/app/code/Magento/Sales/Model/Order/Status/History.php index edd828956c8241f11fcc8d929f4618a36d75e858..b442ebba05f7df7af8691e42949e79c174b0cb74 100644 --- a/app/code/Magento/Sales/Model/Order/Status/History.php +++ b/app/code/Magento/Sales/Model/Order/Status/History.php @@ -14,7 +14,6 @@ use Magento\Sales\Model\AbstractModel; * * @method \Magento\Sales\Model\Resource\Order\Status\History _getResource() * @method \Magento\Sales\Model\Resource\Order\Status\History getResource() - * @method \Magento\Sales\Model\Order\Status\History setCreatedAt(string $value) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class History extends AbstractModel implements OrderStatusHistoryInterface @@ -196,6 +195,14 @@ class History extends AbstractModel implements OrderStatusHistoryInterface return $this->getData(OrderStatusHistoryInterface::CREATED_AT); } + /** + * {@inheritdoc} + */ + public function setCreatedAt($createdAt) + { + return $this->setData(OrderStatusHistoryInterface::CREATED_AT, $createdAt); + } + /** * Returns entity_id * diff --git a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php index 69f3f6e0df310d27dc7c7c6774ad5a62ca1e82cb..9359af03ee68dfcb020c7e7ed1a9923bd4d456ec 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php @@ -63,7 +63,7 @@ class AsyncSendingTest extends \PHPUnit_Framework_TestCase public function testAfterSave($value, $oldValue, $eventName) { $path = 'sales_email/general/async_sending'; - $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT; + $scope = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT; $this->object->setData(['value' => $value, 'path' => $path, 'scope' => $scope]); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php index 972cfae38e2ae4273235a2c7a7d1ac260b4f45a3..729c19940ccff8b5f464eeeb895bc74e5aeafa16 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php @@ -63,7 +63,7 @@ class AsyncIndexingTest extends \PHPUnit_Framework_TestCase public function testAfterSave($value, $oldValue, $eventName) { $path = 'dev/grid/async_indexing'; - $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT; + $scope = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT; $this->object->setData(['value' => $value, 'path' => $path, 'scope' => $scope]); diff --git a/app/code/Magento/Sales/etc/data_object.xml b/app/code/Magento/Sales/etc/service_data_attributes.xml similarity index 78% rename from app/code/Magento/Sales/etc/data_object.xml rename to app/code/Magento/Sales/etc/service_data_attributes.xml index 8ef8080db8a29120f255cedfe0c083f45d85e5e5..1e7f320c18ed6e98b0c979d23e80e6ff48478fa4 100644 --- a/app/code/Magento/Sales/etc/data_object.xml +++ b/app/code/Magento/Sales/etc/service_data_attributes.xml @@ -5,10 +5,10 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Sales\Api\Data\OrderInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Sales\Api\Data\OrderInterface"> <attribute code="applied_taxes" type="Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface[]" /> <attribute code="item_applied_taxes" type="Magento\Tax\Api\Data\OrderTaxDetailsItemInterface[]" /> <attribute code="converting_from_quote" type="boolean" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/app/code/Magento/Search/Model/SearchEngine.php b/app/code/Magento/Search/Model/SearchEngine.php index c4d5c4787a17af5dbfd7f042da520650493642c3..f80e6bfd8796f2d6e2dae2901f27028964c97b6c 100644 --- a/app/code/Magento/Search/Model/SearchEngine.php +++ b/app/code/Magento/Search/Model/SearchEngine.php @@ -17,14 +17,21 @@ class SearchEngine implements SearchEngineInterface /** * @var AdapterInterface */ - protected $adapter; + private $adapter = null; + + /** + * Adapter factory + * + * @var AdapterFactory + */ + private $adapterFactory; /** * @param AdapterFactory $adapterFactory */ public function __construct(AdapterFactory $adapterFactory) { - $this->adapter = $adapterFactory->create(); + $this->adapterFactory = $adapterFactory; } /** @@ -32,6 +39,19 @@ class SearchEngine implements SearchEngineInterface */ public function search(RequestInterface $request) { - return $this->adapter->query($request); + return $this->getAdapter()->query($request); + } + + /** + * Get adapter + * + * @return AdapterInterface + */ + protected function getAdapter() + { + if ($this->adapter === null) { + $this->adapter = $this->adapterFactory->create(); + } + return $this->adapter; } } diff --git a/app/code/Magento/Shipping/Model/Order/Track.php b/app/code/Magento/Shipping/Model/Order/Track.php index 81b506a7d10d9a821df999357d74a3c914feb8a9..9763f2bdb6e49035dd9b5d16b0677b635b6dd39b 100644 --- a/app/code/Magento/Shipping/Model/Order/Track.php +++ b/app/code/Magento/Shipping/Model/Order/Track.php @@ -19,7 +19,6 @@ use Magento\Framework\Api\AttributeValueFactory; * @method string getTitle() * @method string getCarrierCode() * @method string getCreatedAt() - * @method \Magento\Sales\Model\Order\Shipment\Track setCreatedAt(string $value) * @method string getUpdatedAt() * @method \Magento\Sales\Api\Data\ShipmentTrackExtensionInterface getExtensionAttributes() */ diff --git a/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php b/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php index 4f03885e8cd7a910cb35abf05970bd86deb5a790..7bb9981369440c9c8062ffe6308376c8b089a05f 100644 --- a/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php +++ b/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php @@ -7,6 +7,9 @@ */ namespace Magento\Store\Model\Config\Reader; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\LocalizedException; + class DefaultReader implements \Magento\Framework\App\Config\Scope\ReaderInterface { /** @@ -42,14 +45,21 @@ class DefaultReader implements \Magento\Framework\App\Config\Scope\ReaderInterfa /** * Read configuration data * + * @param null|string $scope + * @throws LocalizedException Exception is thrown when scope other than default is given * @return array */ - public function read() + public function read($scope = null) { - $config = $this->_initialConfig->getData(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT); + $scope = $scope === null ? ScopeConfigInterface::SCOPE_TYPE_DEFAULT : $scope; + if ($scope !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT) { + throw new \Magento\Framework\Exception\LocalizedException(__("Only default scope allowed")); + } + + $config = $this->_initialConfig->getData($scope); $collection = $this->_collectionFactory->create( - ['scope' => \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT] + ['scope' => $scope] ); $dbDefaultConfig = []; foreach ($collection as $item) { diff --git a/app/code/Magento/Store/Model/Config/Reader/Store.php b/app/code/Magento/Store/Model/Config/Reader/Store.php index c5456bcb102f9ad2952c17f33ac41f48c1ae777a..151c3625f2b1dbf596d222eaf10b3f7c9e07ce5e 100644 --- a/app/code/Magento/Store/Model/Config/Reader/Store.php +++ b/app/code/Magento/Store/Model/Config/Reader/Store.php @@ -5,6 +5,7 @@ */ namespace Magento\Store\Model\Config\Reader; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\NoSuchEntityException; class Store implements \Magento\Framework\App\Config\Scope\ReaderInterface @@ -66,7 +67,7 @@ class Store implements \Magento\Framework\App\Config\Scope\ReaderInterface /** * Read configuration by code * - * @param string $code + * @param null|string $code * @return array * @throws NoSuchEntityException */ @@ -74,7 +75,7 @@ class Store implements \Magento\Framework\App\Config\Scope\ReaderInterface { if (empty($code)) { $store = $this->_storeManager->getStore(); - } elseif (($code == \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT)) { + } elseif (($code == ScopeConfigInterface::SCOPE_TYPE_DEFAULT)) { $store = $this->_storeManager->getDefaultStoreView(); } else { $store = $this->_storeFactory->create(); diff --git a/app/code/Magento/Store/Model/Config/Reader/Website.php b/app/code/Magento/Store/Model/Config/Reader/Website.php index 1448b04bf9afbf5e6b49b5e223f53b9ec2a52f69..7971deb6c9713199cfe7c85617ebe767556edb18 100644 --- a/app/code/Magento/Store/Model/Config/Reader/Website.php +++ b/app/code/Magento/Store/Model/Config/Reader/Website.php @@ -5,6 +5,8 @@ */ namespace Magento\Store\Model\Config\Reader; +use Magento\Framework\App\Config\ScopeConfigInterface; + class Website implements \Magento\Framework\App\Config\Scope\ReaderInterface { /** @@ -62,7 +64,7 @@ class Website implements \Magento\Framework\App\Config\Scope\ReaderInterface public function read($code = null) { $config = array_replace_recursive( - $this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT)->getSource(), + $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT)->getSource(), $this->_initialConfig->getData("websites|{$code}") ); diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 1da41cae73e20593dcdc498b6b4a1bc94bb5a0b6..494d1c129254154208d81e6956d72027003f9dc4 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -5,6 +5,7 @@ */ namespace Magento\Store\Model; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Http\Context; use Magento\Framework\Model\AbstractModel; @@ -55,7 +56,7 @@ class Store extends AbstractModel implements const XML_PATH_UNSECURE_BASE_URL = 'web/unsecure/base_url'; const XML_PATH_SECURE_BASE_URL = 'web/secure/base_url'; - + const XML_PATH_SECURE_IN_FRONTEND = 'web/secure/use_in_frontend'; const XML_PATH_SECURE_IN_ADMINHTML = 'web/secure/use_in_adminhtml'; @@ -479,7 +480,7 @@ class Store extends AbstractModel implements { $data = $this->_config->getValue($path, ScopeInterface::SCOPE_STORE, $this->getCode()); if (!$data) { - $data = $this->_config->getValue($path, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT); + $data = $this->_config->getValue($path, ScopeConfigInterface::SCOPE_TYPE_DEFAULT); } return $data === false ? null : $data; } @@ -788,7 +789,7 @@ class Store extends AbstractModel implements if ($configValue == self::PRICE_SCOPE_GLOBAL) { return $this->_config->getValue( \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); } else { return $this->_getConfig(\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE); diff --git a/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php b/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php index 9e91f4b43281522ac0a2d3688167488893df43e3..25875ae67dd5973525a0a59cbf7e2c1635c58691 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Store\Test\Unit\Model\Config\Reader; +use Magento\Framework\App\Config\ScopeConfigInterface; + class DefaultReaderTest extends \PHPUnit_Framework_TestCase { /** @@ -46,7 +48,7 @@ class DefaultReaderTest extends \PHPUnit_Framework_TestCase )->method( 'getData' )->with( - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT )->will( $this->returnValue(['config' => ['key1' => 'default_value1', 'key2' => 'default_value2']]) ); diff --git a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php index e9a6f8ecf7207c03889a4043d02168d18e6e5b72..8d305848c554f900bd521f04490c7b8f18fca6a4 100644 --- a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php @@ -8,6 +8,7 @@ namespace Magento\Store\Test\Unit\Model; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Store\Model\Store; @@ -399,7 +400,7 @@ class StoreTest extends \PHPUnit_Framework_TestCase ['catalog/price/scope', ScopeInterface::SCOPE_STORE, 'scope_code', $priceScope], [ \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, 'USD' ], diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index ebbc61f6036c8723c1fe943e07795097b63f1f6d..c1584145c2d3db3ad86924dd7ac6b0099155aa18 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -79,7 +79,7 @@ <arguments> <argument name="session" xsi:type="object" shared="false">Magento\Framework\Session\Generic\Proxy</argument> <argument name="isCustomEntryPoint" xsi:type="init_parameter">Magento\Store\Model\Store::CUSTOM_ENTRY_POINT_PARAM</argument> - <argument name="url" xsi:type="object" shared="false">Magento\Framework\UrlInterface</argument> + <argument name="url" xsi:type="object" shared="false">Magento\Framework\UrlInterface\Proxy</argument> </arguments> </type> <type name="Magento\Store\Model\StoreManager"> diff --git a/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php b/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php index 356d39d0bc106519315c3fa76da8321931c116fd..a5865871a5f59f25f9d72d624d277088d5f4ae41 100644 --- a/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php +++ b/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php @@ -7,18 +7,6 @@ namespace Magento\Tax\Api\Data; interface AppliedTaxInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_TAX_RATE_KEY = 'tax_rate_key'; - - const KEY_PERCENT = 'percent'; - - const KEY_AMOUNT = 'amount'; - - const KEY_RATES = 'rates'; - /**#@-*/ - /** * Get tax rate key * diff --git a/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php b/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php index cee4ab16fd1f0acdde90bd353b3d264e4ba52b11..afd3231335ea011e6daed2e0fa5537849c376362 100644 --- a/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php +++ b/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php @@ -8,16 +8,6 @@ namespace Magento\Tax\Api\Data; interface AppliedTaxRateInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_CODE = 'code'; - - const KEY_TITLE = 'title'; - - const KEY_PERCENT = 'percent'; - /**#@-*/ - /** * Get code * diff --git a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php index 7123087eacf565f0341178e6f9643c9a98bf7036..603d287eabff739644b9ed7ada2d397b4c59d857 100644 --- a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php +++ b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php @@ -9,20 +9,6 @@ namespace Magento\Tax\Api\Data; interface OrderTaxDetailsAppliedTaxInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_CODE = 'code'; - - const KEY_TITLE = 'title'; - - const KEY_PERCENT = 'percent'; - - const KEY_AMOUNT = 'amount'; - - const KEY_BASE_AMOUNT = 'base_amount'; - /**#@-*/ - /** * Get code * diff --git a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php index d68de6f5858aab5c3362c60d62a1d006bc22c42d..d57fb6ba5194faac79dbcddd4e0269adb81c6dae 100644 --- a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php +++ b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php @@ -9,10 +9,6 @@ namespace Magento\Tax\Api\Data; interface OrderTaxDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - const KEY_APPLIED_TAXES = 'applied_taxes'; - - const KEY_ITEMS = 'items'; - /** * Get applied taxes at order level * diff --git a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php index 905a0aa203594097e5377130679dfb4e5bb18a92..a43d4ccd7dfc4b5f1f95ed1fbf29527bdea40e89 100644 --- a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php +++ b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php @@ -9,18 +9,6 @@ namespace Magento\Tax\Api\Data; interface OrderTaxDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_TYPE = 'type'; - - const KEY_ITEM_ID = 'item_id'; - - const KEY_ASSOCIATED_ITEM_ID = 'associated_item_id'; - - const KEY_APPLIED_TAXES = 'applied_taxes'; - /**#@-*/ - /** * Get type (shipping, product, weee, gift wrapping, etc) * diff --git a/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php b/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php index f7d69e1572f86eaecfad263eeeb026ebe8f280bc..b3250a1f7caa09419ffc35169ac0362ffdc2ef2a 100644 --- a/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php +++ b/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php @@ -9,22 +9,6 @@ namespace Magento\Tax\Api\Data; interface QuoteDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_BILLING_ADDRESS = 'billing_address'; - - const KEY_SHIPPING_ADDRESS = 'shipping_address'; - - const KEY_CUSTOMER_TAX_CLASS_KEY = 'customer_tax_class_key'; - - const KEY_ITEMS = 'items'; - - const CUSTOMER_TAX_CLASS_ID = 'customer_tax_class_id'; - - const KEY_CUSTOMER_ID = 'customer_id'; - /**#@-*/ - /** * Get customer billing address * diff --git a/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php b/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php index ea3e57327f3115581853572e3159628b600c31cd..daffa01d4f87651346965eab83a723e7f806df28 100644 --- a/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php +++ b/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php @@ -7,32 +7,6 @@ namespace Magento\Tax\Api\Data; interface QuoteDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_CODE = 'code'; - - const KEY_TYPE = 'type'; - - const KEY_TAX_CLASS_KEY = 'tax_class_key'; - - const KEY_UNIT_PRICE = 'unit_price'; - - const KEY_QUANTITY = 'quantity'; - - const KEY_TAX_INCLUDED = 'tax_included'; - - const KEY_SHORT_DESCRIPTION = 'short_description'; - - const KEY_DISCOUNT_AMOUNT = 'discount_amount'; - - const KEY_PARENT_CODE = 'parent_code'; - - const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code'; - - const KEY_TAX_CLASS_ID = 'tax_class_id'; - /**#@-*/ - /** * Get code (sku or shipping code) * @@ -172,7 +146,7 @@ interface QuoteDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDat /** * Get associated item code if this item is associated with another item, null otherwise * - * @return mixed|null + * @return int|null */ public function getAssociatedItemCode(); diff --git a/app/code/Magento/Tax/Api/Data/TaxClassInterface.php b/app/code/Magento/Tax/Api/Data/TaxClassInterface.php index 80774ef47c646bed3cb54e3813b7968c37536adc..632219c6ad66986690b0655198feceb4f271b0a5 100644 --- a/app/code/Magento/Tax/Api/Data/TaxClassInterface.php +++ b/app/code/Magento/Tax/Api/Data/TaxClassInterface.php @@ -9,15 +9,6 @@ namespace Magento\Tax\Api\Data; interface TaxClassInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * - * Tax class field key. - */ - const KEY_ID = 'class_id'; - const KEY_NAME = 'class_name'; - const KEY_TYPE = 'class_type'; - /**#@-*/ - /** * Get tax class ID. * diff --git a/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php b/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php index 9f0a25a170cbadbd71ad8e1c1e7d7cd6de7d0c27..d6f5d742ad88069a9810c5f815a3a0ac8668f18a 100644 --- a/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php +++ b/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php @@ -10,19 +10,10 @@ use Magento\Framework\Api\ExtensibleDataInterface; interface TaxClassKeyInterface extends ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_TYPE = 'type'; - - const KEY_VALUE = 'value'; - /**#@-*/ - /**#@+ * Constants defined for type of tax class key */ - const TYPE_ID = 'id'; - + const TYPE_ID = 'id'; const TYPE_NAME = 'name'; /**#@-*/ diff --git a/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php b/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php index f06ba96bc54ca28a6ed4e167d48bbc4e553dcd15..ce0ae30e69e15cf434a0e034be6d913afc9cc245 100644 --- a/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php +++ b/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php @@ -8,21 +8,6 @@ namespace Magento\Tax\Api\Data; interface TaxDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_SUBTOTAL = 'subtotal'; - - const KEY_TAX_AMOUNT = 'tax_amount'; - - const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount'; - - const KEY_APPLIED_TAXES = 'applied_taxes'; - - const KEY_ITEMS = 'items'; - - /**#@-*/ - /** * Get subtotal * diff --git a/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php b/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php index fad3229d7076a589363cbabf9f5bcb064984e04f..0550b6614dfc76ef111e29179a96551e476a831b 100644 --- a/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php +++ b/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php @@ -8,36 +8,6 @@ namespace Magento\Tax\Api\Data; interface TaxDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_CODE = 'code'; - - const KEY_TYPE = 'type'; - - const KEY_TAX_PERCENT = 'tax_percent'; - - const KEY_PRICE = 'price'; - - const KEY_PRICE_INCL_TAX = 'price_incl_tax'; - - const KEY_ROW_TOTAL = 'row_total'; - - const KEY_ROW_TOTAL_INCL_TAX = 'row_total_incl_tax'; - - const KEY_ROW_TAX = 'row_tax'; - - const KEY_TAXABLE_AMOUNT = 'taxable_amount'; - - const KEY_DISCOUNT_AMOUNT = 'discount_amount'; - - const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount'; - - const KEY_APPLIED_TAXES = 'applied_taxes'; - - const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code'; - /**#@-*/ - /** * Get code (sku or shipping code) * @@ -221,7 +191,7 @@ interface TaxDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataI /** * Return associated item code if this item is associated with another item, null otherwise * - * @return mixed|null + * @return int|null */ public function getAssociatedItemCode(); diff --git a/app/code/Magento/Tax/Api/Data/TaxRateInterface.php b/app/code/Magento/Tax/Api/Data/TaxRateInterface.php index 881bb7fa7e013838c558f5514af373dcbe6fceaa..34cf62414862a644f928531ce9fafe996648c32e 100644 --- a/app/code/Magento/Tax/Api/Data/TaxRateInterface.php +++ b/app/code/Magento/Tax/Api/Data/TaxRateInterface.php @@ -9,22 +9,6 @@ namespace Magento\Tax\Api\Data; interface TaxRateInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * Constants defined for keys of array, makes typos less likely - */ - const KEY_ID = 'id'; - const KEY_COUNTRY_ID = 'tax_country_id'; - const KEY_REGION_ID = 'tax_region_id'; - const KEY_REGION_NAME = 'region_name'; - const KEY_POSTCODE = 'tax_postcode'; - const KEY_ZIP_IS_RANGE = 'zip_is_range'; - const KEY_ZIP_RANGE_FROM = 'zip_from'; - const KEY_ZIP_RANGE_TO = 'zip_to'; - const KEY_PERCENTAGE_RATE = 'rate'; - const KEY_CODE = 'code'; - const KEY_TITLES = 'titles'; - /**#@-*/ - /** * Get id * diff --git a/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php b/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php index 8d3991070b9565d9527c7c036aca9e45864d15f4..7dcfd2413759636b184d8e6cac0a789d6ecfacee 100644 --- a/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php +++ b/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php @@ -9,15 +9,6 @@ namespace Magento\Tax\Api\Data; interface TaxRateTitleInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - /**#@+ - * - * Tax rate field key. - */ - const KEY_STORE_ID = 'store_id'; - - const KEY_VALUE_ID = 'value'; - /**#@-*/ - /** * Get store id * diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php index 08a7803aa85474510a9135d22b7819dd4122c03c..84e32da4ae97439c3fbc7d8e8fccefc364b9f6cc 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php @@ -65,6 +65,11 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic */ protected $_taxRateCollection; + /** + * @var \Magento\Tax\Model\Calculation\Rate\Converter + */ + protected $_taxRateConverter; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry @@ -75,6 +80,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic * @param \Magento\Tax\Helper\Data $taxData * @param \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository * @param \Magento\Tax\Model\TaxRateCollection $taxRateCollection + * @param \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -88,6 +94,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic \Magento\Tax\Helper\Data $taxData, \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository, \Magento\Tax\Model\TaxRateCollection $taxRateCollection, + \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter, array $data = [] ) { $this->_regionFactory = $regionFactory; @@ -96,6 +103,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic $this->_taxData = $taxData; $this->_taxRateRepository = $taxRateRepository; $this->_taxRateCollection = $taxRateCollection; + $this->_taxRateConverter = $taxRateConverter; parent::__construct($context, $registry, $formFactory, $data); } @@ -127,7 +135,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic } $sessionFormValues = (array)$this->_coreRegistry->registry(RegistryConstants::CURRENT_TAX_RATE_FORM_DATA); - $formData = isset($taxRateDataObject) ? $this->extractTaxRateData($taxRateDataObject) : []; + $formData = isset($taxRateDataObject) ? $this->_taxRateConverter->createArrayFromServiceObject($taxRateDataObject) : []; $formData = array_merge($formData, $sessionFormValues); if (isset($formData['zip_is_range']) && $formData['zip_is_range'] && !isset($formData['tax_postcode'])) { @@ -286,65 +294,4 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic return parent::_prepareForm(); } - - /** - * Get Tax Rates Collection - * - * @return mixed - */ - public function getRateCollection() - { - if ($this->getData('rate_collection') == null) { - $items = $this->_taxRateCollection->getItems(); - $rates = []; - foreach ($items as $rate) { - $rateData = $rate->getData(); - if (isset($rateData['titles'])) { - foreach ($rateData['titles'] as $storeId => $value) { - $rateData['title[' . $storeId . ']'] = $value; - } - } - unset($rateData['titles']); - $rates[] = $rateData; - } - - $this->setRateCollection($rates); - } - return $this->getData('rate_collection'); - } - - /** - * Extract tax rate data in a format which is - * - * @param \Magento\Tax\Api\Data\TaxRateInterface $taxRate - * @return array - */ - protected function extractTaxRateData($taxRate) - { - $formData = [ - 'tax_calculation_rate_id' => $taxRate->getId(), - 'tax_country_id' => $taxRate->getTaxCountryId(), - 'tax_region_id' => $taxRate->getTaxRegionId(), - 'tax_postcode' => $taxRate->getTaxPostcode(), - 'code' => $taxRate->getCode(), - 'rate' => $taxRate->getRate(), - 'zip_is_range' => false, - ]; - - if ($taxRate->getZipFrom() && $taxRate->getZipTo()) { - $formData['zip_is_range'] = true; - $formData['zip_from'] = $taxRate->getZipFrom(); - $formData['zip_to'] = $taxRate->getZipTo(); - } - - if ($taxRate->getTitles()) { - $titleData = []; - foreach ($taxRate->getTitles() as $title) { - $titleData[] = [$title->getStoreId() => $title->getValue()]; - } - $formData['title'] = $titleData; - } - - return $formData; - } } diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php index 0d2b16bee6f0dff1154bba3c025f3a0f9b0b541a..fb434ca6dd3092395c7df36a0f4cd1dfa0fdd222 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php @@ -297,6 +297,16 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic return $this->getUrl('tax/rate/ajaxSave/'); } + /** + * Retrieve Tax Rate load URL + * + * @return string + */ + public function getTaxRateLoadUrl() + { + return $this->getUrl('tax/rate/ajaxLoad/'); + } + /** * Extract tax rule data in a format which is * diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate.php index 21a165edf65bf48964f43da33811e285fcbdfffe..e25e0a19a87a2752204de23adc90e6106e6c559e 100644 --- a/app/code/Magento/Tax/Controller/Adminhtml/Rate.php +++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate.php @@ -21,38 +21,30 @@ class Rate extends \Magento\Backend\App\Action protected $_coreRegistry; /** - * @var \Magento\Tax\Api\TaxRateRepositoryInterface + * @var \Magento\Tax\Model\Calculation\Rate\Converter */ - protected $_taxRateRepository; + protected $_taxRateConverter; /** - * @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory - */ - protected $_taxRateDataObjectFactory; - - /** - * @var \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory + * @var \Magento\Tax\Api\TaxRateRepositoryInterface */ - protected $_taxRateTitleDataObjectFactory; + protected $_taxRateRepository; /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Registry $coreRegistry + * @param \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter * @param \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository - * @param \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory - * @param \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry, - \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository, - \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory, - \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory + \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter, + \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository ) { $this->_coreRegistry = $coreRegistry; + $this->_taxRateConverter = $taxRateConverter; $this->_taxRateRepository = $taxRateRepository; - $this->_taxRateDataObjectFactory = $taxRateDataObjectFactory; - $this->_taxRateTitleDataObjectFactory = $taxRateTitleDataObjectFactory; parent::__construct($context); } @@ -96,50 +88,4 @@ class Rate extends \Magento\Backend\App\Action { return $this->_authorization->isAllowed('Magento_Tax::manage_tax'); } - - /** - * Populate a tax rate data object - * - * @param array $formData - * @return \Magento\Tax\Api\Data\TaxRateInterface - */ - protected function populateTaxRateData($formData) - { - $taxRate = $this->_taxRateDataObjectFactory->create(); - $taxRate->setId($this->extractFormData($formData, 'tax_calculation_rate_id')) - ->setTaxCountryId($this->extractFormData($formData, 'tax_country_id')) - ->setTaxRegionId($this->extractFormData($formData, 'tax_region_id')) - ->setTaxPostcode($this->extractFormData($formData, 'tax_postcode')) - ->setCode($this->extractFormData($formData, 'code')) - ->setRate($this->extractFormData($formData, 'rate')); - if (isset($formData['zip_is_range']) && $formData['zip_is_range']) { - $taxRate->setZipFrom($this->extractFormData($formData, 'zip_from')) - ->setZipTo($this->extractFormData($formData, 'zip_to'))->setZipIsRange(1); - } - - if (isset($formData['title'])) { - $titles = []; - foreach ($formData['title'] as $storeId => $value) { - $titles[] = $this->_taxRateTitleDataObjectFactory->create()->setStoreId($storeId)->setValue($value); - } - $taxRate->setTitles($titles); - } - - return $taxRate; - } - - /** - * Determines if an array value is set in the form data array and returns it. - * - * @param array $formData the form to get data from - * @param string $fieldName the key - * @return null|string - */ - protected function extractFormData($formData, $fieldName) - { - if (isset($formData[$fieldName])) { - return $formData[$fieldName]; - } - return null; - } } diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxLoad.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxLoad.php new file mode 100755 index 0000000000000000000000000000000000000000..673ea280bab25ed504a240bfcddb25d1e2cbadfe --- /dev/null +++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxLoad.php @@ -0,0 +1,50 @@ +<?php +/** + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Tax\Controller\Adminhtml\Rate; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Controller\ResultFactory; + +class AjaxLoad extends \Magento\Tax\Controller\Adminhtml\Rate +{ + /** + * Json needed for the Ajax Edit Form + * + * @return void + */ + public function execute() + { + $rateId = (int)$this->getRequest()->getParam('id'); + try { + /* @var \Magento\Tax\Api\Data\TaxRateInterface */ + $taxRateDataObject = $this->_taxRateRepository->get($rateId); + /* @var array */ + $resultArray= $this->_taxRateConverter->createArrayFromServiceObject($taxRateDataObject, true); + + $responseContent = [ + 'success' => true, + 'error_message' => '', + 'result'=>$resultArray, + ]; + } catch (NoSuchEntityException $e) { + $responseContent = [ + 'success' => false, + 'error_message' => $e->getMessage(), + ]; + } catch (\Exception $e) { + $responseContent = [ + 'success' => false, + 'error_message' => __('An error occurred while loading this tax rate.'), + ]; + } + + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + $resultJson->setData($responseContent); + return $resultJson; + } +} diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php index fd5471314d2d7066ae4462d3276dfcf0c55bb193..32ba0758fbd58b291e036ec525b06c6c3c3242c4 100644 --- a/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php +++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php @@ -20,7 +20,7 @@ class AjaxSave extends \Magento\Tax\Controller\Adminhtml\Rate try { $rateData = $this->_processRateData($this->getRequest()->getPostValue()); /** @var \Magento\Tax\Api\Data\TaxRateInterface $taxRate */ - $taxRate = $this->populateTaxRateData($rateData); + $taxRate = $this->_taxRateConverter->populateTaxRateData($rateData); $this->_taxRateRepository->save($taxRate); $responseContent = [ 'success' => true, diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php index e56a31a4d348942962cb96a04f4f598bbbdaf4f3..36dbcc284eabb831c206477b1da004c886686429 100644 --- a/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php +++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php @@ -32,7 +32,7 @@ class Save extends \Magento\Tax\Controller\Adminhtml\Rate } try { - $taxData = $this->populateTaxRateData($ratePost); + $taxData = $this->_taxRateConverter->populateTaxRateData($ratePost); $this->_taxRateRepository->save($taxData); $this->messageManager->addSuccess(__('The tax rate has been saved.')); diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php b/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php index d84a09825b08fb4391830a6ec8261b24d152557a..9fede0b0238787abcc72a691b7473f588a90434b 100644 --- a/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php +++ b/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php @@ -6,6 +6,7 @@ */ namespace Magento\Tax\Controller\Adminhtml\Tax; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Controller\ResultFactory; class IgnoreTaxNotification extends \Magento\Tax\Controller\Adminhtml\Tax @@ -43,7 +44,7 @@ class IgnoreTaxNotification extends \Magento\Tax\Controller\Adminhtml\Tax try { $path = 'tax/notification/ignore_' . $section; $this->_objectManager->get('Magento\Config\Model\Resource\Config') - ->saveConfig($path, 1, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0); + ->saveConfig($path, 1, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0); } catch (\Exception $e) { $this->messageManager->addError($e->getMessage()); } diff --git a/app/code/Magento/Tax/Model/Calculation/Rate.php b/app/code/Magento/Tax/Model/Calculation/Rate.php index 18dde7c54f14d54945d1625e034e07ce7833f502..fae528a7d943f8b48b5023d3aaf2ad468a62bb98 100644 --- a/app/code/Magento/Tax/Model/Calculation/Rate.php +++ b/app/code/Magento/Tax/Model/Calculation/Rate.php @@ -20,8 +20,24 @@ use Magento\Tax\Api\Data\TaxRateInterface; * @method \Magento\Tax\Model\Resource\Calculation\Rate getResource() * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\Tax\Api\Data\TaxRateInterface +class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements TaxRateInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_ID = 'id'; + const KEY_COUNTRY_ID = 'tax_country_id'; + const KEY_REGION_ID = 'tax_region_id'; + const KEY_REGION_NAME = 'region_name'; + const KEY_POSTCODE = 'tax_postcode'; + const KEY_ZIP_IS_RANGE = 'zip_is_range'; + const KEY_ZIP_RANGE_FROM = 'zip_from'; + const KEY_ZIP_RANGE_TO = 'zip_to'; + const KEY_PERCENTAGE_RATE = 'rate'; + const KEY_CODE = 'code'; + const KEY_TITLES = 'titles'; + /**#@-*/ + /** * List of tax titles * diff --git a/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php b/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php index 7a407da4de340f4d8a8c82bc2df877786ecdcc3a..5fd8da907a5d8b81a297511758895d0dfa2156bd 100644 --- a/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php +++ b/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php @@ -12,6 +12,27 @@ namespace Magento\Tax\Model\Calculation\Rate; */ class Converter { + /** + * @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory + */ + protected $taxRateDataObjectFactory; + + /** + * @var \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory + */ + protected $taxRateTitleDataObjectFactory; + + /** + * @param \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory + * @param \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory, + */ + public function __construct( + \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory, + \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory + ) { + $this->taxRateDataObjectFactory = $taxRateDataObjectFactory; + $this->taxRateTitleDataObjectFactory = $taxRateTitleDataObjectFactory; + } /** * Convert a tax rate data object to an array of associated titles * @@ -29,4 +50,105 @@ class Converter } return $titleData; } + + /** + * Extract tax rate data in a format which is + * + * @param \Magento\Tax\Api\Data\TaxRateInterface $taxRate + * @param Boolean $returnNumericLogic + * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function createArrayFromServiceObject( + \Magento\Tax\Api\Data\TaxRateInterface $taxRate, + $returnNumericLogic = false + ) { + $taxRateFormData = [ + 'tax_calculation_rate_id' => $taxRate->getId(), + 'tax_country_id' => $taxRate->getTaxCountryId(), + 'tax_region_id' => $taxRate->getTaxRegionId(), + 'tax_postcode' => $taxRate->getTaxPostcode(), + 'code' => $taxRate->getCode(), + 'rate' => $taxRate->getRate(), + 'zip_is_range' => $returnNumericLogic?0:false, + ]; + + if ($taxRate->getZipFrom() && $taxRate->getZipTo()) { + $taxRateFormData['zip_is_range'] = $returnNumericLogic?1:true; + $taxRateFormData['zip_from'] = $taxRate->getZipFrom(); + $taxRateFormData['zip_to'] = $taxRate->getZipTo(); + } + + if ($returnNumericLogic) { + //format for the ajax on multiple sites titles + $titleArray=($this->createTitleArrayFromServiceObject($taxRate)); + if (is_array($titleArray)) { + foreach ($titleArray as $storeId => $title) { + $taxRateFormData['title[' . $storeId . ']']=$title; + } + } + } else { + //format for the form array on multiple sites titles + $titleArray=($this->createTitleArrayFromServiceObject($taxRate)); + if (is_array($titleArray)) { + $titleData = []; + foreach ($titleArray as $storeId => $title) { + $titleData[] = [$storeId => $title]; + } + if (count($titleArray)>0) { + $taxRateFormData['title'] = $titleData; + } + } + } + + return $taxRateFormData; + } + + + /** + * Convert an array to a tax rate data object + * + * @param array $formData + * @return \Magento\Tax\Api\Data\TaxRateInterface + */ + public function populateTaxRateData($formData) + { + $taxRate = $this->taxRateDataObjectFactory->create(); + $taxRate->setId($this->extractFormData($formData, 'tax_calculation_rate_id')) + ->setTaxCountryId($this->extractFormData($formData, 'tax_country_id')) + ->setTaxRegionId($this->extractFormData($formData, 'tax_region_id')) + ->setTaxPostcode($this->extractFormData($formData, 'tax_postcode')) + ->setCode($this->extractFormData($formData, 'code')) + ->setRate($this->extractFormData($formData, 'rate')); + if (isset($formData['zip_is_range']) && $formData['zip_is_range']) { + $taxRate->setZipFrom($this->extractFormData($formData, 'zip_from')) + ->setZipTo($this->extractFormData($formData, 'zip_to'))->setZipIsRange(1); + } + + if (isset($formData['title'])) { + $titles = []; + foreach ($formData['title'] as $storeId => $value) { + $titles[] = $this->taxRateTitleDataObjectFactory->create()->setStoreId($storeId)->setValue($value); + } + $taxRate->setTitles($titles); + } + + return $taxRate; + } + + /** + * Determines if an array value is set in the form data array and returns it. + * + * @param array $formData the form to get data from + * @param string $fieldName the key + * @return null|string + */ + protected function extractFormData($formData, $fieldName) + { + if (isset($formData[$fieldName])) { + return $formData[$fieldName]; + } + return null; + } } diff --git a/app/code/Magento/Tax/Model/Calculation/Rate/Title.php b/app/code/Magento/Tax/Model/Calculation/Rate/Title.php index a98b220548940e240774250f868c8b1abb8c4f1d..141e62435538b94a97d91671966161b6bf174b7c 100644 --- a/app/code/Magento/Tax/Model/Calculation/Rate/Title.php +++ b/app/code/Magento/Tax/Model/Calculation/Rate/Title.php @@ -17,9 +17,16 @@ namespace Magento\Tax\Model\Calculation\Rate; use Magento\Tax\Api\Data\TaxRateTitleInterface; -class Title extends \Magento\Framework\Model\AbstractExtensibleModel implements - \Magento\Tax\Api\Data\TaxRateTitleInterface +class Title extends \Magento\Framework\Model\AbstractExtensibleModel implements TaxRateTitleInterface { + /**#@+ + * + * Tax rate field key. + */ + const KEY_STORE_ID = 'store_id'; + const KEY_VALUE_ID = 'value'; + /**#@-*/ + /** * @return void */ diff --git a/app/code/Magento/Tax/Model/Calculation/RateRepository.php b/app/code/Magento/Tax/Model/Calculation/RateRepository.php index a185acd15844412198f779b0efd4dc58e224a7fa..6f099ea27ae35038fac754ea19d97066355c7b47 100644 --- a/app/code/Magento/Tax/Model/Calculation/RateRepository.php +++ b/app/code/Magento/Tax/Model/Calculation/RateRepository.php @@ -15,7 +15,7 @@ use Magento\Framework\Api\SortOrder; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Tax\Api\Data\TaxRateInterface as TaxRateDataObject; +use Magento\Tax\Model\Calculation\Rate; use Magento\Tax\Model\Calculation\Rate\Converter; use Magento\Tax\Model\Resource\Calculation\Rate\Collection; @@ -210,7 +210,7 @@ class RateRepository implements \Magento\Tax\Api\TaxRateRepositoryInterface protected function translateField($field) { switch ($field) { - case TaxRateDataObject::KEY_REGION_NAME: + case Rate::KEY_REGION_NAME: return 'region_table.code'; default: return "main_table." . $field; diff --git a/app/code/Magento/Tax/Model/Calculation/Rule.php b/app/code/Magento/Tax/Model/Calculation/Rule.php index 13d287ce5da36b67ece5d16a634a1ba3a743ca44..dc7e0cfac6f2c4932e6c1515b277483e78e88a7c 100644 --- a/app/code/Magento/Tax/Model/Calculation/Rule.php +++ b/app/code/Magento/Tax/Model/Calculation/Rule.php @@ -21,21 +21,14 @@ class Rule extends \Magento\Framework\Model\AbstractExtensibleModel implements T * * Tax rule field key. */ - const KEY_ID = 'id'; - - const KEY_CODE = 'code'; - + const KEY_ID = 'id'; + const KEY_CODE = 'code'; const KEY_PRIORITY = 'priority'; - const KEY_POSITION = 'position'; - const KEY_CUSTOMER_TAX_CLASS_IDS = 'customer_tax_class_ids'; - - const KEY_PRODUCT_TAX_CLASS_IDS = 'product_tax_class_ids'; - - const KEY_TAX_RATE_IDS = 'tax_rate_ids'; - - const KEY_CALCULATE_SUBTOTAL = 'calculate_subtotal'; + const KEY_PRODUCT_TAX_CLASS_IDS = 'product_tax_class_ids'; + const KEY_TAX_RATE_IDS = 'tax_rate_ids'; + const KEY_CALCULATE_SUBTOTAL = 'calculate_subtotal'; /**#@-*/ /** diff --git a/app/code/Magento/Tax/Model/ClassModel.php b/app/code/Magento/Tax/Model/ClassModel.php index 603fc708d47286e0dffdd25f8724756445f6cac1..610527d58eb625ed32510e99dabbb11d58af3bc8 100644 --- a/app/code/Magento/Tax/Model/ClassModel.php +++ b/app/code/Magento/Tax/Model/ClassModel.php @@ -19,6 +19,14 @@ use Magento\Tax\Api\Data\TaxClassInterface; class ClassModel extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\Tax\Api\Data\TaxClassInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_ID = 'class_id'; + const KEY_NAME = 'class_name'; + const KEY_TYPE = 'class_type'; + /**#@-*/ + /** * Defines Customer Tax Class string */ diff --git a/app/code/Magento/Tax/Model/ClassModelRegistry.php b/app/code/Magento/Tax/Model/ClassModelRegistry.php index e4667c061776813e7fa833bae372904527505eb5..da438fcbdf3d55ddb4d5127a36b89d402ec2e4cc 100644 --- a/app/code/Magento/Tax/Model/ClassModelRegistry.php +++ b/app/code/Magento/Tax/Model/ClassModelRegistry.php @@ -7,7 +7,6 @@ namespace Magento\Tax\Model; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Tax\Api\Data\TaxClassInterface; use Magento\Tax\Model\ClassModel as TaxClassModel; use Magento\Tax\Model\ClassModelFactory as TaxClassModelFactory; @@ -67,7 +66,7 @@ class ClassModelRegistry $taxClassModel = $this->taxClassModelFactory->create()->load($taxClassId); if (!$taxClassModel->getId()) { // tax class does not exist - throw NoSuchEntityException::singleField(TaxClassInterface::KEY_ID, $taxClassId); + throw NoSuchEntityException::singleField(TaxClassModel::KEY_ID, $taxClassId); } $this->taxClassRegistryById[$taxClassModel->getId()] = $taxClassModel; return $taxClassModel; diff --git a/app/code/Magento/Tax/Model/Config/Notification.php b/app/code/Magento/Tax/Model/Config/Notification.php index c886452442406056cb369dd15210a1c32b0c9829..f4b395a290856cf26c36fae743ac7a3ce9797c9b 100644 --- a/app/code/Magento/Tax/Model/Config/Notification.php +++ b/app/code/Magento/Tax/Model/Config/Notification.php @@ -5,6 +5,8 @@ */ namespace Magento\Tax\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * Tax Config Notification */ @@ -59,7 +61,7 @@ class Notification extends \Magento\Framework\App\Config\Value */ protected function _resetNotificationFlag($path) { - $this->resourceConfig->saveConfig($path, 0, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0); + $this->resourceConfig->saveConfig($path, 0, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0); return $this; } } diff --git a/app/code/Magento/Tax/Model/Rate/Source.php b/app/code/Magento/Tax/Model/Rate/Source.php index 8e4342ca28f161887bfc6aecf3e1d1f6ccb41b44..0d999d6698b6ba76630b3d81a815c8649e466cb5 100644 --- a/app/code/Magento/Tax/Model/Rate/Source.php +++ b/app/code/Magento/Tax/Model/Rate/Source.php @@ -8,8 +8,8 @@ namespace Magento\Tax\Model\Rate; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Convert\Object as Converter; -use Magento\Tax\Api\Data\TaxRateInterface as TaxRate; use Magento\Tax\Api\TaxRateRepositoryInterface; +use Magento\Tax\Model\Calculation\Rate; /** * Tax rate source model. @@ -57,8 +57,8 @@ class Source implements \Magento\Framework\Data\OptionSourceInterface $searchResults = $this->taxRateRepository->getList($searchCriteria); $this->options = $this->converter->toOptionArray( $searchResults->getItems(), - TaxRate::KEY_ID, - TaxRate::KEY_CODE + Rate::KEY_ID, + Rate::KEY_CODE ); } return $this->options; diff --git a/app/code/Magento/Tax/Model/Sales/Order/Details.php b/app/code/Magento/Tax/Model/Sales/Order/Details.php index 5161ab9fe6f7397f08cb88972033a8a6be6f6ef4..0294aff0f68eeef1f3e903a3ecbc85246d4fd3c9 100644 --- a/app/code/Magento/Tax/Model/Sales/Order/Details.php +++ b/app/code/Magento/Tax/Model/Sales/Order/Details.php @@ -13,6 +13,13 @@ namespace Magento\Tax\Model\Sales\Order; class Details extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\Tax\Api\Data\OrderTaxDetailsInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_APPLIED_TAXES = 'applied_taxes'; + const KEY_ITEMS = 'items'; + /**#@-*/ + /** * {@inheritdoc} */ diff --git a/app/code/Magento/Tax/Model/Sales/Order/Tax.php b/app/code/Magento/Tax/Model/Sales/Order/Tax.php index 29ce7e404f5696bcff549d3b276f6b14f6aea1d2..3b67f9222800f2f55585bff22409966003799d2e 100644 --- a/app/code/Magento/Tax/Model/Sales/Order/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Order/Tax.php @@ -25,6 +25,16 @@ namespace Magento\Tax\Model\Sales\Order; class Tax extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_CODE = 'code'; + const KEY_TITLE = 'title'; + const KEY_PERCENT = 'percent'; + const KEY_AMOUNT = 'amount'; + const KEY_BASE_AMOUNT = 'base_amount'; + /**#@-*/ + /** * @return void */ diff --git a/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php b/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php index a6b6148ed0c9eaca83d07b74c4d32f53ce3a1e74..8ecadf3eec08696f763a1f72ea1bbfa256dcd575 100644 --- a/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php +++ b/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php @@ -11,6 +11,15 @@ namespace Magento\Tax\Model\Sales\Order\Tax; class Item extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\Tax\Api\Data\OrderTaxDetailsItemInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_TYPE = 'type'; + const KEY_ITEM_ID = 'item_id'; + const KEY_ASSOCIATED_ITEM_ID = 'associated_item_id'; + const KEY_APPLIED_TAXES = 'applied_taxes'; + /**#@-*/ + /** * {@inheritdoc} */ diff --git a/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php b/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php index c665dfd00e8f8a65f487faacb618c1475991b10b..fed06b4dd1983da04c39ff4625bf22c0626a4707 100644 --- a/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php +++ b/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php @@ -10,7 +10,8 @@ namespace Magento\Tax\Model\Sales\Order; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterfaceFactory as TaxDetailsDataObjectFactory; use Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface as AppliedTax; -use Magento\Tax\Api\Data\OrderTaxDetailsItemInterface as Item; +use Magento\Tax\Model\Sales\Order\Tax; +use Magento\Tax\Model\Sales\Order\Tax\Item; class TaxManagement implements \Magento\Tax\Api\OrderTaxManagementInterface { @@ -83,7 +84,7 @@ class TaxManagement implements \Magento\Tax\Api\OrderTaxManagementInterface * Aggregate item applied taxes to get order applied taxes * * @param TaxDetailsDataObjectFactory $appliedTaxDataObjectFactory - * @param Item[] $items + * @param \Magento\Tax\Api\Data\OrderTaxDetailsItemInterface[] $items * @return AppliedTax[] */ protected function aggregateAppliedTaxes(TaxDetailsDataObjectFactory $appliedTaxDataObjectFactory, $items) @@ -96,25 +97,25 @@ class TaxManagement implements \Magento\Tax\Api\OrderTaxManagementInterface $code = $itemAppliedTax->getCode(); if (!isset($orderAppliedTaxesData[$code])) { $orderAppliedTaxesData[$code] = [ - AppliedTax::KEY_CODE => $code, - AppliedTax::KEY_TITLE => $itemAppliedTax->getTitle(), - AppliedTax::KEY_PERCENT => $itemAppliedTax->getPercent(), - AppliedTax::KEY_AMOUNT => $itemAppliedTax->getAmount(), - AppliedTax::KEY_BASE_AMOUNT => $itemAppliedTax->getBaseAmount(), + Tax::KEY_CODE => $code, + Tax::KEY_TITLE => $itemAppliedTax->getTitle(), + Tax::KEY_PERCENT => $itemAppliedTax->getPercent(), + Tax::KEY_AMOUNT => $itemAppliedTax->getAmount(), + Tax::KEY_BASE_AMOUNT => $itemAppliedTax->getBaseAmount(), ]; } else { - $orderAppliedTaxesData[$code][AppliedTax::KEY_AMOUNT] += $itemAppliedTax->getAmount(); - $orderAppliedTaxesData[$code][AppliedTax::KEY_BASE_AMOUNT] += $itemAppliedTax->getBaseAmount(); + $orderAppliedTaxesData[$code][Tax::KEY_AMOUNT] += $itemAppliedTax->getAmount(); + $orderAppliedTaxesData[$code][Tax::KEY_BASE_AMOUNT] += $itemAppliedTax->getBaseAmount(); } } } foreach ($orderAppliedTaxesData as $orderAppliedTaxData) { $orderAppliedTaxes[] = $appliedTaxDataObjectFactory->create() - ->setCode($orderAppliedTaxData[AppliedTax::KEY_CODE]) - ->setTitle($orderAppliedTaxData[AppliedTax::KEY_TITLE]) - ->setPercent($orderAppliedTaxData[AppliedTax::KEY_PERCENT]) - ->setAmount($orderAppliedTaxData[AppliedTax::KEY_AMOUNT]) - ->setBaseAmount($orderAppliedTaxData[AppliedTax::KEY_BASE_AMOUNT]); + ->setCode($orderAppliedTaxData[Tax::KEY_CODE]) + ->setTitle($orderAppliedTaxData[Tax::KEY_TITLE]) + ->setPercent($orderAppliedTaxData[Tax::KEY_PERCENT]) + ->setAmount($orderAppliedTaxData[Tax::KEY_AMOUNT]) + ->setBaseAmount($orderAppliedTaxData[Tax::KEY_BASE_AMOUNT]); } return $orderAppliedTaxes; } diff --git a/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php b/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php index 71d18024ea5f1a199b99ff3811fa4d82d7009ed7..cd893b122f89381b05baa4b5cc41e59d120bd40b 100644 --- a/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php +++ b/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php @@ -13,12 +13,28 @@ use Magento\Tax\Api\Data\QuoteDetailsItemInterface; */ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_CODE = 'code'; + const KEY_TYPE = 'type'; + const KEY_TAX_CLASS_KEY = 'tax_class_key'; + const KEY_UNIT_PRICE = 'unit_price'; + const KEY_QUANTITY = 'quantity'; + const KEY_TAX_INCLUDED = 'tax_included'; + const KEY_SHORT_DESCRIPTION = 'short_description'; + const KEY_DISCOUNT_AMOUNT = 'discount_amount'; + const KEY_PARENT_CODE = 'parent_code'; + const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code'; + const KEY_TAX_CLASS_ID = 'tax_class_id'; + /**#@-*/ + /** * {@inheritdoc} */ public function getCode() { - return $this->getData(QuoteDetailsItemInterface::KEY_CODE); + return $this->getData(self::KEY_CODE); } /** @@ -26,7 +42,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getType() { - return $this->getData(QuoteDetailsItemInterface::KEY_TYPE); + return $this->getData(self::KEY_TYPE); } /** @@ -34,7 +50,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getTaxClassKey() { - return $this->getData(QuoteDetailsItemInterface::KEY_TAX_CLASS_KEY); + return $this->getData(self::KEY_TAX_CLASS_KEY); } /** @@ -42,7 +58,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getUnitPrice() { - return $this->getData(QuoteDetailsItemInterface::KEY_UNIT_PRICE); + return $this->getData(self::KEY_UNIT_PRICE); } /** @@ -50,7 +66,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getQuantity() { - return $this->getData(QuoteDetailsItemInterface::KEY_QUANTITY); + return $this->getData(self::KEY_QUANTITY); } /** @@ -58,7 +74,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getTaxIncluded() { - return $this->getData(QuoteDetailsItemInterface::KEY_TAX_INCLUDED); + return $this->getData(self::KEY_TAX_INCLUDED); } /** @@ -66,7 +82,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getShortDescription() { - return $this->getData(QuoteDetailsItemInterface::KEY_SHORT_DESCRIPTION); + return $this->getData(self::KEY_SHORT_DESCRIPTION); } /** @@ -74,7 +90,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getDiscountAmount() { - return $this->getData(QuoteDetailsItemInterface::KEY_DISCOUNT_AMOUNT); + return $this->getData(self::KEY_DISCOUNT_AMOUNT); } /** @@ -82,7 +98,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getParentCode() { - return $this->getData(QuoteDetailsItemInterface::KEY_PARENT_CODE); + return $this->getData(self::KEY_PARENT_CODE); } /** @@ -90,7 +106,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getAssociatedItemCode() { - return $this->getData(QuoteDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE); + return $this->getData(self::KEY_ASSOCIATED_ITEM_CODE); } /** @@ -98,7 +114,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function getTaxClassId() { - return $this->getData(QuoteDetailsItemInterface::KEY_TAX_CLASS_ID); + return $this->getData(self::KEY_TAX_CLASS_ID); } /** @@ -109,7 +125,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setCode($code) { - return $this->setData(QuoteDetailsItemInterface::KEY_CODE, $code); + return $this->setData(self::KEY_CODE, $code); } /** @@ -120,7 +136,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setType($type) { - return $this->setData(QuoteDetailsItemInterface::KEY_TYPE, $type); + return $this->setData(self::KEY_TYPE, $type); } /** @@ -131,7 +147,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setTaxClassKey(\Magento\Tax\Api\Data\TaxClassKeyInterface $taxClassKey = null) { - return $this->setData(QuoteDetailsItemInterface::KEY_TAX_CLASS_KEY, $taxClassKey); + return $this->setData(self::KEY_TAX_CLASS_KEY, $taxClassKey); } /** @@ -142,7 +158,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setUnitPrice($unitPrice) { - return $this->setData(QuoteDetailsItemInterface::KEY_UNIT_PRICE, $unitPrice); + return $this->setData(self::KEY_UNIT_PRICE, $unitPrice); } /** @@ -153,7 +169,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setQuantity($quantity) { - return $this->setData(QuoteDetailsItemInterface::KEY_QUANTITY, $quantity); + return $this->setData(self::KEY_QUANTITY, $quantity); } /** @@ -164,7 +180,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setIsTaxIncluded($isTaxIncluded) { - return $this->setData(QuoteDetailsItemInterface::KEY_TAX_INCLUDED, $isTaxIncluded); + return $this->setData(self::KEY_TAX_INCLUDED, $isTaxIncluded); } /** @@ -175,7 +191,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setShortDescription($shortDescription) { - return $this->setData(QuoteDetailsItemInterface::KEY_SHORT_DESCRIPTION, $shortDescription); + return $this->setData(self::KEY_SHORT_DESCRIPTION, $shortDescription); } /** @@ -186,7 +202,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setDiscountAmount($discountAmount) { - return $this->setData(QuoteDetailsItemInterface::KEY_DISCOUNT_AMOUNT, $discountAmount); + return $this->setData(self::KEY_DISCOUNT_AMOUNT, $discountAmount); } /** @@ -197,7 +213,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setParentCode($parentCode) { - return $this->setData(QuoteDetailsItemInterface::KEY_PARENT_CODE, $parentCode); + return $this->setData(self::KEY_PARENT_CODE, $parentCode); } /** @@ -208,7 +224,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setAssociatedItemCode($associatedItemCode) { - return $this->setData(QuoteDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode); + return $this->setData(self::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode); } /** @@ -219,7 +235,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt */ public function setTaxClassId($taxClassId) { - return $this->setData(QuoteDetailsItemInterface::KEY_TAX_CLASS_ID, $taxClassId); + return $this->setData(self::KEY_TAX_CLASS_ID, $taxClassId); } /** diff --git a/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php b/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php index 1ef369ba40d82dfb3655926be7970e003892a152..1569e8496bd0cb9417b6f0609df658f06f21a0ed 100644 --- a/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php +++ b/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php @@ -13,12 +13,23 @@ use Magento\Tax\Api\Data\QuoteDetailsInterface; */ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_BILLING_ADDRESS = 'billing_address'; + const KEY_SHIPPING_ADDRESS = 'shipping_address'; + const KEY_CUSTOMER_TAX_CLASS_KEY = 'customer_tax_class_key'; + const KEY_ITEMS = 'items'; + const KEY_CUSTOMER_TAX_CLASS_ID = 'customer_tax_class_id'; + const KEY_CUSTOMER_ID = 'customer_id'; + /**#@-*/ + /** * {@inheritdoc} */ public function getBillingAddress() { - return $this->getData(QuoteDetailsInterface::KEY_BILLING_ADDRESS); + return $this->getData(self::KEY_BILLING_ADDRESS); } /** @@ -26,7 +37,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function getShippingAddress() { - return $this->getData(QuoteDetailsInterface::KEY_SHIPPING_ADDRESS); + return $this->getData(self::KEY_SHIPPING_ADDRESS); } /** @@ -34,7 +45,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function getCustomerTaxClassKey() { - return $this->getData(QuoteDetailsInterface::KEY_CUSTOMER_TAX_CLASS_KEY); + return $this->getData(self::KEY_CUSTOMER_TAX_CLASS_KEY); } /** @@ -42,7 +53,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function getCustomerId() { - return $this->getData(QuoteDetailsInterface::KEY_CUSTOMER_ID); + return $this->getData(self::KEY_CUSTOMER_ID); } /** @@ -50,7 +61,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function getItems() { - return $this->getData(QuoteDetailsInterface::KEY_ITEMS); + return $this->getData(self::KEY_ITEMS); } /** @@ -58,7 +69,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function getCustomerTaxClassId() { - return $this->getData(QuoteDetailsInterface::CUSTOMER_TAX_CLASS_ID); + return $this->getData(self::KEY_CUSTOMER_TAX_CLASS_ID); } /** @@ -69,7 +80,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function setBillingAddress(\Magento\Customer\Api\Data\AddressInterface $billingAddress = null) { - return $this->setData(QuoteDetailsInterface::KEY_BILLING_ADDRESS, $billingAddress); + return $this->setData(self::KEY_BILLING_ADDRESS, $billingAddress); } /** @@ -80,7 +91,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function setShippingAddress(\Magento\Customer\Api\Data\AddressInterface $shippingAddress = null) { - return $this->setData(QuoteDetailsInterface::KEY_SHIPPING_ADDRESS, $shippingAddress); + return $this->setData(self::KEY_SHIPPING_ADDRESS, $shippingAddress); } /** @@ -91,7 +102,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function setCustomerTaxClassKey(\Magento\Tax\Api\Data\TaxClassKeyInterface $customerTaxClassKey = null) { - return $this->setData(QuoteDetailsInterface::KEY_CUSTOMER_TAX_CLASS_KEY, $customerTaxClassKey); + return $this->setData(self::KEY_CUSTOMER_TAX_CLASS_KEY, $customerTaxClassKey); } /** @@ -102,7 +113,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function setCustomerId($customerId) { - return $this->setData(QuoteDetailsInterface::KEY_CUSTOMER_ID, $customerId); + return $this->setData(self::KEY_CUSTOMER_ID, $customerId); } /** @@ -113,7 +124,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function setItems(array $items = null) { - return $this->setData(QuoteDetailsInterface::KEY_ITEMS, $items); + return $this->setData(self::KEY_ITEMS, $items); } /** @@ -124,7 +135,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf */ public function setCustomerTaxClassId($customerTaxClassId) { - return $this->setData(QuoteDetailsInterface::CUSTOMER_TAX_CLASS_ID, $customerTaxClassId); + return $this->setData(self::KEY_CUSTOMER_TAX_CLASS_ID, $customerTaxClassId); } /** diff --git a/app/code/Magento/Tax/Model/TaxCalculation.php b/app/code/Magento/Tax/Model/TaxCalculation.php index fdbc6cb724bfe1bf27df13375ba11d8ff29af87e..579ee5519899603976a08b1aeb874a59f1f8d774 100644 --- a/app/code/Magento/Tax/Model/TaxCalculation.php +++ b/app/code/Magento/Tax/Model/TaxCalculation.php @@ -6,19 +6,19 @@ namespace Magento\Tax\Model; -use Magento\Tax\Api\Data\TaxDetailsInterface; +use Magento\Tax\Api\TaxCalculationInterface; +use Magento\Tax\Api\TaxClassManagementInterface; use Magento\Tax\Api\Data\TaxDetailsItemInterface; use Magento\Tax\Api\Data\QuoteDetailsItemInterface; use Magento\Tax\Api\Data\TaxDetailsInterfaceFactory; use Magento\Tax\Api\Data\TaxDetailsItemInterfaceFactory; -use Magento\Tax\Api\Data\AppliedTaxRateInterface; -use Magento\Tax\Api\Data\AppliedTaxInterface; -use Magento\Tax\Api\TaxClassManagementInterface; +use Magento\Tax\Model\Calculation\AbstractCalculator; use Magento\Tax\Model\Calculation\CalculatorFactory; use Magento\Tax\Model\Config; -use Magento\Tax\Model\Calculation\AbstractCalculator; +use Magento\Tax\Model\TaxDetails\AppliedTax; +use Magento\Tax\Model\TaxDetails\AppliedTaxRate; +use Magento\Tax\Model\TaxDetails\TaxDetails; use Magento\Store\Model\StoreManagerInterface; -use Magento\Tax\Api\TaxCalculationInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -142,11 +142,11 @@ class TaxCalculation implements TaxCalculationInterface // initial TaxDetails data $taxDetailsData = [ - TaxDetailsInterface::KEY_SUBTOTAL => 0.0, - TaxDetailsInterface::KEY_TAX_AMOUNT => 0.0, - TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, - TaxDetailsInterface::KEY_APPLIED_TAXES => [], - TaxDetailsInterface::KEY_ITEMS => [], + TaxDetails::KEY_SUBTOTAL => 0.0, + TaxDetails::KEY_TAX_AMOUNT => 0.0, + TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, + TaxDetails::KEY_APPLIED_TAXES => [], + TaxDetails::KEY_ITEMS => [], ]; $items = $quoteDetails->getItems(); if (empty($items)) { @@ -327,21 +327,21 @@ class TaxCalculation implements TaxCalculationInterface */ protected function aggregateItemData($taxDetailsData, TaxDetailsItemInterface $item) { - $taxDetailsData[TaxDetailsInterface::KEY_SUBTOTAL] - = $taxDetailsData[TaxDetailsInterface::KEY_SUBTOTAL] + $item->getRowTotal(); + $taxDetailsData[TaxDetails::KEY_SUBTOTAL] + = $taxDetailsData[TaxDetails::KEY_SUBTOTAL] + $item->getRowTotal(); - $taxDetailsData[TaxDetailsInterface::KEY_TAX_AMOUNT] - = $taxDetailsData[TaxDetailsInterface::KEY_TAX_AMOUNT] + $item->getRowTax(); + $taxDetailsData[TaxDetails::KEY_TAX_AMOUNT] + = $taxDetailsData[TaxDetails::KEY_TAX_AMOUNT] + $item->getRowTax(); - $taxDetailsData[TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] = - $taxDetailsData[TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] + $taxDetailsData[TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] = + $taxDetailsData[TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] + $item->getDiscountTaxCompensationAmount(); $itemAppliedTaxes = $item->getAppliedTaxes(); if ($itemAppliedTaxes === null) { return $taxDetailsData; } - $appliedTaxes = $taxDetailsData[TaxDetailsInterface::KEY_APPLIED_TAXES]; + $appliedTaxes = $taxDetailsData[TaxDetails::KEY_APPLIED_TAXES]; foreach ($itemAppliedTaxes as $taxId => $itemAppliedTax) { if (!isset($appliedTaxes[$taxId])) { //convert rate data object to array @@ -349,23 +349,23 @@ class TaxCalculation implements TaxCalculationInterface $rateDataObjects = $itemAppliedTax->getRates(); foreach ($rateDataObjects as $rateDataObject) { $rates[$rateDataObject->getCode()] = [ - AppliedTaxRateInterface::KEY_CODE => $rateDataObject->getCode(), - AppliedTaxRateInterface::KEY_TITLE => $rateDataObject->getTitle(), - AppliedTaxRateInterface::KEY_PERCENT => $rateDataObject->getPercent(), + AppliedTaxRate::KEY_CODE => $rateDataObject->getCode(), + AppliedTaxRate::KEY_TITLE => $rateDataObject->getTitle(), + AppliedTaxRate::KEY_PERCENT => $rateDataObject->getPercent(), ]; } $appliedTaxes[$taxId] = [ - AppliedTaxInterface::KEY_AMOUNT => $itemAppliedTax->getAmount(), - AppliedTaxInterface::KEY_PERCENT => $itemAppliedTax->getPercent(), - AppliedTaxInterface::KEY_RATES => $rates, - AppliedTaxInterface::KEY_TAX_RATE_KEY => $itemAppliedTax->getTaxRateKey(), + AppliedTax::KEY_AMOUNT => $itemAppliedTax->getAmount(), + AppliedTax::KEY_PERCENT => $itemAppliedTax->getPercent(), + AppliedTax::KEY_RATES => $rates, + AppliedTax::KEY_TAX_RATE_KEY => $itemAppliedTax->getTaxRateKey(), ]; } else { - $appliedTaxes[$taxId][AppliedTaxInterface::KEY_AMOUNT] += $itemAppliedTax->getAmount(); + $appliedTaxes[$taxId][AppliedTax::KEY_AMOUNT] += $itemAppliedTax->getAmount(); } } - $taxDetailsData[TaxDetailsInterface::KEY_APPLIED_TAXES] = $appliedTaxes; + $taxDetailsData[TaxDetails::KEY_APPLIED_TAXES] = $appliedTaxes; return $taxDetailsData; } diff --git a/app/code/Magento/Tax/Model/TaxClass/Key.php b/app/code/Magento/Tax/Model/TaxClass/Key.php index 085b018aa88f7ad8ba4ef5a36fea99c31f6ded54..c882d2dae170cfa9589909859f8b0eb995a41d01 100644 --- a/app/code/Magento/Tax/Model/TaxClass/Key.php +++ b/app/code/Magento/Tax/Model/TaxClass/Key.php @@ -13,12 +13,19 @@ use Magento\Tax\Api\Data\TaxClassKeyInterface; */ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_TYPE = 'type'; + const KEY_VALUE = 'value'; + /**#@-*/ + /** * {@inheritdoc} */ public function getType() { - return $this->getData(TaxClassKeyInterface::KEY_TYPE); + return $this->getData(self::KEY_TYPE); } /** @@ -26,7 +33,7 @@ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface */ public function getValue() { - return $this->getData(TaxClassKeyInterface::KEY_VALUE); + return $this->getData(self::KEY_VALUE); } /** @@ -37,7 +44,7 @@ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface */ public function setType($type) { - return $this->setData(TaxClassKeyInterface::KEY_TYPE, $type); + return $this->setData(self::KEY_TYPE, $type); } /** @@ -48,7 +55,7 @@ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface */ public function setValue($value) { - return $this->setData(TaxClassKeyInterface::KEY_VALUE, $value); + return $this->setData(self::KEY_VALUE, $value); } /** diff --git a/app/code/Magento/Tax/Model/TaxClass/Management.php b/app/code/Magento/Tax/Model/TaxClass/Management.php index 679a054b62314f379cfbc94ec0b0633a1142c059..eb7871c1086c22efe4e988254490bf02abdebf48 100644 --- a/app/code/Magento/Tax/Model/TaxClass/Management.php +++ b/app/code/Magento/Tax/Model/TaxClass/Management.php @@ -9,8 +9,8 @@ namespace Magento\Tax\Model\TaxClass; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Tax\Api\Data\TaxClassInterface; use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Model\ClassModel; class Management implements \Magento\Tax\Api\TaxClassManagementInterface { @@ -61,10 +61,10 @@ class Management implements \Magento\Tax\Api\TaxClassManagementInterface return $taxClassKey->getValue(); case TaxClassKeyInterface::TYPE_NAME: $searchCriteria = $this->searchCriteriaBuilder->addFilter( - [$this->filterBuilder->setField(TaxClassInterface::KEY_TYPE)->setValue($taxClassType)->create()] + [$this->filterBuilder->setField(ClassModel::KEY_TYPE)->setValue($taxClassType)->create()] )->addFilter( [ - $this->filterBuilder->setField(TaxClassInterface::KEY_NAME) + $this->filterBuilder->setField(ClassModel::KEY_NAME) ->setValue($taxClassKey->getValue()) ->create(), ] diff --git a/app/code/Magento/Tax/Model/TaxClass/Repository.php b/app/code/Magento/Tax/Model/TaxClass/Repository.php index a0fc6fe76d46ec8718a7cccf2af8a44068b73425..01dd656b0d5e51d5b64e22c8fd4d80e937d19e2b 100644 --- a/app/code/Magento/Tax/Model/TaxClass/Repository.php +++ b/app/code/Magento/Tax/Model/TaxClass/Repository.php @@ -15,8 +15,8 @@ use Magento\Framework\Api\SortOrder; use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException as ModelException; -use Magento\Tax\Api\Data\TaxClassInterface; use Magento\Tax\Api\TaxClassManagementInterface; +use Magento\Tax\Model\ClassModel; use Magento\Tax\Model\ClassModelRegistry; use Magento\Tax\Model\Resource\TaxClass\Collection as TaxClassCollection; use Magento\Tax\Model\Resource\TaxClass\CollectionFactory as TaxClassCollectionFactory; @@ -164,19 +164,19 @@ class Repository implements \Magento\Tax\Api\TaxClassRepositoryInterface $exception = new InputException(); if (!\Zend_Validate::is(trim($taxClass->getClassName()), 'NotEmpty')) { - $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => TaxClassInterface::KEY_NAME])); + $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => ClassModel::KEY_NAME])); } $classType = $taxClass->getClassType(); if (!\Zend_Validate::is(trim($classType), 'NotEmpty')) { - $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => TaxClassInterface::KEY_TYPE])); + $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => ClassModel::KEY_TYPE])); } elseif ($classType !== TaxClassManagementInterface::TYPE_CUSTOMER && $classType !== TaxClassManagementInterface::TYPE_PRODUCT ) { $exception->addError( __( InputException::INVALID_FIELD_VALUE, - ['fieldName' => TaxClassInterface::KEY_TYPE, 'value' => $classType] + ['fieldName' => ClassModel::KEY_TYPE, 'value' => $classType] ) ); } diff --git a/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php b/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php index 4a52c81b669fb5a3a13b775ab60686f18ecb5c99..ca0dd21abdd84a2714adf6c8eb26aa89c83584de 100644 --- a/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php +++ b/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php @@ -8,7 +8,7 @@ namespace Magento\Tax\Model\TaxClass\Source; use Magento\Framework\Exception\StateException; use Magento\Tax\Api\TaxClassManagementInterface; -use Magento\Tax\Api\Data\TaxClassInterface as TaxClass; +use Magento\Tax\Model\ClassModel; /** * Customer tax class source model. @@ -57,7 +57,7 @@ class Customer extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource { if (empty($this->_options)) { $options = []; - $filter = $this->filterBuilder->setField(TaxClass::KEY_TYPE) + $filter = $this->filterBuilder->setField(ClassModel::KEY_TYPE) ->setValue(TaxClassManagementInterface::TYPE_CUSTOMER) ->create(); $searchCriteria = $this->searchCriteriaBuilder->addFilter([$filter])->create(); diff --git a/app/code/Magento/Tax/Model/TaxClass/Source/Product.php b/app/code/Magento/Tax/Model/TaxClass/Source/Product.php index bf8711f93228abcfec3c8c6836ff0dd3847a4b23..37844ab57076c71cfc5fb8c57010de4db1fe5444 100644 --- a/app/code/Magento/Tax/Model/TaxClass/Source/Product.php +++ b/app/code/Magento/Tax/Model/TaxClass/Source/Product.php @@ -7,8 +7,8 @@ namespace Magento\Tax\Model\TaxClass\Source; use Magento\Framework\DB\Ddl\Table; -use Magento\Tax\Api\Data\TaxClassInterface as TaxClass; use Magento\Tax\Api\TaxClassManagementInterface; +use Magento\Tax\Model\ClassModel; /** * Product tax class source model. @@ -68,7 +68,7 @@ class Product extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource { if (!$this->_options) { $filter = $this->_filterBuilder - ->setField(TaxClass::KEY_TYPE) + ->setField(ClassModel::KEY_TYPE) ->setValue(TaxClassManagementInterface::TYPE_PRODUCT) ->create(); $searchCriteria = $this->_searchCriteriaBuilder->addFilter([$filter])->create(); diff --git a/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php b/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php index 79265cacbc0c8615b0989d9deae57cb4ab6ee405..c5857168feddd83a0280449aadd5bc05c831838e 100644 --- a/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php +++ b/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php @@ -13,12 +13,21 @@ use Magento\Tax\Api\Data\AppliedTaxInterface; */ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_TAX_RATE_KEY = 'tax_rate_key'; + const KEY_PERCENT = 'percent'; + const KEY_AMOUNT = 'amount'; + const KEY_RATES = 'rates'; + /**#@-*/ + /** * {@inheritdoc} */ public function getTaxRateKey() { - return $this->getData(AppliedTaxInterface::KEY_TAX_RATE_KEY); + return $this->getData(self::KEY_TAX_RATE_KEY); } /** @@ -26,7 +35,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function getPercent() { - return $this->getData(AppliedTaxInterface::KEY_PERCENT); + return $this->getData(self::KEY_PERCENT); } /** @@ -34,7 +43,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function getAmount() { - return $this->getData(AppliedTaxInterface::KEY_AMOUNT); + return $this->getData(self::KEY_AMOUNT); } /** @@ -42,7 +51,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function getRates() { - return $this->getData(AppliedTaxInterface::KEY_RATES); + return $this->getData(self::KEY_RATES); } /** @@ -53,7 +62,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function setTaxRateKey($taxRateKey) { - return $this->setData(AppliedTaxInterface::KEY_TAX_RATE_KEY, $taxRateKey); + return $this->setData(self::KEY_TAX_RATE_KEY, $taxRateKey); } /** @@ -64,7 +73,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function setPercent($percent) { - return $this->setData(AppliedTaxInterface::KEY_PERCENT, $percent); + return $this->setData(self::KEY_PERCENT, $percent); } /** @@ -75,7 +84,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function setAmount($amount) { - return $this->setData(AppliedTaxInterface::KEY_AMOUNT, $amount); + return $this->setData(self::KEY_AMOUNT, $amount); } /** @@ -86,7 +95,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface */ public function setRates(array $rates = null) { - return $this->setData(AppliedTaxInterface::KEY_RATES, $rates); + return $this->setData(self::KEY_RATES, $rates); } /** diff --git a/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php b/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php index 8cd44cb70e0e36de4ebe281b05e439e18be8312d..a04aed56a7f4a8b6224f628d9786d98a136821f3 100644 --- a/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php +++ b/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php @@ -13,12 +13,20 @@ use Magento\Tax\Api\Data\AppliedTaxRateInterface; */ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_CODE = 'code'; + const KEY_TITLE = 'title'; + const KEY_PERCENT = 'percent'; + /**#@-*/ + /** * {@inheritdoc} */ public function getCode() { - return $this->getData(AppliedTaxRateInterface::KEY_CODE); + return $this->getData(self::KEY_CODE); } /** @@ -26,7 +34,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn */ public function getTitle() { - return $this->getData(AppliedTaxRateInterface::KEY_TITLE); + return $this->getData(self::KEY_TITLE); } /** @@ -34,7 +42,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn */ public function getPercent() { - return $this->getData(AppliedTaxRateInterface::KEY_PERCENT); + return $this->getData(self::KEY_PERCENT); } /** @@ -45,7 +53,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn */ public function setCode($code) { - return $this->setData(AppliedTaxRateInterface::KEY_CODE, $code); + return $this->setData(self::KEY_CODE, $code); } /** @@ -56,7 +64,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn */ public function setTitle($title) { - return $this->setData(AppliedTaxRateInterface::KEY_TITLE, $title); + return $this->setData(self::KEY_TITLE, $title); } /** @@ -67,7 +75,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn */ public function setPercent($percent) { - return $this->setData(AppliedTaxRateInterface::KEY_PERCENT, $percent); + return $this->setData(self::KEY_PERCENT, $percent); } /** diff --git a/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php b/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php index 9ba71b43dedf1c2bf676436e6a3754e4f168d69f..d4f1628fbdbb8f4921e1162f01e23bd68aa94223 100644 --- a/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php +++ b/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php @@ -13,12 +13,30 @@ use Magento\Tax\Api\Data\TaxDetailsItemInterface; */ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_CODE = 'code'; + const KEY_TYPE = 'type'; + const KEY_TAX_PERCENT = 'tax_percent'; + const KEY_PRICE = 'price'; + const KEY_PRICE_INCL_TAX = 'price_incl_tax'; + const KEY_ROW_TOTAL = 'row_total'; + const KEY_ROW_TOTAL_INCL_TAX = 'row_total_incl_tax'; + const KEY_ROW_TAX = 'row_tax'; + const KEY_TAXABLE_AMOUNT = 'taxable_amount'; + const KEY_DISCOUNT_AMOUNT = 'discount_amount'; + const KEY_APPLIED_TAXES = 'applied_taxes'; + const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code'; + const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount'; + /**#@-*/ + /** * {@inheritdoc} */ public function getCode() { - return $this->getData(TaxDetailsItemInterface::KEY_CODE); + return $this->getData(self::KEY_CODE); } /** @@ -26,7 +44,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getType() { - return $this->getData(TaxDetailsItemInterface::KEY_TYPE); + return $this->getData(self::KEY_TYPE); } /** @@ -34,7 +52,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getTaxPercent() { - return $this->getData(TaxDetailsItemInterface::KEY_TAX_PERCENT); + return $this->getData(self::KEY_TAX_PERCENT); } /** @@ -42,7 +60,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getPrice() { - return $this->getData(TaxDetailsItemInterface::KEY_PRICE); + return $this->getData(self::KEY_PRICE); } /** @@ -50,7 +68,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getPriceInclTax() { - return $this->getData(TaxDetailsItemInterface::KEY_PRICE_INCL_TAX); + return $this->getData(self::KEY_PRICE_INCL_TAX); } /** @@ -58,7 +76,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getRowTotal() { - return $this->getData(TaxDetailsItemInterface::KEY_ROW_TOTAL); + return $this->getData(self::KEY_ROW_TOTAL); } /** @@ -66,7 +84,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getRowTotalInclTax() { - return $this->getData(TaxDetailsItemInterface::KEY_ROW_TOTAL_INCL_TAX); + return $this->getData(self::KEY_ROW_TOTAL_INCL_TAX); } /** @@ -74,7 +92,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getRowTax() { - return $this->getData(TaxDetailsItemInterface::KEY_ROW_TAX); + return $this->getData(self::KEY_ROW_TAX); } /** @@ -82,7 +100,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getTaxableAmount() { - return $this->getData(TaxDetailsItemInterface::KEY_TAXABLE_AMOUNT); + return $this->getData(self::KEY_TAXABLE_AMOUNT); } /** @@ -90,7 +108,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getDiscountAmount() { - return $this->getData(TaxDetailsItemInterface::KEY_DISCOUNT_AMOUNT); + return $this->getData(self::KEY_DISCOUNT_AMOUNT); } /** @@ -98,7 +116,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getDiscountTaxCompensationAmount() { - return $this->getData(TaxDetailsItemInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT); + return $this->getData(self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT); } /** @@ -106,7 +124,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getAppliedTaxes() { - return $this->getData(TaxDetailsItemInterface::KEY_APPLIED_TAXES); + return $this->getData(self::KEY_APPLIED_TAXES); } /** @@ -114,7 +132,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function getAssociatedItemCode() { - return $this->getData(TaxDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE); + return $this->getData(self::KEY_ASSOCIATED_ITEM_CODE); } /** @@ -125,7 +143,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setCode($code) { - return $this->setData(TaxDetailsItemInterface::KEY_CODE, $code); + return $this->setData(self::KEY_CODE, $code); } /** @@ -136,7 +154,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setType($type) { - return $this->setData(TaxDetailsItemInterface::KEY_TYPE, $type); + return $this->setData(self::KEY_TYPE, $type); } /** @@ -147,7 +165,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setTaxPercent($taxPercent) { - return $this->setData(TaxDetailsItemInterface::KEY_TAX_PERCENT, $taxPercent); + return $this->setData(self::KEY_TAX_PERCENT, $taxPercent); } /** @@ -158,7 +176,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setPrice($price) { - return $this->setData(TaxDetailsItemInterface::KEY_PRICE, $price); + return $this->setData(self::KEY_PRICE, $price); } /** @@ -169,7 +187,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setPriceInclTax($priceInclTax) { - return $this->setData(TaxDetailsItemInterface::KEY_PRICE_INCL_TAX, $priceInclTax); + return $this->setData(self::KEY_PRICE_INCL_TAX, $priceInclTax); } /** @@ -180,7 +198,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setRowTotal($rowTotal) { - return $this->setData(TaxDetailsItemInterface::KEY_ROW_TOTAL, $rowTotal); + return $this->setData(self::KEY_ROW_TOTAL, $rowTotal); } /** @@ -191,7 +209,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setRowTotalInclTax($rowTotalInclTax) { - return $this->setData(TaxDetailsItemInterface::KEY_ROW_TOTAL_INCL_TAX, $rowTotalInclTax); + return $this->setData(self::KEY_ROW_TOTAL_INCL_TAX, $rowTotalInclTax); } /** @@ -202,7 +220,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setRowTax($rowTax) { - return $this->setData(TaxDetailsItemInterface::KEY_ROW_TAX, $rowTax); + return $this->setData(self::KEY_ROW_TAX, $rowTax); } /** @@ -213,7 +231,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setTaxableAmount($taxableAmount) { - return $this->setData(TaxDetailsItemInterface::KEY_TAXABLE_AMOUNT, $taxableAmount); + return $this->setData(self::KEY_TAXABLE_AMOUNT, $taxableAmount); } /** @@ -224,7 +242,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setDiscountAmount($discountAmount) { - return $this->setData(TaxDetailsItemInterface::KEY_DISCOUNT_AMOUNT, $discountAmount); + return $this->setData(self::KEY_DISCOUNT_AMOUNT, $discountAmount); } /** @@ -236,7 +254,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter public function setDiscountTaxCompensationAmount($discountTaxCompensationAmount) { return $this->setData( - TaxDetailsItemInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT, + self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT, $discountTaxCompensationAmount ); } @@ -249,7 +267,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setAppliedTaxes(array $appliedTaxes = null) { - return $this->setData(TaxDetailsItemInterface::KEY_APPLIED_TAXES, $appliedTaxes); + return $this->setData(self::KEY_APPLIED_TAXES, $appliedTaxes); } /** @@ -260,7 +278,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter */ public function setAssociatedItemCode($associatedItemCode) { - return $this->setData(TaxDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode); + return $this->setData(self::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode); } /** diff --git a/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php b/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php index 91f1587c8b575ce08e5f52a0196c724f40917a36..721dedffeb29e43cb8433b1222e9e0059e8a4f7a 100644 --- a/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php +++ b/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php @@ -13,12 +13,22 @@ use Magento\Tax\Api\Data\TaxDetailsInterface; */ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface { + /**#@+ + * Constants defined for keys of array, makes typos less likely + */ + const KEY_SUBTOTAL = 'subtotal'; + const KEY_TAX_AMOUNT = 'tax_amount'; + const KEY_APPLIED_TAXES = 'applied_taxes'; + const KEY_ITEMS = 'items'; + const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount'; + /**#@-*/ + /** * {@inheritdoc} */ public function getSubtotal() { - return $this->getData(TaxDetailsInterface::KEY_SUBTOTAL); + return $this->getData(self::KEY_SUBTOTAL); } /** @@ -26,7 +36,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function getTaxAmount() { - return $this->getData(TaxDetailsInterface::KEY_TAX_AMOUNT); + return $this->getData(self::KEY_TAX_AMOUNT); } /** @@ -34,7 +44,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function getDiscountTaxCompensationAmount() { - return $this->getData(TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT); + return $this->getData(self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT); } /** @@ -42,7 +52,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function getAppliedTaxes() { - return $this->getData(TaxDetailsInterface::KEY_APPLIED_TAXES); + return $this->getData(self::KEY_APPLIED_TAXES); } /** @@ -50,7 +60,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function getItems() { - return $this->getData(TaxDetailsInterface::KEY_ITEMS); + return $this->getData(self::KEY_ITEMS); } /** @@ -61,7 +71,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function setSubtotal($subtotal) { - return $this->setData(TaxDetailsInterface::KEY_SUBTOTAL, $subtotal); + return $this->setData(self::KEY_SUBTOTAL, $subtotal); } /** @@ -72,7 +82,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function setTaxAmount($taxAmount) { - return $this->setData(TaxDetailsInterface::KEY_TAX_AMOUNT, $taxAmount); + return $this->setData(self::KEY_TAX_AMOUNT, $taxAmount); } /** @@ -84,7 +94,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface public function setDiscountTaxCompensationAmount($discountTaxCompensationAmount) { return $this->setData( - TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT, + self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT, $discountTaxCompensationAmount ); } @@ -97,7 +107,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function setAppliedTaxes(array $appliedTaxes = null) { - return $this->setData(TaxDetailsInterface::KEY_APPLIED_TAXES, $appliedTaxes); + return $this->setData(self::KEY_APPLIED_TAXES, $appliedTaxes); } /** @@ -108,7 +118,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface */ public function setItems(array $items = null) { - return $this->setData(TaxDetailsInterface::KEY_ITEMS, $items); + return $this->setData(self::KEY_ITEMS, $items); } /** diff --git a/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Rate/AjaxLoadTest.php b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Rate/AjaxLoadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6373e5aa514bf11a2d6174c78e64489bd7d558b0 --- /dev/null +++ b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Rate/AjaxLoadTest.php @@ -0,0 +1,238 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Tax\Test\Unit\Controller\Adminhtml\Rate; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Test for AjaxLoadTest + */ +class AjaxLoadTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\App\Request\Http + */ + private $request; + + /** + * @var \Magento\Framework\App\Response\Http + */ + private $resultFactory; + + /** + * @var \Magento\Tax\Model\Calculation\RateRepository + */ + private $taxRateRepository; + + /* + * test setup + */ + public function setUp() + { + $this->request = $this->getMockBuilder('\Magento\Framework\App\Request\Http') + ->disableOriginalConstructor() + ->setMethods(['getParam']) + ->getMock(); + + $this->resultFactory = $this->getMockBuilder('Magento\Framework\Controller\ResultFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->taxRateRepository = $this->getMockBuilder('\Magento\Tax\Model\Calculation\RateRepository') + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + } + + /** + * Executes the controller action and asserts non exception logic + */ + public function testExecute() + { + $taxRateId=1; + $returnArray=[ + 'tax_calculation_rate_id' => null, + 'tax_country_id' => 'US', + 'tax_region_id' => 2, + 'tax_postcode' => null, + 'code' => 'Tax Rate Code', + 'rate' => 7.5, + 'zip_is_range'=> 0, + 'title[1]' => 'texas', + ]; + $objectManager = new ObjectManager($this); + $rateTitles = [$objectManager->getObject( + '\Magento\Tax\Model\Calculation\Rate\Title', + ['data' => ['store_id' => 1, 'value' => 'texas']] + ) + ]; + $rateMock = $objectManager->getObject( + 'Magento\Tax\Model\Calculation\Rate', + [ + 'data' => + [ + 'tax_country_id' => 'US', + 'tax_region_id' => 2, + 'tax_postcode' => null, + 'rate' => 7.5, + 'code' => 'Tax Rate Code', + 'titles' => $rateTitles, + ], + ] + ); + + $this->request->expects($this->any()) + ->method('getParam') + ->will($this->returnValue($taxRateId)); + + $this->taxRateRepository->expects($this->any()) + ->method('get') + ->with($taxRateId) + ->will($this->returnValue($rateMock)); + + $taxRateConverter = $this->getMockBuilder('\Magento\Tax\Model\Calculation\Rate\Converter') + ->disableOriginalConstructor() + ->getMock(); + + $taxRateConverter->expects($this->any()) + ->method('createArrayFromServiceObject') + ->with($rateMock, true) + ->willReturn($returnArray); + + $jsonObject= $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->setMethods(['setData']) + ->getMock(); + + $jsonObject->expects($this->once()) + ->method('setData') + ->with(['success' => true, 'error_message' => '', 'result'=> + $returnArray, + ]); + + $this->resultFactory->expects($this->any()) + ->method('create') + ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) + ->willReturn($jsonObject); + + $notification = $objectManager->getObject( + 'Magento\Tax\Controller\Adminhtml\Rate\AjaxLoad', + [ + 'taxRateRepository' => $this->taxRateRepository, + 'taxRateConverter' => $taxRateConverter, + 'request' => $this->request, + 'resultFactory' => $this->resultFactory, + ] + ); + + + // No exception thrown + $this->assertSame($jsonObject, $notification->execute()); + + } + + /** + * Check if validation throws a localized catched exception in case of incorrect id + */ + public function testExecuteLocalizedException() + { + $taxRateId=999; + $exceptionMessage='No such entity with taxRateId = '.$taxRateId; + $noSuchEntityEx= new NoSuchEntityException(__($exceptionMessage)); + + $objectManager = new ObjectManager($this); + + $this->request->expects($this->any()) + ->method('getParam') + ->will($this->returnValue($taxRateId)); + + $this->taxRateRepository->expects($this->any()) + ->method('get') + ->with($taxRateId) + ->willThrowException($noSuchEntityEx); + + $jsonObject= $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->setMethods(['setData']) + ->getMock(); + + $jsonObject->expects($this->once()) + ->method('setData') + ->with([ + 'success' => false, + 'error_message' => $exceptionMessage, + ]); + + $this->resultFactory->expects($this->any()) + ->method('create') + ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) + ->willReturn($jsonObject); + + $notification = $objectManager->getObject( + 'Magento\Tax\Controller\Adminhtml\Rate\AjaxLoad', + [ + 'taxRateRepository' => $this->taxRateRepository, + 'request' => $this->request, + 'resultFactory' => $this->resultFactory, + ] + ); + + //exception thrown with catch + $this->assertSame($jsonObject, $notification->execute()); + } + + /** + * Check if validation throws a localized catched exception in case of incorrect id + */ + public function testExecuteException() + { + $taxRateId=999; + $exceptionMessage=__('An error occurred while loading this tax rate.'); + $noSuchEntityEx= new \Exception(); + + $objectManager = new ObjectManager($this); + + $this->request->expects($this->any()) + ->method('getParam') + ->will($this->returnValue($taxRateId)); + + $this->taxRateRepository->expects($this->any()) + ->method('get') + ->with($taxRateId) + ->willThrowException($noSuchEntityEx); + + $jsonObject= $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->setMethods(['setData']) + ->getMock(); + + $jsonObject->expects($this->once()) + ->method('setData') + ->with([ + 'success' => false, + 'error_message' => $exceptionMessage, + ]); + + $this->resultFactory->expects($this->any()) + ->method('create') + ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) + ->willReturn($jsonObject); + + $notification = $objectManager->getObject( + 'Magento\Tax\Controller\Adminhtml\Rate\AjaxLoad', + [ + 'taxRateRepository' => $this->taxRateRepository, + 'request' => $this->request, + 'resultFactory' => $this->resultFactory, + ] + ); + + //exception thrown with catch + $this->assertSame($jsonObject, $notification->execute()); + } +} diff --git a/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php index 2f2a823d5f64d3cb8c3b00a4c864a7108e181c32..8714905efc56bc118efd516eb8346ec1e2045ad6 100644 --- a/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php +++ b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Tax\Test\Unit\Controller\Adminhtml\Tax; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class IgnoreTaxNotificationTest extends \PHPUnit_Framework_TestCase @@ -51,7 +52,7 @@ class IgnoreTaxNotificationTest extends \PHPUnit_Framework_TestCase ->getMock(); $config->expects($this->once()) ->method('saveConfig') - ->with('tax/notification/ignore_tax', 1, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0) + ->with('tax/notification/ignore_tax', 1, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0) ->willReturn(null); $manager = $this->getMockBuilder('\Magento\Framework\ObjectManagerInterface') diff --git a/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php b/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php index 7fa4b63e75e603558c6a26fe028472552f58fd72..dc4dfcf84c4a377205e513c526e15c427018307c 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Tax\Test\Unit\Model\Calculation\Rate; -use \Magento\Tax\Model\Calculation\Rate\Converter; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class ConverterTest extends \PHPUnit_Framework_TestCase { @@ -14,9 +14,45 @@ class ConverterTest extends \PHPUnit_Framework_TestCase */ protected $converter; + /** + * @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory + */ + protected $taxRateDataObjectFactory; + + /** + * @var \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory + */ + protected $taxRateTitleDataObjectFactory; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper + */ + protected $objectManager; + public function setUp() { - $this->converter = new Converter(); + $this->taxRateDataObjectFactory = $this->getMockBuilder( + '\Magento\Tax\Api\Data\TaxRateInterfaceFactory' + ) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->taxRateTitleDataObjectFactory = $this->getMockBuilder( + '\Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory' + ) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->objectManager = new ObjectManager($this); + $this->converter = $this->objectManager->getObject( + 'Magento\Tax\Model\Calculation\Rate\Converter', + [ + 'taxRateDataObjectFactory' => $this->taxRateDataObjectFactory, + 'taxRateTitleDataObjectFactory' => $this->taxRateTitleDataObjectFactory, + ] + ); } public function testCreateTitlesFromServiceObject() @@ -39,4 +75,48 @@ class ConverterTest extends \PHPUnit_Framework_TestCase $this->assertEquals([], $this->converter->createTitleArrayFromServiceObject($taxRateMock)); } + + + public function testCreateArrayFromServiceObject() + { + $taxRateMock = $this->getMock('Magento\Tax\Api\Data\TaxRateInterface'); + $titlesMock = $this->getMock('Magento\Tax\Api\Data\TaxRateTitleInterface'); + + $taxRateMock->expects($this->atLeastOnce())->method('getTitles')->willReturn([$titlesMock]); + $titlesMock->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $titlesMock->expects($this->atLeastOnce())->method('getValue')->willReturn('Value'); + + $this->assertArrayHasKey('title[1]', $this->converter->createArrayFromServiceObject($taxRateMock, true)); + $this->assertArrayHasKey('title', $this->converter->createArrayFromServiceObject($taxRateMock)); + $this->assertTrue(is_array($this->converter->createArrayFromServiceObject($taxRateMock))); + } + + public function testPopulateTaxRateData() + { + $rateTitles = [$this->objectManager->getObject( + '\Magento\Tax\Model\Calculation\Rate\Title', + ['data' => ['store_id' => 1, 'value' => 'texas']] + ) + ]; + $dataArray=[ + 'tax_country_id' => 'US', + 'tax_region_id' => 2, + 'tax_postcode' => null, + 'rate' => 7.5, + 'code' => 'Tax Rate Code', + 'titles' => $rateTitles, + ]; + + $taxRate = $this->objectManager->getObject( + 'Magento\Tax\Model\Calculation\Rate', + [ + 'data' =>$dataArray, + ] + ); + + $this->taxRateDataObjectFactory->expects($this->once())->method('create')->willReturn($taxRate); + + $this->assertSame($taxRate, $this->converter->populateTaxRateData($dataArray)); + $this->assertEquals($taxRate->getTitles(), $rateTitles); + } } diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php index f4bd04f1adf82bf8c77346c6ead678e85bb99f2c..d88ea2756c2e95330fc1a520c2d2a42c8744b259 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php @@ -199,11 +199,11 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase $customerId = 100; $taxClassId = 200; $taxDetailsData = [ - \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_SUBTOTAL => 0.0, - \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_TAX_AMOUNT => 0.0, - \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, - \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_APPLIED_TAXES => [], - \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_ITEMS => [], + \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_SUBTOTAL => 0.0, + \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_TAX_AMOUNT => 0.0, + \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, + \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_APPLIED_TAXES => [], + \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_ITEMS => [], ]; $quoteDetailsMock = $this->getMock('\Magento\Tax\Api\Data\QuoteDetailsInterface'); diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php index 3ceefb565192b79193555b490f15d159f414024b..de391779d3ec915a2796f3404b84206c024cbdbb 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php @@ -79,8 +79,8 @@ class ManagementTest extends \PHPUnit_Framework_TestCase ->method('setField') ->with( $this->logicalOr( - \Magento\Tax\Api\Data\TaxClassInterface::KEY_TYPE, - \Magento\Tax\Api\Data\TaxClassInterface::KEY_NAME + \Magento\Tax\Model\ClassModel::KEY_TYPE, + \Magento\Tax\Model\ClassModel::KEY_NAME ) )->willReturnSelf(); diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php index 6adec4612fe7b22e96f97bba7dd73119d4a4449c..c36618f8faf5c2a7cdc68b9aab3a16fbe274a5d9 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php @@ -120,7 +120,7 @@ class CustomerTest extends \PHPUnit_Framework_TestCase $this->filterBuilderMock->expects($this->once()) ->method('setField') - ->with(\Magento\Tax\Api\Data\TaxClassInterface::KEY_TYPE) + ->with(\Magento\Tax\Model\ClassModel::KEY_TYPE) ->willReturnSelf(); $this->filterBuilderMock->expects($this->once()) ->method('setValue') diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml index e1941d06d1d4973744aa9e6cc415e282cc799780..4861e4b3bdc539fe555e6eefa3eaad39a489ecdb 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml @@ -73,16 +73,33 @@ require([ id = select.find('option').eq(index).attr('value'), item; - for(var i = 0, c = taxRateCollection.length; i < c; i++) { - if (taxRateCollection[i].tax_calculation_rate_id == id) { - item = taxRateCollection[i]; - break; - } - } - item.itemElement = that.prev(); - $('#tax-rate-form') - .dialogRates({itemRate: item}) - .dialogRates('open'); + $('body').trigger('processStart') + $.ajax({ + type: "POST", + data: {id:id}, + url: '<?php echo $block->getTaxRateLoadUrl()?>', + success: function(result, status) { + $('body').trigger('processStop'); + if (result.success) { + item=result.result; + item.itemElement = that.prev(); + $('#tax-rate-form') + .dialogRates({itemRate: item}) + .dialogRates('open'); + + } else { + if (result.error_message) + alert(result.error_message); + else + alert('<?php echo __('An error occurred'); ?>'); + } + }, + error: function () { + $('body').trigger('processStop'); + alert('<?php echo __('An error occurred'); ?>'); + }, + dataType: "json" + }); }; TaxRateEditableMultiselect.prototype.init = function () { @@ -142,6 +159,7 @@ require([ index = that.parent().index(), select = that.closest('.mselect-list').prev(); + $('body').trigger('processStart') var ajaxOptions = { type: 'POST', data: { @@ -151,12 +169,20 @@ require([ dataType: 'json', url: '<?php echo $block->getTaxRateDeleteUrl()?>', success: function(result, status) { + $('body').trigger('processStop'); if (result.success) { that.parent().remove(); select.find('option').eq(index).remove(); } else { - alert(result.error_message); + if (result.error_message) + alert(result.error_message); + else + alert('<?php echo __('An error occurred'); ?>'); } + }, + error: function () { + $('body').trigger('processStop'); + alert('<?php echo __('An error occurred'); ?>'); } }; $.ajax(ajaxOptions); @@ -215,13 +241,15 @@ require([ if (!taxRateFormElement.validation().valid()) { return; } - + $('.tax-rate-popup').trigger('processStart'); + $('.loading-mask').css('z-index','1004'); var ajaxOptions = { type: 'POST', data: itemRateData, dataType: 'json', url: '<?php echo $block->getTaxRateSaveUrl()?>', success: function(result, status) { + $('body').trigger('processStop'); if (result.success) { itemRate.code = result.code; if (itemRate.tax_calculation_rate_id) { @@ -238,10 +266,16 @@ require([ .val(itemRate.tax_calculation_rate_id); } taxRateForm.dialogRates("close"); - taxRateCollection.push(itemRate); } else { - alert(result.error_message); + if (result.error_message) + alert(result.error_message); + else + alert('<?php echo __('An error occurred'); ?>'); } + }, + error: function () { + $('body').trigger('processStop'); + alert('<?php echo __('An error occurred'); ?>'); } }; $.ajax(ajaxOptions); @@ -256,6 +290,7 @@ require([ } }] }); + $('.grid-loading-mask').hide(); } }; diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml index eae01d9d0b302c71017afb3571349eb82bcc6043..d5c0d8aee3b6a8f84af22f3fd90f2c2afde1f0f5 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml @@ -5,14 +5,13 @@ */ /* @var $block \Magento\Tax\Block\Adminhtml\Rate\Form */ ?> + +<div data-role="spinner" class="grid-loading-mask"> + <div class="grid-loader"></div> +</div> + <div class="form-inline" id="<?php echo $block->getNameInLayout() ?>" style="display:none"> <?php echo $block->getFormHtml();?> <?php echo $block->getChildHtml('form_after');?> </div> -<script> - - - var taxRateCollection = <?php echo json_encode($block->getRateCollection());?>; - -</script> diff --git a/app/code/Magento/Theme/Model/Config.php b/app/code/Magento/Theme/Model/Config.php index 5993e9bf010d5586384e03bdc4df50010f882547..39332cc9a0b7b7705db033cb7996e0fd3c482e90 100644 --- a/app/code/Magento/Theme/Model/Config.php +++ b/app/code/Magento/Theme/Model/Config.php @@ -9,6 +9,8 @@ */ namespace Magento\Theme\Model; +use Magento\Framework\App\Config\ScopeConfigInterface; + class Config { /** @@ -173,7 +175,7 @@ class Config protected function _assignThemeToDefaultScope($themeId, &$isReassigned) { $configPath = \Magento\Framework\View\DesignInterface::XML_PATH_THEME_ID; - $this->_configWriter->save($configPath, $themeId, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT); + $this->_configWriter->save($configPath, $themeId, ScopeConfigInterface::SCOPE_TYPE_DEFAULT); $isReassigned = true; return $this; } diff --git a/app/code/Magento/Theme/Model/View/Design.php b/app/code/Magento/Theme/Model/View/Design.php index f839d6ba900f71e2998634aa9e36776bcd863105..f0cf795f6b4482b9c8ac59bc444e62f4af24f14c 100644 --- a/app/code/Magento/Theme/Model/View/Design.php +++ b/app/code/Magento/Theme/Model/View/Design.php @@ -6,6 +6,8 @@ namespace Magento\Theme\Model\View; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * Keeps design settings for current request */ @@ -167,7 +169,7 @@ class Design implements \Magento\Framework\View\DesignInterface if ($this->_storeManager->isSingleStoreMode()) { $theme = $this->_scopeConfig->getValue( self::XML_PATH_THEME_ID, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); } else { $theme = (string) $this->_scopeConfig->getValue( diff --git a/app/code/Magento/Theme/view/adminhtml/requirejs-config.js b/app/code/Magento/Theme/view/adminhtml/requirejs-config.js index 251c057d689f12e75e85991ed46d8b0e2faee673..0cc7098574c9d120f5235644bdf74ded7d9c82b2 100644 --- a/app/code/Magento/Theme/view/adminhtml/requirejs-config.js +++ b/app/code/Magento/Theme/view/adminhtml/requirejs-config.js @@ -5,6 +5,9 @@ var config = { "shim": { + "extjs/ext-tree": [ + "prototype" + ], "extjs/ext-tree-checkbox": [ "extjs/ext-tree", "extjs/defaults" diff --git a/app/code/Magento/User/Controller/Adminhtml/User/Edit.php b/app/code/Magento/User/Controller/Adminhtml/User/Edit.php index 3d3ad64cd201a0bf5cc7eb3691852ab451317e21..39ac43d55391ba02f47089b28233a7f13e791d86 100644 --- a/app/code/Magento/User/Controller/Adminhtml/User/Edit.php +++ b/app/code/Magento/User/Controller/Adminhtml/User/Edit.php @@ -6,6 +6,8 @@ */ namespace Magento\User\Controller\Adminhtml\User; +use Magento\Framework\Locale\Resolver; + class Edit extends \Magento\User\Controller\Adminhtml\User { /** @@ -25,7 +27,7 @@ class Edit extends \Magento\User\Controller\Adminhtml\User return; } } else { - $model->setInterfaceLocale(\Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE); + $model->setInterfaceLocale(Resolver::DEFAULT_LOCALE); } // Restore previously entered form data from session diff --git a/app/code/Magento/Webapi/Controller/Rest/Router/Route.php b/app/code/Magento/Webapi/Controller/Rest/Router/Route.php index 5c125c29611d9e2000436a7147329dfa449ab729..03e6d2fd8ca06eb864bfa318af4a91182e8c1bb7 100644 --- a/app/code/Magento/Webapi/Controller/Rest/Router/Route.php +++ b/app/code/Magento/Webapi/Controller/Rest/Router/Route.php @@ -69,7 +69,7 @@ class Route implements RouterInterface $this->variables[$key] = substr($value, 1); $value = null; } - $result[$key] = strtolower($value); + $result[$key] = $value; } return $result; } @@ -92,19 +92,12 @@ class Route implements RouterInterface /** * Retrieve unified requested path * - * Lowercase all path chunks, except variables names. - * E.g. the path '/V1/Categories/:categoryId' will be converted to '/v1/categories/:categoryId'. - * * @param string $path * @return array */ protected function getPathParts($path) { - $result = explode('/', trim($path, '/')); - array_walk($result, function (&$item) { - $item = substr($item, 0, 1) === ":" ? $item : strtolower($item); - }); - return $result; + return explode('/', trim($path, '/')); } /** diff --git a/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php b/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php index 25d196ce6a80461109ec3246bbe3a5bb895cfa22..ec73bd7236c104f5364b03433e83d8c622e12414 100644 --- a/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php +++ b/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php @@ -15,6 +15,7 @@ use Magento\Framework\Webapi\ServiceInputProcessor; use Magento\Webapi\Controller\Soap\Request as SoapRequest; use Magento\Framework\Webapi\Exception as WebapiException; use Magento\Webapi\Model\Soap\Config as SoapConfig; +use Magento\Framework\Reflection\MethodsMap; /** * Handler of requests to SOAP server. @@ -48,6 +49,9 @@ class Handler /** @var DataObjectProcessor */ protected $_dataObjectProcessor; + /** @var MethodsMap */ + protected $methodsMapProcessor; + /** * Initialize dependencies. * @@ -58,6 +62,7 @@ class Handler * @param SimpleDataObjectConverter $dataObjectConverter * @param ServiceInputProcessor $serviceInputProcessor * @param DataObjectProcessor $dataObjectProcessor + * @param MethodsMap $methodsMapProcessor */ public function __construct( SoapRequest $request, @@ -66,7 +71,8 @@ class Handler AuthorizationInterface $authorization, SimpleDataObjectConverter $dataObjectConverter, ServiceInputProcessor $serviceInputProcessor, - DataObjectProcessor $dataObjectProcessor + DataObjectProcessor $dataObjectProcessor, + MethodsMap $methodsMapProcessor ) { $this->_request = $request; $this->_objectManager = $objectManager; @@ -75,6 +81,7 @@ class Handler $this->_dataObjectConverter = $dataObjectConverter; $this->serviceInputProcessor = $serviceInputProcessor; $this->_dataObjectProcessor = $dataObjectProcessor; + $this->methodsMapProcessor = $methodsMapProcessor; } /** @@ -149,7 +156,7 @@ class Handler protected function _prepareResponseData($data, $serviceClassName, $serviceMethodName) { /** @var string $dataType */ - $dataType = $this->_dataObjectProcessor->getMethodReturnType($serviceClassName, $serviceMethodName); + $dataType = $this->methodsMapProcessor->getMethodReturnType($serviceClassName, $serviceMethodName); $result = null; if (is_object($data)) { $result = $this->_dataObjectConverter diff --git a/app/code/Magento/Webapi/Model/Rest/Config.php b/app/code/Magento/Webapi/Model/Rest/Config.php index dfee1d873c02158645f482b3fe7242ecc5636bfe..51d0e67d7803603ef4ff02e6db406f041166b2d9 100644 --- a/app/code/Magento/Webapi/Model/Rest/Config.php +++ b/app/code/Magento/Webapi/Model/Rest/Config.php @@ -67,7 +67,7 @@ class Config /** @var $route \Magento\Webapi\Controller\Rest\Router\Route */ $route = $this->_routeFactory->createRoute( 'Magento\Webapi\Controller\Rest\Router\Route', - $this->_formatRoutePath($routeData[self::KEY_ROUTE_PATH]) + $routeData[self::KEY_ROUTE_PATH] ); $route->setServiceClass($routeData[self::KEY_CLASS]) @@ -78,22 +78,6 @@ class Config return $route; } - /** - * Lowercase all parts of the given route path except for the path parameters. - * - * @param string $routePath The route path (e.g. '/V1/Categories/:categoryId') - * @return string The modified route path (e.g. '/v1/categories/:categoryId') - */ - protected function _formatRoutePath($routePath) - { - $routePathParts = explode('/', $routePath); - $pathParts = []; - foreach ($routePathParts as $pathPart) { - $pathParts[] = substr($pathPart, 0, 1) === ":" ? $pathPart : strtolower($pathPart); - } - return implode('/', $pathParts); - } - /** * Get service base URL * diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php index 6732d012dc1e726e29e4afab132711759d4ce7f9..4a9192b36882b9f69a66755af08cd3f050374beb 100644 --- a/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php @@ -95,10 +95,7 @@ class RouteTest extends \PHPUnit_Framework_TestCase ['/V1/one/two/:threeValue/four/:fiveValue', '/V1/one/two/3/four/5', ['threeValue' => 3, 'fiveValue' => 5]], ['/v1/One', '/v1/One', []], - ['/v1/oNe', '/V1/one', []], - ['/v1/onE', '/V1/oNe', []], - ['/v1/One/:twoValue', '/V1/one/2', ['twoValue' => 2]], ['/v1/oNe/:TwoValue', '/v1/oNe/2', ['TwoValue' => 2]], ['/v1/onE/:twovalue', '/v1/onE/2', ['twovalue' => 2]], @@ -108,6 +105,9 @@ class RouteTest extends \PHPUnit_Framework_TestCase ['/V1/one-one/:two_value', '/V1/one-one/2', ['two_value' => 2]], // Error + ['/v1/oNe', '/V1/one', false], + ['/v1/onE', '/V1/oNe', false], + ['/v1/One/:twoValue', '/V1/one/2', false], ['/V1/one', '/V1/two', false], ['/V1/one/:twoValue', '/V1/one', false], ['/V1/one/two', '/V1/one', false], diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php index 2800d5466585fdc8eec97ba82dd53d7f5fee0c93..8c3741c3b8d48c86e0e876125cee6558a2c3a403 100644 --- a/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php @@ -40,6 +40,9 @@ class HandlerTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Framework\Reflection\DataObjectProcessor|\PHPUnit_Framework_MockObject_MockObject */ protected $_dataObjectProcessorMock; + /** @var \Magento\Framework\Reflection\MethodsMap|\PHPUnit_Framework_MockObject_MockObject */ + protected $_methodsMapProcessorMock; + /** @var array */ protected $_arguments; @@ -67,7 +70,13 @@ class HandlerTest extends \PHPUnit_Framework_TestCase ); $this->_dataObjectProcessorMock = $this->getMock( 'Magento\Framework\Reflection\DataObjectProcessor', - ['getMethodReturnType'], + [], + [], + '', + false); + $this->_methodsMapProcessorMock = $this->getMock( + 'Magento\Framework\Reflection\MethodsMap', + [], [], '', false); @@ -80,7 +89,8 @@ class HandlerTest extends \PHPUnit_Framework_TestCase $this->_authorizationMock, $this->_dataObjectConverter, $this->_serviceInputProcessorMock, - $this->_dataObjectProcessorMock + $this->_dataObjectProcessorMock, + $this->_methodsMapProcessorMock ); parent::setUp(); } @@ -128,10 +138,6 @@ class HandlerTest extends \PHPUnit_Framework_TestCase ->method('process') ->will($this->returnArgument(2)); - $this->_dataObjectProcessorMock->expects($this->any())->method('getMethodReturnType') - ->with($className, $methodName) - ->will($this->returnValue('string')); - /** Execute SUT. */ $this->assertEquals( ['result' => $serviceResponse], diff --git a/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php b/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php index 3453c7e1d7951de030d96012f813477fe73efd4b..c3f586d835a8d3c3d1d85e3626e258680541441c 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php @@ -23,7 +23,21 @@ class DataObjectProcessorTest extends \PHPUnit_Framework_TestCase protected function setup() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->dataObjectProcessor = $objectManager->getObject('Magento\Framework\Reflection\DataObjectProcessor'); + $methodsMapProcessor = $objectManager->getObject( + 'Magento\Framework\Reflection\MethodsMap', + [ + 'fieldNamer' => $objectManager->getObject('Magento\Framework\Reflection\FieldNamer'), + 'typeProcessor' => $objectManager->getObject('Magento\Framework\Reflection\TypeProcessor'), + ] + ); + $this->dataObjectProcessor = $objectManager->getObject( + 'Magento\Framework\Reflection\DataObjectProcessor', + [ + 'methodsMapProcessor' => $methodsMapProcessor, + 'typeCaster' => $objectManager->getObject('Magento\Framework\Reflection\TypeCaster'), + 'fieldNamer' => $objectManager->getObject('Magento\Framework\Reflection\FieldNamer'), + ] + ); parent::setUp(); } diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php b/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php index 1b4f971c2c3a447261a22ce6bb898311813e65b7..2010509ff416165858cbdcb1b1d3b3ae298c9759 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php @@ -8,11 +8,23 @@ namespace Magento\Webapi\Test\Unit\Model\Files; interface TestDataInterface { + /** + * @return string + */ public function getId(); + /** + * @return string + */ public function getAddress(); + /** + * @return string + */ public function isDefaultShipping(); + /** + * @return string + */ public function isRequiredBilling(); } diff --git a/app/code/Magento/Webapi/etc/di.xml b/app/code/Magento/Webapi/etc/di.xml index 12b661f639c428c681b98b1df7c0f5a8131da632..c58acdbf51c4b9e3252745d425b3d365e2a60449 100644 --- a/app/code/Magento/Webapi/etc/di.xml +++ b/app/code/Magento/Webapi/etc/di.xml @@ -22,11 +22,17 @@ <type name="Magento\Framework\Xml\Parser" shared="false" /> <type name="Magento\Framework\Code\Scanner\DirectoryScanner" shared="false" /> <type name="Magento\Server\Reflection" shared="false" /> - <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <type name="Magento\Framework\Reflection\MethodsMap"> <arguments> <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Webapi</argument> </arguments> </type> + <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessor\Proxy</argument> + <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument> + </arguments> + </type> <type name="Magento\Integration\Model\ConfigBasedIntegrationManager"> <plugin name="webapiSetup" type="Magento\Webapi\Model\Plugin\Manager" /> </type> diff --git a/app/code/Magento/Webapi/etc/webapi_rest/di.xml b/app/code/Magento/Webapi/etc/webapi_rest/di.xml index 48420045d4afca1644c159d3fa68a541cd8c403a..7941e76f564b0fa13986a9847e250a34f36d383c 100644 --- a/app/code/Magento/Webapi/etc/webapi_rest/di.xml +++ b/app/code/Magento/Webapi/etc/webapi_rest/di.xml @@ -71,4 +71,25 @@ <type name="Magento\Framework\Authorization"> <plugin name="guestAuthorization" type="Magento\Webapi\Model\Plugin\GuestAuthorization" /> </type> + + <!-- Configuration to check that the permissions are checked on fields --> + <virtualType name="Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked" type="Magento\Framework\Reflection\ExtensionAttributesProcessor"> + <arguments> + <argument name="isPermissionChecked" xsi:type="boolean">true</argument> + <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessor\Proxy</argument> + </arguments> + </virtualType> + <virtualType name="Magento\Framework\Reflection\DataObjectProcessorPermissionChecked" type="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked</argument> + <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument> + </arguments> + </virtualType> + + <type name="Magento\Framework\Webapi\ServiceOutputProcessor"> + <arguments> + <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessorPermissionChecked</argument> + </arguments> + </type> + <!-- End of configuration to check that permissions are checked on fields --> </config> diff --git a/app/code/Magento/Webapi/etc/webapi_soap/di.xml b/app/code/Magento/Webapi/etc/webapi_soap/di.xml index 6d5607c49c18dc46d9e6d9cbc429bc11f4a26467..c8cabfba0ac2529cb3365da99ae129086bee75fe 100644 --- a/app/code/Magento/Webapi/etc/webapi_soap/di.xml +++ b/app/code/Magento/Webapi/etc/webapi_soap/di.xml @@ -39,4 +39,26 @@ <type name="Magento\Framework\Authorization"> <plugin name="guestAuthorization" type="Magento\Webapi\Model\Plugin\GuestAuthorization" /> </type> + + <!-- Configuration to check that the permissions are checked on fields --> + <virtualType name="Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked" type="Magento\Framework\Reflection\ExtensionAttributesProcessor"> + <arguments> + <argument name="isPermissionChecked" xsi:type="boolean">true</argument> + <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessor\Proxy</argument> + </arguments> + </virtualType> + <virtualType name="Magento\Framework\Reflection\DataObjectProcessorPermissionChecked" type="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked</argument> + <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument> + </arguments> + </virtualType> + + <type name="Magento\Webapi\Controller\Soap\Request\Handler"> + <arguments> + <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessorPermissionChecked</argument> + </arguments> + </type> + <!-- End of configuration to check that permissions are checked on fields --> + </config> diff --git a/app/code/Magento/Weee/Model/Observer.php b/app/code/Magento/Weee/Model/Observer.php index daca840e903b7e2e8c0f11390a84c259a66ff837..08831d28845bc7fb62e842af1178310da6208160 100644 --- a/app/code/Magento/Weee/Model/Observer.php +++ b/app/code/Magento/Weee/Model/Observer.php @@ -194,4 +194,37 @@ class Observer extends \Magento\Framework\Model\AbstractModel $response->setTypes($types); return $this; } + + /** + * Modify the options config for the front end to resemble the weee final price + * + * @param \Magento\Framework\Event\Observer $observer + * @return $this + */ + public function getPriceConfiguration(\Magento\Framework\Event\Observer $observer) + { + if ($this->_weeeData->isEnabled()) { + $priceConfigObj=$observer->getData('configObj'); + $priceConfig=$priceConfigObj->getConfig(); + if (is_array($priceConfig)) { + foreach ($priceConfig as $keyConfigs => $configs) { + if (is_array($configs)) { + if (array_key_exists('prices', $configs)) { + $priceConfig[$keyConfigs]['prices']['weeePrice'] = [ + 'amount' => $configs['prices']['finalPrice']['amount'], + ]; + } else { + foreach ($configs as $keyConfig => $config) { + $priceConfig[$keyConfigs][$keyConfig]['prices']['weeePrice'] = [ + 'amount' => $config['prices']['finalPrice']['amount'], + ]; + } + } + } + } + } + $priceConfigObj->setConfig($priceConfig); + } + return $this; + } } diff --git a/app/code/Magento/Weee/Pricing/Render/Adjustment.php b/app/code/Magento/Weee/Pricing/Render/Adjustment.php index 2272c719cb94c6b6196e023f0540071693975efb..f13c044532f0514e0c230b8f51ae88b045d060b1 100644 --- a/app/code/Magento/Weee/Pricing/Render/Adjustment.php +++ b/app/code/Magento/Weee/Pricing/Render/Adjustment.php @@ -61,6 +61,14 @@ class Adjustment extends AbstractAdjustment return $this->toHtml(); } + /** + * @return float + */ + public function getRawFinalAmount() + { + return $this->finalAmount; + } + /** * Obtain adjustment code * diff --git a/app/code/Magento/Weee/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Weee/Test/Unit/Model/ObserverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..63a13abb53622e5f93590a1586d695fb17e18707 --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Model/ObserverTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Test class for \Magento\Weee\Model\Observer + */ +namespace Magento\Weee\Test\Unit\Model; + +use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class ObserverTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests the methods that rely on the ScopeConfigInterface object to provide their return values + * + */ + public function testGetPriceConfiguration() + { + $testArray=[ + [ + [ + 'prices' => + [ + 'finalPrice' => [ + 'amount' => 31.50, + ], + ], + ], + [ + 'prices' => + [ + 'finalPrice' =>[ + 'amount' => 31.50, + ], + ], + ], + ], + ]; + + $configObj = new \Magento\Framework\Object( + [ + 'config' => $testArray, + ] + ); + + $testArrayWithWeee=$testArray; + $testArrayWithWeee[0][0]['prices']['weeePrice']= [ + 'amount' => $testArray[0][0]['prices']['finalPrice']['amount'], + ]; + $testArrayWithWeee[0][1]['prices']['weeePrice']= [ + 'amount' => $testArray[0][1]['prices']['finalPrice']['amount'], + ]; + + $weeHelper=$this->getMock('Magento\Weee\Helper\Data', [], [], '', false); + $weeHelper->expects($this->any()) + ->method('isEnabled') + ->will($this->returnValue(true)); + + $observerObject=$this->getMock('Magento\Framework\Event\Observer', [], [], '', false); + + $observerObject->expects($this->any()) + ->method('getData') + ->with('configObj') + ->will($this->returnValue($configObj)); + + $objectManager = new ObjectManager($this); + $weeeObserverObject = $objectManager->getObject( + 'Magento\Weee\Model\Observer', + [ + 'weeeData' => $weeHelper, + ] + ); + $weeeObserverObject->getPriceConfiguration($observerObject); + + $this->assertEquals($testArrayWithWeee, $configObj->getData('config')); + } +} diff --git a/app/code/Magento/Weee/etc/events.xml b/app/code/Magento/Weee/etc/events.xml index b6ef0f804b1fd72258baff580d5257b90dd1a31e..f4300f38b2369817d96c1902c29c57d378676f17 100644 --- a/app/code/Magento/Weee/etc/events.xml +++ b/app/code/Magento/Weee/etc/events.xml @@ -9,4 +9,7 @@ <event name="catalog_entity_attribute_save_before"> <observer name="weee" instance="Magento\Weee\Model\Observer" method="assignBackendModelToAttribute" shared="false" /> </event> + <event name="catalog_product_option_price_configuration_after"> + <observer name="weee" instance="Magento\Weee\Model\Observer" method="getPriceConfiguration" shared="false" /> + </event> </config> diff --git a/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml index 86267dea51a757c481bff1d110c6ce121cd41a5e..f8a77699ff4d9356fa99cf9eed5925efbc1b3a3d 100644 --- a/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml +++ b/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml @@ -28,5 +28,7 @@ $closeBrace = ')'; data-label="<?php echo $block->renderWeeeTaxAttributeName($weeeTaxAttribute); ?>"><?php echo $block->renderWeeeTaxAttribute($weeeTaxAttribute); ?></span> <?php endforeach; ?> <span class="price-final_price" + data-price-type="weeePrice" + data-price-amount="<?php echo $block->getRawFinalAmount(); ?>" data-label="<?php echo __('Final Price'); ?>"><?php echo $block->getFinalAmount(); ?></span> <?php endif; ?> diff --git a/app/etc/di.xml b/app/etc/di.xml index c461dc254c841721a21146ff4010a00f848f1124..7ffd8702c787b917c82fb2073953559106c98119 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -964,6 +964,11 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Object\Copy\Config"> + <arguments> + <argument name="dataStorage" xsi:type="object">Magento\Framework\Object\Copy\Config\Data\Proxy</argument> + </arguments> + </type> <type name="Magento\Framework\Object\Copy\Config\Reader"> <arguments> <argument name="fileName" xsi:type="string">fieldset.xml</argument> @@ -1101,4 +1106,9 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Url\Decoder"> + <arguments> + <argument name="urlBuilder" xsi:type="object">Magento\Framework\UrlInterface\Proxy</argument> + </arguments> + </type> </config> diff --git a/dev/tests/api-functional/_files/Magento/TestModule1/etc/data_object.xml b/dev/tests/api-functional/_files/Magento/TestModule1/etc/service_data_attributes.xml similarity index 69% rename from dev/tests/api-functional/_files/Magento/TestModule1/etc/data_object.xml rename to dev/tests/api-functional/_files/Magento/TestModule1/etc/service_data_attributes.xml index de2236e2195f380aedd5614447065bc9e480f7aa..fa96514a063b6112004e7a6073a2cc131a87f44c 100644 --- a/dev/tests/api-functional/_files/Magento/TestModule1/etc/data_object.xml +++ b/dev/tests/api-functional/_files/Magento/TestModule1/etc/service_data_attributes.xml @@ -5,13 +5,13 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\TestModule1\Service\V1\Entity\Item"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\TestModule1\Service\V1\Entity\Item"> <attribute code="custom_attribute_data_object" type="Magento\TestModuleMSC\Model\Data\CustomAttributeDataObject" /> <attribute code="custom_attribute_string" type="string" /> - </custom_attributes> - <custom_attributes for="Magento\TestModuleMSC\Model\Data\CustomAttributeDataObject"> + </extension_attributes> + <extension_attributes for="Magento\TestModuleMSC\Model\Data\CustomAttributeDataObject"> <attribute code="custom_attribute_nested" type="Magento\TestModuleMSC\Model\Data\CustomAttributeNestedDataObject" /> <attribute code="custom_attribute_int" type="int" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/data_object.xml b/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/service_data_attributes.xml similarity index 69% rename from dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/data_object.xml rename to dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/service_data_attributes.xml index ece2b1f581775daa25e1acec4f4fc120a876c431..f11e639ccd2753296d65071cf32f3e5fb47e0e7d 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/data_object.xml +++ b/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/service_data_attributes.xml @@ -5,13 +5,13 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\TestModuleMSC\Api\Data\ItemInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\TestModuleMSC\Api\Data\ItemInterface"> <attribute code="custom_attribute_data_object" type="Magento\TestModuleMSC\Api\Data\CustomAttributeDataObjectInterface" /> <attribute code="custom_attribute_string" type="string" /> - </custom_attributes> - <custom_attributes for="Magento\TestModuleMSC\Api\Data\CustomAttributeDataObjectInterface"> + </extension_attributes> + <extension_attributes for="Magento\TestModuleMSC\Api\Data\CustomAttributeDataObjectInterface"> <attribute code="custom_attribute_nested" type="Magento\TestModuleMSC\Api\Data\CustomAttributeNestedDataObjectInterface" /> <attribute code="custom_attribute_int" type="int" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index fb6588a7f4107cf2ff07095ff3820e442ee10a87..a0f06e0ce7aac3fa6752c18a2adb47af1ba2e544 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -126,7 +126,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract public function testProductLinks() { - $this->markTestSkipped('Skipped until MAGETWO-35458 is ready'); // Create simple product $productData = [ ProductInterface::SKU => "product_simple_500", @@ -140,9 +139,10 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ]; $this->saveProduct($productData); + $productLinkData = ["product_sku" => "product_simple_with_related_500", "link_type" => "related", "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple", - "position" => 0]; + "position" => 0, "extension_attributes" => []]; $productWithRelatedData = [ ProductInterface::SKU => "product_simple_with_related_500", ProductInterface::NAME => "Product Simple with Related 500", @@ -166,7 +166,7 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract // update link information $productLinkData = ["product_sku" => "product_simple_with_related_500", "link_type" => "upsell", "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple", - "position" => 0]; + "position" => 0, "extension_attributes" => []]; $productWithUpsellData = [ ProductInterface::SKU => "product_simple_with_related_500", ProductInterface::NAME => "Product Simple with Related 500", @@ -174,7 +174,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ProductInterface::TYPE_ID => 'simple', ProductInterface::PRICE => 100, ProductInterface::STATUS => 1, - ProductInterface::TYPE_ID => 'simple', ProductInterface::ATTRIBUTE_SET_ID => 4, "product_links" => [$productLinkData] ]; @@ -195,18 +194,15 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ProductInterface::TYPE_ID => 'simple', ProductInterface::PRICE => 100, ProductInterface::STATUS => 1, - ProductInterface::TYPE_ID => 'simple', ProductInterface::ATTRIBUTE_SET_ID => 4, "product_links" => [] ]; $this->saveProduct($productWithNoLinkData); $response = $this->getProduct("product_simple_with_related_500"); - $this->assertArrayHasKey('product_links', $response); $links = $response['product_links']; - $this->assertEquals(1, count($links)); - $this->assertEquals([], $links[0]); + $this->assertEquals([], $links); $this->deleteProduct("product_simple_500"); $this->deleteProduct("product_simple_with_related_500"); diff --git a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0a5a6a9faa72c64b8ceb5dcce87fd81f55ec92f3 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php @@ -0,0 +1,398 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogInventory\Api; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\Framework\Api\ExtensibleDataInterface; +use Magento\TestFramework\TestCase\WebapiAbstract; + +class ProductRepositoryInterfaceTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const RESOURCE_PATH = '/V1/products'; + + const KEY_EXTENSION_ATTRIBUTES = ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY; + const KEY_STOCK_ITEM = StockStatusInterface::STOCK_ITEM; + const KEY_QTY = StockStatusInterface::QTY; + const KEY_ITEM_ID = 'item_id'; + const KEY_PRODUCT_ID = StockStatusInterface::PRODUCT_ID; + const KEY_WEBSITE_ID = StockStatusInterface::WEBSITE_ID; + const KEY_CUSTOM_ATTRIBUTES = 'custom_attributes'; + const KEY_ATTRIBUTE_CODE = \Magento\Eav\Api\Data\AttributeInterface::ATTRIBUTE_CODE; + const CODE_QUANTITY_AND_STOCK_STATUS = 'quantity_and_stock_status'; + + const PRODUCT_SKU = 'sku-test-catalog-inventory'; + + /** + * Tests the 'happy path' + */ + public function testCatalogInventory() + { + // create a simple product with catalog inventory + $qty = 1234; + $productData = $this->getSimpleProductData($qty); + $stockItemData = $this->getStockItemData($qty); + $this->assertArrayNotHasKey(self::KEY_ITEM_ID, $stockItemData); + $this->assertArrayNotHasKey(self::KEY_WEBSITE_ID, $stockItemData); + $this->assertArrayNotHasKey(self::KEY_PRODUCT_ID, $stockItemData); + $productData[self::KEY_EXTENSION_ATTRIBUTES] = $stockItemData; + + $response = $this->saveProduct($productData); + + $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response); + $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM])); + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $returnedQty = $stockItemData[self::KEY_QTY]; + $this->assertEquals($qty, $returnedQty, 'CREATE: Expected qty to be same: ' . $qty .', '. $returnedQty); + $this->assertArrayHasKey(self::KEY_ITEM_ID, $stockItemData); + $this->assertArrayHasKey(self::KEY_PRODUCT_ID, $stockItemData); + $this->assertArrayHasKey(self::KEY_WEBSITE_ID, $stockItemData); + + // officially get the product + $response = $this->getProduct($productData[ProductInterface::SKU]); + + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $returnedQty = $stockItemData[self::KEY_QTY]; + $this->assertEquals($qty, $returnedQty, 'GET: Expected qty to be same: ' . $qty .', '. $returnedQty); + + // update the catalog inventory + $qty = $this->getDifferent($qty); // update the quantity + $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_QTY] = $qty; + + $response = $this->updateProduct($response); + + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $returnedQty = $stockItemData[self::KEY_QTY]; + $this->assertEquals($qty, $returnedQty, 'UPDATE 1: Expected qty to be same: ' . $qty .', '. $returnedQty); + + $customAttributeQty = $this->findCustomAttributeQty($response[self::KEY_CUSTOM_ATTRIBUTES]); + $this->assertTrue(!is_bool($customAttributeQty), 'Expected to find a quantity in the custom attributes'); + $this->assertEquals( + $qty, + $customAttributeQty, + 'UPDATE 1: Expected custom attribute qty to be updated: ' . $qty .', '. $customAttributeQty + ); + + // update the product without any mention of catalog inventory; no change expected for catalog inventory + // note: $qty expected to be the same as previously set, above + $newPrice = $this->getDifferent($response[ProductInterface::PRICE]); + $response[ProductInterface::PRICE] = $newPrice; + unset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]); + + $response = $this->updateProduct($response); + + $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response); + $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM])); + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $returnedQty = $stockItemData[self::KEY_QTY]; + $this->assertEquals($qty, $returnedQty, 'UPDATE 2: Expected qty to be same: ' . $qty .', '. $returnedQty); + $this->assertEquals($newPrice, $response[ProductInterface::PRICE]); + + // delete the product; expect that all goes well + $response = $this->deleteProduct($productData[ProductInterface::SKU]); + $this->assertTrue($response); + } + + /** + * Tests conditions that stray from the 'happy path' + */ + public function testCatalogInventoryWithBogusData() + { + // create a simple product with catalog inventory + $qty = 666; + $productData = $this->getSimpleProductData($qty); + $stockItemData = $this->getStockItemData($qty); + $this->assertArrayNotHasKey(self::KEY_ITEM_ID, $stockItemData); + $this->assertArrayNotHasKey(self::KEY_WEBSITE_ID, $stockItemData); + $this->assertArrayNotHasKey(self::KEY_PRODUCT_ID, $stockItemData); + $productData[self::KEY_EXTENSION_ATTRIBUTES] = $stockItemData; + + $response = $this->saveProduct($productData); + + $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response); + $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM])); + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $returnedQty = $stockItemData[self::KEY_QTY]; + $this->assertEquals($qty, $returnedQty, 'POST 1: Expected qty to be same: ' . $qty .', '. $returnedQty); + $this->assertArrayHasKey(self::KEY_ITEM_ID, $stockItemData); + $this->assertArrayHasKey(self::KEY_PRODUCT_ID, $stockItemData); + $this->assertArrayHasKey(self::KEY_WEBSITE_ID, $stockItemData); + + // re-save the catalog inventory: + // -- update quantity (which should be honored) + // -- supply an incorrect product id (which should be ignored, and be replaced with the actual one) + // -- supply an incorrect website id (which should be ignored, and be replaced with the actual one) + $qty = 777; // update the quantity + $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_QTY] = $qty; + + $originalProductId = $stockItemData[self::KEY_PRODUCT_ID]; + $bogusProductId = $this->getDifferent($originalProductId); + $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_PRODUCT_ID] = $bogusProductId; + + $originalWebsiteId = $stockItemData[self::KEY_WEBSITE_ID]; + $bogusWebsiteId = $this->getDifferent($originalWebsiteId); + $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_WEBSITE_ID] = $bogusWebsiteId; + + $response = $this->saveProduct($response); + + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $returnedQty = $stockItemData[self::KEY_QTY]; + $this->assertEquals($qty, $returnedQty, 'POST 2: Expected qty to be same: ' . $qty .', '. $returnedQty); + + $returnedProductId = $stockItemData[self::KEY_PRODUCT_ID]; + $this->assertEquals($originalProductId, $returnedProductId); + + $returnedWebsiteId = $stockItemData[self::KEY_WEBSITE_ID]; + $this->assertEquals($originalWebsiteId, $returnedWebsiteId); + + // delete the product; expect that all goes well + $response = $this->deleteProduct($productData[ProductInterface::SKU]); + $this->assertTrue($response); + } + + /** + * Tests that creating a simple product has a side-effect of creating catalog inventory + */ + public function testSimpleProductCreationWithoutSpecifyingCatalogInventory() + { + // create a simple product with catalog inventory + $qty = null; + $productData = $this->getSimpleProductData($qty); + $this->assertArrayNotHasKey(self::KEY_CUSTOM_ATTRIBUTES, $productData); + $this->assertArrayNotHasKey(self::KEY_EXTENSION_ATTRIBUTES, $productData); + + $response = $this->saveProduct($productData); + + $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response); + $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM])); + $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]; + $this->assertArrayHasKey(self::KEY_ITEM_ID, $stockItemData); + $this->assertArrayHasKey(self::KEY_PRODUCT_ID, $stockItemData); + $this->assertArrayHasKey(self::KEY_WEBSITE_ID, $stockItemData); + + // delete the product; expect that all goes well + $response = $this->deleteProduct($productData[ProductInterface::SKU]); + $this->assertTrue($response); + } + + // --- my helpers ----------------------------------------------------------------------------- + + /** + * Return a value that is different than the original one + * + * @param int $original + * @return int + */ + protected function getDifferent($original) + { + return 1 + $original * $original; + } + + /** + * Returns the product's quantity from the array of custom attributes. + * If no quantity can be found, will return false. + * + * @param array $customAttributes + * @return int|bool + */ + protected function findCustomAttributeQty($customAttributes) + { + $qty = false; + $qtyAndStockStatus = []; + foreach ($customAttributes as $customAttribute) { + if ($customAttribute[self::KEY_ATTRIBUTE_CODE] == self::CODE_QUANTITY_AND_STOCK_STATUS) { + $qtyAndStockStatus = $customAttribute['value']; + break; + } + } + if (!empty($qtyAndStockStatus) && is_array($qtyAndStockStatus)) { + + if (array_key_exists('any_type', $qtyAndStockStatus)) { + // for SOAP, need to use the inner array + $qtyAndStockStatus = $qtyAndStockStatus['any_type']; + } + + // ex: [true, 1234] + if (is_bool($qtyAndStockStatus[0]) || is_string($qtyAndStockStatus[0])) { + $qty = $qtyAndStockStatus[1]; + } else { + $qty = $qtyAndStockStatus[0]; + } + } + return $qty; + } + + /** + * Get Simple Product Data + * + * @param int $qty + * @return array + */ + protected function getSimpleProductData($qty = 1000) + { + $productData = [ + ProductInterface::SKU => self::PRODUCT_SKU, + ProductInterface::NAME => self::PRODUCT_SKU, + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::PRICE => 10, + ProductInterface::STATUS => 1, + ProductInterface::ATTRIBUTE_SET_ID => 4, + ]; + + if ($qty != null) { + $productData[self::KEY_CUSTOM_ATTRIBUTES] = [ + [self::KEY_ATTRIBUTE_CODE => 'description', 'value' => 'My Product Description'], + [self::KEY_ATTRIBUTE_CODE => self::CODE_QUANTITY_AND_STOCK_STATUS, 'value' => [true, $qty]], + ]; + } + + return $productData; + } + + /** + * Get sample Stock Item data + * + * @param int $qty + * @return array + */ + protected function getStockItemData($qty = 1000) + { + return [ + self::KEY_STOCK_ITEM => [ + self::KEY_QTY => $qty, + 'is_in_stock' => true, + 'is_qty_decimal' => false, + 'show_default_notification_message' => false, + 'use_config_min_qty' => true, + 'min_qty' => 0, + 'use_config_min_sale_qty' => 1, + 'min_sale_qty' => 1, + 'use_config_max_sale_qty' => true, + 'max_sale_qty' => 10000, + 'use_config_backorders' => true, + 'backorders' => 0, + 'use_config_notify_stock_qty' => true, + 'notify_stock_qty' => 1, + 'use_config_qty_increments' => true, + 'qty_increments' => 0, + 'use_config_enable_qty_inc' => false, + 'enable_qty_increments' => false, + 'use_config_manage_stock' => false, + 'manage_stock' => true, + 'low_stock_date' => "0", + 'is_decimal_divided' => false, + 'stock_status_changed_auto' => 0, + ] + ]; + } + + // --- common REST helpers -------------------------------------------------------------------- + + /** + * Get a product via its sku + * + * @param string $sku + * @return array the product data + */ + protected function getProduct($sku) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $sku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + + $response = $this->_webApiCall($serviceInfo, ['sku' => $sku]); + return $response; + } + + /** + * Save a product + * + * @param array $product + * @return array the created product data + */ + protected function saveProduct($product) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Update an existing product via its sku + * + * @param array $product + * @return array the product data, including any updates + */ + protected function updateProduct($product) + { + $sku = $product[ProductInterface::SKU]; + if (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) { + $product[ProductInterface::SKU] = null; + } + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $sku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Delete a product via its sku + * + * @param string $sku + * @return bool + */ + protected function deleteProduct($sku) + { + $resourcePath = self::RESOURCE_PATH . '/' . $sku; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $resourcePath, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'deleteById', + ], + ]; + $requestData = ['sku' => $sku]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php index 0513f5e68abfdbe603bb5972604abb8d21453165..f8ef77f8e297b3bdf6a41c501c70adb40d31db99 100644 --- a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php +++ b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php @@ -223,7 +223,7 @@ class StockItemTest extends WebapiAbstract 'enable_qty_increments' => '', 'use_config_manage_stock' => 1, 'manage_stock' => 1, - 'low_stock_date' => 0, + 'low_stock_date' => '', 'is_decimal_divided' => '', 'stock_status_changed_auto' => 0 ], diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php index 4551eaa3e45e2d713487938cf485344c5ff10b5c..bf9607d24969cae9ad70a61e46973c1ce2d95fb3 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php @@ -166,7 +166,6 @@ class OptionRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstrac ]; $option = [ 'attribute_id' => 'test_configurable', - 'type' => 'select', 'label' => 'Test', 'values' => [ [ diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionTypesListTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionTypesListTest.php deleted file mode 100644 index a96d5ca79a5cb22ccf148210a39a37f0b545ba81..0000000000000000000000000000000000000000 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionTypesListTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ConfigurableProduct\Api; - -class OptionTypesListTest extends \Magento\TestFramework\TestCase\WebapiAbstract -{ - const SERVICE_READ_NAME = 'configurableProductOptionTypesListV1'; - const SERVICE_VERSION = 'V1'; - const RESOURCE_PATH = '/V1/configurable-products/:sku/options/'; - - public function testGetTypes() - { - $expectedTypes = ['multiselect', 'select']; - $result = $this->getTypes(); - $this->assertEquals($expectedTypes, $result); - } - - /** - * @return array - */ - protected function getTypes() - { - $serviceInfo = [ - 'rest' => [ - 'resourcePath' => str_replace(':sku/', '', self::RESOURCE_PATH) . 'types', - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET - ], - 'soap' => [ - 'service' => self::SERVICE_READ_NAME, - 'serviceVersion' => self::SERVICE_VERSION, - 'operation' => self::SERVICE_READ_NAME . 'GetItems' - ] - ]; - return $this->_webApiCall($serviceInfo); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b9a2c658dfa3bd771656523fa6a3669ec2eaf411 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php @@ -0,0 +1,486 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Api; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Api\ExtensibleDataInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Class ProductRepositoryTest for testing ConfigurableProduct integration + */ +class ProductRepositoryTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const RESOURCE_PATH = '/V1/products'; + const CONFIGURABLE_PRODUCT_SKU = 'configurable-product-sku'; + + /** + * @var \Magento\Eav\Model\Config + */ + protected $eavConfig; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $objectManager; + + /** + * @var \Magento\Catalog\Model\Entity\Attribute + */ + protected $configurableAttribute; + + /** + * Execute per test initialization + */ + public function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->eavConfig = $this->objectManager->get('Magento\Eav\Model\Config'); + } + + /** + * Execute per test cleanup + */ + public function tearDown() + { + $this->deleteProductBySku(self::CONFIGURABLE_PRODUCT_SKU); + parent::tearDown(); + } + + protected function getConfigurableAttributeOptions() + { + /** @var \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection $optionCollection */ + $optionCollection = $this->objectManager->create( + 'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection' + ); + $options = $optionCollection->setAttributeFilter($this->configurableAttribute->getId())->getData(); + return $options; + } + + protected function createConfigurableProduct() + { + $productId1 = 10; + $productId2 = 20; + + $label = "color"; + + $this->configurableAttribute = $this->eavConfig->getAttribute('catalog_product', 'test_configurable'); + $this->assertNotNull($this->configurableAttribute); + + $options = $this->getConfigurableAttributeOptions(); + $this->assertEquals(2, count($options)); + + $configurableProductOptions = [ + [ + "attribute_id" => $this->configurableAttribute->getId(), + "label" => $label, + "position" => 0, + "values" => [ + [ + "pricing_value" => 10, + "is_percent" => 0, + "value_index" => $options[0]['option_id'], + ], + [ + "pricing_value" => 5, + "is_percent" => 1, + "value_index" => $options[1]['option_id'], + ] + ], + ] + ]; + + $product = [ + "sku" => self::CONFIGURABLE_PRODUCT_SKU, + "name" => self::CONFIGURABLE_PRODUCT_SKU, + "type_id" => "configurable", + "price" => 50, + 'attribute_set_id' => 4, + "custom_attributes" => [ + [ + "attribute_code" => $this->configurableAttribute->getAttributeCode(), + "value" => $options[0]['option_id'], + ], + ], + "extension_attributes" => [ + "configurable_product_options" => $configurableProductOptions, + "configurable_product_links" => [$productId1, $productId2], + ], + ]; + + $response = $this->createProduct($product); + return $response; + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testCreateConfigurableProduct() + { + $productId1 = 10; + $productId2 = 20; + $label = "color"; + + $response = $this->createConfigurableProduct(); + $this->assertEquals(self::CONFIGURABLE_PRODUCT_SKU, $response[ProductInterface::SKU]); + $this->assertEquals(50, $response['price']); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]) + ); + $resultConfigurableProductOptions + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]; + $this->assertEquals(1, count($resultConfigurableProductOptions)); + $this->assertTrue(isset($resultConfigurableProductOptions[0]['label'])); + $this->assertTrue(isset($resultConfigurableProductOptions[0]['id'])); + $this->assertEquals($label, $resultConfigurableProductOptions[0]['label']); + $this->assertTrue( + isset($resultConfigurableProductOptions[0]['values']) + ); + $this->assertEquals(2, count($resultConfigurableProductOptions[0]['values'])); + + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"]) + ); + $resultConfigurableProductLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"]; + $this->assertEquals(2, count($resultConfigurableProductLinks)); + + $this->assertEquals([$productId1, $productId2], $resultConfigurableProductLinks); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testDeleteConfigurableProductOption() + { + $productId1 = 10; + $productId2 = 20; + + $response = $this->createConfigurableProduct(); + //delete existing option + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'] = []; + //leave the product links unchanged + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links']); + $response = $this->saveProduct($response); + + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]) + ); + $resultConfigurableProductOptions + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]; + $this->assertEquals(0, count($resultConfigurableProductOptions)); + + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"]) + ); + $resultConfigurableProductLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"]; + $this->assertEquals(2, count($resultConfigurableProductLinks)); + + $this->assertEquals([$productId1, $productId2], $resultConfigurableProductLinks); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testUpdateConfigurableProductOption() + { + $productId1 = 10; + $newLabel = 'size'; + + $response = $this->createConfigurableProduct(); + $option = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"][0]; + + $optionId = $option['id']; + $updatedOption = [ + 'id' => $optionId, + 'attribute_id' => $option['attribute_id'], + 'label' => $newLabel, + 'position' => 1, + 'values' => [ + [ + 'pricing_value' => 15, + 'is_percent' => 1, + 'value_index' => $option['values'][0]['value_index'], + ], + ], + ]; + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'][0] = + $updatedOption; + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [$productId1]; + $response = $this->saveProduct($response); + + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]) + ); + $resultConfigurableProductOptions + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]; + $this->assertEquals(1, count($resultConfigurableProductOptions)); + + $this->assertEquals($updatedOption, $resultConfigurableProductOptions[0]); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testUpdateConfigurableProductLinks() + { + $productId1 = 10; + $productId2 = 20; + + $response = $this->createConfigurableProduct(); + $options = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']; + //leave existing option untouched + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']); + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [$productId1]; + $response = $this->saveProduct($response); + + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]) + ); + $resultConfigurableProductOptions + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"]; + $this->assertEquals(1, count($resultConfigurableProductOptions)); + //Since one product is removed, the available values for the option is reduced + $this->assertEquals(1, count($resultConfigurableProductOptions[0]['values'])); + + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"]) + ); + $resultConfigurableProductLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"]; + $this->assertEquals(1, count($resultConfigurableProductLinks)); + $this->assertEquals([$productId1], $resultConfigurableProductLinks); + + //adding back the product links, the option value should be restored + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']); + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] + = [$productId1, $productId2]; + //set the value for required attribute + $response["custom_attributes"][] = + [ + "attribute_code" => $this->configurableAttribute->getAttributeCode(), + "value" => $resultConfigurableProductOptions[0]['values'][0]['value_index'], + ]; + + $response = $this->saveProduct($response); + $currentOptions = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']; + + $this->assertEquals($options, $currentOptions); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testUpdateConfigurableProductLinksWithNonExistingProduct() + { + $productId1 = 10; + $nonExistingId = 999; + + $response = $this->createConfigurableProduct(); + //leave existing option untouched + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']); + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [ + $productId1, $nonExistingId + ]; + + $expectedMessage = 'Product with id "%1" does not exist.'; + try { + $this->saveProduct($response); + $this->fail("Expected exception"); + } catch (\SoapFault $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage(), + "SoapFault does not contain expected message." + ); + } catch (\Exception $e) { + $errorObj = $this->processRestExceptionResult($e); + $this->assertEquals($expectedMessage, $errorObj['message']); + $this->assertEquals(['0' => '999'], $errorObj['parameters']); + } + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testUpdateConfigurableProductLinksWithDuplicateAttributes() + { + $productId1 = 10; + $productId2 = 20; + + $response = $this->createConfigurableProduct(); + $options = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']; + //make product2 and product1 have the same value for the configurable attribute + $optionValue1 = $options[0]['values'][0]['value_index']; + $product2 = $this->getProduct('simple_' . $productId2); + $product2['custom_attributes'] = [ + [ + 'attribute_code' => 'test_configurable', + 'value' => $optionValue1, + ] + ]; + $this->saveProduct($product2); + + //leave existing option untouched + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']); + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [ + $productId1, $productId2 + ]; + + $expectedMessage = 'Products "%1" and %2 have the same set of attribute values.'; + try { + $this->saveProduct($response); + $this->fail("Expected exception"); + } catch (\SoapFault $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage(), + "SoapFault does not contain expected message." + ); + } catch (\Exception $e) { + $errorObj = $this->processRestExceptionResult($e); + $this->assertEquals($expectedMessage, $errorObj['message']); + $this->assertEquals(['0' => 20, '1' => 10], $errorObj['parameters']); + } + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testUpdateConfigurableProductLinksWithWithoutVariationAttributes() + { + $productId1 = 10; + $productId2 = 20; + + $response = $this->createConfigurableProduct(); + + /** delete all variation attribute */ + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'] = []; + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [ + $productId1, $productId2 + ]; + + $expectedMessage = 'The configurable product does not have any variation attribute.'; + try { + $this->saveProduct($response); + $this->fail("Expected exception"); + } catch (\SoapFault $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage(), + "SoapFault does not contain expected message." + ); + } catch (\Exception $e) { + $errorObj = $this->processRestExceptionResult($e); + $this->assertEquals($expectedMessage, $errorObj['message']); + } + } + + /** + * Get product + * + * @param string $productSku + * @return array the product data + */ + protected function getProduct($productSku) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + + $response = (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ? + $this->_webApiCall($serviceInfo, ['sku' => $productSku]) : $this->_webApiCall($serviceInfo); + + return $response; + } + + /** + * Create product + * + * @param array $product + * @return array the created product data + */ + protected function createProduct($product) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Delete a product by sku + * + * @param $productSku + * @return bool + */ + protected function deleteProductBySku($productSku) + { + $resourcePath = self::RESOURCE_PATH . '/' . $productSku; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $resourcePath, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'deleteById', + ], + ]; + $requestData = ["sku" => $productSku]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Save product + * + * @param array $product + * @return array the created product data + */ + protected function saveProduct($product) + { + $resourcePath = self::RESOURCE_PATH . '/' . $product['sku']; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $resourcePath, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php index 4234afc66f27883af89395192189e18bf3c154e0..656da53f38e842a1f3122b6fd35fbd87cdb24c05 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php @@ -80,9 +80,10 @@ class LinkRepositoryTest extends WebapiAbstract protected function getTargetProduct($isScopeGlobal = false) { $objectManager = Bootstrap::getObjectManager(); - $product = $objectManager->get('Magento\Catalog\Model\ProductFactory')->create()->load(1); if ($isScopeGlobal) { - $product->setStoreId(0); + $product = $objectManager->get('Magento\Catalog\Model\ProductFactory')->create()->setStoreId(0)->load(1); + } else { + $product = $objectManager->get('Magento\Catalog\Model\ProductFactory')->create()->load(1); } return $product; @@ -114,18 +115,18 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => true, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Title', 'sort_order' => 1, 'price' => 10.1, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'file', - 'link_file' => [ + 'link_file_content' => [ 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'image.jpg', ], - 'sample_file' => [ + 'sample_file_content' => [ 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'image.jpg', ], @@ -137,15 +138,15 @@ class LinkRepositoryTest extends WebapiAbstract $globalScopeLink = $this->getTargetLink($this->getTargetProduct(true), $newLinkId); $link = $this->getTargetLink($this->getTargetProduct(), $newLinkId); $this->assertNotNull($link); - $this->assertEquals($requestData['linkContent']['title'], $link->getTitle()); - $this->assertEquals($requestData['linkContent']['title'], $globalScopeLink->getTitle()); - $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder()); - $this->assertEquals($requestData['linkContent']['price'], $link->getPrice()); - $this->assertEquals($requestData['linkContent']['price'], $globalScopeLink->getPrice()); - $this->assertEquals($requestData['linkContent']['shareable'], $link->getIsShareable()); - $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads()); - $this->assertEquals($requestData['linkContent']['link_type'], $link->getLinkType()); - $this->assertEquals($requestData['linkContent']['sample_type'], $link->getSampleType()); + $this->assertEquals($requestData['link']['title'], $link->getTitle()); + $this->assertEquals($requestData['link']['title'], $globalScopeLink->getTitle()); + $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder()); + $this->assertEquals($requestData['link']['price'], $link->getPrice()); + $this->assertEquals($requestData['link']['price'], $globalScopeLink->getPrice()); + $this->assertEquals($requestData['link']['is_shareable'], $link->getIsShareable()); + $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads()); + $this->assertEquals($requestData['link']['link_type'], $link->getLinkType()); + $this->assertEquals($requestData['link']['sample_type'], $link->getSampleType()); $this->assertStringEndsWith('.jpg', $link->getSampleFile()); $this->assertStringEndsWith('.jpg', $link->getLinkFile()); $this->assertNull($link->getLinkUrl()); @@ -160,11 +161,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Store View Title', 'sort_order' => 1, 'price' => 150, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_url' => 'http://www.example.com/', 'link_type' => 'url', @@ -177,15 +178,15 @@ class LinkRepositoryTest extends WebapiAbstract $link = $this->getTargetLink($this->getTargetProduct(), $newLinkId); $globalScopeLink = $this->getTargetLink($this->getTargetProduct(true), $newLinkId); $this->assertNotNull($link); - $this->assertEquals($requestData['linkContent']['title'], $link->getTitle()); - $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder()); - $this->assertEquals($requestData['linkContent']['price'], $link->getPrice()); - $this->assertEquals($requestData['linkContent']['shareable'], $link->getIsShareable()); - $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads()); - $this->assertEquals($requestData['linkContent']['link_url'], $link->getLinkUrl()); - $this->assertEquals($requestData['linkContent']['link_type'], $link->getLinkType()); - $this->assertEquals($requestData['linkContent']['sample_url'], $link->getSampleUrl()); - $this->assertEquals($requestData['linkContent']['sample_type'], $link->getSampleType()); + $this->assertEquals($requestData['link']['title'], $link->getTitle()); + $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder()); + $this->assertEquals($requestData['link']['price'], $link->getPrice()); + $this->assertEquals($requestData['link']['is_shareable'], $link->getIsShareable()); + $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads()); + $this->assertEquals($requestData['link']['link_url'], $link->getLinkUrl()); + $this->assertEquals($requestData['link']['link_type'], $link->getLinkType()); + $this->assertEquals($requestData['link']['sample_url'], $link->getSampleUrl()); + $this->assertEquals($requestData['link']['sample_type'], $link->getSampleType()); $this->assertEmpty($globalScopeLink->getTitle()); $this->assertEmpty($globalScopeLink->getPrice()); } @@ -198,11 +199,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link with URL resources', 'sort_order' => 1, 'price' => 10.1, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_url' => 'http://www.example.com/', 'link_type' => 'url', @@ -214,15 +215,15 @@ class LinkRepositoryTest extends WebapiAbstract $newLinkId = $this->_webApiCall($this->createServiceInfo, $requestData); $link = $this->getTargetLink($this->getTargetProduct(), $newLinkId); $this->assertNotNull($link); - $this->assertEquals($requestData['linkContent']['title'], $link->getTitle()); - $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder()); - $this->assertEquals($requestData['linkContent']['price'], $link->getPrice()); - $this->assertEquals($requestData['linkContent']['shareable'], $link->getIsShareable()); - $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads()); - $this->assertEquals($requestData['linkContent']['link_url'], $link->getLinkUrl()); - $this->assertEquals($requestData['linkContent']['link_type'], $link->getLinkType()); - $this->assertEquals($requestData['linkContent']['sample_type'], $link->getSampleType()); - $this->assertEquals($requestData['linkContent']['sample_url'], $link->getSampleUrl()); + $this->assertEquals($requestData['link']['title'], $link->getTitle()); + $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder()); + $this->assertEquals($requestData['link']['price'], $link->getPrice()); + $this->assertEquals($requestData['link']['is_shareable'], $link->getIsShareable()); + $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads()); + $this->assertEquals($requestData['link']['link_url'], $link->getLinkUrl()); + $this->assertEquals($requestData['link']['link_type'], $link->getLinkType()); + $this->assertEquals($requestData['link']['sample_type'], $link->getSampleType()); + $this->assertEquals($requestData['link']['sample_url'], $link->getSampleUrl()); } /** @@ -235,12 +236,15 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link with URL resources', 'sort_order' => 1, 'price' => 10.1, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, + 'link_type' => 'invalid', + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com', ], ]; @@ -257,16 +261,16 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 1, 'price' => 10, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'url', 'link_url' => 'http://www.example.com/', 'sample_type' => 'file', - 'sample_file' => [ + 'sample_file_content' => [ 'file_data' => 'not_a_base64_encoded_content', 'name' => 'image.jpg', ], @@ -286,17 +290,19 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 1, 'price' => 10, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'file', - 'link_file' => [ + 'link_file_content' => [ 'file_data' => 'not_a_base64_encoded_content', 'name' => 'image.jpg', ], + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/', ], ]; @@ -313,17 +319,19 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Title', 'sort_order' => 15, 'price' => 10, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'file', - 'link_file' => [ + 'link_file_content' => [ 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'name/with|forbidden{characters', ], + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/', ], ]; @@ -340,16 +348,16 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 1, 'price' => 10, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'url', 'link_url' => 'http://www.example.com/', 'sample_type' => 'file', - 'sample_file' => [ + 'sample_file_content' => [ 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'name/with|forbidden{characters', ], @@ -369,14 +377,16 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 1, 'price' => 10, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'link_type' => 'url', 'link_url' => 'http://example<.>com/', + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/', ], ]; @@ -393,11 +403,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 1, 'price' => 150, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 0, 'sample_type' => 'url', 'sample_url' => 'http://example<.>com/', @@ -420,11 +430,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 1, 'price' => $linkPrice, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 0, 'sample_type' => 'url', 'sample_url' => 'http://example.com/', @@ -442,7 +452,6 @@ class LinkRepositoryTest extends WebapiAbstract public function getInvalidLinkPrice() { return [ - ['string_value'], [-1.5], ]; } @@ -458,11 +467,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => $sortOrder, 'price' => 10, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 0, 'sample_type' => 'url', 'sample_url' => 'http://example.com/', @@ -494,11 +503,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 0, 'price' => 10, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => $numberOfDownloads, 'sample_type' => 'url', 'sample_url' => 'http://example.com/', @@ -530,11 +539,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'simple', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 50, 'price' => 200, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 10, 'sample_type' => 'url', 'sample_url' => 'http://example.com/', @@ -555,11 +564,11 @@ class LinkRepositoryTest extends WebapiAbstract $requestData = [ 'isGlobalScopeContent' => false, 'sku' => 'wrong-sku', - 'linkContent' => [ + 'link' => [ 'title' => 'Link Title', 'sort_order' => 15, 'price' => 200, - 'shareable' => true, + 'is_shareable' => true, 'number_of_downloads' => 100, 'sample_type' => 'url', 'sample_url' => 'http://example.com/', @@ -580,24 +589,26 @@ class LinkRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/{$linkId}"; $requestData = [ 'isGlobalScopeContent' => false, - 'linkId' => $linkId, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ + 'id' => $linkId, 'title' => 'Updated Title', 'sort_order' => 2, 'price' => 100.10, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 50, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; $this->assertEquals($linkId, $this->_webApiCall($this->updateServiceInfo, $requestData)); $link = $this->getTargetLink($this->getTargetProduct(), $linkId); $this->assertNotNull($link); - $this->assertEquals($requestData['linkContent']['title'], $link->getTitle()); - $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder()); - $this->assertEquals($requestData['linkContent']['price'], $link->getPrice()); - $this->assertEquals($requestData['linkContent']['shareable'], (bool)$link->getIsShareable()); - $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads()); + $this->assertEquals($requestData['link']['title'], $link->getTitle()); + $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder()); + $this->assertEquals($requestData['link']['price'], $link->getPrice()); + $this->assertEquals($requestData['link']['is_shareable'], (bool)$link->getIsShareable()); + $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads()); } /** @@ -611,14 +622,16 @@ class LinkRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/{$linkId}"; $requestData = [ 'isGlobalScopeContent' => true, - 'linkId' => $linkId, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ + 'id' => $linkId, 'title' => 'Updated Title', 'sort_order' => 2, 'price' => 100.10, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 50, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; @@ -629,11 +642,11 @@ class LinkRepositoryTest extends WebapiAbstract // Title and price were set on store view level in fixture so they must be the same $this->assertEquals($originalLink->getTitle(), $link->getTitle()); $this->assertEquals($originalLink->getPrice(), $link->getPrice()); - $this->assertEquals($requestData['linkContent']['title'], $globalScopeLink->getTitle()); - $this->assertEquals($requestData['linkContent']['price'], $globalScopeLink->getPrice()); - $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder()); - $this->assertEquals($requestData['linkContent']['shareable'], (bool)$link->getIsShareable()); - $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads()); + $this->assertEquals($requestData['link']['title'], $globalScopeLink->getTitle()); + $this->assertEquals($requestData['link']['price'], $globalScopeLink->getPrice()); + $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder()); + $this->assertEquals($requestData['link']['is_shareable'], (bool)$link->getIsShareable()); + $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads()); } /** @@ -645,14 +658,16 @@ class LinkRepositoryTest extends WebapiAbstract $this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong-sku/downloadable-links/1'; $requestData = [ 'isGlobalScopeContent' => true, - 'linkId' => 1, 'sku' => 'wrong-sku', - 'linkContent' => [ + 'link' => [ + 'id' => 1, 'title' => 'Updated Title', 'sort_order' => 2, 'price' => 100.10, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 50, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; $this->_webApiCall($this->updateServiceInfo, $requestData); @@ -670,14 +685,16 @@ class LinkRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/{$linkId}"; $requestData = [ 'isGlobalScopeContent' => true, - 'linkId' => 9999, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ + 'id' => $linkId, 'title' => 'Title', 'sort_order' => 2, 'price' => 100.10, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 50, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; @@ -697,14 +714,16 @@ class LinkRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/{$linkId}"; $requestData = [ 'isGlobalScopeContent' => false, - 'linkId' => $linkId, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ + 'id' => $linkId, 'title' => 'Updated Link Title', 'sort_order' => 2, 'price' => $linkPrice, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 50, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; @@ -724,14 +743,16 @@ class LinkRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/{$linkId}"; $requestData = [ 'isGlobalScopeContent' => false, - 'linkId' => $linkId, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ + 'id' => $linkId, 'title' => 'Updated Link Title', 'sort_order' => $sortOrder, 'price' => 100.50, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => 50, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; $this->_webApiCall($this->updateServiceInfo, $requestData); @@ -750,14 +771,16 @@ class LinkRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/{$linkId}"; $requestData = [ 'isGlobalScopeContent' => false, - 'linkId' => $linkId, 'sku' => 'downloadable-product', - 'linkContent' => [ + 'link' => [ + 'id' => $linkId, 'title' => 'Updated Link Title', 'sort_order' => 200, 'price' => 100.50, - 'shareable' => false, + 'is_shareable' => false, 'number_of_downloads' => $numberOfDownloads, + 'link_type' => 'url', + 'sample_type' => 'url', ], ]; $this->_webApiCall($this->updateServiceInfo, $requestData); @@ -771,7 +794,7 @@ class LinkRepositoryTest extends WebapiAbstract $linkId = $this->getTargetLink($this->getTargetProduct())->getId(); $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/{$linkId}"; $requestData = [ - 'linkId' => $linkId, + 'id' => $linkId, ]; $this->assertTrue($this->_webApiCall($this->deleteServiceInfo, $requestData)); @@ -788,7 +811,7 @@ class LinkRepositoryTest extends WebapiAbstract $linkId = 9999; $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/{$linkId}"; $requestData = [ - 'linkId' => $linkId, + 'id' => $linkId, ]; $this->_webApiCall($this->deleteServiceInfo, $requestData); diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a079eb6d4b6bfa58256a646f396a535c91a0e0cc --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php @@ -0,0 +1,627 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Downloadable\Api; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Api\ExtensibleDataInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\Bundle\Api\Data\LinkInterface; + +/** + * Class ProductRepositoryTest for testing ProductRepository interface with Downloadable Product + */ +class ProductRepositoryTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const RESOURCE_PATH = '/V1/products'; + const PRODUCT_SKU = 'sku-test-product-downloadable'; + + /** + * @var string + */ + protected $testImagePath; + + protected function setUp() + { + $this->testImagePath = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test_image.jpg'; + } + + /** + * Execute per test cleanup + */ + public function tearDown() + { + $this->deleteProductBySku(self::PRODUCT_SKU); + parent::tearDown(); + } + + protected function getLinkData() + { + return [ + 'link1' => [ + 'title' => "link1", + 'sort_order'=> 10, + 'is_shareable' => 1, + 'price' => 2.0, + 'number_of_downloads' => 0, + 'link_type' => 'file', + 'link_file_content' => [ + 'name' => 'link1_content.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + 'sample_type' => 'file', + 'sample_file_content' => [ + 'name' => 'link1_sample.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + ], + 'link2' => [ + 'title' => 'link2', + 'sort_order'=> 20, + 'is_shareable' => 0, + 'price' => 3.0, + 'number_of_downloads' => 100, + 'link_type' => "url", + 'link_url' => 'http://www.example.com/link2.jpg', + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/link2.jpg', + ], + ]; + } + + protected function getExpectedLinkData() + { + return [ + [ + 'title' => 'link1', + 'sort_order' => 10, + 'is_shareable' => 1, + 'price' => 2, + 'number_of_downloads' => 0, + 'link_type' => 'file', + 'sample_type' => 'file', + ], + [ + 'title' => 'link2', + 'sort_order' => 20, + 'is_shareable' => 0, + 'price' => 3, + 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://www.example.com/link2.jpg', + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/link2.jpg', + ], + ]; + } + + protected function getSampleData() + { + return [ + 'sample1' => [ + 'title' => 'sample1', + 'sort_order' => 10, + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/sample1.jpg', + ], + 'sample2' => [ + 'title' => 'sample2', + 'sort_order' => 20, + 'sample_type' => 'file', + 'sample_file_content' => [ + 'name' => 'sample2.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + ], + ]; + } + + protected function getExpectedSampleData() + { + return [ + [ + 'title' => 'sample1', + 'sort_order' => 10, + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/sample1.jpg', + ], + [ + 'title' => 'sample2', + 'sort_order' => 20, + 'sample_type' => 'file', + ], + ]; + } + + protected function createDownloadableProduct() + { + $product = [ + "sku" => self::PRODUCT_SKU, + "name" => self::PRODUCT_SKU, + "type_id" => \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE, + "price" => 10, + 'attribute_set_id' => 4, + "extension_attributes" => [ + "downloadable_product_links" => array_values($this->getLinkData()), + "downloadable_product_samples" => array_values($this->getSampleData()), + ], + ]; + + $response = $this->createProduct($product); + $this->assertEquals(self::PRODUCT_SKU, $response[ProductInterface::SKU]); + $this->assertEquals(10, $response['price']); + $this->assertEquals( + \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE, + $response['type_id'] + ); + return $response; + } + + /** + * Create a downloadable product with two links and two samples + */ + public function testCreateDownloadableProduct() + { + $response = $this->createDownloadableProduct(); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]) + ); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]) + ); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + $this->assertEquals(2, count($resultLinks)); + $this->assertTrue(isset($resultLinks[0]['id'])); + $this->assertTrue(isset($resultLinks[0]['link_file'])); + $this->assertTrue(isset($resultLinks[0]['sample_file'])); + unset($resultLinks[0]['id']); + unset($resultLinks[0]['link_file']); + unset($resultLinks[0]['sample_file']); + $this->assertTrue(isset($resultLinks[1]['id'])); + unset($resultLinks[1]['id']); + + $expectedLinkData = $this->getExpectedLinkData(); + $this->assertEquals($expectedLinkData, $resultLinks); + + $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $this->assertEquals(2, count($resultSamples)); + $this->assertTrue(isset($resultSamples[0]['id'])); + unset($resultSamples[0]['id']); + $this->assertTrue(isset($resultSamples[1]['id'])); + $this->assertTrue(isset($resultSamples[1]['sample_file'])); + unset($resultSamples[1]['sample_file']); + unset($resultSamples[1]['id']); + + $expectedSampleData = $this->getExpectedSampleData(); + $this->assertEquals($expectedSampleData, $resultSamples); + } + + /** + * Update downloadable product, update a link, add two link, delete a link + */ + public function testUpdateDownloadableProductLinks() + { + $response = $this->createDownloadableProduct(); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + $link1Id = $resultLinks[0]['id']; + $link2Id = $resultLinks[1]['id']; + + $linkFile = $resultLinks[0]['link_file']; + $sampleFile = $resultLinks[0]['sample_file']; + $updatedLink1Data = [ + 'id' => $link1Id, + 'title' => 'link1_updated', + 'sort_order' => 1, //the sort order needs to be smaller than 10 + 'is_shareable' => 0, + 'price' => 5.0, + 'number_of_downloads' => 999, + 'link_type' => 'file', + 'sample_type' => 'file', + 'link_file' => 'http://www.example.com/invalid', //this field will be overridden + 'sample_file' => 'http://www.example.com/invalid', //this field will be overridden + ]; + $linkData = $this->getLinkData(); + + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"] = + [$updatedLink1Data, $linkData['link1'], $linkData['link2']]; + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]); + + $response = $this->saveProduct($response); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]) + ); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]) + ); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + + $this->assertEquals(3, count($resultLinks)); + $this->assertTrue(isset($resultLinks[0]['id'])); + $this->assertEquals($link1Id, $resultLinks[0]['id']); + $this->assertTrue(isset($resultLinks[0]['link_file'])); + $this->assertEquals($linkFile, $resultLinks[0]['link_file']); + $this->assertTrue(isset($resultLinks[0]['sample_file'])); + $this->assertEquals($sampleFile, $resultLinks[0]['sample_file']); + unset($resultLinks[0]['id']); + unset($resultLinks[0]['link_file']); + unset($resultLinks[0]['sample_file']); + $this->assertTrue(isset($resultLinks[1]['id'])); + $this->assertGreaterThan($link2Id, $resultLinks[1]['id']); + $this->assertTrue(isset($resultLinks[1]['link_file'])); + $this->assertTrue(isset($resultLinks[1]['sample_file'])); + unset($resultLinks[1]['id']); + unset($resultLinks[1]['link_file']); + unset($resultLinks[1]['sample_file']); + $this->assertTrue(isset($resultLinks[2]['id'])); + $this->assertGreaterThan($link2Id, $resultLinks[2]['id']); + unset($resultLinks[2]['id']); + + $expectedLinkData[] = [ + 'title' => 'link1_updated', + 'sort_order' => 1, //the sort order needs to be smaller than 10 + 'is_shareable' => 0, + 'price' => 5.0, + 'number_of_downloads' => 999, + 'link_type' => 'file', + 'sample_type' => 'file', + ]; + $expectedLinkData = array_merge($expectedLinkData, $this->getExpectedLinkData()); + $this->assertEquals($expectedLinkData, $resultLinks); + + $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $this->assertEquals(2, count($resultSamples)); + } + + /** + * Update downloadable product, update two links and change file content + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testUpdateDownloadableProductLinksWithNewFile() + { + $response = $this->createDownloadableProduct(); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + $link1Id = $resultLinks[0]['id']; + $link2Id = $resultLinks[1]['id']; + + $linkFile = 'link1_content_updated.jpg'; + $sampleFile = 'link1_sample_updated.jpg'; + $updatedLink1Data = [ + 'id' => $link1Id, + 'title' => 'link1_updated', + 'sort_order' => 1, //the sort order needs to be smaller than 10 + 'is_shareable' => 0, + 'price' => 5.0, + 'number_of_downloads' => 999, + 'link_type' => 'file', + 'link_file_content' => [ + 'name' => $linkFile, + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + 'sample_type' => 'file', + 'sample_file_content' => [ + 'name' => $sampleFile, + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + ]; + $updatedLink2Data = [ + 'id' => $link2Id, + 'title' => 'link2_updated', + 'sort_order' => 2, + 'is_shareable' => 0, + 'price' => 6.0, + 'number_of_downloads' => 0, + 'link_type' => 'file', + 'link_file_content' => [ + 'name' => 'link2_content.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + 'sample_type' => 'file', + 'sample_file_content' => [ + 'name' => 'link2_sample.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + ]; + + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"] = + [$updatedLink1Data, $updatedLink2Data]; + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]); + + $response = $this->saveProduct($response); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]) + ); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]) + ); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + + $this->assertEquals(2, count($resultLinks)); + $this->assertTrue(isset($resultLinks[0]['id'])); + $this->assertEquals($link1Id, $resultLinks[0]['id']); + $this->assertTrue(isset($resultLinks[0]['link_file'])); + $this->assertGreaterThan(0, strpos($resultLinks[0]['link_file'], $linkFile)); + $this->assertTrue(isset($resultLinks[0]['sample_file'])); + $this->assertGreaterThan(0, strpos($resultLinks[0]['sample_file'], $sampleFile)); + unset($resultLinks[0]['id']); + unset($resultLinks[0]['link_file']); + unset($resultLinks[0]['sample_file']); + $this->assertTrue(isset($resultLinks[1]['id'])); + $this->assertEquals($link2Id, $resultLinks[1]['id']); + $this->assertTrue(isset($resultLinks[1]['link_file'])); + $this->assertTrue(isset($resultLinks[1]['sample_file'])); + unset($resultLinks[1]['id']); + unset($resultLinks[1]['link_file']); + unset($resultLinks[1]['sample_file']); + + $expectedLinkData = [ + [ + 'title' => 'link1_updated', + 'sort_order' => 1, //the sort order needs to be smaller than 10 + 'is_shareable' => 0, + 'price' => 5.0, + 'number_of_downloads' => 999, + 'link_type' => 'file', + 'sample_type' => 'file', + ], + [ + 'title' => 'link2_updated', + 'sort_order' => 2, + 'is_shareable' => 0, + 'price' => 6.0, + 'number_of_downloads' => 0, + 'link_type' => 'file', + 'sample_type' => 'file', + 'link_url' => 'http://www.example.com/link2.jpg', //urls are still saved, just not used + 'sample_url' => 'http://www.example.com/link2.jpg', + ] + ]; + $this->assertEquals($expectedLinkData, $resultLinks); + + $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $this->assertEquals(2, count($resultSamples)); + } + + public function testUpdateDownloadableProductSamples() + { + $response = $this->createDownloadableProduct(); + + $resultSample + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $sample1Id = $resultSample[0]['id']; + $sample2Id = $resultSample[1]['id']; + + $updatedSample1Data = [ + 'id' => $sample1Id, + 'title' => 'sample1_updated', + 'sort_order' => 1, + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/sample1.jpg', + ]; + $sampleData = $this->getSampleData(); + + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]); + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] = + [$updatedSample1Data, $sampleData['sample1'], $sampleData['sample2']]; + + $response = $this->saveProduct($response); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]) + ); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]) + ); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + + $this->assertEquals(2, count($resultLinks)); + + $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $this->assertEquals(3, count($resultSamples)); + $this->assertTrue(isset($resultSamples[0]['id'])); + $this->assertEquals($sample1Id, $resultSamples[0]['id']); + unset($resultSamples[0]['id']); + $this->assertTrue(isset($resultSamples[1]['id'])); + $this->assertGreaterThan($sample2Id, $resultSamples[1]['id']); + unset($resultSamples[1]['id']); + $this->assertTrue(isset($resultSamples[2]['id'])); + $this->assertGreaterThan($sample2Id, $resultSamples[2]['id']); + $this->assertTrue(isset($resultSamples[2]['sample_file'])); + unset($resultSamples[2]['sample_file']); + unset($resultSamples[2]['id']); + + $expectedSampleData[] = [ + 'title' => 'sample1_updated', + 'sort_order' => 1, + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/sample1.jpg', + ]; + $expectedSampleData = array_merge($expectedSampleData, $this->getExpectedSampleData()); + $this->assertEquals($expectedSampleData, $resultSamples); + } + + public function testUpdateDownloadableProductSamplesWithNewFile() + { + $response = $this->createDownloadableProduct(); + + $resultSample + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $sample1Id = $resultSample[0]['id']; + $sample2Id = $resultSample[1]['id']; + + //upload a file for sample 1 + $updatedSample1Data = [ + 'id' => $sample1Id, + 'title' => 'sample1_updated', + 'sort_order' => 1, + 'sample_type' => 'file', + 'sample_file_content' => [ + 'name' => 'sample1.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], + ]; + //change title for sample 2 + $updatedSamp2e1Data = [ + 'id' => $sample2Id, + 'title' => 'sample2_updated', + 'sort_order' => 2, + 'sample_type' => 'file', + ]; + + unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]); + $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] = + [$updatedSample1Data, $updatedSamp2e1Data]; + + $response = $this->saveProduct($response); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]) + ); + $this->assertTrue( + isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]) + ); + $resultLinks + = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]; + + $this->assertEquals(2, count($resultLinks)); + + $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]; + $this->assertEquals(2, count($resultSamples)); + $this->assertTrue(isset($resultSamples[0]['id'])); + $this->assertEquals($sample1Id, $resultSamples[0]['id']); + unset($resultSamples[0]['id']); + $this->assertTrue(isset($resultSamples[0]['sample_file'])); + $this->assertContains('sample1.jpg', $resultSamples[0]['sample_file']); + unset($resultSamples[0]['sample_file']); + $this->assertTrue(isset($resultSamples[1]['id'])); + $this->assertEquals($sample2Id, $resultSamples[1]['id']); + unset($resultSamples[1]['id']); + $this->assertTrue(isset($resultSamples[1]['sample_file'])); + $this->assertContains('sample2.jpg', $resultSamples[1]['sample_file']); + unset($resultSamples[1]['sample_file']); + + $expectedSampleData = [ + [ + 'title' => 'sample1_updated', + 'sort_order' => 1, + 'sample_type' => 'file', + 'sample_url' => 'http://www.example.com/sample1.jpg', + ], + [ + 'title' => 'sample2_updated', + 'sort_order' => 2, + 'sample_type' => 'file', + ], + ]; + $this->assertEquals($expectedSampleData, $resultSamples); + } + + /** + * Get product + * + * @param string $productSku + * @return array the product data + */ + protected function getProduct($productSku) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + + $response = (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ? + $this->_webApiCall($serviceInfo, ['sku' => $productSku]) : $this->_webApiCall($serviceInfo); + + return $response; + } + + /** + * Create product + * + * @param array $product + * @return array the created product data + */ + protected function createProduct($product) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Delete a product by sku + * + * @param $productSku + * @return bool + */ + protected function deleteProductBySku($productSku) + { + $resourcePath = self::RESOURCE_PATH . '/' . $productSku; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $resourcePath, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'deleteById', + ], + ]; + $requestData = ["sku" => $productSku]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Save product + * + * @param array $product + * @return array the created product data + */ + protected function saveProduct($product) + { + $resourcePath = self::RESOURCE_PATH . '/' . $product['sku']; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $resourcePath, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php index c4fe8606cb8bc9f680e3db3da0bdc4061941a05c..0a1bba69a89890b7feeec2afd84a793181e2d239 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php @@ -79,9 +79,11 @@ class SampleRepositoryTest extends WebapiAbstract */ protected function getTargetProduct($isScopeGlobal = false) { - $product = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\ProductFactory')->create()->load(1); if ($isScopeGlobal) { - $product->setStoreId(0); + $product = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\ProductFactory') + ->create()->setStoreId(0)->load(1); + } else { + $product = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\ProductFactory')->create()->load(1); } return $product; @@ -120,11 +122,11 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => true, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Title', 'sort_order' => 1, - 'sample_file' => [ + 'sample_file_content' => [ 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'image.jpg', ], @@ -136,10 +138,11 @@ class SampleRepositoryTest extends WebapiAbstract $globalScopeSample = $this->getTargetSample($this->getTargetProduct(true), $newSampleId); $sample = $this->getTargetSample($this->getTargetProduct(), $newSampleId); $this->assertNotNull($sample); - $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle()); - $this->assertEquals($requestData['sampleContent']['title'], $globalScopeSample->getTitle()); - $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder()); - $this->assertEquals($requestData['sampleContent']['sample_type'], $sample->getSampleType()); + $this->assertNotNull($sample->getId()); + $this->assertEquals($requestData['sample']['title'], $sample->getTitle()); + $this->assertEquals($requestData['sample']['title'], $globalScopeSample->getTitle()); + $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder()); + $this->assertEquals($requestData['sample']['sample_type'], $sample->getSampleType()); $this->assertStringEndsWith('.jpg', $sample->getSampleFile()); $this->assertNull($sample->getSampleUrl()); } @@ -151,8 +154,8 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Store View Title', 'sort_order' => 1, 'sample_url' => 'http://www.sample.example.com/', @@ -164,10 +167,10 @@ class SampleRepositoryTest extends WebapiAbstract $sample = $this->getTargetSample($this->getTargetProduct(), $newSampleId); $globalScopeSample = $this->getTargetSample($this->getTargetProduct(true), $newSampleId); $this->assertNotNull($sample); - $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle()); - $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder()); - $this->assertEquals($requestData['sampleContent']['sample_url'], $sample->getSampleUrl()); - $this->assertEquals($requestData['sampleContent']['sample_type'], $sample->getSampleType()); + $this->assertEquals($requestData['sample']['title'], $sample->getTitle()); + $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder()); + $this->assertEquals($requestData['sample']['sample_url'], $sample->getSampleUrl()); + $this->assertEquals($requestData['sample']['sample_type'], $sample->getSampleType()); $this->assertEmpty($globalScopeSample->getTitle()); } @@ -178,8 +181,8 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Sample with URL resource', 'sort_order' => 1, 'sample_url' => 'http://www.sample.example.com/', @@ -190,10 +193,10 @@ class SampleRepositoryTest extends WebapiAbstract $newSampleId = $this->_webApiCall($this->createServiceInfo, $requestData); $sample = $this->getTargetSample($this->getTargetProduct(), $newSampleId); $this->assertNotNull($sample); - $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle()); - $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder()); - $this->assertEquals($requestData['sampleContent']['sample_type'], $sample->getSampleType()); - $this->assertEquals($requestData['sampleContent']['sample_url'], $sample->getSampleUrl()); + $this->assertEquals($requestData['sample']['title'], $sample->getTitle()); + $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder()); + $this->assertEquals($requestData['sample']['sample_type'], $sample->getSampleType()); + $this->assertEquals($requestData['sample']['sample_url'], $sample->getSampleUrl()); } /** @@ -201,14 +204,15 @@ class SampleRepositoryTest extends WebapiAbstract * @expectedException \Exception * @expectedExceptionMessage Invalid sample type. */ - public function testCreateThrowsExceptionIfSampleTypeIsNotSpecified() + public function testCreateThrowsExceptionIfSampleTypeIsInvalid() { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Sample with URL resource', 'sort_order' => 1, + 'sample_type' => 'invalid', ], ]; @@ -224,12 +228,12 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Sample Title', 'sort_order' => 1, 'sample_type' => 'file', - 'sample_file' => [ + 'sample_file_content' => [ 'file_data' => 'not_a_base64_encoded_content', 'name' => 'image.jpg', ], @@ -248,12 +252,12 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Title', 'sort_order' => 15, 'sample_type' => 'file', - 'sample_file' => [ + 'sample_file_content' => [ 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'name/with|forbidden{characters', ], @@ -272,8 +276,8 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Sample Title', 'sort_order' => 1, 'sample_type' => 'url', @@ -294,8 +298,8 @@ class SampleRepositoryTest extends WebapiAbstract { $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ 'title' => 'Sample Title', 'sort_order' => $sortOrder, 'sample_type' => 'url', @@ -325,8 +329,8 @@ class SampleRepositoryTest extends WebapiAbstract $this->createServiceInfo['rest']['resourcePath'] = '/V1/products/simple/downloadable-links/samples'; $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'simple', - 'sampleContent' => [ + 'sku' => 'simple', + 'sample' => [ 'title' => 'Sample Title', 'sort_order' => 50, 'sample_type' => 'url', @@ -345,8 +349,8 @@ class SampleRepositoryTest extends WebapiAbstract $this->createServiceInfo['rest']['resourcePath'] = '/V1/products/wrong-sku/downloadable-links/samples'; $requestData = [ 'isGlobalScopeContent' => false, - 'productSku' => 'wrong-sku', - 'sampleContent' => [ + 'sku' => 'wrong-sku', + 'sample' => [ 'title' => 'Title', 'sort_order' => 15, 'sample_type' => 'url', @@ -366,19 +370,21 @@ class SampleRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}"; $requestData = [ 'isGlobalScopeContent' => false, - 'sampleId' => $sampleId, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ + 'id' => $sampleId, 'title' => 'Updated Title', 'sort_order' => 2, + 'sample_type' => 'url', ], ]; $this->assertEquals($sampleId, $this->_webApiCall($this->updateServiceInfo, $requestData)); $sample = $this->getTargetSample($this->getTargetProduct(), $sampleId); $this->assertNotNull($sample); - $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle()); - $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder()); + $this->assertEquals($requestData['sample']['id'], $sample->getId()); + $this->assertEquals($requestData['sample']['title'], $sample->getTitle()); + $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder()); } /** @@ -392,11 +398,12 @@ class SampleRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}"; $requestData = [ 'isGlobalScopeContent' => true, - 'sampleId' => $sampleId, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ + 'id' => $sampleId, 'title' => 'Updated Title', 'sort_order' => 2, + 'sample_type' => 'url', ], ]; @@ -406,8 +413,8 @@ class SampleRepositoryTest extends WebapiAbstract $this->assertNotNull($sample); // Title was set on store view level in fixture so it must be the same $this->assertEquals($originalSample->getTitle(), $sample->getTitle()); - $this->assertEquals($requestData['sampleContent']['title'], $globalScopeSample->getTitle()); - $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder()); + $this->assertEquals($requestData['sample']['title'], $globalScopeSample->getTitle()); + $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder()); } /** @@ -419,11 +426,12 @@ class SampleRepositoryTest extends WebapiAbstract $this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong-sku/downloadable-links/samples/1'; $requestData = [ 'isGlobalScopeContent' => true, - 'sampleId' => 1, - 'productSku' => 'wrong-sku', - 'sampleContent' => [ + 'sku' => 'wrong-sku', + 'sample' => [ + 'id' => 1, 'title' => 'Updated Title', 'sort_order' => 2, + 'sample_type' => 'url', ], ]; $this->_webApiCall($this->updateServiceInfo, $requestData); @@ -441,11 +449,12 @@ class SampleRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}"; $requestData = [ 'isGlobalScopeContent' => true, - 'sampleId' => 9999, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ + 'id' => $sampleId, 'title' => 'Title', 'sort_order' => 2, + 'sample_type' => 'url', ], ]; @@ -465,11 +474,12 @@ class SampleRepositoryTest extends WebapiAbstract = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}"; $requestData = [ 'isGlobalScopeContent' => false, - 'sampleId' => $sampleId, - 'productSku' => 'downloadable-product', - 'sampleContent' => [ + 'sku' => 'downloadable-product', + 'sample' => [ + 'id' => $sampleId, 'title' => 'Updated Sample Title', 'sort_order' => $sortOrder, + 'sample_type' => 'url', ], ]; $this->_webApiCall($this->updateServiceInfo, $requestData); @@ -483,7 +493,7 @@ class SampleRepositoryTest extends WebapiAbstract $sampleId = $this->getTargetSample($this->getTargetProduct())->getId(); $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/samples/{$sampleId}"; $requestData = [ - 'sampleId' => $sampleId, + 'id' => $sampleId, ]; $this->assertTrue($this->_webApiCall($this->deleteServiceInfo, $requestData)); @@ -500,7 +510,7 @@ class SampleRepositoryTest extends WebapiAbstract $sampleId = 9999; $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/samples/{$sampleId}"; $requestData = [ - 'sampleId' => $sampleId, + 'id' => $sampleId, ]; $this->_webApiCall($this->deleteServiceInfo, $requestData); diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c8ab24527c633a12fd46442a9528570a4586e458 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php @@ -0,0 +1,203 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\GroupedProduct\Api; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\TestFramework\TestCase\WebapiAbstract; + +class ProductRepositoryInterfaceTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const RESOURCE_PATH = '/V1/products'; + + /** + * Get Product + * + * @param $sku + * @return ProductInterface + */ + protected function getProduct($sku) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $sku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + + $response = $this->_webApiCall($serviceInfo, ['sku' => $sku]); + return $response; + } + + /** + * Update Product + * + * @param $product + * @return mixed + */ + protected function updateProduct($product) + { + $sku = $product[ProductInterface::SKU]; + if (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) { + $product[ProductInterface::SKU] = null; + } + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $sku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + $response = $this->_webApiCall($serviceInfo, $requestData); + return $response; + } + + /** + * Save Product + * + * @param $product + * @return mixed + */ + protected function saveProduct($product) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $requestData = ['product' => $product]; + return $this->_webApiCall($serviceInfo, $requestData); + } + + /** + * Delete Product + * + * @param string $sku + * @return boolean + */ + protected function deleteProduct($sku) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $sku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'DeleteById', + ], + ]; + + return (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ? + $this->_webApiCall($serviceInfo, ['sku' => $sku]) : $this->_webApiCall($serviceInfo); + } + + public function testProductLinks() + { + // Create simple product + $productData = [ + ProductInterface::SKU => "product_simple_500", + ProductInterface::NAME => "Product Simple 500", + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::PRICE => 100, + ProductInterface::STATUS => 1, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::ATTRIBUTE_SET_ID => 4, + ]; + + $this->saveProduct($productData); + + // Create a group product + $productLinkData = ["product_sku" => "group_product_500", "link_type" => "associated", + "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple", + "position" => 0, "extension_attributes" => ["qty" => 1]]; + $productWithGroupData = [ + ProductInterface::SKU => "group_product_500", + ProductInterface::NAME => "Group Product 500", + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'grouped', + ProductInterface::PRICE => 300, + ProductInterface::STATUS => 1, + ProductInterface::ATTRIBUTE_SET_ID => 4, + "product_links" => [$productLinkData] + ]; + + $this->saveProduct($productWithGroupData); + $response = $this->getProduct("group_product_500"); + $this->assertArrayHasKey('product_links', $response); + $links = $response['product_links']; + $this->assertEquals(1, count($links)); + $this->assertEquals($productLinkData, $links[0]); + + // update link information for Group Product + $productLinkData1 = ["product_sku" => "group_product_500", "link_type" => "associated", + "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple", + "position" => 0, "extension_attributes" => ["qty" => 4]]; + $productLinkData2 = ["product_sku" => "group_product_500", "link_type" => "upsell", + "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple", + "position" => 0, "extension_attributes" => []]; + $productWithGroupData = [ + ProductInterface::SKU => "group_product_500", + ProductInterface::NAME => "Group Product 500", + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'grouped', + ProductInterface::PRICE => 300, + ProductInterface::STATUS => 1, + ProductInterface::ATTRIBUTE_SET_ID => 4, + "product_links" => [$productLinkData1, $productLinkData2] + ]; + + $this->saveProduct($productWithGroupData); + $response = $this->getProduct("group_product_500"); + + $this->assertArrayHasKey('product_links', $response); + $links = $response['product_links']; + $this->assertEquals(2, count($links)); + $this->assertEquals($productLinkData1, $links[1]); + $this->assertEquals($productLinkData2, $links[0]); + + // Remove link + $productWithNoLinkData = [ + ProductInterface::SKU => "group_product_500", + ProductInterface::NAME => "Group Product 500", + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'grouped', + ProductInterface::PRICE => 300, + ProductInterface::STATUS => 1, + ProductInterface::ATTRIBUTE_SET_ID => 4, + "product_links" => [] + ]; + + $this->saveProduct($productWithNoLinkData); + $response = $this->getProduct("group_product_500"); + $this->assertArrayHasKey('product_links', $response); + $links = $response['product_links']; + $this->assertEquals([], $links); + + $this->deleteProduct("product_simple_500"); + $this->deleteProduct("group_product_500"); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php index 1557278ce1bb31a76ac96d90a90c57b6bada4d21..2d20a4e9110939aa3c8298b8eb6e317436bf2454 100644 --- a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php @@ -12,6 +12,7 @@ use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Tax\Api\Data\TaxClassInterfaceFactory; +use Magento\Tax\Model\ClassModel; use Magento\Tax\Model\ClassModelRegistry; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -171,9 +172,9 @@ class TaxClassRepositoryTest extends WebapiAbstract ]; $requestData = ['taxClassId' => $taxClassId]; $taxClassData = $this->_webApiCall($serviceInfo, $requestData); - $this->assertEquals($taxClassData[Data\TaxClassInterface::KEY_NAME], $taxClassName); + $this->assertEquals($taxClassData[ClassModel::KEY_NAME], $taxClassName); $this->assertEquals( - $taxClassData[Data\TaxClassInterface::KEY_TYPE], + $taxClassData[ClassModel::KEY_TYPE], TaxClassManagementInterface::TYPE_CUSTOMER ); } @@ -220,7 +221,7 @@ class TaxClassRepositoryTest extends WebapiAbstract public function testSearchTaxClass() { $taxClassName = 'Retail Customer'; - $taxClassNameField = Data\TaxClassInterface::KEY_NAME; + $taxClassNameField = ClassModel::KEY_NAME; $filter = $this->filterBuilder->setField($taxClassNameField) ->setValue($taxClassName) ->create(); @@ -249,23 +250,23 @@ class TaxClassRepositoryTest extends WebapiAbstract public function testSearchTaxClassMultipleFilterGroups() { $productTaxClass = [ - Data\TaxClassInterface::KEY_NAME => 'Taxable Goods', - Data\TaxClassInterface::KEY_TYPE => 'PRODUCT', + ClassModel::KEY_NAME => 'Taxable Goods', + ClassModel::KEY_TYPE => 'PRODUCT', ]; - $customerTaxClass = [Data\TaxClassInterface::KEY_NAME => 'Retail Customer', - Data\TaxClassInterface::KEY_TYPE => 'CUSTOMER', ]; + $customerTaxClass = [ClassModel::KEY_NAME => 'Retail Customer', + ClassModel::KEY_TYPE => 'CUSTOMER', ]; - $filter1 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_NAME) - ->setValue($productTaxClass[Data\TaxClassInterface::KEY_NAME]) + $filter1 = $this->filterBuilder->setField(ClassModel::KEY_NAME) + ->setValue($productTaxClass[ClassModel::KEY_NAME]) ->create(); - $filter2 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_NAME) - ->setValue($customerTaxClass[Data\TaxClassInterface::KEY_NAME]) + $filter2 = $this->filterBuilder->setField(ClassModel::KEY_NAME) + ->setValue($customerTaxClass[ClassModel::KEY_NAME]) ->create(); - $filter3 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_TYPE) - ->setValue($productTaxClass[Data\TaxClassInterface::KEY_TYPE]) + $filter3 = $this->filterBuilder->setField(ClassModel::KEY_TYPE) + ->setValue($productTaxClass[ClassModel::KEY_TYPE]) ->create(); - $filter4 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_TYPE) - ->setValue($customerTaxClass[Data\TaxClassInterface::KEY_TYPE]) + $filter4 = $this->filterBuilder->setField(ClassModel::KEY_TYPE) + ->setValue($customerTaxClass[ClassModel::KEY_TYPE]) ->create(); /** @@ -291,12 +292,12 @@ class TaxClassRepositoryTest extends WebapiAbstract $searchResults = $this->_webApiCall($serviceInfo, $requestData); $this->assertEquals(2, $searchResults['total_count']); $this->assertEquals( - $productTaxClass[Data\TaxClassInterface::KEY_NAME], - $searchResults['items'][0][Data\TaxClassInterface::KEY_NAME] + $productTaxClass[ClassModel::KEY_NAME], + $searchResults['items'][0][ClassModel::KEY_NAME] ); $this->assertEquals( - $customerTaxClass[Data\TaxClassInterface::KEY_NAME], - $searchResults['items'][1][Data\TaxClassInterface::KEY_NAME] + $customerTaxClass[ClassModel::KEY_NAME], + $searchResults['items'][1][ClassModel::KEY_NAME] ); /** class_name == 'Retail Customer' && ( class_type == 'CUSTOMER' || class_type == 'PRODUCT') */ @@ -309,8 +310,8 @@ class TaxClassRepositoryTest extends WebapiAbstract $searchResults = $this->_webApiCall($serviceInfo, $requestData); $this->assertEquals(1, $searchResults['total_count']); $this->assertEquals( - $customerTaxClass[Data\TaxClassInterface::KEY_NAME], - $searchResults['items'][0][Data\TaxClassInterface::KEY_NAME] + $customerTaxClass[ClassModel::KEY_NAME], + $searchResults['items'][0][ClassModel::KEY_NAME] ); } } diff --git a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php index 9f47505721b8f6bb1e4b403e6b2f56b861fd3668..a140347e2498fe75163c9822a4f5f8eb80fd9506 100644 --- a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php @@ -10,7 +10,7 @@ use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteria; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; -use Magento\Tax\Api\Data\TaxRateInterface as TaxRate; +use Magento\Tax\Model\Calculation\Rate; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -433,7 +433,7 @@ class TaxRateRepositoryTest extends WebapiAbstract $rates = $this->setupTaxRatesForSearch(); // Find rates whose code is 'codeUs12' - $filter = $this->filterBuilder->setField(TaxRate::KEY_CODE) + $filter = $this->filterBuilder->setField(Rate::KEY_CODE) ->setValue('codeUs12') ->create(); @@ -480,11 +480,11 @@ class TaxRateRepositoryTest extends WebapiAbstract $rates = $this->setupTaxRatesForSearch(); // Find rates which country id 'CZ' - $filter = $this->filterBuilder->setField(TaxRate::KEY_COUNTRY_ID) + $filter = $this->filterBuilder->setField(Rate::KEY_COUNTRY_ID) ->setValue('CZ') ->create(); $sortOrder = $this->sortOrderBuilder - ->setField(TaxRate::KEY_POSTCODE) + ->setField(Rate::KEY_POSTCODE) ->setDirection(SearchCriteria::SORT_DESC) ->create(); // Order them by descending postcode (not the default order) diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php index 41d6e8a2914deb9a863b42d2634d0c9aab934324..a64744df17ad68e016255d6d2396bad4e59564f6 100644 --- a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php @@ -81,7 +81,7 @@ class RestErrorHandlingTest extends \Magento\TestFramework\TestCase\WebapiAbstra { $serviceInfo = [ 'rest' => [ - 'resourcePath' => '/V1/errortest/otherexception', + 'resourcePath' => '/V1/errortest/otherException', 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, ], ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php index 9854602a507fcc0045fcc2f5be86a85f96073b0d..3809d7f293849488c696e4631c03e6ed34e0de02 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php @@ -9,6 +9,8 @@ */ namespace Magento\TestFramework\Annotation; +use Magento\Framework\App\Config\ScopeConfigInterface; + class ConfigFixture { /** @@ -73,7 +75,7 @@ class ConfigFixture )->setValue( $configPath, $value, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); } } else { diff --git a/dev/tests/integration/framework/bootstrap.php b/dev/tests/integration/framework/bootstrap.php index 41453aa0becc0e2d32513736f2cb99ddc60e8854..b71bb17d945ccd42f693ed57588116e05c474dbb 100644 --- a/dev/tests/integration/framework/bootstrap.php +++ b/dev/tests/integration/framework/bootstrap.php @@ -8,6 +8,11 @@ use Magento\Framework\Autoload\AutoloaderRegistry; require_once __DIR__ . '/../../../../app/bootstrap.php'; require_once __DIR__ . '/autoload.php'; +$updateAppBootstrap = __DIR__ . '/../../../../update/app/bootstrap.php'; +if (file_exists($updateAppBootstrap)) { + require_once $updateAppBootstrap; +} + $testsBaseDir = dirname(__DIR__); $testsTmpDir = "{$testsBaseDir}/tmp"; $magentoBaseDir = realpath("{$testsBaseDir}/../../../"); diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist index 7a9e365c8f15056e5060cd27827720e2d18d2fce..bd6cbffb247d64f6617eef5fc9c8ebaebb753b7e 100644 --- a/dev/tests/integration/phpunit.xml.dist +++ b/dev/tests/integration/phpunit.xml.dist @@ -18,6 +18,7 @@ </testsuite> <testsuite name="Magento Integration Tests"> <directory suffix="Test.php">testsuite</directory> + <directory suffix="Test.php">../../../update/dev/tests/integration/testsuite</directory> <exclude>testsuite/Magento/Test/Integrity</exclude> <exclude>testsuite/Magento/MemoryUsageTest.php</exclude> </testsuite> @@ -27,6 +28,7 @@ <whitelist addUncoveredFilesFromWhiteList="true"> <directory suffix=".php">../../../app/code/Magento</directory> <directory suffix=".php">../../../lib/internal/Magento</directory> + <directory suffix=".php">../../../update/app/code</directory> </whitelist> </filter> <!-- PHP INI settings and constants definition --> diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt index fc890c803aae34f8ca74f4c00a541415b426034a..dc07065f7be1e4b2db53584c769bb52abb79de21 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt @@ -1 +1 @@ -<ul id="nav" role="menubar" ><li data-ui-id="magento-backend-system" class="item-system parent last level-0" id="magento-backend-system" aria-haspopup="true" role="menu-item"><a href="#" onclick="return false;" class=""><span>System</span></a><div class="submenu" aria-labelledby="magento-backend-system"><ul role="menu" ><li data-ui-id="magento-backend-system-report" class="item-system-report parent level-1" role="menu-item"><strong class="submenu-group-title" role="presentation"><span>Report</span></strong><div class="submenu"><ul role="menu" ><li data-ui-id="magento-backend-system-report-private-sales" class="item-system-report-private-sales level-2" role="menu-item"><a href="#" onclick="return false;" class=""><span>Private Sales</span></a></li><li data-ui-id="magento-backend-system-report-magento-invite-general" class="item-system-report-magento-invite-general level-2" role="menu-item"><a href="#" onclick="return false;" class=""><span>Invite</span></a></li><li data-ui-id="magento-backend-system-report-magento-invite-customer" class="item-system-report-magento-invite-customer level-2" role="menu-item"><a href="#" onclick="return false;" class=""><span>Invited Customers</span></a></li></ul></div></li></ul></div></li></ul> \ No newline at end of file +<ul id="nav" role="menubar" ><li data-ui-id="magento-backend-system" class="item-system parent last level-0" id="magento-backend-system" aria-haspopup="true" role="menu-item"><a href="#" onclick="return false;" class=""><span>System</span></a><div class="submenu" aria-labelledby="magento-backend-system"><ul role="menu" ><div class="submenu"><ul role="menu" ><li data-ui-id="magento-backend-system-report-private-sales" class="item-system-report-private-sales level-2" role="menu-item"><a href="#" onclick="return false;" class=""><span>Private Sales</span></a></li><li data-ui-id="magento-backend-system-report-magento-invite-general" class="item-system-report-magento-invite-general level-2" role="menu-item"><a href="#" onclick="return false;" class=""><span>Invite</span></a></li><li data-ui-id="magento-backend-system-report-magento-invite-customer" class="item-system-report-magento-invite-customer level-2" role="menu-item"><a href="#" onclick="return false;" class=""><span>Invited Customers</span></a></li></ul></div></ul></div></li></ul> \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php index 09c21758cba336cbd15982d610875cbc0aa19117..c0cc101fd36bfe94dd5dcc19481d81d14b926132 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Backend\Model\Locale; +use Magento\Framework\Locale\Resolver; + /** * @magentoAppArea adminhtml */ @@ -28,7 +30,7 @@ class ResolverTest extends \PHPUnit_Framework_TestCase */ public function testSetLocaleWithDefaultLocale() { - $this->_checkSetLocale(\Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE); + $this->_checkSetLocale(Resolver::DEFAULT_LOCALE); } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/data_object.xml b/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/service_data_attributes.xml similarity index 63% rename from dev/tests/integration/testsuite/Magento/Catalog/_files/etc/data_object.xml rename to dev/tests/integration/testsuite/Magento/Catalog/_files/etc/service_data_attributes.xml index c2888a64ee00c0e6f18a7d1126920e4c85a8b091..93cf7bfb484a6de773ab318549385ad2436f2fa4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/data_object.xml +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/service_data_attributes.xml @@ -5,22 +5,22 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> - </custom_attributes> - <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="stock_item" type="Magento\CatalogInventory\Service\Data\V1\StockItem" /> - </custom_attributes> - <custom_attributes for="Magento\Catalog\Api\Data\CategoryInterface"> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\CategoryInterface"> <attribute code="category_attribute_1" type="Magento\Catalog\Api\Data\CategoryAttributeType1" /> <attribute code="category_attribute_2" type="Magento\Catalog\Api\Data\CategoryAttributeType2" /> - </custom_attributes> - <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface"> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface"> <attribute code="customer_attribute_1" type="Magento\Customer\Api\Data\CustomerAttributeType1" /> <attribute code="customer_attribute_2" type="Magento\Customer\Api\Data\CustomerAttributeType2" /> - </custom_attributes> - <custom_attributes for="Magento\Customer\Api\Data\Address"> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\Address"> <attribute code="address_attribute_1" type="Magento\Customer\Api\Data\AddressAttributeType1" /> <attribute code="address_attribute_2" type="Magento\Customer\Api\Data\AddressAttributeType2" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index def5584983e613573b91eeff95d766599b82655a..a83bb8be1c9feec57cb2e722d69021549a786615 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -125,4 +125,50 @@ class ProductTest extends \PHPUnit_Framework_TestCase ); } } + + /** + * Verifies if exception processing works properly + * + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php + */ + public function testExceptionInGetExportData() + { + $exception = new \Exception('Error'); + + $rowCustomizerMock = $this->getMockBuilder('Magento\CatalogImportExport\Model\Export\RowCustomizerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $loggerMock = $this->getMockBuilder('\Psr\Log\LoggerInterface')->getMock(); + + $directoryMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false); + $directoryMock->expects($this->any())->method('getParentDirectory')->will($this->returnValue('some#path')); + $directoryMock->expects($this->any())->method('isWritable')->will($this->returnValue(true)); + + $filesystemMock = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); + $filesystemMock->expects($this->once())->method('getDirectoryWrite')->will($this->returnValue($directoryMock)); + + $exportAdapter = new \Magento\ImportExport\Model\Export\Adapter\Csv($filesystemMock); + + $rowCustomizerMock->expects($this->once())->method('prepareData')->willThrowException($exception); + $loggerMock->expects($this->once())->method('critical')->with($exception); + + $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + '\Magento\Catalog\Model\Resource\Product\Collection' + ); + + /** @var \Magento\CatalogImportExport\Model\Export\Product $model */ + $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\CatalogImportExport\Model\Export\Product', + [ + 'rowCustomizer' => $rowCustomizerMock, + 'logger' => $loggerMock, + 'collection' => $collection + ] + ); + + + $data = $model->setWriter($exportAdapter)->export(); + $this->assertEmpty($data); + } } 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 2a99dede484227e4b7bc38fd9040e78573584869..ab40348430e024c0d58e42d8ee649321c0ec754b 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 @@ -79,7 +79,6 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase public function testSetUsedProductAttributeIds() { $testConfigurable = $this->_getAttributeByCode('test_configurable'); - $this->assertEmpty($this->_product->getData('_cache_instance_configurable_attributes')); $this->_model->setUsedProductAttributeIds([$testConfigurable->getId()], $this->_product); $attributes = $this->_product->getData('_cache_instance_configurable_attributes'); $this->assertArrayHasKey(0, $attributes); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..5772edc9317ae5a0a41a9d4aafd419ea982e53fa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config'); +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); +if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute + && $attribute->getId() +) { + $attribute->delete(); +} +$eavConfig->clear(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php index b083d7e52dfb293b687d837f145b54909240e617..cc615bf03ea85243af323bf7844bc3d33acac7c9 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php @@ -12,17 +12,16 @@ $installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create ['resourceName' => 'catalog_setup'] ); -/* Create simple products per each option */ -/** @var $options \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection */ -$options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - 'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection' -); -$options->setAttributeFilter($attribute->getId()); +/* Create simple products per each option value*/ + +/** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); $attributeValues = []; $productIds = []; $attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); $productIds = [10, 20]; +array_shift($options); //remove the first option which is empty foreach ($options as $option) { /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); @@ -36,13 +35,13 @@ foreach ($options as $option) { )->setWebsiteIds( [1] )->setName( - 'Configurable Option' . $option->getId() + 'Configurable Option' . $option->getLabel() )->setSku( 'simple_' . $productId )->setPrice( 10 )->setTestConfigurable( - $option->getId() + $option->getValue() )->setVisibility( \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE )->setStatus( @@ -54,7 +53,7 @@ foreach ($options as $option) { $attributeValues[] = [ 'label' => 'test', 'attribute_id' => $attribute->getId(), - 'value_index' => $option->getId(), + 'value_index' => $option->getValue(), 'is_percent' => false, 'pricing_value' => 5, ]; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php index c1b23ce233c9e09240533d5bc7ee97f90a7510da..f1e17b724c8b476f76a622837d2120b0df1c96cd 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php @@ -26,6 +26,11 @@ $product->load(1); if ($product->getId()) { $product->delete(); } +\Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get('Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data') + ->setProductPrice(1, null); + +require __DIR__ . '/configurable_attribute_rollback.php'; $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php b/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php index 48d81d30eb84119c45e440be2e57bd48b4c3c8e9..9ee9ed911f7c092aac6034ed441e5c96f2e46aa2 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php @@ -21,7 +21,7 @@ class FileResolverStub implements \Magento\Framework\Config\FileResolverInterfac 'path' => realpath(__DIR__ . '/../_files/etc'), ] ); - $paths = ['data_object.xml']; + $paths = ['service_data_attributes.xml']; return new \Magento\Framework\Config\FileIterator($readDirectory, $paths); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/etc/data_object.xml b/dev/tests/integration/testsuite/Magento/Customer/_files/etc/service_data_attributes.xml similarity index 63% rename from dev/tests/integration/testsuite/Magento/Customer/_files/etc/data_object.xml rename to dev/tests/integration/testsuite/Magento/Customer/_files/etc/service_data_attributes.xml index a75fa2a653213ade29aa7ea81e8482e752ed9210..a7ae041eda7683ce99dd168befc50c2ae5a46f3c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/etc/data_object.xml +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/etc/service_data_attributes.xml @@ -5,22 +5,22 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> - </custom_attributes> - <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="stock_item" type="Magento\CatalogInventory\Service\Data\V1\StockItem" /> - </custom_attributes> - <custom_attributes for="Magento\Catalog\Api\Data\CategoryInterface"> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\CategoryInterface"> <attribute code="category_attribute_1" type="Magento\Catalog\Api\Data\CategoryAttributeType1" /> <attribute code="category_attribute_2" type="Magento\Catalog\Api\Data\CategoryAttributeType2" /> - </custom_attributes> - <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface"> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface"> <attribute code="customer_attribute_1" type="Magento\Customer\Api\Data\CustomerAttributeType1" /> <attribute code="customer_attribute_2" type="Magento\Customer\Api\Data\CustomerAttributeType2" /> - </custom_attributes> - <custom_attributes for="Magento\Customer\Api\Data\AddressInterface"> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\AddressInterface"> <attribute code="address_attribute_1" type="Magento\Customer\Api\Data\AddressAttributeType1" /> <attribute code="address_attribute_2" type="Magento\Customer\Api\Data\AddressAttributeType2" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php index 351a0ba03144a218481d9ab798cca18989ca8db8..07fceddb6424b3bc01d7e4448833cb92a709d6c9 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php @@ -76,12 +76,24 @@ class ReaderTest extends \PHPUnit_Framework_TestCase $expectedArray = [ 'Magento\Tax\Api\Data\TaxRateInterface' => [], 'Magento\Catalog\Api\Data\Product' => [ - 'stock_item' => "Magento\CatalogInventory\Api\Data\StockItem", + 'stock_item' => [ + "type" => "Magento\CatalogInventory\Api\Data\StockItem", + "resourceRefs" => [], + ], ], 'Magento\Customer\Api\Data\CustomerInterface' => [ - 'custom_1' => "Magento\Customer\Api\Data\CustomerCustom", - 'custom_2' => "Magento\CustomerExtra\Api\Data\CustomerCustom22", - 'custom_3' => "Magento\Customer\Api\Data\CustomerCustom3", + 'custom_1' => [ + "type" => "Magento\Customer\Api\Data\CustomerCustom", + "resourceRefs" => [], + ], + 'custom_2' => [ + "type" => "Magento\CustomerExtra\Api\Data\CustomerCustom22", + "resourceRefs" => [], + ], + 'custom_3' => [ + "type" => "Magento\Customer\Api\Data\CustomerCustom3", + "resourceRefs" => [], + ], ], ]; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml index f5a3d522454ab487f461f697e6108f118f1769e9..82aa641d5e8bd9040d9fa26689d95a09b74e19f3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml @@ -5,14 +5,14 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> - </custom_attributes> - <custom_attributes for="Magento\Catalog\Api\Data\Product"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\Product"> <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItem" /> - </custom_attributes> - <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface"> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface"> <attribute code="custom_1" type="Magento\Customer\Api\Data\CustomerCustom" /> <attribute code="custom_2" type="Magento\CustomerExtra\Service\V1\Data\CustomerCustom21" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml index 629efb9b7a30a400dad479894f71d0711da8f24d..5596f61e572f4b076a1d850b38002b6a448a7297 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml @@ -5,9 +5,9 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface"> <attribute code="custom_2" type="Magento\CustomerExtra\Api\Data\CustomerCustom22" /> <attribute code="custom_3" type="Magento\Customer\Api\Data\CustomerCustom3" /> - </custom_attributes> + </extension_attributes> </config> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php index c8379e5e9c39f37bb5d4edd46ed4720aa250b788..18a4314710c3d9a19f51bf5688afcc7087b90f93 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php @@ -3,153 +3,214 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Framework\Session; - -class SessionManagerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Magento\Framework\Session\SessionManagerInterface - */ - protected $_model; - - /** - * @var \Magento\Framework\Session\SidResolverInterface - */ - protected $_sidResolver; - - /** - * @var string - */ - protected $sessionName; - - /** - * @var \Magento\Framework\App\RequestInterface - */ - protected $request; - - protected function setUp() - { - $this->sessionName = 'frontEndSession'; - - ini_set('session.use_only_cookies', '0'); - ini_set('session.name', $this->sessionName); - - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - /** @var \Magento\Framework\Session\SidResolverInterface $sidResolver */ - $this->_sidResolver = $objectManager->get('Magento\Framework\Session\SidResolverInterface'); - - $this->request = $objectManager->get('Magento\Framework\App\RequestInterface'); - - /** @var \Magento\Framework\Session\SessionManager _model */ - $this->_model = $objectManager->create( - 'Magento\Framework\Session\SessionManager', - [ - $this->request, - $this->_sidResolver, - $objectManager->get('Magento\Framework\Session\Config\ConfigInterface'), - $objectManager->get('Magento\Framework\Session\SaveHandlerInterface'), - $objectManager->get('Magento\Framework\Session\ValidatorInterface'), - $objectManager->get('Magento\Framework\Session\StorageInterface') - ] - ); - } - - public function testSessionNameFromIni() - { - $this->_model->start(); - $this->assertSame($this->sessionName, $this->_model->getName()); - $this->_model->destroy(); - } - - public function testSessionUseOnlyCookies() - { - $expectedValue = '1'; - $sessionUseOnlyCookies = ini_get('session.use_only_cookies'); - $this->assertSame($expectedValue, $sessionUseOnlyCookies); - } - - public function testGetData() - { - $this->_model->setData(['test_key' => 'test_value']); - $this->assertEquals('test_value', $this->_model->getData('test_key', true)); - $this->assertNull($this->_model->getData('test_key')); - } - - public function testGetSessionId() - { - $this->assertEquals(session_id(), $this->_model->getSessionId()); - } - - public function testGetName() - { - $this->assertEquals(session_name(), $this->_model->getName()); - } - - public function testSetName() - { - $this->_model->setName('test'); - $this->assertEquals('test', $this->_model->getName()); - } - - public function testDestroy() - { - $data = ['key' => 'value']; - $this->_model->setData($data); - - $this->assertEquals($data, $this->_model->getData()); - $this->_model->destroy(); - - $this->assertEquals([], $this->_model->getData()); - } +// @codingStandardsIgnoreStart +namespace { + $mockPHPFunctions = false; +} - public function testSetSessionId() - { - $sessionId = $this->_model->getSessionId(); - $this->_model->setSessionId($this->_sidResolver->getSid($this->_model)); - $this->assertEquals($sessionId, $this->_model->getSessionId()); - - $this->_model->setSessionId('test'); - $this->assertEquals('test', $this->_model->getSessionId()); - } +namespace Magento\Framework\Session { + // @codingStandardsIgnoreEnd /** - * @magentoConfigFixture current_store web/session/use_frontend_sid 1 + * Mock session_status if in test mode, or continue normal execution otherwise + * + * @return int Session status code */ - public function testSetSessionIdFromParam() + function session_status() { - $this->assertNotEquals('test_id', $this->_model->getSessionId()); - $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test-id'); - $this->_model->setSessionId($this->_sidResolver->getSid($this->_model)); - - $this->assertEquals('test-id', $this->_model->getSessionId()); - - /* Use not valid identifier */ - $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test_id'); - $this->_model->setSessionId($this->_sidResolver->getSid($this->_model)); - $this->assertEquals('test-id', $this->_model->getSessionId()); + global $mockPHPFunctions; + if ($mockPHPFunctions) { + return PHP_SESSION_NONE; + } + return call_user_func_array('\session_status', func_get_args()); } - public function testGetSessionIdForHost() + function headers_sent() { - $this->request->getServer()->set('HTTP_HOST', 'localhost'); - $this->_model->start(); - $this->assertEmpty($this->_model->getSessionIdForHost('localhost')); - $this->assertNotEmpty($this->_model->getSessionIdForHost('test')); - $this->_model->destroy(); + global $mockPHPFunctions; + if ($mockPHPFunctions) { + return false; + } + return call_user_func_array('\headers_sent', func_get_args()); } - public function testIsValidForHost() + class SessionManagerTest extends \PHPUnit_Framework_TestCase { - $this->request->getServer()->set('HTTP_HOST', 'localhost'); - $this->_model->start(); - - $reflection = new \ReflectionMethod($this->_model, '_addHost'); - $reflection->setAccessible(true); - $reflection->invoke($this->_model); - - $this->assertFalse($this->_model->isValidForHost('test.com')); - $this->assertTrue($this->_model->isValidForHost('localhost')); - $this->_model->destroy(); + /** + * @var \Magento\Framework\Session\SessionManagerInterface + */ + protected $_model; + + /** + * @var \Magento\Framework\Session\SidResolverInterface + */ + protected $_sidResolver; + + /** + * @var string + */ + protected $sessionName; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $objectManager; + + protected function setUp() + { + $this->sessionName = 'frontEndSession'; + + ini_set('session.use_only_cookies', '0'); + ini_set('session.name', $this->sessionName); + + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + /** @var \Magento\Framework\Session\SidResolverInterface $sidResolver */ + $this->_sidResolver = $this->objectManager->get('Magento\Framework\Session\SidResolverInterface'); + + $this->request = $this->objectManager->get('Magento\Framework\App\RequestInterface'); + + /** @var \Magento\Framework\Session\SessionManager _model */ + $this->_model = $this->objectManager->create( + 'Magento\Framework\Session\SessionManager', + [ + $this->objectManager->get('Magento\Framework\App\Request\Http'), + $this->_sidResolver, + $this->objectManager->get('Magento\Framework\Session\Config\ConfigInterface'), + $this->objectManager->get('Magento\Framework\Session\SaveHandlerInterface'), + $this->objectManager->get('Magento\Framework\Session\ValidatorInterface'), + $this->objectManager->get('Magento\Framework\Session\StorageInterface') + ] + ); + } + + public function testSessionNameFromIni() + { + $this->_model->start(); + $this->assertSame($this->sessionName, $this->_model->getName()); + $this->_model->destroy(); + } + + public function testSessionUseOnlyCookies() + { + $expectedValue = '1'; + $sessionUseOnlyCookies = ini_get('session.use_only_cookies'); + $this->assertSame($expectedValue, $sessionUseOnlyCookies); + } + + public function testGetData() + { + $this->_model->setData(['test_key' => 'test_value']); + $this->assertEquals('test_value', $this->_model->getData('test_key', true)); + $this->assertNull($this->_model->getData('test_key')); + } + + public function testGetSessionId() + { + $this->assertEquals(session_id(), $this->_model->getSessionId()); + } + + public function testGetName() + { + $this->assertEquals(session_name(), $this->_model->getName()); + } + + public function testSetName() + { + $this->_model->setName('test'); + $this->assertEquals('test', $this->_model->getName()); + } + + public function testDestroy() + { + $data = ['key' => 'value']; + $this->_model->setData($data); + + $this->assertEquals($data, $this->_model->getData()); + $this->_model->destroy(); + + $this->assertEquals([], $this->_model->getData()); + } + + public function testSetSessionId() + { + $sessionId = $this->_model->getSessionId(); + $this->_model->setSessionId($this->_sidResolver->getSid($this->_model)); + $this->assertEquals($sessionId, $this->_model->getSessionId()); + + $this->_model->setSessionId('test'); + $this->assertEquals('test', $this->_model->getSessionId()); + } + + /** + * @magentoConfigFixture current_store web/session/use_frontend_sid 1 + */ + public function testSetSessionIdFromParam() + { + $this->assertNotEquals('test_id', $this->_model->getSessionId()); + $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test-id'); + $this->_model->setSessionId($this->_sidResolver->getSid($this->_model)); + $this->assertEquals('test-id', $this->_model->getSessionId()); + /* Use not valid identifier */ + $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test_id'); + $this->_model->setSessionId($this->_sidResolver->getSid($this->_model)); + $this->assertEquals('test-id', $this->_model->getSessionId()); + } + + public function testGetSessionIdForHost() + { + $_SERVER['HTTP_HOST'] = 'localhost'; + $this->_model->start(); + $this->assertEmpty($this->_model->getSessionIdForHost('localhost')); + $this->assertNotEmpty($this->_model->getSessionIdForHost('test')); + $this->_model->destroy(); + } + + public function testIsValidForHost() + { + $_SERVER['HTTP_HOST'] = 'localhost'; + $this->_model->start(); + + $reflection = new \ReflectionMethod($this->_model, '_addHost'); + $reflection->setAccessible(true); + $reflection->invoke($this->_model); + + $this->assertFalse($this->_model->isValidForHost('test.com')); + $this->assertTrue($this->_model->isValidForHost('localhost')); + $this->_model->destroy(); + } + + + /** + * @expectedException \Magento\Framework\Exception\SessionException + * @expectedExceptionMessage Area code not set: Area code must be set before starting a session. + */ + public function testStartAreaNotSet() + { + $scope = $this->objectManager->get('Magento\Framework\Config\ScopeInterface'); + $appState = new \Magento\Framework\App\State($scope); + + /** + * Must be created by "new" in order to get a real Magento\Framework\App\State object that + * is not overridden in the TestFramework + * + * @var \Magento\Framework\Session\SessionManager _model + */ + $this->_model = new \Magento\Framework\Session\SessionManager( + $this->objectManager->get('Magento\Framework\App\Request\Http'), + $this->_sidResolver, + $this->objectManager->get('Magento\Framework\Session\Config\ConfigInterface'), + $this->objectManager->get('Magento\Framework\Session\SaveHandlerInterface'), + $this->objectManager->get('Magento\Framework\Session\ValidatorInterface'), + $this->objectManager->get('Magento\Framework\Session\StorageInterface'), + $this->objectManager->get('Magento\Framework\Stdlib\CookieManagerInterface'), + $this->objectManager->get('Magento\Framework\Stdlib\Cookie\CookieMetadataFactory'), + $appState + ); + + global $mockPHPFunctions; + $mockPHPFunctions = true; + $this->_model->start(); + } } } diff --git a/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php b/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php index 14f306766a10720ac021fc9ce9b94ecff8ee08cd..f83337a307eb201a8d5fd2fc8ef9dc8d6005880a 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Payment\Model; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** * @magentoAppArea adminhtml */ @@ -68,7 +70,7 @@ class ObserverTest extends \PHPUnit_Framework_TestCase $config->saveConfig( 'payment/checkmo/order_status', $statusCode, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0 ); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php index 1313db5e278a098d9687b95ed80501862295b0ec..0a35f0488948b5d142693ee2a423f600eab01e7b 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php @@ -7,6 +7,7 @@ namespace Magento\Sales\Controller\Adminhtml\Order; /** * @magentoAppArea adminhtml + * @magentoDbIsolation enabled */ class CreateTest extends \Magento\Backend\Utility\Controller { diff --git a/dev/tests/integration/testsuite/Magento/Tax/Block/Adminhtml/Rate/FormTest.php b/dev/tests/integration/testsuite/Magento/Tax/Block/Adminhtml/Rate/FormTest.php deleted file mode 100644 index 0bc80a86389f29b09fc9d4b80b001347662d97f8..0000000000000000000000000000000000000000 --- a/dev/tests/integration/testsuite/Magento/Tax/Block/Adminhtml/Rate/FormTest.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Tax\Block\Adminhtml\Rate; - -use Magento\TestFramework\Helper\Bootstrap; - -class FormTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Magento\Framework\ObjectManagerInterface - */ - protected $_objectManager; - - /** @var \Magento\Tax\Block\Adminhtml\Rate\Form */ - protected $_block; - - protected function setUp() - { - $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->_block = $this->_objectManager->create( - 'Magento\Tax\Block\Adminhtml\Rate\Form' - ); - } - - public function testGetRateCollection() - { - /** @var \Magento\Tax\Model\Resource\Calculation\Rate\Collection $collection */ - $collection = Bootstrap::getObjectManager()->get('Magento\Tax\Model\Resource\Calculation\Rate\Collection'); - $dbTaxRatesQty = $collection->count(); - if (($dbTaxRatesQty == 0) || ($collection->getFirstItem()->getId() != 1)) { - $this->fail("Preconditions failed."); - } - - $ratesCollection = $this->_block->getRateCollection(); - - $collectionTaxRatesQty = count($ratesCollection); - $this->assertEquals($dbTaxRatesQty, $collectionTaxRatesQty, 'Tax rates quantity is invalid.'); - $taxRate = $ratesCollection[0]; - $expectedTaxRateData = [ - 'tax_calculation_rate_id' => '1', - 'code' => 'US-CA-*-Rate 1', - 'tax_country_id' => 'US', - 'tax_region_id' => '12', - 'region_name' => 'CA', - 'tax_postcode' => '*', - 'rate' => '8.25', - 'zip_is_range' => null, - 'zip_from' => null, - 'zip_to' => null, - 'rate' => '8.25', - ]; - $this->assertEquals($taxRate, $expectedTaxRateData, 'Tax rate data is invalid.'); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php b/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php index b6e6a11859231e98d4f303fcec041ff6f690ad38..ab5c6cd2ba0d57b05bd90e250dd4550a254010e1 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php @@ -44,6 +44,11 @@ class RateTest extends \Magento\Backend\Utility\Controller $this->assertEquals($expectedData['tax_postcode'], $rate->getTaxPostcode()); } + /** + * Data provider for testAjaxSaveAction + * + * @return array + */ public function ajaxSaveActionDataProvider() { $postData = ['rate' => '10', 'tax_country_id' => 'US', 'tax_region_id' => '1']; @@ -193,4 +198,102 @@ class RateTest extends \Magento\Backend\Utility\Controller ] ]; } + + /** + * @dataProvider ajaxSaveActionDataProvider + * @magentoDbIsolation enabled + * + * @param array $rateClassData + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function testAjaxLoadAction($rateClassData) + { + /** @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory $rateClassFactory */ + $rateClassFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Tax\Api\Data\TaxRateInterfaceFactory' + ); + + $rateClass = $rateClassFactory->create(); + $rateClass->setRate($rateClassData['rate']) + ->setTaxCountryId($rateClassData['tax_country_id']) + ->setTaxRegionId($rateClassData['tax_region_id']) + ->setCode($rateClassData['code']) + ->setZipFrom($rateClassData['zip_from']) + ->setZipIsRange($rateClassData['zip_is_range']) + ->setZipFrom($rateClassData['zip_from']) + ->setZipTo($rateClassData['zip_to']) + ->setTaxPostcode($rateClassData['tax_postcode']); + + $rateClass->save($rateClass); + + $rateClassId=$rateClass->getTaxCalculationRateId(); + /** @var $class \Magento\Tax\Model\Calculation\Rate */ + $class = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Tax\Model\Calculation\Rate') + ->load($rateClassId, 'tax_calculation_rate_id'); + + $this->assertEquals($rateClassData['tax_country_id'], $class->getTaxCountryId()); + $this->assertEquals($rateClassData['tax_region_id'], $class->getTaxRegionId()); + $this->assertEquals($rateClassData['code'], $class->getCode()); + $this->assertEquals($rateClassData['rate'], $class->getRate()); + $this->assertEquals($rateClassData['zip_is_range']==1 ? 1 : 0, $class->getZipIsRange() ? 1 : 0); + if ($rateClassData['zip_is_range']=='1') { + $this->assertEquals($rateClassData['zip_from'], $class->getZipFrom()); + $this->assertEquals($rateClassData['zip_to'], $class->getZipTo()); + } + + $postData = [ 'id' => $rateClassId ]; + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/tax/rate/ajaxLoad'); + $jsonBody = $this->getResponse()->getBody(); + + $result = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Framework\Json\Helper\Data' + )->jsonDecode( + $jsonBody + ); + + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey('success', $result); + $this->assertTrue($result['success'] == true); + $this->assertArrayHasKey('result', $result); + $this->assertTrue(is_array($result['result'])); + $this->assertEquals($result['result']['tax_country_id'], $class->getTaxCountryId()); + $this->assertEquals($result['result']['tax_region_id'], $class->getTaxRegionId()); + $this->assertEquals($result['result']['tax_postcode'], $class->getTaxPostcode()); + $this->assertEquals($result['result']['code'], $class->getCode()); + $this->assertEquals($result['result']['rate'], $class->getRate()); + + $expectedZipIsRange=$result['result']['zip_is_range'] == 1 ? 1 : 0; + $this->assertEquals($expectedZipIsRange, $class->getZipIsRange() ? 1 : 0); + if ($expectedZipIsRange) { + $this->assertEquals($result['result']['zip_from'], $class->getZipFrom()); + $this->assertEquals($result['result']['zip_to'], $class->getZipTo()); + } + } + + /** + * @magentoDbIsolation enabled + * + */ + public function testAjaxNonLoadAction() + { + $postData = [ 'id' => 99999999 ]; + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/tax/rate/ajaxLoad'); + $jsonBody = $this->getResponse()->getBody(); + + $result = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Framework\Json\Helper\Data' + )->jsonDecode( + $jsonBody + ); + + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey('success', $result); + $this->assertTrue($result['success'] == false); + $this->assertTrue(!array_key_exists('result', $result)); + $this->assertArrayHasKey('error_message', $result); + $this->assertTrue(strlen($result['error_message'])>0); + } } diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php index 1e9c43ca7fd49621116be74791c4a180c2c4763d..2b0d311757846900c2a9b1f9ccffb29cd221d4c1 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php @@ -9,6 +9,7 @@ namespace Magento\Tax\Model\Calculation; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Tax\Api\Data\TaxRateInterface; +use Magento\Tax\Model\Calculation\Rate; use Magento\Tax\Model\TaxRuleFixtureFactory; use Magento\TestFramework\Helper\Bootstrap; @@ -566,14 +567,14 @@ class RateRepositoryTest extends \PHPUnit_Framework_TestCase return [ 'eq' => [ - [$filterBuilder->setField(TaxRateInterface::KEY_REGION_ID)->setValue(42)->create()], + [$filterBuilder->setField(Rate::KEY_REGION_ID)->setValue(42)->create()], null, ['US - 42 - 7.5', 'US - 42 - 22'], ], 'and' => [ [ - $filterBuilder->setField(TaxRateInterface::KEY_REGION_ID)->setValue(42)->create(), - $filterBuilder->setField(TaxRateInterface::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(), + $filterBuilder->setField(Rate::KEY_REGION_ID)->setValue(42)->create(), + $filterBuilder->setField(Rate::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(), ], [], ['US - 42 - 22'], @@ -581,15 +582,14 @@ class RateRepositoryTest extends \PHPUnit_Framework_TestCase 'or' => [ [], [ - $filterBuilder->setField(TaxRateInterface::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(), - $filterBuilder->setField(TaxRateInterface::KEY_PERCENTAGE_RATE)->setValue(10.0)->create(), + $filterBuilder->setField(Rate::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(), + $filterBuilder->setField(Rate::KEY_PERCENTAGE_RATE)->setValue(10.0)->create(), ], ['US - 42 - 22', 'US - 12 - 10'], ], 'like' => [ [ - $filterBuilder->setField(TaxRateInterface::KEY_CODE)->setValue('%7.5')->setConditionType('like') - ->create(), + $filterBuilder->setField(Rate::KEY_CODE)->setValue('%7.5')->setConditionType('like')->create(), ], [], ['US - 42 - 7.5', 'US - 12 - 7.5'], diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php index 861079efbac7df145af34557037fa6b79bb886e0..331bfebab31ad4078259e912e982010cb44302e6 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php @@ -8,6 +8,7 @@ namespace Magento\Tax\Model\Sales\Total\Quote; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Tax\Model\Config; use Magento\Tax\Model\Calculation; @@ -221,7 +222,7 @@ class SetupUtil $config->saveConfig( $path, $value, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0 ); } diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php index 0c3af306bdeaa8f70d4a0b9e3e0d4bda252693e1..4f8e5da64ba2d3f77397905e9a470127626a84a6 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php @@ -5,8 +5,8 @@ */ namespace Magento\Tax\Model; -use Magento\Framework\Model\AbstractExtensibleModel; use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Model\TaxClass\Key; use Magento\TestFramework\Helper\Bootstrap; /** @@ -121,8 +121,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase 'quantity' => 2, 'unit_price' => 10, 'tax_class_key' => [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, - TaxClassKeyInterface::KEY_VALUE => 'DefaultProductClass', + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, + Key::KEY_VALUE => 'DefaultProductClass', ], ]; $oneProductResults = [ @@ -649,8 +649,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase ], ], 'customer_tax_class_key' => [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, - TaxClassKeyInterface::KEY_VALUE => 'DefaultCustomerClass', + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, + Key::KEY_VALUE => 'DefaultCustomerClass', ], ], 'expected_tax_details' => [ @@ -1064,8 +1064,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase 'quantity' => 9, 'unit_price' => 0.33, // this is including the store tax of 10%. Pre tax is 0.3 'tax_class_key' => [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, - TaxClassKeyInterface::KEY_VALUE => 'HigherProductClass', + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, + Key::KEY_VALUE => 'HigherProductClass', ], 'tax_included' => true, ]; @@ -1817,8 +1817,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase && is_string($value) ) { $value = [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, - TaxClassKeyInterface::KEY_VALUE => $this->taxClassIds[$value], + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, + Key::KEY_VALUE => $this->taxClassIds[$value], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php index 606540aa09c90b91c3b41a2c6345447a247bfd38..853a42f225c85c999748b0763388cc58d48a83e9 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php @@ -8,6 +8,7 @@ namespace Magento\Tax\Model\TaxClass; use Magento\Tax\Api\Data\TaxClassInterfaceFactory; use Magento\Tax\Api\Data\TaxClassKeyInterface; use Magento\Tax\Api\TaxClassManagementInterface; +use Magento\Tax\Model\TaxClass\Key; use Magento\TestFramework\Helper\Bootstrap; class ManagementTest extends \PHPUnit_Framework_TestCase @@ -63,8 +64,8 @@ class ManagementTest extends \PHPUnit_Framework_TestCase $this->dataObjectHelper->populateWithArray( $taxClassKeyTypeId, [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, - TaxClassKeyInterface::KEY_VALUE => $taxClassId, + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID, + Key::KEY_VALUE => $taxClassId, ], '\Magento\Tax\Api\Data\TaxClassKeyInterface' ); @@ -76,8 +77,8 @@ class ManagementTest extends \PHPUnit_Framework_TestCase $this->dataObjectHelper->populateWithArray( $taxClassKeyTypeName, [ - TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, - TaxClassKeyInterface::KEY_VALUE => $taxClassName, + Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME, + Key::KEY_VALUE => $taxClassName, ], '\Magento\Tax\Api\Data\TaxClassKeyInterface' ); diff --git a/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml b/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml index 911f2482ebcec61f1d6bf914463511ffd1252359..0a0b04ba6836b74eb5d7584a9290136e540f8d8b 100644 --- a/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml +++ b/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml @@ -29,6 +29,8 @@ <!-- The price rule condition: minimum products amount in shopping cart for price rule to be applied --> <customers>20</customers> <!-- Number of customers to generate --> + <orders>80</orders> + <!-- Orders count --> <configs> <!-- Config variables and values for change --> <config> <path>admin/security/use_form_key</path> @@ -79,5 +81,39 @@ <value>8080</value> </config> </configs> + <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) --> + <indexer> + <id>catalog_category_product</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_category</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_price</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_attribute</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>cataloginventory_stock</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_rule</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_product</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogsearch_fulltext</id> + <set_scheduled>false</set_scheduled> + </indexer> + </indexers> </profile> -</config> \ No newline at end of file +</config> diff --git a/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php b/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php index 00cd8ce5cb1d148ff81ebcad4ad463fd1933df2f..056fe99b815516c7bb1b20181297a78013312268 100644 --- a/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php +++ b/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php @@ -3,6 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Framework\App\Config\ScopeConfigInterface; /** @var \Magento\TestFramework\Application $this */ @@ -13,7 +14,7 @@ $configData = $this->getObjectManager()->create('Magento\Framework\App\Config\Va $configData->setPath( 'catalog/frontend/flat_catalog_category' )->setScope( - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT )->setScopeId( 0 )->setValue( diff --git a/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php b/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php index 2258e72023d4ad642e1e799134e8b32cbb94d1ab..e06ed0e26c053f2742d6a132f17b4a0f4b8558ce 100644 --- a/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php +++ b/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php @@ -3,6 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Framework\App\Config\ScopeConfigInterface; /** @var \Magento\TestFramework\Application $this */ @@ -13,7 +14,7 @@ $configData = $this->getObjectManager()->create('Magento\Framework\App\Config\Va $configData->setPath( 'catalog/frontend/flat_catalog_product' )->setScope( - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT )->setScopeId( 0 )->setValue( diff --git a/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php b/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php index e0f50505c4adcf397a358a5d8d31e336bd00a3e6..15b445ce1f8b5ca75cf4b6eb1ae20a179bcfa356 100644 --- a/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php +++ b/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php @@ -3,6 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Framework\App\Config\ScopeConfigInterface; /** @var \Magento\TestFramework\Application $this */ @@ -13,7 +14,7 @@ $configData = $this->getObjectManager()->create('Magento\Framework\App\Config\Va $configData->setPath( 'carriers/flatrate/active' )->setScope( - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT )->setScopeId( 0 )->setValue( diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt index 2fd6ad123621cb9b830d5d02b3840463d4520fbc..564d756255d7e6b7be5db625ccf8ebb0e634a026 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt @@ -35,5 +35,6 @@ lib/internal/Magento/Framework/System lib/internal/Magento/Framework/Test lib/internal/Magento/Framework/App/Utility lib/internal/Magento/Framework/Url +lib/internal/Magento/Framework/UrlInterface lib/internal/Magento/Framework/Xml lib/internal/Magento/Framework diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php index a3fa40a7f43a6718bc2949067c23dd314b874c11..b0e6f2627029a8da911b3e6a4358c3aff4f133af 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php @@ -702,4 +702,26 @@ return [ 'Use \Magento\Eav\Model\Entity\Type::getDefaultAttributeSetId() method instead', ], ['CONFIG_PATH_WSDL_CACHE_ENABLED', 'Magento\Webapi\Model\Soap\Server'], + ['ENTITY', 'Magento\Framework\App\Config\ValueInterface'], + ['XML_PATH_ALLOW_CURRENCIES_INSTALLED', 'Magento\Framework\Locale\CurrencyInterface'], + [ + 'DEFAULT_CURRENCY', + 'Magento\Framework\Locale\CurrencyInterface', + 'Magento\Framework\Locale\Currency::DEFAULT_CURRENCY' + ], + [ + 'DEFAULT_LOCALE', + 'Magento\Framework\Locale\ResolverInterface', + 'Magento\Framework\Locale\Resolver::DEFAULT_LOCALE' + ], + [ + 'DEFAULT_GROUP', + 'Magento\Framework\Message\ManagerInterface', + 'Magento\Framework\Message\Manager::DEFAULT_GROUP' + ], + [ + 'SCOPE_DEFAULT', + 'Magento\Framework\App\ScopeInterface', + 'Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT' + ] ]; diff --git a/dev/tests/unit/framework/bootstrap.php b/dev/tests/unit/framework/bootstrap.php index b30486b50a17dc3a6e0b67adaa072868b1747736..45ae70bc21fbb39fc79b347cc4749a6243d18d7f 100755 --- a/dev/tests/unit/framework/bootstrap.php +++ b/dev/tests/unit/framework/bootstrap.php @@ -6,6 +6,11 @@ require_once __DIR__ . '/../../../../app/autoload.php'; +$updateAppBootstrap = __DIR__ . '/../../../../update/app/bootstrap.php'; +if (file_exists($updateAppBootstrap)) { + require_once $updateAppBootstrap; +} + if (!defined('TESTS_TEMP_DIR')) { define('TESTS_TEMP_DIR', dirname(__DIR__) . '/tmp'); } diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index 5c785e252e48aa8c82aa8bcfe817c761b38ecc23..4c5587187da794847e7e0e6c47157106193a14fd 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -17,6 +17,7 @@ <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory> <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory> <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory> + <directory suffix="Test.php">../../../update/dev/tests/unit/testsuite</directory> </testsuite> <php> <ini name="date.timezone" value="America/Los_Angeles"/> @@ -26,6 +27,7 @@ <directory suffix=".php">../../../app/code/*</directory> <directory suffix=".php">../../../lib/internal/Magento</directory> <directory suffix=".php">../../../setup/src/*</directory> + <directory suffix=".php">../../../update/app/code/*</directory> <exclude> <directory>../../../app/code/*/*/Test</directory> <directory>../../../lib/internal/*/*/Test</directory> diff --git a/dev/tools/performance-toolkit/fixtures/indexers_states_apply.php b/dev/tools/performance-toolkit/fixtures/indexers_states_apply.php new file mode 100644 index 0000000000000000000000000000000000000000..8b072e2b68995399e051ab77cf868efb15638630 --- /dev/null +++ b/dev/tools/performance-toolkit/fixtures/indexers_states_apply.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Class IndexersStatesApplyFixture + */ +class IndexersStatesApplyFixture extends \Magento\ToolkitFramework\Fixture +{ + /** + * @var int + */ + protected $priority = 170; + + /** + * {@inheritdoc} + */ + public function execute() + { + $indexers = \Magento\ToolkitFramework\Config::getInstance()->getValue('indexers', []); + if (!isset($indexers["indexer"]) || empty($indexers["indexer"])) { + return; + } + $this->application->resetObjectManager(); + foreach ($indexers["indexer"] as $indexer) { + $this->application->indexersStates[$indexer['id']] = ($indexer['set_scheduled'] == "true"); + } + $this->application->getObjectManager()->get('Magento\Framework\App\CacheInterface') + ->clean([\Magento\Framework\App\Config::CACHE_TAG]); + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Indexers Mode Changes'; + } + + /** + * {@inheritdoc} + */ + public function introduceParamLabels() + { + return []; + } +} + +return new IndexersStatesApplyFixture($this); diff --git a/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php b/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php index 083ac9fefbc6f619d4e0f2fecc1c007e23ae1cb5..d1df2a830d5d1b7a1df8785c278e3147ffe8d28a 100644 --- a/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php +++ b/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php @@ -69,6 +69,13 @@ class Application */ protected $_initArguments; + /** + * Indexers states values + * + * @var array + */ + public $indexersStates; + /** * @param string $applicationBaseDir * @param \Magento\Framework\Shell $shell diff --git a/dev/tools/performance-toolkit/generate.php b/dev/tools/performance-toolkit/generate.php index 861abee07ef81a10ceee9e80fc2aa9ef91ccb20d..3eae0197757f726c3e509d244dcf0c9ea88aab66 100644 --- a/dev/tools/performance-toolkit/generate.php +++ b/dev/tools/performance-toolkit/generate.php @@ -11,7 +11,8 @@ try { $shell = new Zend_Console_Getopt( [ - 'profile-s' => 'Profile configuration file', + 'profile=s' => 'Profile configuration file', + 'skip-reindex-i' => 'Skip reindex (Default - 0)', ] ); @@ -46,10 +47,10 @@ try { $indexerListIds = $config->getIndexers(); /** @var $indexerRegistry \Magento\Indexer\Model\IndexerRegistry */ $indexerRegistry = $application->getObjectManager()->create('Magento\Indexer\Model\IndexerRegistry'); - $indexersState = []; + $application->indexerStates = []; foreach ($indexerListIds as $key => $indexerId) { $indexer = $indexerRegistry->get($indexerId['indexer_id']); - $indexersState[$indexerId['indexer_id']] = $indexer->isScheduled(); + $application->indexersStates[$indexerId['indexer_id']] = $indexer->isScheduled(); $indexer->setScheduled(true); } @@ -65,10 +66,12 @@ try { foreach ($indexerListIds as $indexerId) { /** @var $indexer \Magento\Indexer\Model\Indexer */ $indexer = $indexerRegistry->get($indexerId['indexer_id']); - $indexer->setScheduled($indexersState[$indexerId['indexer_id']]); + $indexer->setScheduled($application->indexersStates[$indexerId['indexer_id']]); } - $application->reindex(); + if (!\Magento\ToolkitFramework\Helper\Cli::getOption('skip-reindex')) { + $application->reindex(); + } $totalEndTime = microtime(true); $totalResultTime = $totalEndTime - $totalStartTime; diff --git a/dev/tools/performance-toolkit/profiles/ce/extra_large.xml b/dev/tools/performance-toolkit/profiles/ce/extra_large.xml index 758fbef97f8730233c826ba009a5e24424b663fa..9d5767fbfba33f62963bed898213dc28ae30d95c 100644 --- a/dev/tools/performance-toolkit/profiles/ce/extra_large.xml +++ b/dev/tools/performance-toolkit/profiles/ce/extra_large.xml @@ -34,5 +34,39 @@ <value>1</value> </config> </configs> + <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) --> + <indexer> + <id>catalog_category_product</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_category</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_price</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_attribute</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>cataloginventory_stock</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_rule</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_product</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalogsearch_fulltext</id> + <set_scheduled>true</set_scheduled> + </indexer> + </indexers> </profile> </config> diff --git a/dev/tools/performance-toolkit/profiles/ce/large.xml b/dev/tools/performance-toolkit/profiles/ce/large.xml index f89fa10faddc16f5e05f7be400b71f9ff94a8f1d..bdbce2f2b8b7ca464606e3b5b351cc658f5ce624 100644 --- a/dev/tools/performance-toolkit/profiles/ce/large.xml +++ b/dev/tools/performance-toolkit/profiles/ce/large.xml @@ -34,5 +34,39 @@ <value>1</value> </config> </configs> + <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) --> + <indexer> + <id>catalog_category_product</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_category</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_price</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_attribute</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>cataloginventory_stock</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_rule</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_product</id> + <set_scheduled>true</set_scheduled> + </indexer> + <indexer> + <id>catalogsearch_fulltext</id> + <set_scheduled>true</set_scheduled> + </indexer> + </indexers> </profile> </config> diff --git a/dev/tools/performance-toolkit/profiles/ce/medium.xml b/dev/tools/performance-toolkit/profiles/ce/medium.xml index fbcb4168bce8dbe2eb1b4f2bbe90a63c29bee4dd..8c91400d50bd697ab080985541bf35c760067642 100644 --- a/dev/tools/performance-toolkit/profiles/ce/medium.xml +++ b/dev/tools/performance-toolkit/profiles/ce/medium.xml @@ -34,5 +34,39 @@ <value>1</value> </config> </configs> + <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) --> + <indexer> + <id>catalog_category_product</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_category</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_price</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_attribute</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>cataloginventory_stock</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_rule</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_product</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogsearch_fulltext</id> + <set_scheduled>false</set_scheduled> + </indexer> + </indexers> </profile> </config> diff --git a/dev/tools/performance-toolkit/profiles/ce/small.xml b/dev/tools/performance-toolkit/profiles/ce/small.xml index 6507aa52f618843fac56a130461696f4342ac99f..79f2605bc775d7c3fdb0fe92106df966c6330c0c 100644 --- a/dev/tools/performance-toolkit/profiles/ce/small.xml +++ b/dev/tools/performance-toolkit/profiles/ce/small.xml @@ -34,5 +34,39 @@ <value>1</value> </config> </configs> + <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) --> + <indexer> + <id>catalog_category_product</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_category</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_price</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalog_product_attribute</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>cataloginventory_stock</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_rule</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogrule_product</id> + <set_scheduled>false</set_scheduled> + </indexer> + <indexer> + <id>catalogsearch_fulltext</id> + <set_scheduled>false</set_scheduled> + </indexer> + </indexers> </profile> </config> diff --git a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php index 98f5dc5d5e07250fe7b427ca68286cb99c22ee35..aa91250b41fb3ce83f95c8d3df94e144c79a9bfb 100644 --- a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php +++ b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php @@ -8,6 +8,7 @@ namespace Magento\Framework\Api\Code\Generator; use Magento\Framework\Code\Generator\DefinedClasses; use Magento\Framework\Code\Generator\Io; use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Framework\Api\Config\Converter; /** * Code generator for data object extensions. @@ -79,14 +80,15 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent protected function _getClassMethods() { $methods = []; - foreach ($this->getCustomAttributes() as $attributeName => $attributeType) { + foreach ($this->getCustomAttributes() as $attributeName => $attributeMetadata) { + $attributeType = $attributeMetadata[Converter::DATA_TYPE]; $propertyName = SimpleDataObjectConverter::snakeCaseToCamelCase($attributeName); $getterName = 'get' . ucfirst($propertyName); $setterName = 'set' . ucfirst($propertyName); $methods[] = [ 'name' => $getterName, 'body' => "return \$this->_get('{$attributeName}');", - 'docblock' => ['tags' => [['name' => 'return', 'description' => $attributeType]]], + 'docblock' => ['tags' => [['name' => 'return', 'description' => $attributeType . '|null']]], ]; $methods[] = [ 'name' => $setterName, @@ -150,11 +152,12 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent } $dataInterface = ltrim($this->getSourceClassName(), '\\'); if (isset($this->allCustomAttributes[$dataInterface])) { - foreach ($this->allCustomAttributes[$dataInterface] as $attributeName => $attributeType) { + foreach ($this->allCustomAttributes[$dataInterface] as $attributeName => $attributeMetadata) { + $attributeType = $attributeMetadata[Converter::DATA_TYPE]; if (strpos($attributeType, '\\') !== false) { /** Add preceding slash to class names, while leaving primitive types as is */ $attributeType = $this->_getFullyQualifiedClassName($attributeType); - $this->allCustomAttributes[$dataInterface][$attributeName] = + $this->allCustomAttributes[$dataInterface][$attributeName][Converter::DATA_TYPE] = $this->_getFullyQualifiedClassName($attributeType); } } diff --git a/lib/internal/Magento/Framework/Api/Config/Converter.php b/lib/internal/Magento/Framework/Api/Config/Converter.php index f21db3bbd9b224b1c29eaa36be3f6043dcdf4ae9..68914a98b5c77ab1bc452c90fc26dfbee64797d8 100644 --- a/lib/internal/Magento/Framework/Api/Config/Converter.php +++ b/lib/internal/Magento/Framework/Api/Config/Converter.php @@ -7,6 +7,9 @@ namespace Magento\Framework\Api\Config; class Converter implements \Magento\Framework\Config\ConverterInterface { + const RESOURCE_PERMISSIONS = "resourceRefs"; + const DATA_TYPE = "type"; + /** * Convert dom node tree to array * @@ -21,7 +24,7 @@ class Converter implements \Magento\Framework\Config\ConverterInterface } /** @var \DOMNodeList $types */ - $types = $source->getElementsByTagName('custom_attributes'); + $types = $source->getElementsByTagName('extension_attributes'); /** @var \DOMNode $type */ foreach ($types as $type) { $typeConfig = []; @@ -32,9 +35,22 @@ class Converter implements \Magento\Framework\Config\ConverterInterface $code = $attribute->getAttribute('code'); $codeType = $attribute->getAttribute('type'); - if ($code && $codeType) { - $typeConfig[$code] = $codeType; + $resourcesElement = $attribute->getElementsByTagName('resources')->item(0); + $resourceRefs = []; + if ($resourcesElement && $resourcesElement->nodeType === XML_ELEMENT_NODE) { + $singleResourceElements = $resourcesElement->getElementsByTagName('resource'); + foreach ($singleResourceElements as $element) { + if ($element->nodeType != XML_ELEMENT_NODE) { + continue; + } + $resourceRefs[] = $element->attributes->getNamedItem('ref')->nodeValue; + } } + + $typeConfig[$code] = [ + self::DATA_TYPE => $codeType, + self::RESOURCE_PERMISSIONS => $resourceRefs, + ]; } $output[$typeName] = $typeConfig; diff --git a/lib/internal/Magento/Framework/Api/Config/Reader.php b/lib/internal/Magento/Framework/Api/Config/Reader.php index 8cd1a8ac3293568553c30f0e126ab1ee61b78a38..6bc97c71c11563cfb46efeaaedf2ecd03fd3bf83 100644 --- a/lib/internal/Magento/Framework/Api/Config/Reader.php +++ b/lib/internal/Magento/Framework/Api/Config/Reader.php @@ -13,8 +13,8 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem * @var array */ protected $_idAttributes = [ - '/config/custom_attributes' => 'for', - '/config/custom_attributes/attribute' => 'code', + '/config/extension_attributes' => 'for', + '/config/extension_attributes/attribute' => 'code', ]; /** @@ -32,7 +32,7 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem \Magento\Framework\Api\Config\Converter $converter, \Magento\Framework\Api\Config\SchemaLocator $schemaLocator, \Magento\Framework\Config\ValidationStateInterface $validationState, - $fileName = 'data_object.xml', + $fileName = 'service_data_attributes.xml', $idAttributes = [], $domDocumentClass = 'Magento\Framework\Config\Dom', $defaultScope = 'global' diff --git a/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php index 86662ab2528136ecc4c8182d650a55b9e80f0a2f..1e8334c643def347b9392eba52d624b5fdaab556 100644 --- a/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php +++ b/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php @@ -16,7 +16,7 @@ class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface */ public function getSchema() { - return realpath(__DIR__ . '/../etc/data_object.xsd'); + return realpath(__DIR__ . '/../etc/service_data_attributes.xsd'); } /** diff --git a/lib/internal/Magento/Framework/Api/DataObjectHelper.php b/lib/internal/Magento/Framework/Api/DataObjectHelper.php index d0a22a194be444bbc4b71f5d60dd6bf29fd1de42..fc3a3b0920c60430f28de95db7cd6a6ab84f6868 100644 --- a/lib/internal/Magento/Framework/Api/DataObjectHelper.php +++ b/lib/internal/Magento/Framework/Api/DataObjectHelper.php @@ -6,6 +6,8 @@ namespace Magento\Framework\Api; +use Magento\Framework\Reflection\MethodsMap; + class DataObjectHelper { /** @@ -28,22 +30,30 @@ class DataObjectHelper */ protected $extensionFactory; + /** + * @var MethodsMap + */ + protected $methodsMapProcessor; + /** * @param ObjectFactory $objectFactory * @param \Magento\Framework\Reflection\DataObjectProcessor $objectProcessor * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + * @param MethodsMap $methodsMapProcessor */ public function __construct( ObjectFactory $objectFactory, \Magento\Framework\Reflection\DataObjectProcessor $objectProcessor, \Magento\Framework\Reflection\TypeProcessor $typeProcessor, - \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, + MethodsMap $methodsMapProcessor ) { $this->objectFactory = $objectFactory; $this->objectProcessor = $objectProcessor; $this->typeProcessor = $typeProcessor; $this->extensionFactory = $extensionFactory; + $this->methodsMapProcessor = $methodsMapProcessor; } /** @@ -128,7 +138,7 @@ class DataObjectHelper if ($interfaceName == null) { $interfaceName = get_class($dataObject); } - $returnType = $this->objectProcessor->getMethodReturnType($interfaceName, $getterMethodName); + $returnType = $this->methodsMapProcessor->getMethodReturnType($interfaceName, $getterMethodName); if ($this->typeProcessor->isTypeSimple($returnType)) { $dataObject->$methodName($value); return $this; diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php index 5207a20588c73dcbf83aa767b907f585db8bb114..efdeb4ae914348fa51302891271d018b06c70067 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php @@ -6,6 +6,8 @@ // @codingStandardsIgnoreFile namespace Magento\Framework\Api\Test\Unit\Code\Generator; +use Magento\Framework\Api\Config\Converter; + class ExtensionAttributesGeneratorTest extends \PHPUnit_Framework_TestCase { /** @@ -44,11 +46,21 @@ class ExtensionAttributesGeneratorTest extends \PHPUnit_Framework_TestCase ->willReturn( [ 'Magento\Catalog\Api\Data\ProductInterface' => [ - 'string_attribute' => 'string', - 'complex_object_attribute' => '\Magento\Bundle\Api\Data\OptionInterface[]' + 'string_attribute' => [ + Converter::DATA_TYPE => 'string', + Converter::RESOURCE_PERMISSIONS => [], + + ], + 'complex_object_attribute' => [ + Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\OptionInterface[]', + Converter::RESOURCE_PERMISSIONS => [], + ], ], 'Magento\Catalog\Api\Data\Product' => [ - 'should_not_include' => 'string', + 'should_not_include' => [ + Converter::DATA_TYPE => 'string', + Converter::RESOURCE_PERMISSIONS => [], + ], ], ] ); diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php index 8f80baf39beeeec1955800e7ea4a51ac12d02382..33d4ecf9e7906c2abd4bfe15ef429c539405b059 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php @@ -6,6 +6,8 @@ // @codingStandardsIgnoreFile namespace Magento\Framework\Api\Test\Unit\Code\Generator; +use Magento\Framework\Api\Config\Converter; + class ExtensionAttributesInterfaceGeneratorTest extends \PHPUnit_Framework_TestCase { public function testGenerate() @@ -19,11 +21,20 @@ class ExtensionAttributesInterfaceGeneratorTest extends \PHPUnit_Framework_TestC ->willReturn( [ 'Magento\Catalog\Api\Data\ProductInterface' => [ - 'string_attribute' => 'string', - 'complex_object_attribute' => '\Magento\Bundle\Api\Data\OptionInterface[]' + 'string_attribute' => [ + Converter::DATA_TYPE => 'string', + Converter::RESOURCE_PERMISSIONS => [], + ], + 'complex_object_attribute' => [ + Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\OptionInterface[]', + Converter::RESOURCE_PERMISSIONS => [], + ], ], 'Magento\Catalog\Api\Data\Product' => [ - 'should_not_include' => 'string', + 'should_not_include' => [ + Converter::DATA_TYPE => 'string', + Converter::RESOURCE_PERMISSIONS => [], + ], ], ] ); diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt index 8b5caad1ecc4456ae51380e25c735be8f2660d37..0f9838bd8736c9c0545b6bb82ab5adbe9762a946 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt @@ -6,7 +6,7 @@ namespace Magento\Catalog\Api\Data; class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements \Magento\Catalog\Api\Data\ProductExtensionInterface { /** - * @return string + * @return string|null */ public function getStringAttribute() { @@ -24,7 +24,7 @@ class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject imple } /** - * @return \Magento\Bundle\Api\Data\OptionInterface[] + * @return \Magento\Bundle\Api\Data\OptionInterface[]|null */ public function getComplexObjectAttribute() { diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt index ec9edd7affc2d68e5444d914b833bff81ccc1d4d..75dde39b2151952b49beb587ce4eecff56418997 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt @@ -6,7 +6,7 @@ namespace Magento\Catalog\Api\Data; interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface { /** - * @return string + * @return string|null */ public function getStringAttribute(); @@ -17,7 +17,7 @@ interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttr public function setStringAttribute($stringAttribute); /** - * @return \Magento\Bundle\Api\Data\OptionInterface[] + * @return \Magento\Bundle\Api\Data\OptionInterface[]|null */ public function getComplexObjectAttribute(); diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php index 94f5a4494a75dd9c57afa409c90eb622ad26827c..d4629213b20be2d7bbca2ff16bacf0b36de608e5 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\Api\Test\Unit\Config; +use Magento\Framework\Api\Config\Converter; + class ConverterTest extends \PHPUnit_Framework_TestCase { /** @@ -47,15 +49,39 @@ class ConverterTest extends \PHPUnit_Framework_TestCase 'Magento\Tax\Api\Data\TaxRateInterface' => [ ], 'Magento\Catalog\Api\Data\ProductInterface' => [ - 'stock_item' => 'Magento\CatalogInventory\Api\Data\StockItemInterface' + 'stock_item' => [ + Converter::DATA_TYPE => 'Magento\CatalogInventory\Api\Data\StockItemInterface', + Converter::RESOURCE_PERMISSIONS => [], + ], ], 'Magento\Customer\Api\Data\CustomerInterface' => [ - 'custom_1' => 'Magento\Customer\Api\Data\CustomerCustom', - 'custom_2' => 'Magento\CustomerExtra\Api\Data\CustomerCustom2' + 'custom_1' => [ + Converter::DATA_TYPE => 'Magento\Customer\Api\Data\CustomerCustom', + Converter::RESOURCE_PERMISSIONS => [], + ], + 'custom_2' => [ + Converter::DATA_TYPE => 'Magento\CustomerExtra\Api\Data\CustomerCustom2', + Converter::RESOURCE_PERMISSIONS => [], + ], + ], + 'Magento\Customer\Api\Data\CustomerInterface2' => [ + 'custom_with_permission' => [ + Converter::DATA_TYPE => 'Magento\Customer\Api\Data\CustomerCustom', + Converter::RESOURCE_PERMISSIONS => [ + 'Magento_Customer::manage', + ], + ], + 'custom_with_multiple_permissions' => [ + Converter::DATA_TYPE => 'Magento\CustomerExtra\Api\Data\CustomerCustom2', + Converter::RESOURCE_PERMISSIONS => [ + 'Magento_Customer::manage', + 'Magento_Customer::manage2', + ], + ], ], ]; - $xmlFile = __DIR__ . '/_files/data_object_valid.xml'; + $xmlFile = __DIR__ . '/_files/service_data_attributes.xml'; $dom = new \DOMDocument(); $dom->loadXML(file_get_contents($xmlFile)); $result = $this->_converter->convert($dom); diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php index f0df38d59a070958d77d6ddccb0d339162d256da..f05583db0490194e949261bce0a2c176932dbdb5 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php @@ -22,7 +22,7 @@ class SchemaLocatorTest extends \PHPUnit_Framework_TestCase public function testGetSchema() { - $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/data_object.xsd'); + $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd'); $actual = str_replace('\\', '/', $this->_model->getSchema()); $this->assertEquals($expected, $actual); } @@ -30,7 +30,7 @@ class SchemaLocatorTest extends \PHPUnit_Framework_TestCase public function testGetPerFileSchema() { $actual = str_replace('\\', '/', $this->_model->getPerFileSchema()); - $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/data_object.xsd'); + $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd'); $this->assertEquals($expected, $actual); } } diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/data_object_valid.xml b/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/data_object_valid.xml deleted file mode 100644 index 67606deab2eb46e78dc3022a723b41a1bb25bfa3..0000000000000000000000000000000000000000 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/data_object_valid.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd"> - <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> - </custom_attributes> - <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface"> - <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface" /> - </custom_attributes> - <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface"> - <attribute code="custom_1" type="Magento\Customer\Api\Data\CustomerCustom" /> - <attribute code="custom_2" type="Magento\CustomerExtra\Api\Data\CustomerCustom2" /> - </custom_attributes> -</config> diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/service_data_attributes.xml b/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/service_data_attributes.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f6a9838bfcdb6bd9dbfa20b18ffcfde80bd625d --- /dev/null +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/service_data_attributes.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd"> + <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface"> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> + <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface" /> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface"> + <attribute code="custom_1" type="Magento\Customer\Api\Data\CustomerCustom" /> + <attribute code="custom_2" type="Magento\CustomerExtra\Api\Data\CustomerCustom2" /> + </extension_attributes> + <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface2"> + <attribute code="custom_with_permission" type="Magento\Customer\Api\Data\CustomerCustom"> + <resources> + <resource ref="Magento_Customer::manage"/> + </resources> + </attribute> + <attribute code="custom_with_multiple_permissions" type="Magento\CustomerExtra\Api\Data\CustomerCustom2"> + <resources> + <resource ref="Magento_Customer::manage"/> + <resource ref="Magento_Customer::manage2"/> + </resources> + </attribute> + </extension_attributes> +</config> diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php index ff229abd7d22e9be0f810da4034336e5e9631f63..65c7cb906d1f2f15b8f251145633cdb6e85848c5 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php @@ -43,6 +43,11 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase */ protected $attributeValueFactoryMock; + /** + * @var \Magento\Framework\Reflection\MethodsMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $methodsMapProcessor; + public function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -53,6 +58,9 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase $this->objectProcessorMock = $this->getMockBuilder('\Magento\Framework\Reflection\DataObjectProcessor') ->disableOriginalConstructor() ->getMock(); + $this->methodsMapProcessor = $this->getMockBuilder('\Magento\Framework\Reflection\MethodsMap') + ->disableOriginalConstructor() + ->getMock(); $this->attributeValueFactoryMock = $this->getMockBuilder('\Magento\Framework\Api\AttributeValueFactory') ->disableOriginalConstructor() ->getMock(); @@ -63,6 +71,7 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase 'objectFactory' => $this->objectFactoryMock, 'typeProcessor' => $this->typeProcessor, 'objectProcessor' => $this->objectProcessorMock, + 'methodsMapProcessor' => $this->methodsMapProcessor, ] ); } @@ -103,11 +112,11 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase ], ]; - $this->objectProcessorMock->expects($this->at(0)) + $this->methodsMapProcessor->expects($this->at(0)) ->method('getMethodReturnType') ->with('\Magento\Customer\Api\Data\AddressInterface', 'getStreet') ->willReturn('string[]'); - $this->objectProcessorMock->expects($this->at(1)) + $this->methodsMapProcessor->expects($this->at(1)) ->method('getMethodReturnType') ->with('\Magento\Customer\Api\Data\AddressInterface', 'getRegion') ->willReturn('\Magento\Customer\Api\Data\RegionInterface'); @@ -317,11 +326,11 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase ->method('buildOutputDataArray') ->with($secondAddressDataObject, get_class($firstAddressDataObject)) ->willReturn($data2); - $this->objectProcessorMock->expects($this->at(1)) + $this->methodsMapProcessor->expects($this->at(0)) ->method('getMethodReturnType') ->with('Magento\Customer\Model\Data\Address', 'getStreet') ->willReturn('string[]'); - $this->objectProcessorMock->expects($this->at(2)) + $this->methodsMapProcessor->expects($this->at(1)) ->method('getMethodReturnType') ->with('Magento\Customer\Model\Data\Address', 'getRegion') ->willReturn('\Magento\Customer\Api\Data\RegionInterface'); diff --git a/lib/internal/Magento/Framework/Api/etc/data_object.xsd b/lib/internal/Magento/Framework/Api/etc/data_object.xsd deleted file mode 100644 index 0983dbbf912385c5a83dd1b8db18d768071c42fc..0000000000000000000000000000000000000000 --- a/lib/internal/Magento/Framework/Api/etc/data_object.xsd +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="config"> - <xs:complexType> - <xs:sequence> - <xs:element name="custom_attributes" type="custom_attributesType" minOccurs="1" maxOccurs="unbounded"> - <xs:annotation> - <xs:documentation>Main schema element. Extended Attributes</xs:documentation> - </xs:annotation> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:complexType name="attributeType"> - <xs:simpleContent> - <xs:extension base="xs:string"> - <xs:attribute type="xs:string" name="code" use="required"/> - <xs:attribute type="xs:string" name="type" use="required"/> - </xs:extension> - </xs:simpleContent> - </xs:complexType> - <xs:complexType name="custom_attributesType"> - <xs:sequence> - <xs:element type="attributeType" name="attribute" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - <xs:attribute type="xs:string" name="for" use="required"/> - </xs:complexType> -</xs:schema> diff --git a/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd b/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd new file mode 100644 index 0000000000000000000000000000000000000000..24590e3d758c79afeef7ce85c8cb9f6547e6f524 --- /dev/null +++ b/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="config"> + <xs:complexType> + <xs:sequence> + <xs:element name="extension_attributes" type="extension_attributesType" minOccurs="1" + maxOccurs="unbounded"> + <xs:annotation> + <xs:documentation>Main schema element. Extended Attributes</xs:documentation> + </xs:annotation> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:complexType name="attributeType"> + <xs:sequence> + <xs:element name="resources" type="resourcesType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute type="xs:string" name="code" use="required"/> + <xs:attribute type="xs:string" name="type" use="required"/> + </xs:complexType> + <xs:complexType name="extension_attributesType"> + <xs:sequence> + <xs:element type="attributeType" name="attribute" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute type="xs:string" name="for" use="required"/> + </xs:complexType> + <xs:complexType name="resourcesType"> + <xs:sequence> + <xs:element name="resource" type="resourceType" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <xs:complexType name="resourceType"> + <xs:attribute name="ref" use="required"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:pattern value=".+(, ?.+)*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> +</xs:schema> diff --git a/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php b/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php index bbdd3787b1825ffa9c35882776a9173c03b9607a..e8a3e92263e7d9b50226911e4d445b1a663171f7 100644 --- a/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php +++ b/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php @@ -7,6 +7,10 @@ */ namespace Magento\Framework\App\Area; +/** + * Interface FrontNameResolverInterface + * @api + */ interface FrontNameResolverInterface { /** diff --git a/lib/internal/Magento/Framework/App/AreaInterface.php b/lib/internal/Magento/Framework/App/AreaInterface.php index 806a7983424599690e3b7f02443fb7e9b4b328a5..544ac56e19e6f10dd13c5a284d56dba2d6e4b010 100644 --- a/lib/internal/Magento/Framework/App/AreaInterface.php +++ b/lib/internal/Magento/Framework/App/AreaInterface.php @@ -6,6 +6,9 @@ */ namespace Magento\Framework\App; +/** + * Interface AreaInterface + */ interface AreaInterface { const PART_CONFIG = 'config'; diff --git a/lib/internal/Magento/Framework/App/AreaList.php b/lib/internal/Magento/Framework/App/AreaList.php index 270fb22e36675b08576b06500e7cd6da38f39466..c010388797d870fa25e7aec9a192edef8ed0a73b 100644 --- a/lib/internal/Magento/Framework/App/AreaList.php +++ b/lib/internal/Magento/Framework/App/AreaList.php @@ -63,6 +63,7 @@ class AreaList * * @param string $frontName * @return null|string + * @api */ public function getCodeByFrontName($frontName) { @@ -84,6 +85,7 @@ class AreaList * * @param string $areaCode * @return string + * @api */ public function getFrontName($areaCode) { @@ -94,6 +96,7 @@ class AreaList * Retrieve area codes * * @return string[] + * @api */ public function getCodes() { @@ -105,6 +108,7 @@ class AreaList * * @param string $areaCode * @return string + * @api */ public function getDefaultRouter($areaCode) { diff --git a/lib/internal/Magento/Framework/App/Cache/Type/Config.php b/lib/internal/Magento/Framework/App/Cache/Type/Config.php index ded73329969b9f7faec6409ed73715cab06390eb..506eff32a8e099edc9a4a9193b7e42bafdc2ac58 100644 --- a/lib/internal/Magento/Framework/App/Cache/Type/Config.php +++ b/lib/internal/Magento/Framework/App/Cache/Type/Config.php @@ -25,10 +25,40 @@ class Config extends TagScope implements CacheInterface const CACHE_TAG = 'CONFIG'; /** - * @param FrontendPool $cacheFrontendPool + * @var \Magento\Framework\App\Cache\Type\FrontendPool */ - public function __construct(FrontendPool $cacheFrontendPool) + private $cacheFrontendPool; + + /** + * @param \Magento\Framework\App\Cache\Type\FrontendPool $cacheFrontendPool + */ + public function __construct(\Magento\Framework\App\Cache\Type\FrontendPool $cacheFrontendPool) + { + $this->cacheFrontendPool = $cacheFrontendPool; + } + + /** + * Retrieve cache frontend instance being decorated + * + * @return \Magento\Framework\Cache\FrontendInterface + */ + protected function _getFrontend() + { + $frontend = parent::_getFrontend(); + if (!$frontend) { + $frontend = $this->cacheFrontendPool->get(self::TYPE_IDENTIFIER); + $this->setFrontend($frontend); + } + return $frontend; + } + + /** + * Retrieve cache tag name + * + * @return string + */ + public function getTag() { - parent::__construct($cacheFrontendPool->get(self::TYPE_IDENTIFIER), self::CACHE_TAG); + return self::CACHE_TAG; } } diff --git a/lib/internal/Magento/Framework/App/Config.php b/lib/internal/Magento/Framework/App/Config.php index 047849f0034bde6164c2a054c81817e2daccdd2a..898a19a00ea5f20ff98bce5a7cb5eaa322972869 100644 --- a/lib/internal/Magento/Framework/App/Config.php +++ b/lib/internal/Magento/Framework/App/Config.php @@ -7,6 +7,8 @@ */ namespace Magento\Framework\App; +use Magento\Framework\App\Config\ScopeConfigInterface; + class Config implements \Magento\Framework\App\Config\ScopeConfigInterface { /** @@ -37,7 +39,7 @@ class Config implements \Magento\Framework\App\Config\ScopeConfigInterface */ public function getValue( $path = null, - $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null ) { return $this->_scopePool->getScope($scope, $scopeCode)->getValue($path); @@ -51,7 +53,7 @@ class Config implements \Magento\Framework\App\Config\ScopeConfigInterface * @param null|string $scopeCode * @return bool */ - public function isSetFlag($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeCode = null) + public function isSetFlag($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null) { return (bool) $this->getValue($path, $scope, $scopeCode); } diff --git a/lib/internal/Magento/Framework/App/Config/Data.php b/lib/internal/Magento/Framework/App/Config/Data.php index 3b5f17fe71639559575a77bc431fccc7071388f0..38389e71e40aaf297d4983bdde8377db615d5493 100644 --- a/lib/internal/Magento/Framework/App/Config/Data.php +++ b/lib/internal/Magento/Framework/App/Config/Data.php @@ -42,10 +42,7 @@ class Data implements DataInterface } /** - * Retrieve configuration value by path - * - * @param null|string $path - * @return array|string + * @inheritdoc */ public function getValue($path = null) { @@ -65,11 +62,7 @@ class Data implements DataInterface } /** - * Set configuration value - * - * @param string $path - * @param mixed $value - * @return void + * @inheritdoc */ public function setValue($path, $value) { diff --git a/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php b/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php index 8f473e2a37b2ba3c9cf3a0e707dd43f9bbe70902..8084dcd5d08f45c755a9c5b0490bf1fea78f8894 100644 --- a/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php +++ b/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php @@ -30,21 +30,22 @@ class ProcessorFactory /** * Get concrete Processor Interface instance * - * @param string $model + * @param string $processorModel Classname of the instance to get * @return ProcessorInterface - * @throws \InvalidArgumentException + * @throws \InvalidArgumentException In case the given classname is not an instance of ProcessorInterface + * @api */ - public function get($model) + public function get($processorModel) { - if (!isset($this->_pool[$model])) { - $instance = $this->_objectManager->create($model); + if (!isset($this->_pool[$processorModel])) { + $instance = $this->_objectManager->create($processorModel); if (!$instance instanceof ProcessorInterface) { throw new \InvalidArgumentException( - $model . ' is not instance of \Magento\Framework\App\Config\Data\ProcessorInterface' + $processorModel . ' is not instance of \Magento\Framework\App\Config\Data\ProcessorInterface' ); } - $this->_pool[$model] = $instance; + $this->_pool[$processorModel] = $instance; } - return $this->_pool[$model]; + return $this->_pool[$processorModel]; } } diff --git a/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php b/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php index 71102131f42cf4071b6d213d225ac8178956945a..d4467339676739d2e92250b986d992458772cc68 100644 --- a/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php +++ b/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php @@ -1,19 +1,22 @@ <?php /** - * Processor interface - * * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\App\Config\Data; +/** + * Processes data from admin store configuration fields + * + * @api + */ interface ProcessorInterface { /** * Process config value * - * @param string $value - * @return string + * @param string $value Raw value of the configuration field + * @return string Processed value */ public function processValue($value); } diff --git a/lib/internal/Magento/Framework/App/Config/DataInterface.php b/lib/internal/Magento/Framework/App/Config/DataInterface.php index bbdaf54d1c342a11803527f92160ebacef27a0a6..b07ca0e0df1fc3ce8085227057148619c371f0f9 100644 --- a/lib/internal/Magento/Framework/App/Config/DataInterface.php +++ b/lib/internal/Magento/Framework/App/Config/DataInterface.php @@ -5,11 +5,27 @@ */ namespace Magento\Framework\App\Config; +/** + * Configuration data storage + * + * @api + */ interface DataInterface { /** + * Retrieve configuration value by path + * * @param string|null $path - * @return string|array + * @return mixed */ public function getValue($path); + + /** + * Set configuration value by path + * + * @param string $path + * @param mixed $value + * @return void + */ + public function setValue($path, $value); } diff --git a/lib/internal/Magento/Framework/App/Config/Initial.php b/lib/internal/Magento/Framework/App/Config/Initial.php index 68db7b1eb84d42c669f3874b180038292a087983..3b2beb5ca37423ee054d94d2d3289bdacd21bca5 100644 --- a/lib/internal/Magento/Framework/App/Config/Initial.php +++ b/lib/internal/Magento/Framework/App/Config/Initial.php @@ -7,6 +7,8 @@ */ namespace Magento\Framework\App\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; + class Initial { /** @@ -50,14 +52,14 @@ class Initial /** * Get initial data by given scope * - * @param string $scope + * @param string $scope Format is scope type and scope code separated by pipe: e.g. "type|code" * @return array */ public function getData($scope) { list($scopeType, $scopeCode) = array_pad(explode('|', $scope), 2, null); - if (\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT == $scopeType) { + if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT == $scopeType) { return isset($this->_data[$scopeType]) ? $this->_data[$scopeType] : []; } elseif ($scopeCode) { return isset($this->_data[$scopeType][$scopeCode]) ? $this->_data[$scopeType][$scopeCode] : []; diff --git a/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php b/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php index 37a371b547f40439addcfad205d29dd9c178fb07..f68048ddc94611267e17df9c85eb203bf28097a2 100644 --- a/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php +++ b/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php @@ -8,6 +8,9 @@ namespace Magento\Framework\App\Config; +/** + * @api + */ interface MutableScopeConfigInterface extends \Magento\Framework\App\Config\ScopeConfigInterface { /** @@ -15,14 +18,14 @@ interface MutableScopeConfigInterface extends \Magento\Framework\App\Config\Scop * * @param string $path * @param mixed $value - * @param string $scope + * @param string $scopeType * @param null|string $scopeCode * @return void */ public function setValue( $path, $value, - $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + $scopeType = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null ); } diff --git a/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php b/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php index 5b9d9d78eb3b4789a2ac6477e5e686ff3e4f0c6f..f9fc887a878e0ec0b73e2ad956ae0ff3b1ae166a 100644 --- a/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php +++ b/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php @@ -8,6 +8,9 @@ namespace Magento\Framework\App\Config; +/** + * @api + */ interface ReinitableConfigInterface extends \Magento\Framework\App\Config\MutableScopeConfigInterface { /** diff --git a/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php b/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php index aa0e06048980767a44d1f0688ebe6956bbb13355..8084c21ab0d9cd26be2319dd9cfad4e00992dae7 100644 --- a/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php +++ b/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php @@ -6,12 +6,12 @@ namespace Magento\Framework\App\Config\Resource; /** - * Resource Config Interface + * Resource for storing store configuration values */ interface ConfigInterface { /** - * Save config value + * Save config value to the storage resource * * @param string $path * @param string $value @@ -22,7 +22,7 @@ interface ConfigInterface public function saveConfig($path, $value, $scope, $scopeId); /** - * Delete config value + * Delete config value from the storage resource * * @param string $path * @param string $scope diff --git a/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php b/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php index bf20ecd1d530881e76749275afcff6dbd87939d1..cdcbb42828e51d4ff2f3e13c9449037c64ac9ad5 100644 --- a/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php +++ b/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php @@ -12,7 +12,9 @@ interface ReaderInterface /** * Read configuration scope * + * @param string|null $scopeType + * @throws \Exception May throw an exception if the given scope is invalid * @return array */ - public function read(); + public function read($scopeType = null); } diff --git a/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php b/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php index ac84e7358b5d2599a531ea45047d92626370bd44..e98fe284024f1780d731500d810516011b7528ba 100644 --- a/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php +++ b/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php @@ -8,25 +8,33 @@ namespace Magento\Framework\App\Config; +/** + * @api + */ interface ScopeConfigInterface { /** - * Retrieve config value by path and scope + * Default scope type + */ + const SCOPE_TYPE_DEFAULT = 'default'; + + /** + * Retrieve config value by path and scope. * - * @param string $path - * @param string $scope + * @param string $path The path through the tree of configuration values, e.g., 'general/store_information/name' + * @param string $scopeType The scope to use to determine config value, e.g., 'store' or 'default' * @param null|string $scopeCode * @return mixed */ - public function getValue($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeCode = null); + public function getValue($path, $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null); /** * Retrieve config flag by path and scope * - * @param string $path - * @param string $scope + * @param string $path The path through the tree of configuration values, e.g., 'general/store_information/name' + * @param string $scopeType The scope to use to determine config value, e.g., 'store' or 'default' * @param null|string $scopeCode * @return bool */ - public function isSetFlag($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeCode = null); + public function isSetFlag($path, $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null); } diff --git a/lib/internal/Magento/Framework/App/Config/ScopePool.php b/lib/internal/Magento/Framework/App/Config/ScopePool.php index 34b24deb0028b63402dc2d169f2dbb47566dae9d..1a15575b1a514a0a0a04b728e454c2c18ef0645e 100644 --- a/lib/internal/Magento/Framework/App/Config/ScopePool.php +++ b/lib/internal/Magento/Framework/App/Config/ScopePool.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\App\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; + class ScopePool { const CACHE_TAG = 'config_scopes'; @@ -78,7 +80,7 @@ class ScopePool $data = unserialize($data); } else { $reader = $this->_readerPool->getReader($scopeType); - if ($scopeType === \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT) { + if ($scopeType === ScopeConfigInterface::SCOPE_TYPE_DEFAULT) { $data = $reader->read(); } else { $data = $reader->read($scopeCode); @@ -111,7 +113,7 @@ class ScopePool protected function _getScopeCode($scopeType, $scopeCode) { if (($scopeCode === null || is_numeric($scopeCode)) - && $scopeType !== \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + && $scopeType !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT ) { $scopeResolver = $this->_scopeResolverPool->get($scopeType); $scopeCode = $scopeResolver->getScope($scopeCode); diff --git a/lib/internal/Magento/Framework/App/Config/Storage/Writer.php b/lib/internal/Magento/Framework/App/Config/Storage/Writer.php index b98e4074efa1b953fac38e02be2bbe435568d8c5..63b5ae83dfec68c85f61ebf17ae11396b485373e 100644 --- a/lib/internal/Magento/Framework/App/Config/Storage/Writer.php +++ b/lib/internal/Magento/Framework/App/Config/Storage/Writer.php @@ -7,6 +7,8 @@ */ namespace Magento\Framework\App\Config\Storage; +use Magento\Framework\App\Config\ScopeConfigInterface; + class Writer implements \Magento\Framework\App\Config\Storage\WriterInterface { /** @@ -32,7 +34,7 @@ class Writer implements \Magento\Framework\App\Config\Storage\WriterInterface * @param int $scopeId * @return void */ - public function delete($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0) + public function delete($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0) { $this->_resource->deleteConfig(rtrim($path, '/'), $scope, $scopeId); } @@ -46,7 +48,7 @@ class Writer implements \Magento\Framework\App\Config\Storage\WriterInterface * @param int $scopeId * @return void */ - public function save($path, $value, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0) + public function save($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0) { $this->_resource->saveConfig(rtrim($path, '/'), $value, $scope, $scopeId); } diff --git a/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php b/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php index 3a3a3af6241361ca757699bf1d1e37ecfab6ad45..67b2aebd5d4a6da8d19eaad74175ca26726a57e0 100644 --- a/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php +++ b/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php @@ -7,6 +7,8 @@ */ namespace Magento\Framework\App\Config\Storage; +use Magento\Framework\App\Config\ScopeConfigInterface; + interface WriterInterface { /** @@ -17,7 +19,7 @@ interface WriterInterface * @param int $scopeId * @return void */ - public function delete($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0); + public function delete($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0); /** * Save config value to storage @@ -28,5 +30,5 @@ interface WriterInterface * @param int $scopeId * @return void */ - public function save($path, $value, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0); + public function save($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0); } diff --git a/lib/internal/Magento/Framework/App/Config/Value.php b/lib/internal/Magento/Framework/App/Config/Value.php index 17c50d20b8b3a854cb7f61a319ba5d21b21e1dac..92ee527a9c48eb77a35f527c2c471c2507d1b546 100644 --- a/lib/internal/Magento/Framework/App/Config/Value.php +++ b/lib/internal/Magento/Framework/App/Config/Value.php @@ -91,7 +91,7 @@ class Value extends \Magento\Framework\Model\AbstractModel implements \Magento\F { return (string)$this->_config->getValue( $this->getPath(), - $this->getScope() ?: \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + $this->getScope() ?: ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $this->getScopeCode() ); } diff --git a/lib/internal/Magento/Framework/App/MutableScopeConfig.php b/lib/internal/Magento/Framework/App/MutableScopeConfig.php index c94fb73f9d5e4cb35ddf246710bf464a932a25ad..418817465bd6f11af59b2ae5f4eaf53eea7559cd 100644 --- a/lib/internal/Magento/Framework/App/MutableScopeConfig.php +++ b/lib/internal/Magento/Framework/App/MutableScopeConfig.php @@ -9,6 +9,7 @@ namespace Magento\Framework\App; use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; class MutableScopeConfig extends Config implements MutableScopeConfigInterface { @@ -24,7 +25,7 @@ class MutableScopeConfig extends Config implements MutableScopeConfigInterface public function setValue( $path, $value, - $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, + $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null ) { if (empty($scopeCode)) { diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index c09ead13054e791d5e6bff403623545b4e24f9e6..e6e502cf38171387aef13cdf606845d082f8664d 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -7,6 +7,7 @@ */ namespace Magento\Framework\App\Request; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface; use Magento\Framework\HTTP\PhpEnvironment\Request; @@ -384,7 +385,7 @@ class Http extends Request implements RequestInterface $offLoaderHeader = trim( (string)$config->getValue( self::XML_PATH_OFFLOADER_HEADER, - \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT + ScopeConfigInterface::SCOPE_TYPE_DEFAULT ) ); diff --git a/lib/internal/Magento/Framework/App/Resource.php b/lib/internal/Magento/Framework/App/Resource.php index b38629a460a4fa8a7ed78c1a9e77450ec23c1c8a..b498ec9d349fea88b942b42d2ff6277c23a2449a 100644 --- a/lib/internal/Magento/Framework/App/Resource.php +++ b/lib/internal/Magento/Framework/App/Resource.php @@ -126,6 +126,7 @@ class Resource * @param string|string[] $modelEntity * @param string $connectionName * @return string + * @api */ public function getTableName($modelEntity, $connectionName = self::DEFAULT_READ_RESOURCE) { diff --git a/lib/internal/Magento/Framework/App/Route/ConfigInterface.php b/lib/internal/Magento/Framework/App/Route/ConfigInterface.php index c6798f7ce4ab659e813488a155d0aa467ac41449..d96a4af89eac0d2a4c3932995643c1cbccec25e5 100644 --- a/lib/internal/Magento/Framework/App/Route/ConfigInterface.php +++ b/lib/internal/Magento/Framework/App/Route/ConfigInterface.php @@ -1,12 +1,15 @@ <?php /** - * Routes configuration interface - * * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\App\Route; +/** + * Routes configuration interface + * + * @api + */ interface ConfigInterface { /** @@ -32,7 +35,7 @@ interface ConfigInterface * * @param string $frontName * @param string $scope - * @return array + * @return string[] */ public function getModulesByFrontName($frontName, $scope = null); } diff --git a/lib/internal/Magento/Framework/App/ScopeInterface.php b/lib/internal/Magento/Framework/App/ScopeInterface.php index d244ec5bf3a1f612722795b72e938fef77f1102f..77c8a8b890ac209400fdf8005636ee869d8ecfd0 100644 --- a/lib/internal/Magento/Framework/App/ScopeInterface.php +++ b/lib/internal/Magento/Framework/App/ScopeInterface.php @@ -7,11 +7,6 @@ namespace Magento\Framework\App; interface ScopeInterface { - /** - * Default scope type - */ - const SCOPE_DEFAULT = 'default'; - /** * Retrieve scope code * diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Type/ConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Type/ConfigTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c6887709da50fbd2bf4fdb303f3e256749a4e197 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Type/ConfigTest.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\App\Test\Unit\Cache\Type; + +use Magento\Framework\App\Cache\Type\FrontendPool; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class ConfigTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\App\Cache\Type\Config + */ + protected $model; + + /** + * @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $frontendMock; + + protected function setUp() + { + $cacheFrontendPoolMock = $this->getMockBuilder('Magento\Framework\App\Cache\Type\FrontendPool') + ->disableOriginalConstructor() + ->getMock(); + $this->model = (new ObjectManager($this))->getObject( + 'Magento\Framework\App\Cache\Type\Config', + ['cacheFrontendPool' => $cacheFrontendPoolMock] + ); + $this->frontendMock = $this->getMock('Magento\Framework\Cache\FrontendInterface'); + $cacheFrontendPoolMock->expects($this->once()) + ->method('get') + ->with(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER) + ->willReturn($this->frontendMock); + } + + /** + * @param string $method + * @param array $params + * @param mixed $expectedResult + * @dataProvider proxyMethodDataProvider + */ + public function testProxyMethod($method, $params, $expectedResult) + { + $helper = new \Magento\Framework\TestFramework\Unit\Helper\ProxyTesting(); + $result = $helper->invokeWithExpectations($this->model, $this->frontendMock, $method, $params, $expectedResult); + $this->assertSame($expectedResult, $result); + } + + /** + * @return array + */ + public function proxyMethodDataProvider() + { + return [ + ['test', ['record_id'], 111], + ['load', ['record_id'], '111'], + ['remove', ['record_id'], true], + ['getBackend', [], $this->getMock('Zend_Cache_Backend')], + ['getLowLevelFrontend', [], $this->getMock('Zend_Cache_Core')], + ]; + } + + public function testSave() + { + $expectedResult = new \stdClass(); + $this->frontendMock->expects( + $this->once() + )->method( + 'save' + )->with( + 'test_value', + 'test_id', + ['test_tag_one', 'test_tag_two', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG], + 111 + )->will( + $this->returnValue($expectedResult) + ); + $actualResult = $this->model->save('test_value', 'test_id', ['test_tag_one', 'test_tag_two'], 111); + $this->assertSame($expectedResult, $actualResult); + } + + public function testCleanModeAll() + { + $expectedResult = new \stdClass(); + $this->frontendMock->expects( + $this->once() + )->method( + 'clean' + )->with( + \Zend_Cache::CLEANING_MODE_MATCHING_TAG, + [\Magento\Framework\App\Cache\Type\Config::CACHE_TAG] + )->will( + $this->returnValue($expectedResult) + ); + $actualResult = $this->model->clean( + \Zend_Cache::CLEANING_MODE_ALL, + ['ignored_tag_one', 'ignored_tag_two'] + ); + $this->assertSame($expectedResult, $actualResult); + } + + public function testCleanModeMatchingTag() + { + $expectedResult = new \stdClass(); + $this->frontendMock->expects( + $this->once() + )->method( + 'clean' + )->with( + \Zend_Cache::CLEANING_MODE_MATCHING_TAG, + ['test_tag_one', 'test_tag_two', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG] + )->will( + $this->returnValue($expectedResult) + ); + $actualResult = $this->model->clean( + \Zend_Cache::CLEANING_MODE_MATCHING_TAG, + ['test_tag_one', 'test_tag_two'] + ); + $this->assertSame($expectedResult, $actualResult); + } + + /** + * @param bool $fixtureResultOne + * @param bool $fixtureResultTwo + * @param bool $expectedResult + * @dataProvider cleanModeMatchingAnyTagDataProvider + */ + public function testCleanModeMatchingAnyTag($fixtureResultOne, $fixtureResultTwo, $expectedResult) + { + $this->frontendMock->expects( + $this->at(0) + )->method( + 'clean' + )->with( + \Zend_Cache::CLEANING_MODE_MATCHING_TAG, + ['test_tag_one', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG] + )->will( + $this->returnValue($fixtureResultOne) + ); + $this->frontendMock->expects( + $this->at(1) + )->method( + 'clean' + )->with( + \Zend_Cache::CLEANING_MODE_MATCHING_TAG, + ['test_tag_two', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG] + )->will( + $this->returnValue($fixtureResultTwo) + ); + $actualResult = $this->model->clean( + \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, + ['test_tag_one', 'test_tag_two'] + ); + $this->assertEquals($expectedResult, $actualResult); + } + + public function cleanModeMatchingAnyTagDataProvider() + { + return [ + 'failure, failure' => [false, false, false], + 'failure, success' => [false, true, true], + 'success, failure' => [true, false, true], + 'success, success' => [true, true, true] + ]; + } +} diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php index 57040f09ee4dd82eb4ec80df99f0b827cc859573..887a1cf477663f00253816aed0bfdd273cd6fe5f 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\App\Test\Unit\Config\Storage; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ScopeInterface; class WriterTest extends \PHPUnit_Framework_TestCase @@ -40,7 +41,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase { $this->resource->expects($this->once()) ->method('deleteConfig') - ->with('path', ScopeInterface::SCOPE_DEFAULT, 0); + ->with('path', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0); $this->model->delete('path'); } @@ -58,7 +59,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase { $this->resource->expects($this->once()) ->method('saveConfig') - ->with('path', 'value', ScopeInterface::SCOPE_DEFAULT, 0); + ->with('path', 'value', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0); $this->model->save('path', 'value'); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php index 090580da2726653983a7ed76ec9f16405393092f..8bfb9be2f3bb253932ba96c59d11d9819b704b50 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php @@ -9,6 +9,7 @@ namespace Magento\Framework\App\Test\Unit\Request; +use Magento\Framework\App\Config\ScopeConfigInterface; use \Magento\Framework\App\Request\Http; use Magento\Framework\App\ScopeInterface; use Zend\Stdlib\Parameters; @@ -325,7 +326,7 @@ class HttpTest extends \PHPUnit_Framework_TestCase ->getMock(); $configMock->expects($this->exactly($configCall)) ->method('getValue') - ->with(\Magento\Framework\App\Request\Http::XML_PATH_OFFLOADER_HEADER, ScopeInterface::SCOPE_DEFAULT) + ->with(\Magento\Framework\App\Request\Http::XML_PATH_OFFLOADER_HEADER, ScopeConfigInterface::SCOPE_TYPE_DEFAULT) ->willReturn($configOffloadHeader); $this->objectManager->expects($this->exactly($configCall)) ->method('get') diff --git a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php index b856e8608aa726ebf0f32e07ecb01ad7ac8d7d6f..cae35e66ff83d2b40b562b77aaaeaed4d030221e 100644 --- a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php +++ b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php @@ -27,6 +27,18 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface $this->_frontend = $frontend; } + /** + * Set frontend + * + * @param \Magento\Framework\Cache\FrontendInterface $frontend + * @return $this + */ + protected function setFrontend(\Magento\Framework\Cache\FrontendInterface $frontend) + { + $this->_frontend = $frontend; + return $this; + } + /** * Retrieve cache frontend instance being decorated * @@ -42,7 +54,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function test($identifier) { - return $this->_frontend->test($identifier); + return $this->_getFrontend()->test($identifier); } /** @@ -50,7 +62,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function load($identifier) { - return $this->_frontend->load($identifier); + return $this->_getFrontend()->load($identifier); } /** @@ -60,7 +72,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function save($data, $identifier, array $tags = [], $lifeTime = null) { - return $this->_frontend->save($data, $identifier, $tags, $lifeTime); + return $this->_getFrontend()->save($data, $identifier, $tags, $lifeTime); } /** @@ -68,7 +80,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function remove($identifier) { - return $this->_frontend->remove($identifier); + return $this->_getFrontend()->remove($identifier); } /** @@ -76,7 +88,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, array $tags = []) { - return $this->_frontend->clean($mode, $tags); + return $this->_getFrontend()->clean($mode, $tags); } /** @@ -84,7 +96,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function getBackend() { - return $this->_frontend->getBackend(); + return $this->_getFrontend()->getBackend(); } /** @@ -92,6 +104,6 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface */ public function getLowLevelFrontend() { - return $this->_frontend->getLowLevelFrontend(); + return $this->_getFrontend()->getLowLevelFrontend(); } } diff --git a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php index 32c9e4422417cf220a28db98137ef954a26ba705..0f74b1894865754521bd9b595f0b14a5572e1e8c 100644 --- a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php +++ b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php @@ -45,7 +45,7 @@ class TagScope extends \Magento\Framework\Cache\Frontend\Decorator\Bare */ public function save($data, $identifier, array $tags = [], $lifeTime = null) { - $tags[] = $this->_tag; + $tags[] = $this->getTag(); return parent::save($data, $identifier, $tags, $lifeTime); } @@ -59,16 +59,16 @@ class TagScope extends \Magento\Framework\Cache\Frontend\Decorator\Bare if ($mode == \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG) { $result = false; foreach ($tags as $tag) { - if (parent::clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [$tag, $this->_tag])) { + if (parent::clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [$tag, $this->getTag()])) { $result = true; } } } else { if ($mode == \Zend_Cache::CLEANING_MODE_ALL) { $mode = \Zend_Cache::CLEANING_MODE_MATCHING_TAG; - $tags = [$this->_tag]; + $tags = [$this->getTag()]; } else { - $tags[] = $this->_tag; + $tags[] = $this->getTag(); } $result = parent::clean($mode, $tags); } diff --git a/lib/internal/Magento/Framework/Data/Collection/Filesystem.php b/lib/internal/Magento/Framework/Data/Collection/Filesystem.php index 817c4b420519697de1ad27984ca0d286e151a125..cec62bc94a82b655866f71fe5118c13336868bef 100644 --- a/lib/internal/Magento/Framework/Data/Collection/Filesystem.php +++ b/lib/internal/Magento/Framework/Data/Collection/Filesystem.php @@ -284,6 +284,7 @@ class Filesystem extends \Magento\Framework\Data\Collection * @param bool $printQuery * @param bool $logQuery * @return $this + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @throws \Exception */ public function loadData($printQuery = false, $logQuery = false) @@ -668,6 +669,7 @@ class Filesystem extends \Magento\Framework\Data\Collection * @param string $value * @param string $type * @return $this + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function addFilter($field, $value, $type = 'and') { @@ -696,7 +698,7 @@ class Filesystem extends \Magento\Framework\Data\Collection */ public function filterCallbackLike($field, $filterValue, $row) { - $filterValueRegex = str_replace('%', '(.*?)', preg_quote($filterValue, '/')); + $filterValueRegex = str_replace('%', '(.*?)', str_replace('\'', '', preg_quote($filterValue, '/'))); return (bool)preg_match("/^{$filterValueRegex}\$/i", $row[$field]); } diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Collection/FilesystemTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Collection/FilesystemTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b764079a841b7f07f35dc9a692b9ffeb6968fe5f --- /dev/null +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Collection/FilesystemTest.php @@ -0,0 +1,30 @@ +<?php +/*** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Data\Test\Unit\Collection; + +class FilesystemTest extends \PHPUnit_Framework_TestCase +{ + /** @var \Magento\Framework\Data\Collection\Filesystem */ + private $model; + + public function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject('Magento\Framework\Data\Collection\Filesystem'); + } + + public function testFilterCallbackLike() + { + $field = 'field'; + $row = [$field => 'beginning_filter_target_end',]; + $filterValueSuccess = new \Zend_Db_Expr('%filter_target%'); + $filterValueFailure = new \Zend_Db_Expr('%not_found_in_the_row%'); + + $this->assertTrue($this->model->filterCallbackLike($field, $filterValueSuccess, $row)); + $this->assertFalse($this->model->filterCallbackLike($field, $filterValueFailure, $row)); + } +} diff --git a/lib/internal/Magento/Framework/Locale/ConfigInterface.php b/lib/internal/Magento/Framework/Locale/ConfigInterface.php index 3539f09409a800e83665f16d58871797f96161d0..e0f28fd015c33675eaed0445956c7a728a5e0d84 100644 --- a/lib/internal/Magento/Framework/Locale/ConfigInterface.php +++ b/lib/internal/Magento/Framework/Locale/ConfigInterface.php @@ -5,19 +5,24 @@ */ namespace Magento\Framework\Locale; +/** + * Provides access to locale-related config information + * + * @api + */ interface ConfigInterface { /** * Get list pre-configured allowed locales * - * @return array + * @return string[] */ public function getAllowedLocales(); /** * Get list pre-configured allowed currencies * - * @return array + * @return string[] */ public function getAllowedCurrencies(); } diff --git a/lib/internal/Magento/Framework/Locale/Currency.php b/lib/internal/Magento/Framework/Locale/Currency.php index 5d3c5a9ec9b7e961d870e4a458bc4b3fa69b5b89..f0be6c12172278b8e3a32b86848f296cd606d96a 100644 --- a/lib/internal/Magento/Framework/Locale/Currency.php +++ b/lib/internal/Magento/Framework/Locale/Currency.php @@ -7,6 +7,10 @@ namespace Magento\Framework\Locale; class Currency implements \Magento\Framework\Locale\CurrencyInterface { + /** + * Default currency + */ + const DEFAULT_CURRENCY = 'USD'; /** * @var array */ @@ -45,20 +49,15 @@ class Currency implements \Magento\Framework\Locale\CurrencyInterface } /** - * Retrieve currency code - * - * @return string + * @inheritdoc */ public function getDefaultCurrency() { - return \Magento\Framework\Locale\CurrencyInterface::DEFAULT_CURRENCY; + return self::DEFAULT_CURRENCY; } /** - * Create \Zend_Currency object for current locale - * - * @param string $currency - * @return \Magento\Framework\Currency + * @inheritdoc */ public function getCurrency($currency) { diff --git a/lib/internal/Magento/Framework/Locale/CurrencyInterface.php b/lib/internal/Magento/Framework/Locale/CurrencyInterface.php index 774f7d5120f3cead8f11e9d7545928e62634faa2..00bbc4452eedd604d19cb9144d1f38e9417e9568 100644 --- a/lib/internal/Magento/Framework/Locale/CurrencyInterface.php +++ b/lib/internal/Magento/Framework/Locale/CurrencyInterface.php @@ -5,27 +5,22 @@ */ namespace Magento\Framework\Locale; +/** + * Provides access to currency config information + * + * @api + */ interface CurrencyInterface { /** - * Default currency - */ - const DEFAULT_CURRENCY = 'USD'; - - /** - * XML path to installed currencies - */ - const XML_PATH_ALLOW_CURRENCIES_INSTALLED = 'system/currency/installed'; - - /** - * Retrieve currency code + * Retrieve default currency code * * @return string */ public function getDefaultCurrency(); /** - * Create \Magento\Framework\Currency object for current locale + * Create Currency object for current locale * * @param string $currency * @return \Magento\Framework\Currency diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index b78be470920ec0638dd4a8a786d13b97470277f3..6a81f60cc5a0cda5d7d55a31f7f9b599e905c94a 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -9,6 +9,10 @@ use Magento\Framework\App\Config\ScopeConfigInterface; class Resolver implements ResolverInterface { + /** + * Default locale + */ + const DEFAULT_LOCALE = 'en_US'; /** * Default locale code * @@ -85,7 +89,7 @@ class Resolver implements ResolverInterface if (!$this->defaultLocale) { $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType); if (!$locale) { - $locale = ResolverInterface::DEFAULT_LOCALE; + $locale = self::DEFAULT_LOCALE; } $this->defaultLocale = $locale; } diff --git a/lib/internal/Magento/Framework/Locale/ResolverInterface.php b/lib/internal/Magento/Framework/Locale/ResolverInterface.php index 08add444266f073aaa354935b6857be2c7edd34f..a17932ed8628870f7ce9f086946807498c836807 100644 --- a/lib/internal/Magento/Framework/Locale/ResolverInterface.php +++ b/lib/internal/Magento/Framework/Locale/ResolverInterface.php @@ -5,13 +5,13 @@ */ namespace Magento\Framework\Locale; +/** + * Manages locale config information + * + * @api + */ interface ResolverInterface { - /** - * Default locale - */ - const DEFAULT_LOCALE = 'en_US'; - /** * Return path to default locale * diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php index 01552d3a972294bd649141074cc91f985829743b..226e8d4d8508be64e2a9a9454aeaa94cbb3de243 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php @@ -8,6 +8,7 @@ namespace Magento\Framework\Locale\Test\Unit; +use Magento\Framework\Locale\Currency; use Magento\Framework\Locale\CurrencyInterface; class CurrencyTest extends \PHPUnit_Framework_TestCase @@ -68,7 +69,7 @@ class CurrencyTest extends \PHPUnit_Framework_TestCase public function testGetDefaultCurrency() { - $expectedDefaultCurrency = CurrencyInterface::DEFAULT_CURRENCY; + $expectedDefaultCurrency = Currency::DEFAULT_CURRENCY; $retrievedDefaultCurrency = $this->testCurrencyObject->getDefaultCurrency(); $this->assertEquals($expectedDefaultCurrency, $retrievedDefaultCurrency); } diff --git a/lib/internal/Magento/Framework/Locale/Validator.php b/lib/internal/Magento/Framework/Locale/Validator.php index b30727562c45755db8b67dfb00be40c18bae09fc..0cdf265af01b329f3a87eca97ac8de8c723902b6 100644 --- a/lib/internal/Magento/Framework/Locale/Validator.php +++ b/lib/internal/Magento/Framework/Locale/Validator.php @@ -29,10 +29,12 @@ class Validator } /** - * Validate locale code + * Validate locale code. Code must be in the list of allowed locales. * * @param string $localeCode * @return bool + * + * @api */ public function isValid($localeCode) { diff --git a/lib/internal/Magento/Framework/Message/Factory.php b/lib/internal/Magento/Framework/Message/Factory.php index 4c16064b5b6599dbb98f48307601dfff7eda1ee3..be7e3c9bb566a041c026a5916f4e51364e06b07d 100644 --- a/lib/internal/Magento/Framework/Message/Factory.php +++ b/lib/internal/Magento/Framework/Message/Factory.php @@ -45,11 +45,12 @@ class Factory } /** - * Create message instance with specified parameters + * Create a message instance of a given type with given text. * - * @param string $type - * @param string $text - * @throws \InvalidArgumentException + * @param string $type The message type to create, must correspond to a message type under the + * namespace Magento\Framework\Message\ + * @param string $text The text to inject into the message + * @throws \InvalidArgumentException Exception gets thrown if type does not correspond to a valid Magento message * @return MessageInterface */ public function create($type, $text) diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php index 73b212a58e3ee88769bd653a815c15ecafde2578..815296c80bad2d8453564f10797ade913a2e5c7f 100644 --- a/lib/internal/Magento/Framework/Message/Manager.php +++ b/lib/internal/Magento/Framework/Message/Manager.php @@ -14,6 +14,10 @@ use Psr\Log\LoggerInterface as Logger; */ class Manager implements ManagerInterface { + /** + * Default message group + */ + const DEFAULT_GROUP = 'default'; /** * @var Session */ @@ -74,9 +78,7 @@ class Manager implements ManagerInterface } /** - * Retrieve default message group - * - * @return string + * {@inheritdoc} */ public function getDefaultGroup() { @@ -95,7 +97,7 @@ class Manager implements ManagerInterface } /** - * Retrieve messages + * @inheritdoc * * @param string|null $group * @param bool $clear @@ -118,7 +120,7 @@ class Manager implements ManagerInterface } /** - * Adding new message to message collection + * @inheritdoc * * @param MessageInterface $message * @param string|null $group @@ -133,7 +135,7 @@ class Manager implements ManagerInterface } /** - * Adding messages array to message collection + * @inheritdoc * * @param MessageInterface[] $messages * @param string|null $group @@ -148,7 +150,7 @@ class Manager implements ManagerInterface } /** - * Adding new error message + * @inheritdoc * * @param string $message * @param string|null $group @@ -161,7 +163,7 @@ class Manager implements ManagerInterface } /** - * Adding new warning message + * @inheritdoc * * @param string $message * @param string|null $group @@ -174,7 +176,7 @@ class Manager implements ManagerInterface } /** - * Adding new notice message + * @inheritdoc * * @param string $message * @param string|null $group @@ -187,7 +189,7 @@ class Manager implements ManagerInterface } /** - * Adding new success message + * @inheritdoc * * @param string $message * @param string|null $group @@ -200,7 +202,7 @@ class Manager implements ManagerInterface } /** - * Adds messages array to message collection, but doesn't add duplicates to it + * @inheritdoc * * @param MessageInterface[]|MessageInterface $messages * @param string|null $group @@ -244,7 +246,7 @@ class Manager implements ManagerInterface } /** - * Not Magento exception handling + * @inheritdoc * * @param \Exception $exception * @param string $alternativeText diff --git a/lib/internal/Magento/Framework/Message/ManagerInterface.php b/lib/internal/Magento/Framework/Message/ManagerInterface.php index 36851dc866c49be7c9e0f0ea15490c79b776db4a..dd73d2a2fcfbc8acbd9aabdef42a3f5d59616f4d 100644 --- a/lib/internal/Magento/Framework/Message/ManagerInterface.php +++ b/lib/internal/Magento/Framework/Message/ManagerInterface.php @@ -6,15 +6,12 @@ namespace Magento\Framework\Message; /** - * Message manager interface + * Adds different types of messages to the session, and allows access to existing messages. + * + * @api */ interface ManagerInterface { - /** - * Default message group - */ - const DEFAULT_GROUP = 'default'; - /** * Retrieve messages * @@ -32,7 +29,7 @@ interface ManagerInterface public function getDefaultGroup(); /** - * Adding new message to message collection + * Adds new message to message collection * * @param MessageInterface $message * @param string|null $group @@ -41,7 +38,7 @@ interface ManagerInterface public function addMessage(MessageInterface $message, $group = null); /** - * Adding messages array to message collection + * Adds messages array to message collection * * @param array $messages * @param string|null $group @@ -50,7 +47,7 @@ interface ManagerInterface public function addMessages(array $messages, $group = null); /** - * Adding new error message + * Adds new error message * * @param string $message * @param string|null $group @@ -59,7 +56,7 @@ interface ManagerInterface public function addError($message, $group = null); /** - * Adding new warning message + * Adds new warning message * * @param string $message * @param string|null $group @@ -68,7 +65,7 @@ interface ManagerInterface public function addWarning($message, $group = null); /** - * Adding new notice message + * Adds new notice message * * @param string $message * @param string|null $group @@ -77,7 +74,7 @@ interface ManagerInterface public function addNotice($message, $group = null); /** - * Adding new success message + * Adds new success message * * @param string $message * @param string|null $group @@ -86,7 +83,7 @@ interface ManagerInterface public function addSuccess($message, $group = null); /** - * Adds messages array to message collection, but doesn't add duplicates to it + * Adds messages array to message collection, without adding duplicate messages * * @param array|MessageInterface $messages * @param string|null $group @@ -95,7 +92,7 @@ interface ManagerInterface public function addUniqueMessages($messages, $group = null); /** - * Not Magento exception handling + * Adds a message describing an exception. Does not contain Exception handling logic. * * @param \Exception $exception * @param string $alternativeText diff --git a/lib/internal/Magento/Framework/Message/MessageInterface.php b/lib/internal/Magento/Framework/Message/MessageInterface.php index e5342f49ac042e79fb0b572fca328a1285ab8b22..d9d60e047b1713fd6a6288a86b2a7b74b8ddae0a 100644 --- a/lib/internal/Magento/Framework/Message/MessageInterface.php +++ b/lib/internal/Magento/Framework/Message/MessageInterface.php @@ -6,7 +6,9 @@ namespace Magento\Framework\Message; /** - * Interface for message + * Represent a message with a type, content text, and an isSticky attribute to prevent message from being cleared. + * + * @api */ interface MessageInterface { diff --git a/lib/internal/Magento/Framework/Message/Session.php b/lib/internal/Magento/Framework/Message/Session.php index da86d172577a4f0ae18dce396deaf6688cd42d2e..8ceb66543e370ccf34321cef02be325bedb2cf85 100644 --- a/lib/internal/Magento/Framework/Message/Session.php +++ b/lib/internal/Magento/Framework/Message/Session.php @@ -8,6 +8,6 @@ namespace Magento\Framework\Message; /** * Message session model */ -class Session extends \Magento\Framework\Session\Generic +class Session extends \Magento\Framework\Session\SessionManager { } diff --git a/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php b/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php index c4980974bc289f00167474eb50040197e020cb66..d65a5459a4ee92dae76a266743cb03d39c3220b6 100644 --- a/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php +++ b/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\Message\Test\Unit; +use Magento\Framework\Message\Manager; use Magento\Framework\Message\MessageInterface; use Magento\Framework\Message\ManagerInterface; @@ -86,7 +87,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase public function testGetDefaultGroup() { - $this->assertEquals(ManagerInterface::DEFAULT_GROUP, $this->model->getDefaultGroup()); + $this->assertEquals(Manager::DEFAULT_GROUP, $this->model->getDefaultGroup()); $customDefaultGroup = 'some_group'; $customManager = $this->objectManager->getObject( @@ -117,7 +118,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase )->method( 'getData' )->with( - ManagerInterface::DEFAULT_GROUP + Manager::DEFAULT_GROUP )->will( $this->returnValue(null) ); @@ -126,7 +127,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase )->method( 'setData' )->with( - ManagerInterface::DEFAULT_GROUP, + Manager::DEFAULT_GROUP, $messageCollection )->will( $this->returnValue($this->session) @@ -136,7 +137,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase )->method( 'getData' )->with( - ManagerInterface::DEFAULT_GROUP + Manager::DEFAULT_GROUP )->will( $this->returnValue($messageCollection) ); @@ -161,7 +162,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase )->method( 'getData' )->with( - ManagerInterface::DEFAULT_GROUP + Manager::DEFAULT_GROUP )->will( $this->returnValue($messageCollection) ); @@ -209,7 +210,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase )->method( 'getData' )->with( - ManagerInterface::DEFAULT_GROUP + Manager::DEFAULT_GROUP )->will( $this->returnValue($messageCollection) ); diff --git a/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php b/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php index 831dcf5ae71fb2f832a7372efd8abdfe49a0ac40..c2a9bd81aad960e498303fae9f00a0f333a26272 100644 --- a/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php +++ b/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php @@ -61,6 +61,7 @@ abstract class AbstractResource * Start resource transaction * * @return $this + * @api */ public function beginTransaction() { @@ -73,6 +74,7 @@ abstract class AbstractResource * * @param array $callback * @return $this + * @api */ public function addCommitCallback($callback) { @@ -85,6 +87,7 @@ abstract class AbstractResource * Commit resource transaction * * @return $this + * @api */ public function commit() { @@ -109,6 +112,7 @@ abstract class AbstractResource * Roll back resource transaction * * @return $this + * @api */ public function rollBack() { diff --git a/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php index 351365b08d9eb57d45b6b65a50c53edbb93d482c..ba80e64a256222d7d02ac7eaae6cd95f7ec3717a 100644 --- a/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php +++ b/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php @@ -233,6 +233,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso * * @throws LocalizedException * @return string + * @api */ public function getIdFieldName() { @@ -248,6 +249,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso * * @throws LocalizedException * @return string + * @api */ public function getMainTable() { @@ -262,6 +264,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso * * @param string $tableName * @return string + * @api */ public function getTable($tableName) { @@ -391,6 +394,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso * @param \Magento\Framework\Model\AbstractModel $object * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @api */ public function save(\Magento\Framework\Model\AbstractModel $object) { diff --git a/lib/internal/Magento/Framework/Object/Copy.php b/lib/internal/Magento/Framework/Object/Copy.php index 9145e0aba3bc9b9a106863967d2b7c5a3bd8b124..c0d87722872490ec10c4b15efb33fc3f2385fd33 100644 --- a/lib/internal/Magento/Framework/Object/Copy.php +++ b/lib/internal/Magento/Framework/Object/Copy.php @@ -47,6 +47,8 @@ class Copy * @param array|\Magento\Framework\Object $target * @param string $root * @return array|\Magento\Framework\Object|null the value of $target + * + * @api */ public function copyFieldsetToTarget($fieldset, $aspect, $source, $target, $root = 'global') { @@ -91,6 +93,8 @@ class Copy * @param array|\Magento\Framework\Object $source * @param string $root * @return array $data + * + * @api */ public function getDataFromFieldset($fieldset, $aspect, $source, $root = 'global') { diff --git a/lib/internal/Magento/Framework/Object/Copy/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Object/Copy/Config/Data/Proxy.php new file mode 100644 index 0000000000000000000000000000000000000000..ea974307333d3da54ea344c57d8b72331f30a44f --- /dev/null +++ b/lib/internal/Magento/Framework/Object/Copy/Config/Data/Proxy.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Object\Copy\Config\Data; + +/** + * Proxy class for @see \Magento\Framework\Object\Copy\Config\Data + */ +class Proxy extends \Magento\Framework\Object\Copy\Config\Data +{ + /** + * Object Manager instance + * + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $_objectManager = null; + + /** + * Proxied instance name + * + * @var string + */ + protected $_instanceName = null; + + /** + * Proxied instance + * + * @var \Magento\Framework\Object\Copy\Config\Data + */ + protected $_subject = null; + + /** + * Instance shareability flag + * + * @var bool + */ + protected $_isShared = null; + + /** + * Proxy constructor + * + * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @param string $instanceName + * @param bool $shared + */ + public function __construct( + \Magento\Framework\ObjectManagerInterface $objectManager, + $instanceName = '\\Magento\\Framework\\Object\\Copy\\Config\\Data', + $shared = true + ) { + $this->_objectManager = $objectManager; + $this->_instanceName = $instanceName; + $this->_isShared = $shared; + } + + /** + * @return array + */ + public function __sleep() + { + return ['_subject', '_isShared']; + } + + /** + * Retrieve ObjectManager from global scope + * + * @return void + */ + public function __wakeup() + { + $this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + } + + /** + * Clone proxied instance + * + * @return void + */ + public function __clone() + { + $this->_subject = clone $this->_getSubject(); + } + + /** + * Get proxied instance + * + * @return \Magento\Framework\Object\Copy\Config\Data + */ + protected function _getSubject() + { + if (!$this->_subject) { + $this->_subject = true === $this->_isShared + ? $this->_objectManager->get($this->_instanceName) + : $this->_objectManager->create($this->_instanceName); + } + return $this->_subject; + } + + /** + * {@inheritdoc} + */ + public function merge(array $config) + { + return $this->_getSubject()->merge($config); + } + + /** + * {@inheritdoc} + */ + public function get($path = null, $default = null) + { + return $this->_getSubject()->get($path, $default); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + return $this->_getSubject()->reset(); + } +} diff --git a/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php b/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php index ad3864500bbc9502a1d8d6ecbaa296c76ac82d19..895377327281b908c2a8d0944a9415bbdb400275 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php +++ b/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php @@ -8,6 +8,11 @@ namespace Magento\Framework\ObjectManager\Profiler; class FactoryDecorator implements \Magento\Framework\ObjectManager\FactoryInterface { + /** + * Name of the class that generates logging wrappers + */ + const GENERATOR_NAME = 'Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger'; + /** * @var \Magento\Framework\ObjectManager\FactoryInterface */ @@ -45,9 +50,12 @@ class FactoryDecorator implements \Magento\Framework\ObjectManager\FactoryInterf { $this->log->startCreating($requestedType); $result = $this->subject->create($requestedType, $arguments); - $loggerClassName = get_class($result) . "\\Logger"; - $wrappedResult = new $loggerClassName($result, $this->log); - $this->log->stopCreating($result); - return $wrappedResult; + if ($requestedType !== self::GENERATOR_NAME) { + $loggerClassName = get_class($result) . "\\Logger"; + $wrappedResult = new $loggerClassName($result, $this->log); + $this->log->stopCreating($result); + $result = $wrappedResult; + } + return $result; } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Profiler/FactoryDecoratorTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Profiler/FactoryDecoratorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1d0fa91e42286c72588946788f40491de72e925e --- /dev/null +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Profiler/FactoryDecoratorTest.php @@ -0,0 +1,75 @@ +<?php +/*** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\ObjectManager\Test\Unit\Profiler; + +class FactoryDecoratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * Name of the base class to wrap in logger + */ + const CLASS_NAME = 'Magento\Test\Di\WrappedClass'; + + /** + * Name of the wrapper class that does logging + */ + const LOGGER_NAME = 'Magento\Test\Di\WrappedClass\Logger'; + + /** + * Name of the class that generates wrappers - should not be wrapped by logger + */ + const GENERATOR_NAME = 'Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger'; + + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\ObjectManager\FactoryInterface*/ + private $objectManagerMock; + + /** @var \Magento\Framework\ObjectManager\Profiler\FactoryDecorator */ + private $model; + + public function setUp() + { + require_once __DIR__ . '/../_files/logger_classes.php'; + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->objectManagerMock = $this->getMockBuilder('Magento\Framework\ObjectManager\FactoryInterface') + ->disableOriginalConstructor() + ->getMock(); + + // Instantiate SUT + $this->model = $objectManager->getObject( + 'Magento\Framework\ObjectManager\Profiler\FactoryDecorator', + ['subject' => $this->objectManagerMock] + ); + } + + public function testCreate() + { + $baseObjectName = self::CLASS_NAME; + $baseObject = new $baseObjectName(); + + $arguments = [1, 2, 3]; + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with(self::CLASS_NAME, $arguments) + ->willReturn($baseObject); + + $this->assertInstanceOf(self::LOGGER_NAME, $this->model->create(self::CLASS_NAME, $arguments)); + } + + public function testCreateNeglectGenerator() + { + $arguments = [1, 2, 3]; + $loggerMock = $this->getMockBuilder(self::GENERATOR_NAME)->disableOriginalConstructor()->getMock(); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with(self::GENERATOR_NAME, $arguments) + ->willReturn($loggerMock); + + $this->assertSame($loggerMock, $this->model->create(self::GENERATOR_NAME, $arguments)); + } +} diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/_files/logger_classes.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/_files/logger_classes.php new file mode 100644 index 0000000000000000000000000000000000000000..da593225f2079af53a730190a56afc0cb7715400 --- /dev/null +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/_files/logger_classes.php @@ -0,0 +1,26 @@ +<?php +/*** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +//@codingStandardsIgnoreStart +namespace Magento\Test\Di { + + /** + * Test classes used for \Magento\Framework\ObjectManager\Test\Unit\Profiler\FactoryDecoratorTest + */ + class WrappedClass + { + + } +} + +namespace Magento\Test\Di\WrappedClass { + + class Logger + { + + } +} +//@codingStandardsIgnoreEnd diff --git a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php index 9a0c8b2925b027ea2f72dfe536820fe2d3888181..980a8ff079c9b6f7c369099d801144ada04fdcee 100644 --- a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php +++ b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php @@ -89,4 +89,11 @@ interface PriceCurrencyInterface * @return \Magento\Framework\Model\AbstractModel */ public function getCurrency($scope = null, $currency = null); + + /** + * @param null|string|bool|int|\Magento\Framework\App\ScopeInterface $scope + * @param \Magento\Framework\Model\AbstractModel|string|null $currency + * @return string + */ + public function getCurrencySymbol($scope = null, $currency = null); } diff --git a/lib/internal/Magento/Framework/Pricing/Render/Amount.php b/lib/internal/Magento/Framework/Pricing/Render/Amount.php index f58510aaf55fcbe87f23b80bcd733fcd614117de..a17c6d08af4c5bf66afd4e9832f5b237c9858ba7 100644 --- a/lib/internal/Magento/Framework/Pricing/Render/Amount.php +++ b/lib/internal/Magento/Framework/Pricing/Render/Amount.php @@ -142,6 +142,14 @@ class Amount extends Template implements AmountRenderInterface return $this->priceCurrency->getCurrency()->getCurrencyCode(); } + /** + * @return string + */ + public function getDisplayCurrencySymbol() + { + return $this->priceCurrency->getCurrencySymbol(); + } + /** * @return bool */ diff --git a/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php b/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php index 66d0dc84a169876c2a7b025697b75fd93194eb4d..3779c69dd31ca6781a6a75b44f78c907518640df 100644 --- a/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php +++ b/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php @@ -50,6 +50,11 @@ interface AmountRenderInterface */ public function getDisplayCurrencyCode(); + /** + * @return string + */ + public function getDisplayCurrencySymbol(); + /** * @return string */ diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php index 552cee4846656bd5fcdc6ce7d996bcadc0cb9535..fbf8d4f834f2fb7b1cc54af8ac75c6aad527c174 100644 --- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php +++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php @@ -127,6 +127,15 @@ class AmountTest extends \PHPUnit_Framework_TestCase $this->assertEquals($result, $this->model->formatCurrency($amount, $includeContainer, $precision)); } + public function testGetDisplayCurrencySymbol() + { + $currencySymbol = '$'; + $this->priceCurrency->expects($this->once()) + ->method('getCurrencySymbol') + ->willReturn($currencySymbol); + $this->assertEquals($currencySymbol, $this->model->getDisplayCurrencySymbol()); + } + /** * Test case for getAdjustmentRenders method through toHtml() */ diff --git a/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php b/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php index 8552cba5a5d2458014a60e259857fc5c01becf90..6824dca09e58c813d114dba3f3c720dad27a710d 100644 --- a/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php +++ b/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php @@ -44,7 +44,7 @@ class AttributeTypeResolver implements AttributeTypeResolverInterface $config = isset($data[$context]) ? $data[$context] : []; $output = get_class($value); if (isset($config[$attributeCode])) { - $type = $config[$attributeCode]; + $type = $config[$attributeCode]['type']; $output = $this->typeProcessor->getArrayItemType($type); if (!(class_exists($output) || interface_exists($output))) { throw new \LogicException( diff --git a/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php new file mode 100644 index 0000000000000000000000000000000000000000..0a98c9e39e8c2f135b962bfbf0ef51d44924c183 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection; + +use Magento\Framework\Phrase; +use Magento\Framework\Api\AttributeInterface; +use Magento\Framework\Api\AttributeValue; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Zend\Code\Reflection\MethodReflection; +use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Framework\Api\AttributeTypeResolverInterface; + +/** + * Processes custom attributes and produces an array for the data. + */ +class CustomAttributesProcessor +{ + /** + * @var DataObjectProcessor + */ + private $dataObjectProcessor; + + /** + * @var AttributeTypeResolverInterface + */ + private $attributeTypeResolver; + + /** + * @param DataObjectProcessor $dataObjectProcessor + * @param AttributeTypeResolverInterface $typeResolver + */ + public function __construct( + DataObjectProcessor $dataObjectProcessor, + AttributeTypeResolverInterface $typeResolver + ) { + $this->dataObjectProcessor = $dataObjectProcessor; + $this->attributeTypeResolver = $typeResolver; + } + + /** + * Writes out the custom attributes for a given object into an array. + * + * @param CustomAttributesDataInterface $objectWithCustomAttributes + * @param string $dataObjectType + * @return array + */ + public function buildOutputDataArray(CustomAttributesDataInterface $objectWithCustomAttributes, $dataObjectType) + { + $customAttributes = $objectWithCustomAttributes->getCustomAttributes(); + $result = []; + foreach ($customAttributes as $customAttribute) { + $result[] = $this->convertCustomAttribute($customAttribute, $dataObjectType); + } + return $result; + } + + /** + * Convert custom_attribute object to use flat array structure + * + * @param AttributeInterface $customAttribute + * @param string $dataObjectType + * @return array + */ + private function convertCustomAttribute(AttributeInterface $customAttribute, $dataObjectType) + { + $data = []; + $data[AttributeValue::ATTRIBUTE_CODE] = $customAttribute->getAttributeCode(); + $value = $customAttribute->getValue(); + if (is_object($value)) { + $type = $this->attributeTypeResolver->resolveObjectType( + $customAttribute->getAttributeCode(), + $value, + $dataObjectType + ); + $value = $this->dataObjectProcessor->buildOutputDataArray($value, $type); + } elseif (is_array($value)) { + $valueResult = []; + foreach ($value as $singleValue) { + if (is_object($singleValue)) { + $type = $this->attributeTypeResolver->resolveObjectType( + $customAttribute->getAttributeCode(), + $singleValue, + $dataObjectType + ); + $singleValue = $this->dataObjectProcessor->buildOutputDataArray($singleValue, $type); + } + // Cannot cast to a type because the type is unknown + $valueResult[] = $singleValue; + } + $value = $valueResult; + } + $data[AttributeValue::VALUE] = $value; + return $data; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php index 2b9977d7355f565e808b15cc539c7c892f38afa6..4adceb83c7720be26a310fe8d7dea3f19726219c 100644 --- a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php @@ -14,54 +14,54 @@ use Zend\Code\Reflection\ClassReflection; use Zend\Code\Reflection\MethodReflection; /** - * Data object processor for de-serialization using class reflection + * Data object processor for array serialization using class reflection */ class DataObjectProcessor { - const IS_METHOD_PREFIX = 'is'; - const HAS_METHOD_PREFIX = 'has'; - const GETTER_PREFIX = 'get'; - const SERVICE_INTERFACE_METHODS_CACHE_PREFIX = 'serviceInterfaceMethodsMap'; - const BASE_MODEL_CLASS = 'Magento\Framework\Model\AbstractExtensibleModel'; - /** - * @var \Magento\Framework\Cache\FrontendInterface + * @var MethodsMap */ - protected $cache; + private $methodsMapProcessor; /** - * @var TypeProcessor + * @var TypeCaster */ - protected $typeProcessor; + private $typeCaster; /** - * @var array + * @var FieldNamer */ - protected $dataInterfaceMethodsMap = []; + private $fieldNamer; /** - * @var array + * @var ExtensionAttributesProcessor */ - protected $serviceInterfaceMethodsMap = []; + private $extensionAttributesProcessor; /** - * @var \Magento\Framework\Api\AttributeTypeResolverInterface + * @var CustomAttributesProcessor */ - protected $attributeTypeResolver; + private $customAttributesProcessor; /** - * @param \Magento\Framework\Cache\FrontendInterface $cache - * @param TypeProcessor $typeProcessor - * @param \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver + * @param MethodsMap $methodsMapProcessor + * @param TypeCaster $typeCaster + * @param FieldNamer $fieldNamer + * @param CustomAttributesProcessor $customAttributesProcessor + * @param ExtensionAttributesProcessor $extensionAttributesProcessor */ public function __construct( - \Magento\Framework\Cache\FrontendInterface $cache, - TypeProcessor $typeProcessor, - \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver + MethodsMap $methodsMapProcessor, + TypeCaster $typeCaster, + FieldNamer $fieldNamer, + CustomAttributesProcessor $customAttributesProcessor, + ExtensionAttributesProcessor $extensionAttributesProcessor ) { - $this->cache = $cache; - $this->typeProcessor = $typeProcessor; - $this->attributeTypeResolver = $typeResolver; + $this->methodsMapProcessor = $methodsMapProcessor; + $this->typeCaster = $typeCaster; + $this->fieldNamer = $fieldNamer; + $this->extensionAttributesProcessor = $extensionAttributesProcessor; + $this->customAttributesProcessor = $customAttributesProcessor; } /** @@ -74,43 +74,36 @@ class DataObjectProcessor */ public function buildOutputDataArray($dataObject, $dataObjectType) { - $methods = $this->getMethodsMap($dataObjectType); + $methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType); $outputData = []; /** @var MethodReflection $method */ - foreach ($methods as $methodName => $methodReflectionData) { - // any method with parameter(s) gets ignored because we do not know the type and value of - // the parameter(s), so we are not able to process - if ($methodReflectionData['parameterCount'] > 0) { + foreach (array_keys($methods) as $methodName) { + if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) { continue; } - $returnType = $methodReflectionData['type']; - if (substr($methodName, 0, 2) === self::IS_METHOD_PREFIX) { - $value = $dataObject->{$methodName}(); - if ($value === null && !$methodReflectionData['isRequired']) { - continue; - } - $key = SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 2)); - $outputData[$key] = $this->castValueToType($value, $returnType); - } elseif (substr($methodName, 0, 3) === self::HAS_METHOD_PREFIX) { - $value = $dataObject->{$methodName}(); - if ($value === null && !$methodReflectionData['isRequired']) { - continue; - } - $key = SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3)); - $outputData[$key] = $this->castValueToType($value, $returnType); - } elseif (substr($methodName, 0, 3) === self::GETTER_PREFIX) { - $value = $dataObject->{$methodName}(); - if ($methodName === 'getCustomAttributes' && $value === []) { - continue; - } - if ($value === null && !$methodReflectionData['isRequired']) { - continue; - } - $key = SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3)); - if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES) { - $value = $this->convertCustomAttributes($value, $dataObjectType); - } elseif (is_object($value) && !($value instanceof Phrase)) { + + $value = $dataObject->{$methodName}(); + $isMethodReturnValueRequired = $this->methodsMapProcessor->isMethodReturnValueRequired( + $dataObjectType, + $methodName + ); + if ($value === null && !$isMethodReturnValueRequired) { + continue; + } + + $returnType = $this->methodsMapProcessor->getMethodReturnType($dataObjectType, $methodName); + $key = $this->fieldNamer->getFieldNameForMethodName($methodName); + if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES && $value === []) { + continue; + } + + if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES) { + $value = $this->customAttributesProcessor->buildOutputDataArray($dataObject, $dataObjectType); + } elseif ($key === "extension_attributes") { + $value = $this->extensionAttributesProcessor->buildOutputDataArray($value, $returnType); + } else { + if (is_object($value) && !($value instanceof Phrase)) { $value = $this->buildOutputDataArray($value, $returnType); } elseif (is_array($value)) { $valueResult = []; @@ -119,192 +112,16 @@ class DataObjectProcessor if (is_object($singleValue) && !($singleValue instanceof Phrase)) { $singleValue = $this->buildOutputDataArray($singleValue, $arrayElementType); } - $valueResult[] = $this->castValueToType($singleValue, $arrayElementType); + $valueResult[] = $this->typeCaster->castValueToType($singleValue, $arrayElementType); } $value = $valueResult; + } else { + $value = $this->typeCaster->castValueToType($value, $returnType); } - $outputData[$key] = $this->castValueToType($value, $returnType); } - } - return $outputData; - } - /** - * Cast the output type to the documented type. This helps for output purposes. - * - * @param mixed $value - * @param string $type - * @return mixed - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function castValueToType($value, $type) - { - if ($value === null) { - return null; + $outputData[$key] = $value; } - - if ($type === "int" || $type === "integer") { - return (int)$value; - } - - if ($type === "string") { - return (string)$value; - } - - if ($type === "bool" || $type === "boolean" || $type === "true" || $type == "false") { - return (bool)$value; - } - - if ($type === "float") { - return (float)$value; - } - - if ($type === "double") { - return (double)$value; - } - - return $value; - } - - /** - * Get return type by interface name and method - * - * @param string $interfaceName - * @param string $methodName - * @return string - */ - public function getMethodReturnType($interfaceName, $methodName) - { - return $this->getMethodsMap($interfaceName)[$methodName]['type']; - } - - /** - * Convert array of custom_attributes to use flat array structure - * - * @param \Magento\Framework\Api\AttributeInterface[] $customAttributes - * @param string $dataObjectType - * @return array - */ - protected function convertCustomAttributes($customAttributes, $dataObjectType) - { - $result = []; - foreach ((array)$customAttributes as $customAttribute) { - $result[] = $this->convertCustomAttribute($customAttribute, $dataObjectType); - } - return $result; - } - - /** - * Convert custom_attribute object to use flat array structure - * - * @param \Magento\Framework\Api\AttributeInterface $customAttribute - * @param string $dataObjectType - * @return array - */ - protected function convertCustomAttribute($customAttribute, $dataObjectType) - { - $data = []; - $data[AttributeValue::ATTRIBUTE_CODE] = $customAttribute->getAttributeCode(); - $value = $customAttribute->getValue(); - if (is_object($value)) { - $type = $this->attributeTypeResolver->resolveObjectType( - $customAttribute->getAttributeCode(), - $value, - $dataObjectType - ); - $value = $this->buildOutputDataArray($value, $type); - } elseif (is_array($value)) { - $valueResult = []; - foreach ($value as $singleValue) { - if (is_object($singleValue)) { - $type = $this->attributeTypeResolver->resolveObjectType( - $customAttribute->getAttributeCode(), - $singleValue, - $dataObjectType - ); - $singleValue = $this->buildOutputDataArray($singleValue, $type); - } - // Cannot cast to a type because the type is unknown - $valueResult[] = $singleValue; - } - $value = $valueResult; - } - $data[AttributeValue::VALUE] = $value; - return $data; - } - - /** - * Return service interface or Data interface methods loaded from cache - * - * @param string $interfaceName - * @return array - * <pre> - * Service methods' reflection data stored in cache as 'methodName' => 'returnType' - * ex. - * [ - * 'create' => '\Magento\Customer\Api\Data\Customer', - * 'validatePassword' => 'boolean' - * ] - * </pre> - */ - public function getMethodsMap($interfaceName) - { - $key = self::SERVICE_INTERFACE_METHODS_CACHE_PREFIX . "-" . md5($interfaceName); - if (!isset($this->serviceInterfaceMethodsMap[$key])) { - $methodMap = $this->cache->load($key); - if ($methodMap) { - $this->serviceInterfaceMethodsMap[$key] = unserialize($methodMap); - } else { - $methodMap = $this->getMethodMapViaReflection($interfaceName); - $this->serviceInterfaceMethodsMap[$key] = $methodMap; - $this->cache->save(serialize($this->serviceInterfaceMethodsMap[$key]), $key); - } - } - return $this->serviceInterfaceMethodsMap[$key]; - } - - /** - * Use reflection to load the method information - * - * @param string $interfaceName - * @return array - */ - protected function getMethodMapViaReflection($interfaceName) - { - $methodMap = []; - $class = new ClassReflection($interfaceName); - $baseClassMethods = false; - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { - // Include all the methods of classes inheriting from AbstractExtensibleObject. - // Ignore all the methods of AbstractExtensibleModel's parent classes - if ($method->class === self::BASE_MODEL_CLASS) { - $baseClassMethods = true; - } elseif ($baseClassMethods) { - // ReflectionClass::getMethods() sorts the methods by class (lowest in inheritance tree first) - // then by the order they are defined in the class definition - break; - } - - if ($this->isSuitableMethod($method)) { - $methodMap[$method->getName()] = $this->typeProcessor->getGetterReturnType($method); - } - } - return $methodMap; - } - - /** - * Determines if the method is suitable to be used by the processor. - * - * @param \ReflectionMethod $method - * @return bool - */ - protected function isSuitableMethod($method) - { - $isSuitableMethodType = !($method->isConstructor() || $method->isFinal() - || $method->isStatic() || $method->isDestructor()); - - $isExcludedMagicMethod = strpos($method->getName(), '__') === 0; - return $isSuitableMethodType && !$isExcludedMagicMethod; + return $outputData; } } diff --git a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php new file mode 100644 index 0000000000000000000000000000000000000000..c2d2d69c758f8b4676da91027080fb5736d5c880 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php @@ -0,0 +1,189 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection; + +use Magento\Framework\Api\Config\Reader as ExtensionAttributesConfigReader; +use Magento\Framework\Api\Config\Converter; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Phrase; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Framework\Api\ExtensionAttributesInterface; +use Magento\Framework\Reflection\MethodsMap; +use Zend\Code\Reflection\MethodReflection; + +/** + * Processes extension attributes and produces an array for the data. + */ +class ExtensionAttributesProcessor +{ + /** + * @var DataObjectProcessor + */ + private $dataObjectProcessor; + + /** + * @var MethodsMap + */ + private $methodsMapProcessor; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var ExtensionAttributesConfigReader + */ + private $configReader; + + /** + * @var bool + */ + private $isPermissionChecked; + + /** + * @var FieldNamer + */ + private $fieldNamer; + + /** + * @var TypeCaster + */ + private $typeCaster; + + /** + * @param DataObjectProcessor $dataObjectProcessor + * @param MethodsMap $methodsMapProcessor + * @param TypeCaster $typeCaster + * @param FieldNamer $fieldNamer + * @param AuthorizationInterface $authorization + * @param ExtensionAttributesConfigReader $configReader + * @param bool $isPermissionChecked + */ + public function __construct( + DataObjectProcessor $dataObjectProcessor, + MethodsMap $methodsMapProcessor, + TypeCaster $typeCaster, + FieldNamer $fieldNamer, + AuthorizationInterface $authorization, + ExtensionAttributesConfigReader $configReader, + $isPermissionChecked = false + ) { + $this->dataObjectProcessor = $dataObjectProcessor; + $this->methodsMapProcessor = $methodsMapProcessor; + $this->typeCaster = $typeCaster; + $this->fieldNamer = $fieldNamer; + $this->authorization = $authorization; + $this->configReader = $configReader; + $this->isPermissionChecked = $isPermissionChecked; + } + + /** + * Writes out the extension attributes in an array. + * + * @param ExtensionAttributeInterface $dataObject + * @param string $dataObjectType + * @return array + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function buildOutputDataArray(ExtensionAttributesInterface $dataObject, $dataObjectType) + { + $methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType); + $outputData = []; + + /** @var MethodReflection $method */ + foreach (array_keys($methods) as $methodName) { + if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) { + continue; + } + + $key = $this->fieldNamer->getFieldNameForMethodName($methodName); + if ($this->isPermissionChecked && !$this->isAttributePermissionValid($dataObjectType, $key)) { + continue; + } + + $value = $dataObject->{$methodName}(); + if ($value === null) { + // all extension attributes are optional so don't need to check if isRequired + continue; + } + + $returnType = $this->methodsMapProcessor->getMethodReturnType($dataObjectType, $methodName); + + if (is_object($value) && !($value instanceof Phrase)) { + $value = $this->dataObjectProcessor->buildOutputDataArray($value, $returnType); + } elseif (is_array($value)) { + $valueResult = []; + $arrayElementType = substr($returnType, 0, -2); + foreach ($value as $singleValue) { + if (is_object($singleValue) && !($singleValue instanceof Phrase)) { + $singleValue = $this->dataObjectProcessor->buildOutputDataArray( + $singleValue, + $arrayElementType + ); + } + $valueResult[] = $this->typeCaster->castValueToType($singleValue, $arrayElementType); + } + $value = $valueResult; + } else { + $value = $this->typeCaster->castValueToType($value, $returnType); + } + + $outputData[$key] = $value; + } + + return $outputData; + } + + /** + * @param string $dataObjectType + * @param string $attributeCode + * @return bool + */ + private function isAttributePermissionValid($dataObjectType, $attributeCode) + { + $typeName = $this->getRegularTypeForExtensionAttributesType($dataObjectType); + $permissions = $this->getPermissionsForTypeAndMethod($typeName, $attributeCode); + foreach ($permissions as $permission) { + if (!$this->authorization->isAllowed($permission)) { + return false; + } + } + + return true; + } + + /** + * @param string $name + * @return string + */ + private function getRegularTypeForExtensionAttributesType($name) + { + return ltrim(str_replace('ExtensionInterface', 'Interface', $name), '\\'); + } + + /** + * @param string $typeName + * @param string $attributeCode + * @return string[] A list of permissions + */ + private function getPermissionsForTypeAndMethod($typeName, $attributeCode) + { + // TODO: Move function to the Config and hope this is cached + $attributes = $this->configReader->read(); + if (isset($attributes[$typeName]) && isset($attributes[$typeName][$attributeCode])) { + $attributeMetadata = $attributes[$typeName][$attributeCode]; + $permissions = []; + foreach ($attributeMetadata[Converter::RESOURCE_PERMISSIONS] as $permission) { + $permissions[] = $permission; + } + return $permissions; + } + + return []; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/FieldNamer.php b/lib/internal/Magento/Framework/Reflection/FieldNamer.php new file mode 100644 index 0000000000000000000000000000000000000000..2e105b9a4f666e2073124e8817a9208762740389 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/FieldNamer.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection; + +use Magento\Framework\Phrase; +use Magento\Framework\Api\AttributeValue; +use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Zend\Code\Reflection\ClassReflection; +use Zend\Code\Reflection\MethodReflection; + +/** + * Determines the name to use for fields in a data output array given method metadata. + */ +class FieldNamer +{ + const IS_METHOD_PREFIX = 'is'; + const HAS_METHOD_PREFIX = 'has'; + const GETTER_PREFIX = 'get'; + + /** + * Converts a method's name into a data field name. + * + * @param string $methodName + * @return string|null + */ + public function getFieldNameForMethodName($methodName) + { + if (substr($methodName, 0, 2) === self::IS_METHOD_PREFIX) { + return SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 2)); + } elseif (substr($methodName, 0, 3) === self::HAS_METHOD_PREFIX) { + return SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3)); + } elseif (substr($methodName, 0, 3) === self::GETTER_PREFIX) { + return SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3)); + } + + return null; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/MethodsMap.php b/lib/internal/Magento/Framework/Reflection/MethodsMap.php new file mode 100644 index 0000000000000000000000000000000000000000..36b446d1b870a32d6f468bf77b293a63b024fabe --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/MethodsMap.php @@ -0,0 +1,180 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection; + +use Zend\Code\Reflection\ClassReflection; +use Zend\Code\Reflection\MethodReflection; + +/** + * Gathers method metadata information. + */ +class MethodsMap +{ + const SERVICE_INTERFACE_METHODS_CACHE_PREFIX = 'serviceInterfaceMethodsMap'; + const BASE_MODEL_CLASS = 'Magento\Framework\Model\AbstractExtensibleModel'; + + /** + * @var \Magento\Framework\Cache\FrontendInterface + */ + private $cache; + + /** + * @var TypeProcessor + */ + private $typeProcessor; + + /** + * @var array + */ + private $serviceInterfaceMethodsMap = []; + + /** + * @var FieldNamer + */ + private $fieldNamer; + + /** + * @param \Magento\Framework\Cache\FrontendInterface $cache + * @param TypeProcessor $typeProcessor + * @param \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver + * @param FieldNamer $fieldNamer + */ + public function __construct( + \Magento\Framework\Cache\FrontendInterface $cache, + TypeProcessor $typeProcessor, + \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver, + FieldNamer $fieldNamer + ) { + $this->cache = $cache; + $this->typeProcessor = $typeProcessor; + $this->attributeTypeResolver = $typeResolver; + $this->fieldNamer = $fieldNamer; + } + + /** + * Get return type by type name and method name. + * + * @param string $typeName + * @param string $methodName + * @return string + */ + public function getMethodReturnType($typeName, $methodName) + { + return $this->getMethodsMap($typeName)[$methodName]['type']; + } + + /** + * Return service interface or Data interface methods loaded from cache + * + * @param string $interfaceName + * @return array + * <pre> + * Service methods' reflection data stored in cache as 'methodName' => 'returnType' + * ex. + * [ + * 'create' => '\Magento\Customer\Api\Data\Customer', + * 'validatePassword' => 'boolean' + * ] + * </pre> + */ + public function getMethodsMap($interfaceName) + { + $key = self::SERVICE_INTERFACE_METHODS_CACHE_PREFIX . "-" . md5($interfaceName); + if (!isset($this->serviceInterfaceMethodsMap[$key])) { + $methodMap = $this->cache->load($key); + if ($methodMap) { + $this->serviceInterfaceMethodsMap[$key] = unserialize($methodMap); + } else { + $methodMap = $this->getMethodMapViaReflection($interfaceName); + $this->serviceInterfaceMethodsMap[$key] = $methodMap; + $this->cache->save(serialize($this->serviceInterfaceMethodsMap[$key]), $key); + } + } + return $this->serviceInterfaceMethodsMap[$key]; + } + + /** + * Use reflection to load the method information + * + * @param string $interfaceName + * @return array + */ + private function getMethodMapViaReflection($interfaceName) + { + $methodMap = []; + $class = new ClassReflection($interfaceName); + $baseClassMethods = false; + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + // Include all the methods of classes inheriting from AbstractExtensibleObject. + // Ignore all the methods of AbstractExtensibleModel's parent classes + if ($method->class === self::BASE_MODEL_CLASS) { + $baseClassMethods = true; + } elseif ($baseClassMethods) { + // ReflectionClass::getMethods() sorts the methods by class (lowest in inheritance tree first) + // then by the order they are defined in the class definition + break; + } + + if ($this->isSuitableMethod($method)) { + $methodMap[$method->getName()] = $this->typeProcessor->getGetterReturnType($method); + } + } + return $methodMap; + } + + /** + * Determines if the method is suitable to be used by the processor. + * + * @param \ReflectionMethod $method + * @return bool + */ + private function isSuitableMethod($method) + { + $isSuitableMethodType = !($method->isConstructor() || $method->isFinal() + || $method->isStatic() || $method->isDestructor()); + + $isExcludedMagicMethod = strpos($method->getName(), '__') === 0; + return $isSuitableMethodType && !$isExcludedMagicMethod; + } + + /** + * Determines if the given method's on the given type is suitable for an output data array. + * + * @param string $type + * @param string $methodName + * @return bool + */ + public function isMethodValidForDataField($type, $methodName) + { + $methods = $this->getMethodsMap($type); + if (isset($methods[$methodName])) { + $methodMetadata = $methods[$methodName]; + // any method with parameter(s) gets ignored because we do not know the type and value of + // the parameter(s), so we are not able to process + if ($methodMetadata['parameterCount'] > 0) { + return false; + } + + return $this->fieldNamer->getFieldNameForMethodName($methodName) !== null; + } + + return false; + } + + /** + * If the method has only non-null return types + * + * @param string $type + * @param string $methodName + * @return bool + */ + public function isMethodReturnValueRequired($type, $methodName) + { + $methods = $this->getMethodsMap($type); + return $methods[$methodName]['isRequired']; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php index 8391d4643482d17da3c717dbbc732b7c43002b19..68902f3851380d368c88ca8e2357bada00a2de7d 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php @@ -62,7 +62,13 @@ class AttributeTypeResolverTest extends \PHPUnit_Framework_TestCase $code = 'some_code'; $value = new \stdClass(); $context = '\Some\Class'; - $config = ['Some\Class' => ['some_code' => '\Magento\Framework\Object']]; + $config = [ + 'Some\Class' => [ + 'some_code' => [ + 'type' => '\Magento\Framework\Object', + ], + ] + ]; $this->typeProcessor->expects($this->once()) ->method('getArrayItemType') @@ -82,7 +88,13 @@ class AttributeTypeResolverTest extends \PHPUnit_Framework_TestCase $code = 'some_code'; $value = new \stdClass(); $context = '\Some\Class'; - $config = ['Some\Class' => ['some_code' => '\Some\Class']]; + $config = [ + 'Some\Class' => [ + 'some_code' => [ + 'type' => '\Some\Class', + ] + ] + ]; $this->typeProcessor->expects($this->once()) ->method('getArrayItemType') diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesObject.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesObject.php new file mode 100644 index 0000000000000000000000000000000000000000..66077712a43b337238402748fe9ccf9c4b0db5f2 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesObject.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Reflection\Test\Unit; + +use Magento\Framework\Api\ExtensionAttributesInterface; + +/** + * Dummy data object to be used by ExtensionAttributesProcessorTest + */ +class ExtensionAttributesObject implements ExtensionAttributesInterface +{ + /** + * @return string + */ + public function getAttrName() + { + return 'attrName'; + } + + /** + * @return bool + */ + public function isActive() + { + return false; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesProcessorTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesProcessorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8da0c1f734f710da1c69d28c9e9f4ac02999db86 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesProcessorTest.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection\Test\Unit; + +use Magento\Framework\Api\Config\Converter; +use Magento\Framework\Api\Config\Reader; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Reflection\ExtensionAttributesProcessor; +use Magento\Framework\Reflection\FieldNamer; +use Magento\Framework\Reflection\MethodsMap; +use Magento\Framework\Reflection\TypeCaster; + +/** + * ExtensionAttributesProcessor test + */ +class ExtensionAttributesProcessorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ExtensionAttributesProcessor + */ + private $model; + + /** + * @var DataObjectProcessor + */ + private $dataObjectProcessorMock; + + /** + * @var MethodsMap + */ + private $methodsMapProcessorMock; + + /** + * @var FieldNamer + */ + private $fieldNamerMock; + + /** + * @var TypeCaster + */ + private $typeCasterMock; + + /** + * @var Reader + */ + private $configReaderMock; + + /** + * @var AuthorizationInterface + */ + private $authorizationMock; + /** + * Set up helper. + */ + public function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->dataObjectProcessorMock = $this->getMockBuilder('Magento\Framework\Reflection\DataObjectProcessor') + ->disableOriginalConstructor() + ->getMock(); + $this->methodsMapProcessorMock = $this->getMockBuilder('Magento\Framework\Reflection\MethodsMap') + ->disableOriginalConstructor() + ->getMock(); + $this->typeCasterMock = $this->getMockBuilder('Magento\Framework\Reflection\TypeCaster') + ->disableOriginalConstructor() + ->getMock(); + $this->fieldNamerMock = $this->getMockBuilder('Magento\Framework\Reflection\FieldNamer') + ->disableOriginalConstructor() + ->getMock(); + $this->configReaderMock = $this->getMockBuilder('Magento\Framework\Api\Config\Reader') + ->disableOriginalConstructor() + ->getMock(); + $this->authorizationMock = $this->getMockBuilder('Magento\Framework\AuthorizationInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->model = $objectManager->getObject( + 'Magento\Framework\Reflection\ExtensionAttributesProcessor', + [ + 'dataObjectProcessor' => $this->dataObjectProcessorMock, + 'methodsMapProcessor' => $this->methodsMapProcessorMock, + 'typeCaster' => $this->typeCasterMock, + 'fieldNamer' => $this->fieldNamerMock, + 'authorization' => $this->authorizationMock, + 'configReader' => $this->configReaderMock, + 'isPermissionChecked' => true, + ] + ); + } + + /** + * @param bool $isPermissionAllowed + * @param array $expectedValue + * @dataProvider buildOutputDataArrayWithPermissionProvider + */ + public function testBuildOutputDataArrayWithPermission($isPermissionAllowed, $expectedValue) + { + $dataObject = new \Magento\Framework\Reflection\Test\Unit\ExtensionAttributesObject(); + $dataObjectType = 'Magento\Framework\Reflection\Test\Unit\ExtensionAttributesObject'; + $methodName = 'getAttrName'; + $attributeName = 'attr_name'; + $attributeValue = 'attrName'; + + $this->methodsMapProcessorMock->expects($this->once()) + ->method('getMethodsMap') + ->with($dataObjectType) + ->will($this->returnValue([$methodName => []])); + $this->methodsMapProcessorMock->expects($this->once()) + ->method('isMethodValidForDataField') + ->with($dataObjectType, $methodName) + ->will($this->returnValue(true)); + $this->fieldNamerMock->expects($this->once()) + ->method('getFieldNameForMethodName') + ->with($methodName) + ->will($this->returnValue($attributeName)); + $permissionName = 'Magento_Permission'; + $this->configReaderMock->expects($this->once()) + ->method('read') + ->will($this->returnValue([ + $dataObjectType => [ + $attributeName => [ Converter::RESOURCE_PERMISSIONS => [ $permissionName ] ] + ] + ])); + $this->authorizationMock->expects($this->once()) + ->method('isAllowed') + ->with($permissionName) + ->will($this->returnValue($isPermissionAllowed)); + + if ($isPermissionAllowed) { + $this->methodsMapProcessorMock->expects($this->once()) + ->method('getMethodReturnType') + ->with($dataObjectType, $methodName) + ->will($this->returnValue('string')); + $this->typeCasterMock->expects($this->once()) + ->method('castValueToType') + ->with($attributeValue, 'string') + ->will($this->returnValue($attributeValue)); + } + + $value = $this->model->buildOutputDataArray( + $dataObject, + $dataObjectType + ); + + $this->assertEquals( + $value, + $expectedValue + ); + } + + public function buildOutputDataArrayWithPermissionProvider() + { + return [ + 'permission allowed' => [ + true, + [ + 'attr_name' => 'attrName', + ], + ], + 'permission not allowed' => [ + false, + [], + ], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/FieldNamerTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/FieldNamerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1d498a940ec1a437ccc546e3629df37767748eeb --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/FieldNamerTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Reflection\Test\Unit; + +use Magento\Framework\Reflection\FieldNamer; + +/** + * Field namer Test + */ +class FieldNamerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FieldNamer + */ + private $model; + + /** + * Set up helper. + */ + public function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject('Magento\Framework\Reflection\FieldNamer'); + } + + /** + * @param string $methodName + * @param string $expectedName + * @dataProvider methodNameProvider + */ + public function testGetFieldNameForMethodName($methodName, $expectedName) + { + $value = $this->model->getFieldNameForMethodName($methodName); + $this->assertEquals($value, $expectedName); + } + + /** + * @return array + */ + public function methodNameProvider() + { + return [ + 'isMethod' => ['isValid', 'valid'], + 'getMethod' => ['getValue', 'value'], + 'hasMethod' => ['hasStuff', 'stuff'], + 'randomMethod' => ['randomMethod', null], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/MethodsMapTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/MethodsMapTest.php new file mode 100644 index 0000000000000000000000000000000000000000..82f859897da9bfdcdd8f26de0ef7d3201712f699 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/MethodsMapTest.php @@ -0,0 +1,174 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection\Test\Unit; + +use Magento\Framework\Reflection\MethodsMap; +use Magento\Framework\Reflection\TypeProcessor; +use Magento\Framework\Reflection\FieldNamer; + +/** + * MethodsMap test + */ +class MethodsMapTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var MethodsMap + */ + private $model; + + /** + * Set up helper. + */ + public function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $cacheMock = $this->getMockBuilder('Magento\Framework\Cache\FrontendInterface') + ->getMockForAbstractClass(); + $cacheMock->expects($this->any()) + ->method('save'); + $cacheMock->expects($this->any()) + ->method('load') + ->will($this->returnValue(null)); + + $attributeTypeResolverMock = $this->getMockBuilder('Magento\Framework\Api\AttributeTypeResolverInterface') + ->getMockForAbstractClass(); + $fieldNamerMock = $this->getMockBuilder('Magento\Framework\Reflection\FieldNamer') + ->getMockForAbstractClass(); + $this->model = $objectManager->getObject( + 'Magento\Framework\Reflection\MethodsMap', + [ + 'cache' => $cacheMock, + 'typeProcessor' => new TypeProcessor(), + 'typeResolver' => $attributeTypeResolverMock, + 'fieldNamer' => $fieldNamerMock, + ] + ); + } + + public function testGetMethodReturnType() + { + $this->assertEquals( + 'string', + $this->model->getMethodReturnType('Magento\Framework\Reflection\FieldNamer', 'getFieldNameForMethodName') + ); + $this->assertEquals( + 'mixed', + $this->model->getMethodReturnType('Magento\Framework\Reflection\TypeCaster', 'castValueToType') + ); + $this->assertEquals( + 'array', + $this->model->getMethodReturnType('Magento\Framework\Reflection\MethodsMap', 'getMethodsMap') + ); + } + + public function testGetMethodsMap() + { + $methodsMap = $this->model->getMethodsMap('Magento\Framework\Reflection\MethodsMap'); + $this->assertEquals( + $methodsMap, + [ + 'getMethodReturnType' => [ + 'type' => 'string', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2, + ], + 'getMethodsMap' => [ + 'type' => 'array', + 'isRequired' => true, + 'description' => "<pre> Service methods' reflection data stored in cache as 'methodName' => " + . "'returnType' ex. [ 'create' => '\Magento\Customer\Api\Data\Customer', 'validatePassword' " + . "=> 'boolean' ] </pre>", + 'parameterCount' => 1, + ], + 'isMethodValidForDataField' => [ + 'type' => 'bool', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2, + ], + 'isMethodReturnValueRequired' => [ + 'type' => 'bool', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2, + ], + ] + ); + } + + /** + * @param string $type + * @param string $methodName + * @param bool $expectedResult + * @dataProvider isMethodValidForDataFieldProvider + */ + public function testIsMethodValidForDataField($type, $methodName, $expectedResult) + { + $this->assertEquals($this->model->isMethodValidForDataField($type, $methodName), $expectedResult); + } + + /** + * @return array + */ + public function isMethodValidForDataFieldProvider() + { + return [ + 'MethodsMap#isMethodValidForDataField' => [ + 'Magento\Framework\Reflection\MethodsMap', + 'isMethodValidForDataField', + false, + ], + 'DataObject#getAttrName' => [ + 'Magento\Framework\Reflection\Test\Unit\DataObject', + 'getAttrName', + true, + ], + 'DataObject#isActive' => [ + 'Magento\Framework\Reflection\Test\Unit\DataObject', + 'isActive', + true, + ], + ]; + } + + /** + * @param string $type + * @param string $methodName + * @param bool $expectedResult + * @dataProvider isMethodReturnValueRequiredProvider + */ + public function testIsMethodReturnValueRequired($type, $methodName, $expectedResult) + { + $this->assertEquals($this->model->isMethodValidForDataField($type, $methodName), $expectedResult); + } + + /** + * @return array + */ + public function isMethodReturnValueRequiredProvider() + { + return [ + 'DataObject#getAttrName' => [ + 'Magento\Framework\Reflection\Test\Unit\DataObject', + 'getAttrName', + true, + ], + 'DataObject#isActive' => [ + 'Magento\Framework\Reflection\Test\Unit\DataObject', + 'isActive', + true, + ], + 'FieldNamer#getFieldNameForMethodName' => [ + 'Magento\Framework\Reflection\FieldNamer', + 'getFieldNameForMethodName', + false, + ], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeCasterTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeCasterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8f7afdbbedcb05e103f92c6c982d5a0686cf7481 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeCasterTest.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Reflection\Test\Unit; + +use Magento\Framework\Reflection\TypeCaster; + +/** + * Type caster Test + */ +class TypeCasterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var TypeCaster + */ + private $model; + + /** + * Set up helper. + */ + public function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject('Magento\Framework\Reflection\TypeCaster'); + } + + /** + * @param mixed $origValue + * @param string $typeToCast + * @param mixed $expectedValue + * @dataProvider typeCastValueProvider + */ + public function testCastValues($origValue, $typeToCast, $expectedValue) + { + $value = $this->model->castValueToType($origValue, $typeToCast); + $this->assertTrue($value === $expectedValue); + } + + /** + * @return array + */ + public function typeCastValueProvider() + { + return [ + 'null' => [null, 'int', null], + 'int' => ['1', 'int', 1], + 'integer' => ['1', 'integer', 1], + 'string' => ['1', 'string', '1'], + 'bool 0' => ['0', 'bool', false], + 'bool 1' => ['1', 'bool', true], + 'boolean 0' => ['0', 'boolean', false], + 'boolean 1' => ['1', 'boolean', true], + 'true' => ['1', 'true', true], + 'false' => ['0', 'false', false], + 'float' => ['1.03', 'float', 1.03], + 'double' => ['1.30', 'double', 1.30], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Reflection/TypeCaster.php b/lib/internal/Magento/Framework/Reflection/TypeCaster.php new file mode 100644 index 0000000000000000000000000000000000000000..185b86c1d880b14061f3271da5de4885bf56cf29 --- /dev/null +++ b/lib/internal/Magento/Framework/Reflection/TypeCaster.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Reflection; + +/** + * Casts values to the type given. + */ +class TypeCaster +{ + /** + * Cast the output type to the documented type. This helps for consistent output (e.g. JSON). + * + * @param mixed $value + * @param string $type + * @return mixed + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function castValueToType($value, $type) + { + if ($value === null) { + return null; + } + + if ($type === "int" || $type === "integer") { + return (int)$value; + } + + if ($type === "string") { + return (string)$value; + } + + if ($type === "bool" || $type === "boolean" || $type === "true" || $type == "false") { + return (bool)$value; + } + + if ($type === "float") { + return (float)$value; + } + + if ($type === "double") { + return (double)$value; + } + + return $value; + } +} diff --git a/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php b/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php index 1cfc1477a7957b46fe1f58f752749b3d8a4193a2..1b534b4dbf4991c3c5f71f2b8009bd6efd9f20df 100644 --- a/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php +++ b/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php @@ -33,7 +33,7 @@ class DataProviderFactory ScopeConfigInterface $scopeConfig, $configPath, $dataProviders, - $scope = ScopeInterface::SCOPE_DEFAULT + $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT ) { $this->objectManager = $objectManager; $configValue = $scopeConfig->getValue($configPath, $scope); diff --git a/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php b/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php index bba9824e19083f82aa5c249338bf16d2d4da5575..80ac66bb04e62950ae6a7d4dedc4e0fd8dd148d4 100644 --- a/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php +++ b/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php @@ -33,7 +33,7 @@ class IntervalFactory ScopeConfigInterface $scopeConfig, $configPath, $intervals, - $scope = ScopeInterface::SCOPE_DEFAULT + $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT ) { $this->objectManager = $objectManager; $configValue = $scopeConfig->getValue($configPath, $scope); diff --git a/lib/internal/Magento/Framework/Search/Request/Mapper.php b/lib/internal/Magento/Framework/Search/Request/Mapper.php index e2dcbef74cda6ab201ed6bdd7dc6b3dcda916cd7..d69d5e4e6d6689170096767166c1cc44fba09d6d 100644 --- a/lib/internal/Magento/Framework/Search/Request/Mapper.php +++ b/lib/internal/Magento/Framework/Search/Request/Mapper.php @@ -14,6 +14,11 @@ use Magento\Framework\Phrase; */ class Mapper { + /** + * @var QueryInterface + */ + private $rootQuery; + /** * @var array */ @@ -45,9 +50,9 @@ class Mapper private $objectManager; /** - * @var QueryInterface + * @var string */ - private $rootQuery = null; + private $rootQueryName; /** * @param \Magento\Framework\ObjectManagerInterface $objectManager @@ -70,26 +75,26 @@ class Mapper $this->queries = $queries; $this->aggregations = $aggregations; $this->filters = $filters; - - $this->rootQuery = $this->get($rootQueryName); + $this->rootQueryName = $rootQueryName; } /** * Get Query Interface by name * - * @param string $queryName * @return QueryInterface * @throws \Exception * @throws \InvalidArgumentException * @throws StateException */ - private function get($queryName) + public function getRootQuery() { - $this->mappedQueries = []; - $this->mappedFilters = []; - $query = $this->mapQuery($queryName); - $this->validate(); - return $query; + if (!$this->rootQuery) { + $this->mappedQueries = []; + $this->mappedFilters = []; + $this->rootQuery = $this->mapQuery($this->rootQueryName); + $this->validate(); + } + return $this->rootQuery; } /** @@ -304,14 +309,6 @@ class Mapper $this->validateNotUsed($this->filters, $this->mappedFilters, 'Filter %1 is not used in request hierarchy'); } - /** - * @return QueryInterface - */ - public function getRootQuery() - { - return $this->rootQuery; - } - /** * Build BucketInterface[] from array * diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php index 3b37a0bf3d574e10044e2bdea2f8c4b2c2eccebb..5b0736c5fa2ef890b1362bae14c6d9b70f38bf97 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php @@ -6,6 +6,7 @@ namespace Magento\Framework\Search\Test\Unit\Adapter\Mysql; +use Magento\Framework\App\Config\ScopeConfigInterface; use \Magento\Framework\Search\Adapter\Mysql\Dimensions; use Magento\Framework\Search\Adapter\Mysql\Dimensions as DimensionsBuilder; @@ -103,7 +104,7 @@ class DimensionsTest extends \PHPUnit_Framework_TestCase { $tableAlias = 'search_index'; $name = 'scope'; - $value = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT; + $value = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; $scopeId = -123456; $this->dimension->expects($this->once()) diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php index 0d7b52e00630adde68df4600dd353d1d7e314207..232f789de3ae77014a26ee1e251dbcce6235d0e5 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\Search\Test\Unit\Dynamic; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Search\Dynamic\IntervalInterface; use Magento\Framework\App\ScopeInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -63,7 +64,7 @@ class IntervalFactoryTest extends \PHPUnit_Framework_TestCase { $this->scopeConfig->expects($this->once()) ->method('getValue') - ->with(self::CONFIG_PATH, ScopeInterface::SCOPE_DEFAULT) + ->with(self::CONFIG_PATH, ScopeConfigInterface::SCOPE_TYPE_DEFAULT) ->willReturn(self::CONFIG_PATH . 't'); $this->objectManager->expects($this->once()) ->method('create') @@ -83,7 +84,7 @@ class IntervalFactoryTest extends \PHPUnit_Framework_TestCase { $this->scopeConfig->expects($this->once()) ->method('getValue') - ->with(self::CONFIG_PATH, ScopeInterface::SCOPE_DEFAULT) + ->with(self::CONFIG_PATH, ScopeConfigInterface::SCOPE_TYPE_DEFAULT) ->willReturn('t'); $this->factoryCreate(); @@ -97,7 +98,7 @@ class IntervalFactoryTest extends \PHPUnit_Framework_TestCase { $this->scopeConfig->expects($this->once()) ->method('getValue') - ->with(self::CONFIG_PATH, ScopeInterface::SCOPE_DEFAULT) + ->with(self::CONFIG_PATH, ScopeConfigInterface::SCOPE_TYPE_DEFAULT) ->willReturn(self::CONFIG_PATH . 't'); $this->objectManager->expects($this->once()) ->method('create') diff --git a/lib/internal/Magento/Framework/Session/Generic.php b/lib/internal/Magento/Framework/Session/Generic.php index dd7da87c933a3fff8be209f77e93b77c5eebea1a..bfaf48d74ee9d7f4851e181e8e69f06437da8993 100644 --- a/lib/internal/Magento/Framework/Session/Generic.php +++ b/lib/internal/Magento/Framework/Session/Generic.php @@ -7,38 +7,4 @@ namespace Magento\Framework\Session; class Generic extends SessionManager { - /** - * Constructor - * - * @param \Magento\Framework\App\Request\Http $request - * @param SidResolverInterface $sidResolver - * @param \Magento\Framework\Session\Config\ConfigInterface $sessionConfig - * @param SaveHandlerInterface $saveHandler - * @param ValidatorInterface $validator - * @param StorageInterface $storage - * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager - * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory - */ - public function __construct( - \Magento\Framework\App\Request\Http $request, - SidResolverInterface $sidResolver, - \Magento\Framework\Session\Config\ConfigInterface $sessionConfig, - SaveHandlerInterface $saveHandler, - ValidatorInterface $validator, - StorageInterface $storage, - \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, - \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory - ) { - parent::__construct( - $request, - $sidResolver, - $sessionConfig, - $saveHandler, - $validator, - $storage, - $cookieManager, - $cookieMetadataFactory - ); - $this->start(); - } } diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php index 17636fa0776804b433129011dd25b2edaec33275..c66672e87ca8f6b7098a3ecf17a0df582dad6072 100644 --- a/lib/internal/Magento/Framework/Session/SessionManager.php +++ b/lib/internal/Magento/Framework/Session/SessionManager.php @@ -11,6 +11,7 @@ use Magento\Framework\Session\Config\ConfigInterface; /** * Session Manager + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SessionManager implements SessionManagerInterface { @@ -87,8 +88,11 @@ class SessionManager implements SessionManagerInterface protected $cookieMetadataFactory; /** - * Constructor - * + * @var \Magento\Framework\App\State + */ + private $appState; + + /** * @param \Magento\Framework\App\Request\Http $request * @param SidResolverInterface $sidResolver * @param ConfigInterface $sessionConfig @@ -97,6 +101,8 @@ class SessionManager implements SessionManagerInterface * @param StorageInterface $storage * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\App\State $appState + * @throws \Magento\Framework\Exception\SessionException */ public function __construct( \Magento\Framework\App\Request\Http $request, @@ -106,7 +112,8 @@ class SessionManager implements SessionManagerInterface ValidatorInterface $validator, StorageInterface $storage, \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, - \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\App\State $appState ) { $this->request = $request; $this->sidResolver = $sidResolver; @@ -116,9 +123,11 @@ class SessionManager implements SessionManagerInterface $this->storage = $storage; $this->cookieManager = $cookieManager; $this->cookieMetadataFactory = $cookieMetadataFactory; + $this->appState = $appState; // Enable session.use_only_cookies ini_set('session.use_only_cookies', '1'); + $this->start(); } /** @@ -152,12 +161,25 @@ class SessionManager implements SessionManagerInterface /** * Configure session handler and start session * + * @throws \Magento\Framework\Exception\SessionException * @return $this */ public function start() { if (!$this->isSessionExists()) { \Magento\Framework\Profiler::start('session_start'); + + try { + $this->appState->getAreaCode(); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + throw new \Magento\Framework\Exception\SessionException( + new \Magento\Framework\Phrase( + 'Area code not set: Area code must be set before starting a session.' + ), + $e + ); + } + // Need to apply the config options so they can be ready by session_start $this->initIniOptions(); $this->registerSaveHandler(); diff --git a/lib/internal/Magento/Framework/Shell.php b/lib/internal/Magento/Framework/Shell.php index 0652d49d039e0c43d21375d3d87153db78b1e243..2372ba4088571c89d7865d4a9d2b7523fdd6e1eb 100644 --- a/lib/internal/Magento/Framework/Shell.php +++ b/lib/internal/Magento/Framework/Shell.php @@ -49,7 +49,7 @@ class Shell implements ShellInterface $command = $this->commandRenderer->render($command, $arguments); $this->log($command); - $disabled = explode(',', ini_get('disable_functions')); + $disabled = explode(',', str_replace(' ', ',', ini_get('disable_functions'))); if (in_array('exec', $disabled)) { throw new Exception\LocalizedException(new \Magento\Framework\Phrase("exec function is disabled.")); } diff --git a/lib/internal/Magento/Framework/Simplexml/Config.php b/lib/internal/Magento/Framework/Simplexml/Config.php index 4e1265ebd4a4c180d7bbc4a8b84b0334b5b1676a..2f45cd6550b87a9417f2e50355866765c26d2a2b 100644 --- a/lib/internal/Magento/Framework/Simplexml/Config.php +++ b/lib/internal/Magento/Framework/Simplexml/Config.php @@ -118,12 +118,12 @@ class Config */ public function getNode($path = null) { - if (!$this->_xml instanceof Element) { + if (!$this->getXml() instanceof Element) { return false; } elseif ($path === null) { - return $this->_xml; + return $this->getXml(); } else { - return $this->_xml->descend($path); + return $this->getXml()->descend($path); } } @@ -135,11 +135,12 @@ class Config */ public function getXpath($xpath) { - if (empty($this->_xml)) { + $xml = $this->getXml(); + if (empty($xml)) { return false; } - if (!($result = @$this->_xml->xpath($xpath))) { + if (!($result = @$xml->xpath($xpath))) { return false; } @@ -476,7 +477,7 @@ class Config if (!empty($string)) { $xml = simplexml_load_string($string, $this->_elementClass); if ($xml) { - $this->_xml = $xml; + $this->setXml($xml); return true; } } @@ -493,7 +494,7 @@ class Config { $xml = simplexml_import_dom($dom, $this->_elementClass); if ($xml) { - $this->_xml = $xml; + $this->setXml($xml); return true; } @@ -510,7 +511,7 @@ class Config */ public function setNode($path, $value, $overwrite = true) { - $this->_xml->setNode($path, $value, $overwrite); + $this->getXml()->setNode($path, $value, $overwrite); return $this; } @@ -575,4 +576,14 @@ class Config { $this->_xml = null; } + + /** + * Getter for xml element + * + * @return Element + */ + protected function getXml() + { + return $this->_xml; + } } diff --git a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php index 46d9a13f0bf0a5290fd6086d78aab72a9bf502ca..69e2260002ce05ff621e5b63baf613c7e30a879b 100644 --- a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php @@ -73,9 +73,10 @@ class UrlTest extends \PHPUnit_Framework_TestCase } /** + * @param bool $resolve * @return \Magento\Framework\Url\RouteParamsResolverFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected function getRouteParamsResolver() + protected function getRouteParamsResolverFactory($resolve = true) { $routeParamsResolverFactoryMock = $this->getMock( 'Magento\Framework\Url\RouteParamsResolverFactory', @@ -84,8 +85,10 @@ class UrlTest extends \PHPUnit_Framework_TestCase '', false ); - $routeParamsResolverFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->routeParamsResolverMock)); + if ($resolve) { + $routeParamsResolverFactoryMock->expects($this->once())->method('create') + ->will($this->returnValue($this->routeParamsResolverMock)); + } return $routeParamsResolverFactoryMock; } @@ -152,7 +155,10 @@ class UrlTest extends \PHPUnit_Framework_TestCase public function testGetBaseUrlNotLinkType() { $model = $this->getUrlModel( - ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver()] + [ + 'scopeResolver' => $this->scopeResolverMock, + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory() + ] ); $baseUrl = 'base-url'; @@ -184,9 +190,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase $requestMock = $this->getRequestMock(); $routeConfigMock = $this->getMock('Magento\Framework\App\Route\ConfigInterface'); $model = $this->getUrlModel( - ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), + [ + 'scopeResolver' => $this->scopeResolverMock, + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'queryParamsResolver' => $this->queryParamsResolverMock, - 'request' => $requestMock, 'routeConfig' => $routeConfigMock, ] + 'request' => $requestMock, 'routeConfig' => $routeConfigMock, + ] ); $baseUrl = 'http://localhost/index.php/'; @@ -218,7 +227,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase { $model = $this->getUrlModel([ 'scopeResolver' => $this->scopeResolverMock, - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), ]); $model->setData('route_path', 'catalog/product/view'); @@ -233,7 +242,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase { $model = $this->getUrlModel([ 'scopeResolver' => $this->scopeResolverMock, - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'request' => $this->getRequestMock() ]); $model->setData('route_name', 'catalog'); @@ -251,7 +260,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(['foo' => 'bar', 'true' => false])); $model = $this->getUrlModel([ 'scopeResolver' => $this->scopeResolverMock, - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'request' => $this->getRequestMock() ]); @@ -270,7 +279,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase $request->expects($this->once())->method('getAlias')->will($this->returnValue('/catalog/product/view/')); $model = $this->getUrlModel([ 'scopeResolver' => $this->scopeResolverMock, - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'request' => $request, ]); @@ -299,9 +308,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase $requestMock = $this->getRequestMock(); $routeConfigMock = $this->getMock('Magento\Framework\App\Route\ConfigInterface'); $model = $this->getUrlModel( - ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), + [ + 'scopeResolver' => $this->scopeResolverMock, + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'queryParamsResolver' => $this->queryParamsResolverMock, - 'request' => $requestMock, 'routeConfig' => $routeConfigMock, ] + 'request' => $requestMock, 'routeConfig' => $routeConfigMock, + ] ); $baseUrl = 'http://localhost/index.php/'; @@ -332,9 +344,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase $requestMock = $this->getRequestMock(); $routeConfigMock = $this->getMock('Magento\Framework\App\Route\ConfigInterface'); $model = $this->getUrlModel( - ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), + [ + 'scopeResolver' => $this->scopeResolverMock, + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'queryParamsResolver' => $this->queryParamsResolverMock, - 'request' => $requestMock, 'routeConfig' => $routeConfigMock, ] + 'request' => $requestMock, 'routeConfig' => $routeConfigMock, + ] ); $baseUrl = 'http://localhost/index.php/'; @@ -363,7 +378,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase 'request' => $requestMock, 'sidResolver' => $this->sidResolverMock, 'scopeResolver' => $this->scopeResolverMock, - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(false), 'queryParamsResolver' => $this->queryParamsResolverMock, ]); @@ -376,8 +391,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase public function testGetRedirectUrl() { $model = $this->getUrlModel( - ['routeParamsResolver' => $this->getRouteParamsResolver(), 'session' => $this->sessionMock, - 'sidResolver' => $this->sidResolverMock, 'queryParamsResolver' => $this->queryParamsResolverMock, ] + [ + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), + 'session' => $this->sessionMock, + 'sidResolver' => $this->sidResolverMock, + 'queryParamsResolver' => $this->queryParamsResolverMock, + ] ); $this->sidResolverMock->expects($this->once())->method('getUseSessionInUrl')->will($this->returnValue(true)); @@ -396,8 +415,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase public function testGetRedirectUrlWithSessionId() { $model = $this->getUrlModel( - ['routeParamsResolver' => $this->getRouteParamsResolver(), 'session' => $this->sessionMock, - 'sidResolver' => $this->sidResolverMock, 'queryParamsResolver' => $this->queryParamsResolverMock, ] + [ + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(false), + 'session' => $this->sessionMock, + 'sidResolver' => $this->sidResolverMock, + 'queryParamsResolver' => $this->queryParamsResolverMock, + ] ); $this->sidResolverMock->expects($this->once())->method('getUseSessionInUrl')->will($this->returnValue(true)); @@ -422,7 +445,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase public function testGetRouteUrlWithValidUrl() { - $model = $this->getUrlModel(['routeParamsResolver' => $this->getRouteParamsResolver()]); + $model = $this->getUrlModel(['routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(false)]); $this->routeParamsResolverMock->expects($this->never())->method('unsetData'); $this->assertEquals('http://example.com', $model->getRouteUrl('http://example.com')); @@ -485,7 +508,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase $urlSecurityInfoMock = $this->getMock('Magento\Framework\Url\SecurityInfoInterface'); $model = $this->getUrlModel([ 'urlSecurityInfo' => $urlSecurityInfoMock, - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'scopeResolver' => $this->scopeResolverMock, 'scopeConfig' => $this->scopeConfig, ]); @@ -530,7 +553,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase public function testGetConfigDataWithSecureIsForcedParam() { $model = $this->getUrlModel([ - 'routeParamsResolver' => $this->getRouteParamsResolver(), + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), 'scopeResolver' => $this->scopeResolverMock, 'scopeConfig' => $this->scopeConfig, ]); @@ -562,8 +585,13 @@ class UrlTest extends \PHPUnit_Framework_TestCase { $requestMock = $this->getRequestMock(); $model = $this->getUrlModel( - ['session' => $this->sessionMock, 'request' => $requestMock, 'sidResolver' => $this->sidResolverMock, - 'scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), ] + [ + 'session' => $this->sessionMock, + 'request' => $requestMock, + 'sidResolver' => $this->sidResolverMock, + 'scopeResolver' => $this->scopeResolverMock, + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), + ] ); $requestMock->expects($this->once()) @@ -585,8 +613,13 @@ class UrlTest extends \PHPUnit_Framework_TestCase { $requestMock = $this->getRequestMock(); $model = $this->getUrlModel( - ['session' => $this->sessionMock, 'request' => $requestMock, 'sidResolver' => $this->sidResolverMock, - 'scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), ] + [ + 'session' => $this->sessionMock, + 'request' => $requestMock, + 'sidResolver' => $this->sidResolverMock, + 'scopeResolver' => $this->scopeResolverMock, + 'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(), + ] ); $requestMock->expects($this->once())->method('getHttpHost')->will($this->returnValue('localhost')); diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php index efffc744e3fe46da126f655f16a95c9f1c5bd4a7..697e461df050f1f623075655acd3ea4fc13ccaa9 100644 --- a/lib/internal/Magento/Framework/Url.php +++ b/lib/internal/Magento/Framework/Url.php @@ -56,6 +56,7 @@ namespace Magento\Framework; * - G: route_path * - H: route_url * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInterface { @@ -133,8 +134,12 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt /** * @var \Magento\Framework\Url\RouteParamsResolverInterface */ - protected $_routeParamsResolver; + private $_routeParamsResolver; + /** + * @var \Magento\Framework\Url\RouteParamsResolverFactory + */ + private $_routeParamsResolverFactory; /** * @var \Magento\Framework\Url\ScopeResolverInterface */ @@ -157,7 +162,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver * @param \Magento\Framework\Session\Generic $session * @param \Magento\Framework\Session\SidResolverInterface $sidResolver - * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver + * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param string $scopeType @@ -171,7 +176,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt \Magento\Framework\Url\ScopeResolverInterface $scopeResolver, \Magento\Framework\Session\Generic $session, \Magento\Framework\Session\SidResolverInterface $sidResolver, - \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver, + \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory, \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, $scopeType, @@ -183,7 +188,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt $this->_scopeResolver = $scopeResolver; $this->_session = $session; $this->_sidResolver = $sidResolver; - $this->_routeParamsResolver = $routeParamsResolver->create(); + $this->_routeParamsResolverFactory = $routeParamsResolverFactory; $this->_queryParamsResolver = $queryParamsResolver; $this->_scopeConfig = $scopeConfig; $this->_scopeType = $scopeType; @@ -322,10 +327,10 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt */ protected function _getType() { - if (!$this->_routeParamsResolver->hasData('type')) { - $this->_routeParamsResolver->setData('type', self::DEFAULT_URL_TYPE); + if (!$this->getRouteParamsResolver()->hasData('type')) { + $this->getRouteParamsResolver()->setData('type', self::DEFAULT_URL_TYPE); } - return $this->_routeParamsResolver->getType(); + return $this->getRouteParamsResolver()->getType(); } /** @@ -335,27 +340,27 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt */ protected function _isSecure() { - if ($this->_routeParamsResolver->hasData('secure_is_forced')) { - return (bool) $this->_routeParamsResolver->getData('secure'); + if ($this->getRouteParamsResolver()->hasData('secure_is_forced')) { + return (bool) $this->getRouteParamsResolver()->getData('secure'); } if (!$this->_getScope()->isUrlSecure()) { return false; } - if (!$this->_routeParamsResolver->hasData('secure')) { + if (!$this->getRouteParamsResolver()->hasData('secure')) { if ($this->_getType() == UrlInterface::URL_TYPE_LINK) { $pathSecure = $this->_urlSecurityInfo->isSecure('/' . $this->_getActionPath()); - $this->_routeParamsResolver->setData('secure', $pathSecure); + $this->getRouteParamsResolver()->setData('secure', $pathSecure); } elseif ($this->_getType() == UrlInterface::URL_TYPE_STATIC) { $isRequestSecure = $this->_getRequest()->isSecure(); - $this->_routeParamsResolver->setData('secure', $isRequestSecure); + $this->getRouteParamsResolver()->setData('secure', $isRequestSecure); } else { - $this->_routeParamsResolver->setData('secure', true); + $this->getRouteParamsResolver()->setData('secure', true); } } - return $this->_routeParamsResolver->getData('secure'); + return $this->getRouteParamsResolver()->getData('secure'); } /** @@ -367,7 +372,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt public function setScope($params) { $this->setData('scope', $this->_scopeResolver->getScope($params)); - $this->_routeParamsResolver->setScope($this->_scopeResolver->getScope($params)); + $this->getRouteParamsResolver()->setScope($this->_scopeResolver->getScope($params)); return $this; } @@ -401,11 +406,11 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt $this->setScope($params['_scope']); } if (isset($params['_type'])) { - $this->_routeParamsResolver->setType($params['_type']); + $this->getRouteParamsResolver()->setType($params['_type']); } if (isset($params['_secure'])) { - $this->_routeParamsResolver->setSecure($params['_secure']); + $this->getRouteParamsResolver()->setSecure($params['_secure']); } /** @@ -416,13 +421,13 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt $this->_getRouteFrontName() ) ) { - $this->_routeParamsResolver->setType(UrlInterface::URL_TYPE_DIRECT_LINK); + $this->getRouteParamsResolver()->setType(UrlInterface::URL_TYPE_DIRECT_LINK); } $result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure()); // setting back the original scope $this->setScope($origScope); - $this->_routeParamsResolver->setType(self::DEFAULT_URL_TYPE); + $this->getRouteParamsResolver()->setType(self::DEFAULT_URL_TYPE); return $result; } @@ -471,7 +476,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt $key = array_shift($routePieces); if (!empty($routePieces)) { $value = array_shift($routePieces); - $this->_routeParamsResolver->setRouteParam($key, $value); + $this->getRouteParamsResolver()->setRouteParam($key, $value); } } } @@ -650,7 +655,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt */ protected function _setRouteParams(array $data, $unsetOldParams = true) { - $this->_routeParamsResolver->setRouteParams($data, $unsetOldParams); + $this->getRouteParamsResolver()->setRouteParams($data, $unsetOldParams); return $this; } @@ -661,7 +666,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt */ protected function _getRouteParams() { - return $this->_routeParamsResolver->getRouteParams(); + return $this->getRouteParamsResolver()->getRouteParams(); } /** @@ -677,7 +682,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt return $routePath; } - $this->_routeParamsResolver->unsetData('route_params'); + $this->getRouteParamsResolver()->unsetData('route_params'); if (isset($routeParams['_direct'])) { if (is_array($routeParams)) { @@ -786,7 +791,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt * this method has condition for adding default controller and action names * in case when we have params */ - $this->_routeParamsResolver->unsetData('secure'); + $this->getRouteParamsResolver()->unsetData('secure'); $fragment = null; if (isset($routeParams['_fragment'])) { $fragment = $routeParams['_fragment']; @@ -840,7 +845,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt if (!is_null($fragment)) { $url .= '#' . $fragment; } - $this->_routeParamsResolver->unsetData('secure'); + $this->getRouteParamsResolver()->unsetData('secure'); return $this->escape($url); } @@ -1038,4 +1043,17 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt $url = $this->_request->getScheme() . '://' . $this->_request->getHttpHost() . $port . $requestUri; return $url; } + + /** + * Get Route Params Resolver + * + * @return Url\RouteParamsResolverInterface + */ + protected function getRouteParamsResolver() + { + if (!$this->_routeParamsResolver) { + $this->_routeParamsResolver = $this->_routeParamsResolverFactory->create(); + } + return $this->_routeParamsResolver; + } } diff --git a/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php b/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php index 893443659220d4b68e5ec3514f25beb0ea3c7725..a31ae25c76ea41a6efea88d85ccfb5c424092143 100644 --- a/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php +++ b/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php @@ -10,7 +10,7 @@ interface ScopeResolverInterface extends \Magento\Framework\App\ScopeResolverInt /** * Retrieve area code * - * @return \Magento\Framework\Url\ScopeInterface[] + * @return string|null */ public function getAreaCode(); } diff --git a/lib/internal/Magento/Framework/UrlInterface/Proxy.php b/lib/internal/Magento/Framework/UrlInterface/Proxy.php new file mode 100644 index 0000000000000000000000000000000000000000..7ed1a1bbc7bff3df6959ec70ed2aa2c779b21636 --- /dev/null +++ b/lib/internal/Magento/Framework/UrlInterface/Proxy.php @@ -0,0 +1,212 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\UrlInterface; + +/** + * Proxy class for @see \Magento\Framework\UrlInterface + */ +class Proxy implements \Magento\Framework\UrlInterface +{ + /** + * Object Manager instance + * + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $_objectManager = null; + + /** + * Proxied instance name + * + * @var string + */ + protected $_instanceName = null; + + /** + * Proxied instance + * + * @var \Magento\Framework\UrlInterface + */ + protected $_subject = null; + + /** + * Instance shareability flag + * + * @var bool + */ + protected $_isShared = null; + + /** + * Proxy constructor + * + * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @param string $instanceName + * @param bool $shared + */ + public function __construct( + \Magento\Framework\ObjectManagerInterface $objectManager, + $instanceName = '\\Magento\\Framework\\UrlInterface', + $shared = true + ) { + $this->_objectManager = $objectManager; + $this->_instanceName = $instanceName; + $this->_isShared = $shared; + } + + /** + * @return array + */ + public function __sleep() + { + return ['_subject', '_isShared']; + } + + /** + * Retrieve ObjectManager from global scope + * + * @return void + */ + public function __wakeup() + { + $this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + } + + /** + * Clone proxied instance + * + * @return void + */ + public function __clone() + { + $this->_subject = clone $this->_getSubject(); + } + + /** + * Get proxied instance + * + * @return \Magento\Framework\UrlInterface + */ + protected function _getSubject() + { + if (!$this->_subject) { + $this->_subject = true === $this->_isShared + ? $this->_objectManager->get($this->_instanceName) + : $this->_objectManager->create($this->_instanceName); + } + return $this->_subject; + } + + /** + * {@inheritdoc} + */ + public function getUseSession() + { + return $this->_getSubject()->getUseSession(); + } + + /** + * {@inheritdoc} + */ + public function getBaseUrl($params = []) + { + return $this->_getSubject()->getBaseUrl($params); + } + + /** + * {@inheritdoc} + */ + public function getCurrentUrl() + { + return $this->_getSubject()->getCurrentUrl(); + } + + /** + * {@inheritdoc} + */ + public function getRouteUrl($routePath = null, $routeParams = null) + { + return $this->_getSubject()->getRouteUrl($routePath, $routeParams); + } + + /** + * {@inheritdoc} + */ + public function addSessionParam() + { + return $this->_getSubject()->addSessionParam(); + } + + /** + * {@inheritdoc} + */ + public function addQueryParams(array $data) + { + return $this->_getSubject()->addQueryParams($data); + } + + /** + * {@inheritdoc} + */ + public function setQueryParam($key, $data) + { + return $this->_getSubject()->setQueryParam($key, $data); + } + + /** + * {@inheritdoc} + */ + public function getUrl($routePath = null, $routeParams = null) + { + return $this->_getSubject()->getUrl($routePath, $routeParams); + } + + /** + * {@inheritdoc} + */ + public function escape($value) + { + return $this->_getSubject()->escape($value); + } + + /** + * {@inheritdoc} + */ + public function getDirectUrl($url, $params = []) + { + return $this->_getSubject()->getDirectUrl($url, $params); + } + + /** + * {@inheritdoc} + */ + public function sessionUrlVar($html) + { + return $this->_getSubject()->sessionUrlVar($html); + } + + /** + * {@inheritdoc} + */ + public function isOwnOriginUrl() + { + return $this->_getSubject()->isOwnOriginUrl(); + } + + /** + * {@inheritdoc} + */ + public function getRedirectUrl($url) + { + return $this->_getSubject()->getRedirectUrl($url); + } + + /** + * {@inheritdoc} + */ + public function setScope($params) + { + return $this->_getSubject()->setScope($params); + } +} diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php index a663b91cc745f01f164e1d30a1f6e9f04682087b..ef5679b220c2460dbc1e94d7d09760af0436fa31 100644 --- a/lib/internal/Magento/Framework/Validator/Factory.php +++ b/lib/internal/Magento/Framework/Validator/Factory.php @@ -24,6 +24,11 @@ class Factory */ protected $_configFiles = null; + /** + * @var bool + */ + private $isDefaultTranslatorInitialized = false; + /** * Initialize dependencies * @@ -36,7 +41,6 @@ class Factory ) { $this->_objectManager = $objectManager; $this->_configFiles = $moduleReader->getConfigurationFiles('validation.xml'); - $this->_initializeDefaultTranslator(); } /** @@ -46,15 +50,18 @@ class Factory */ protected function _initializeDefaultTranslator() { - // Pass translations to \Magento\Framework\TranslateInterface from validators - $translatorCallback = function () { - $argc = func_get_args(); - return (string)new \Magento\Framework\Phrase(array_shift($argc), $argc); - }; - /** @var \Magento\Framework\Translate\Adapter $translator */ - $translator = $this->_objectManager->create('Magento\Framework\Translate\Adapter'); - $translator->setOptions(['translator' => $translatorCallback]); - \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($translator); + if (!$this->isDefaultTranslatorInitialized) { + // Pass translations to \Magento\Framework\TranslateInterface from validators + $translatorCallback = function () { + $argc = func_get_args(); + return (string)new \Magento\Framework\Phrase(array_shift($argc), $argc); + }; + /** @var \Magento\Framework\Translate\Adapter $translator */ + $translator = $this->_objectManager->create('Magento\Framework\Translate\Adapter'); + $translator->setOptions(['translator' => $translatorCallback]); + \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($translator); + $this->isDefaultTranslatorInitialized = true; + } } /** @@ -66,6 +73,7 @@ class Factory */ public function getValidatorConfig() { + $this->_initializeDefaultTranslator(); return $this->_objectManager->create('Magento\Framework\Validator\Config', ['configFiles' => $this->_configFiles]); } @@ -79,6 +87,7 @@ class Factory */ public function createValidatorBuilder($entityName, $groupName, array $builderConfig = null) { + $this->_initializeDefaultTranslator(); return $this->getValidatorConfig()->createValidatorBuilder($entityName, $groupName, $builderConfig); } @@ -92,6 +101,7 @@ class Factory */ public function createValidator($entityName, $groupName, array $builderConfig = null) { + $this->_initializeDefaultTranslator(); return $this->getValidatorConfig()->createValidator($entityName, $groupName, $builderConfig); } } diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index 6c857548d18ed329d6767bbc6f35684f8205e059..da8130891b3b05e624f358c2f0fe5b1b52d7444d 100644 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -23,6 +23,12 @@ use Magento\Framework\View\Layout\ScheduledStructure; */ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Framework\View\LayoutInterface { + + /** + * Empty layout xml + */ + const LAYOUT_NODE = '<layout/>'; + /** * Layout Update module * @@ -173,7 +179,6 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra $cacheable = true ) { $this->_elementClass = 'Magento\Framework\View\Layout\Element'; - $this->setXml(simplexml_load_string('<layout/>', $this->_elementClass)); $this->_renderingOutput = new \Magento\Framework\Object(); $this->_processorFactory = $processorFactory; @@ -188,8 +193,6 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra $this->readerContextFactory = $readerContextFactory; $this->generatorContextFactory = $generatorContextFactory; - - $this->readerContext = $this->readerContextFactory->create(); } /** @@ -248,7 +251,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra $this->_update = null; } $this->_blocks = []; - $this->_xml = null; + parent::__destruct(); } /** @@ -278,14 +281,6 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra return $this; } - /** - * @return Layout\Reader\Context - */ - public function getReaderContext() - { - return $this->readerContext; - } - /** * Create structure of elements from the loaded XML configuration * @@ -297,13 +292,12 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra $cacheId = 'structure_' . $this->getUpdate()->getCacheId(); $result = $this->cache->load($cacheId); if ($result) { - /** @var Layout\Reader\Context $readerContext */ $this->readerContext = unserialize($result); } else { \Magento\Framework\Profiler::start('build_structure'); - $this->readerPool->interpret($this->readerContext, $this->getNode()); + $this->readerPool->interpret($this->getReaderContext(), $this->getNode()); \Magento\Framework\Profiler::stop('build_structure'); - $this->cache->save(serialize($this->readerContext), $cacheId, $this->getUpdate()->getHandles()); + $this->cache->save(serialize($this->getReaderContext()), $cacheId, $this->getUpdate()->getHandles()); } $generatorContext = $this->generatorContextFactory->create( @@ -314,7 +308,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra ); \Magento\Framework\Profiler::start('generate_elements'); - $this->generatorPool->process($this->readerContext, $generatorContext); + $this->generatorPool->process($this->getReaderContext(), $generatorContext); \Magento\Framework\Profiler::stop('generate_elements'); $this->addToOutputRootContainers(); @@ -1035,7 +1029,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra public function isCacheable() { $this->build(); - $cacheableXml = !(bool)count($this->_xml->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]')); + $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]')); return $this->cacheable && $cacheableXml; } @@ -1060,4 +1054,30 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra $this->isPrivate = (bool)$isPrivate; return $this; } + + /** + * Getter and lazy loader for xml element + * + * @return \Magento\Framework\Simplexml\Element + */ + protected function getXml() + { + if (!$this->_xml) { + $this->setXml(simplexml_load_string(self::LAYOUT_NODE, $this->_elementClass)); + } + return $this->_xml; + } + + /** + * Getter and lazy loader for reader context + * + * @return Layout\Reader\Context + */ + public function getReaderContext() + { + if (!$this->readerContext) { + $this->readerContext = $this->readerContextFactory->create(); + } + return $this->readerContext; + } } diff --git a/lib/internal/Magento/Framework/View/Layout/Proxy.php b/lib/internal/Magento/Framework/View/Layout/Proxy.php index 8c399f4e4161a7f847b4f22db49e7e97e8528ef2..95220e34913df356a84e427d9d72e04f5c73b6ac 100644 --- a/lib/internal/Magento/Framework/View/Layout/Proxy.php +++ b/lib/internal/Magento/Framework/View/Layout/Proxy.php @@ -6,8 +6,7 @@ namespace Magento\Framework\View\Layout; /** - * Proxy class for \Magento\Framework\View\Layout - * + * Proxy class for @see \Magento\Framework\View\Layout * @SuppressWarnings(PHPMD.ExcessivePublicCount) */ class Proxy extends \Magento\Framework\View\Layout @@ -17,42 +16,44 @@ class Proxy extends \Magento\Framework\View\Layout * * @var \Magento\Framework\ObjectManagerInterface */ - protected $objectManager; + protected $_objectManager = null; /** * Proxied instance name * * @var string */ - protected $instanceName; + protected $_instanceName = null; /** * Proxied instance * * @var \Magento\Framework\View\Layout */ - protected $subject; + protected $_subject = null; /** * Instance shareability flag * * @var bool */ - protected $isShared; + protected $_isShared = null; /** + * Proxy constructor + * * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param string $instanceName * @param bool $shared */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = 'Magento\Framework\View\Layout', + $instanceName = '\\Magento\\Framework\\View\\Layout', $shared = true ) { - $this->objectManager = $objectManager; - $this->instanceName = $instanceName; - $this->isShared = $shared; + $this->_objectManager = $objectManager; + $this->_instanceName = $instanceName; + $this->_isShared = $shared; } /** @@ -70,7 +71,7 @@ class Proxy extends \Magento\Framework\View\Layout */ public function __wakeup() { - $this->objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance(); } /** @@ -80,7 +81,7 @@ class Proxy extends \Magento\Framework\View\Layout */ public function __clone() { - $this->subject = clone $this->getSubject(); + $this->_subject = clone $this->_getSubject(); } /** @@ -88,415 +89,334 @@ class Proxy extends \Magento\Framework\View\Layout * * @return \Magento\Framework\View\Layout */ - protected function getSubject() + protected function _getSubject() { - if (!$this->subject) { - $this->subject = true === $this->isShared - ? $this->objectManager->get($this->instanceName) - : $this->objectManager->create($this->instanceName); + if (!$this->_subject) { + $this->_subject = true === $this->_isShared + ? $this->_objectManager->get($this->_instanceName) + : $this->_objectManager->create($this->_instanceName); } - return $this->subject; + return $this->_subject; } /** - * Retrieve the layout update instance - * - * @return \Magento\Framework\View\Layout\ProcessorInterface + * {@inheritdoc} + */ + public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool) + { + return $this->_getSubject()->setGeneratorPool($generatorPool); + } + + /** + * {@inheritdoc} + */ + public function setBuilder(\Magento\Framework\View\Layout\BuilderInterface $builder) + { + return $this->_getSubject()->setBuilder($builder); + } + + /** + * {@inheritdoc} + */ + public function publicBuild() + { + $this->_getSubject()->publicBuild(); + } + + /** + * {@inheritdoc} */ public function getUpdate() { - return $this->getSubject()->getUpdate(); + return $this->_getSubject()->getUpdate(); } /** - * Layout xml generation - * - * @return $this + * {@inheritdoc} */ public function generateXml() { - return $this->getSubject()->generateXml(); + return $this->_getSubject()->generateXml(); } /** - * Create structure of elements from the loaded XML configuration - * - * @return void + * {@inheritdoc} */ public function generateElements() { - $this->getSubject()->generateElements(); + $this->_getSubject()->generateElements(); } /** - * Get child block if exists - * - * @param string $parentName - * @param string $alias - * @return bool|\Magento\Framework\View\Element\AbstractBlock + * {@inheritdoc} */ public function getChildBlock($parentName, $alias) { - return $this->getSubject()->getChildBlock($parentName, $alias); + return $this->_getSubject()->getChildBlock($parentName, $alias); } /** - * Set child element into layout structure - * - * @param string $parentName - * @param string $elementName - * @param string $alias - * @return $this + * {@inheritdoc} */ public function setChild($parentName, $elementName, $alias) { - return $this->getSubject()->setChild($parentName, $elementName, $alias); + return $this->_getSubject()->setChild($parentName, $elementName, $alias); } /** - * Reorder a child of a specified element - * - * If $offsetOrSibling is null, it will put the element to the end - * If $offsetOrSibling is numeric (integer) value, it will put the element after/before specified position - * Otherwise -- after/before specified sibling - * - * @param string $parentName - * @param string $childName - * @param string|int|null $offsetOrSibling - * @param bool $after - * @return void + * {@inheritdoc} */ public function reorderChild($parentName, $childName, $offsetOrSibling, $after = true) { - $this->getSubject()->reorderChild($parentName, $childName, $offsetOrSibling, $after); + $this->_getSubject()->reorderChild($parentName, $childName, $offsetOrSibling, $after); } /** - * Remove child element from parent - * - * @param string $parentName - * @param string $alias - * @return $this + * {@inheritdoc} */ public function unsetChild($parentName, $alias) { - return $this->getSubject()->unsetChild($parentName, $alias); + return $this->_getSubject()->unsetChild($parentName, $alias); } /** - * Get list of child names - * - * @param string $parentName - * @return array + * {@inheritdoc} */ public function getChildNames($parentName) { - return $this->getSubject()->getChildNames($parentName); + return $this->_getSubject()->getChildNames($parentName); } /** - * Get list of child blocks - * - * Returns associative array of <alias> => <block instance> - * - * @param string $parentName - * @return array + * {@inheritdoc} */ public function getChildBlocks($parentName) { - return $this->getSubject()->getChildBlocks($parentName); + return $this->_getSubject()->getChildBlocks($parentName); } /** - * Get child name by alias - * - * @param string $parentName - * @param string $alias - * @return bool|string + * {@inheritdoc} */ public function getChildName($parentName, $alias) { - return $this->getSubject()->getChildName($parentName, $alias); + return $this->_getSubject()->getChildName($parentName, $alias); } /** - * Find an element in layout, render it and return string with its output - * - * @param string $name - * @param bool $useCache - * @return string + * {@inheritdoc} */ public function renderElement($name, $useCache = true) { - return $this->getSubject()->renderElement($name, $useCache); + return $this->_getSubject()->renderElement($name, $useCache); } /** - * Add element to parent group - * - * @param string $blockName - * @param string $parentGroupName - * @return bool + * {@inheritdoc} + */ + public function renderNonCachedElement($name) + { + return $this->_getSubject()->renderNonCachedElement($name); + } + + /** + * {@inheritdoc} */ public function addToParentGroup($blockName, $parentGroupName) { - return $this->getSubject()->addToParentGroup($blockName, $parentGroupName); + return $this->_getSubject()->addToParentGroup($blockName, $parentGroupName); } /** - * Get element names for specified group - * - * @param string $blockName - * @param string $groupName - * @return array + * {@inheritdoc} */ public function getGroupChildNames($blockName, $groupName) { - return $this->getSubject()->getGroupChildNames($blockName, $groupName); + return $this->_getSubject()->getGroupChildNames($blockName, $groupName); } /** - * Check if element exists in layout structure - * - * @param string $name - * @return bool + * {@inheritdoc} */ public function hasElement($name) { - return $this->getSubject()->hasElement($name); + return $this->_getSubject()->hasElement($name); } /** - * Get property value of an element - * - * @param string $name - * @param string $attribute - * @return mixed + * {@inheritdoc} */ public function getElementProperty($name, $attribute) { - return $this->getSubject()->getElementProperty($name, $attribute); + return $this->_getSubject()->getElementProperty($name, $attribute); } /** - * Whether specified element is a block - * - * @param string $name - * @return bool + * {@inheritdoc} */ public function isBlock($name) { - return $this->getSubject()->isBlock($name); + return $this->_getSubject()->isBlock($name); } /** - * Checks if element with specified name is container - * - * @param string $name - * @return bool + * {@inheritdoc} + */ + public function isUiComponent($name) + { + return $this->_getSubject()->isUiComponent($name); + } + + /** + * {@inheritdoc} */ public function isContainer($name) { - return $this->getSubject()->isContainer($name); + return $this->_getSubject()->isContainer($name); } /** - * Whether the specified element may be manipulated externally - * - * @param string $name - * @return bool + * {@inheritdoc} */ public function isManipulationAllowed($name) { - return $this->getSubject()->isManipulationAllowed($name); + return $this->_getSubject()->isManipulationAllowed($name); } /** - * Save block in blocks registry - * - * @param string $name - * @param \Magento\Framework\View\Element\AbstractBlock $block - * @return $this + * {@inheritdoc} */ public function setBlock($name, $block) { - return $this->getSubject()->setBlock($name, $block); + return $this->_getSubject()->setBlock($name, $block); } /** - * Remove block from registry - * - * @param string $name - * @return $this + * {@inheritdoc} */ public function unsetElement($name) { - return $this->getSubject()->unsetElement($name); + return $this->_getSubject()->unsetElement($name); } /** - * Block Factory - * - * @param string $type - * @param string $name - * @param array $attributes - * @return \Magento\Framework\View\Element\AbstractBlock + * {@inheritdoc} */ - public function createBlock($type, $name = '', array $attributes = []) + public function createBlock($type, $name = '', array $arguments = []) { - return $this->getSubject()->createBlock($type, $name, $attributes); + return $this->_getSubject()->createBlock($type, $name, $arguments); } /** - * Add a block to registry, create new object if needed - * - * @param string|\Magento\Framework\View\Element\AbstractBlock $block - * @param string $name - * @param string $parent - * @param string $alias - * @return \Magento\Framework\View\Element\AbstractBlock + * {@inheritdoc} */ public function addBlock($block, $name = '', $parent = '', $alias = '') { - return $this->getSubject()->addBlock($block, $name, $parent, $alias); + return $this->_getSubject()->addBlock($block, $name, $parent, $alias); } /** - * Insert container into layout structure - * - * @param string $name - * @param string $label - * @param array $options - * @param string $parent - * @param string $alias - * @return void + * {@inheritdoc} */ public function addContainer($name, $label, array $options = [], $parent = '', $alias = '') { - $this->getSubject()->addContainer($name, $label, $options, $parent, $alias); + $this->_getSubject()->addContainer($name, $label, $options, $parent, $alias); } /** - * Rename element in layout and layout structure - * - * @param string $oldName - * @param string $newName - * @return bool + * {@inheritdoc} */ public function renameElement($oldName, $newName) { - return $this->getSubject()->renameElement($oldName, $newName); + return $this->_getSubject()->renameElement($oldName, $newName); } /** - * Retrieve all blocks from registry as array - * - * @return array + * {@inheritdoc} */ public function getAllBlocks() { - return $this->getSubject()->getAllBlocks(); + return $this->_getSubject()->getAllBlocks(); } /** - * Get block object by name - * - * @param string $name - * @return \Magento\Framework\View\Element\AbstractBlock|bool + * {@inheritdoc} */ public function getBlock($name) { - return $this->getSubject()->getBlock($name); + return $this->_getSubject()->getBlock($name); } /** - * Gets parent name of an element with specified name - * - * @param string $childName - * @return bool|string + * {@inheritdoc} + */ + public function getUiComponent($name) + { + return $this->_getSubject()->getUiComponent($name); + } + + /** + * {@inheritdoc} */ public function getParentName($childName) { - return $this->getSubject()->getParentName($childName); + return $this->_getSubject()->getParentName($childName); } /** - * Get element alias by name - * - * @param string $name - * @return bool|string + * {@inheritdoc} */ public function getElementAlias($name) { - return $this->getSubject()->getElementAlias($name); + return $this->_getSubject()->getElementAlias($name); } /** - * Add an element to output - * - * @param string $name - * @return $this + * {@inheritdoc} */ public function addOutputElement($name) { - return $this->getSubject()->addOutputElement($name); + return $this->_getSubject()->addOutputElement($name); } /** - * Remove an element from output - * - * @param string $name - * @return $this + * {@inheritdoc} */ public function removeOutputElement($name) { - return $this->getSubject()->removeOutputElement($name); + return $this->_getSubject()->removeOutputElement($name); } /** - * Get all blocks marked for output - * - * @return string + * {@inheritdoc} */ public function getOutput() { - return $this->getSubject()->getOutput(); + return $this->_getSubject()->getOutput(); } /** - * Retrieve messages block - * - * @return \Magento\Framework\View\Element\Messages + * {@inheritdoc} */ public function getMessagesBlock() { - return $this->getSubject()->getMessagesBlock(); + return $this->_getSubject()->getMessagesBlock(); } /** - * Get block singleton - * - * @param string $type - * @throws \Magento\Framework\Exception\LocalizedException - * @return \Magento\Framework\App\Helper\AbstractHelper + * {@inheritdoc} */ public function getBlockSingleton($type) { - return $this->getSubject()->getBlockSingleton($type); + return $this->_getSubject()->getBlockSingleton($type); } /** - * @param string $namespace - * @param string $staticType - * @param string $dynamicType - * @param string $type - * @param string $template - * @param array $data - * @return $this + * {@inheritdoc} */ public function addAdjustableRenderer($namespace, $staticType, $dynamicType, $type, $template, $data = []) { - return $this->getSubject()->addAdjustableRenderer( + return $this->_getSubject()->addAdjustableRenderer( $namespace, $staticType, $dynamicType, @@ -507,394 +427,298 @@ class Proxy extends \Magento\Framework\View\Layout } /** - * Get renderer options - * - * @param string $namespace - * @param string $staticType - * @param string $dynamicType - * @return array|null + * {@inheritdoc} */ public function getRendererOptions($namespace, $staticType, $dynamicType) { - return $this->getSubject()->getRendererOptions($namespace, $staticType, $dynamicType); + return $this->_getSubject()->getRendererOptions($namespace, $staticType, $dynamicType); } /** - * Execute renderer - * - * @param string $namespace - * @param string $staticType - * @param string $dynamicType - * @param array $data - * @return void + * {@inheritdoc} */ public function executeRenderer($namespace, $staticType, $dynamicType, $data = []) { - $this->getSubject()->executeRenderer($namespace, $staticType, $dynamicType, $data); + $this->_getSubject()->executeRenderer($namespace, $staticType, $dynamicType, $data); } /** - * Init messages by message storage(s), loading and adding messages to layout messages block - * - * @param string|array $messageGroups - * @return void + * {@inheritdoc} */ public function initMessages($messageGroups = []) { - $this->getSubject()->initMessages($messageGroups); + $this->_getSubject()->initMessages($messageGroups); } /** - * Check is exists non-cacheable layout elements - * - * @return bool + * {@inheritdoc} */ public function isCacheable() { - return $this->getSubject()->isCacheable(); + return $this->_getSubject()->isCacheable(); } /** - * Check is exists non-cacheable layout elements - * - * @return bool + * {@inheritdoc} */ public function isPrivate() { - return $this->getSubject()->isPrivate(); + return $this->_getSubject()->isPrivate(); } /** - * Mark layout as private - * - * @param bool $isPrivate - * @return $this + * {@inheritdoc} */ public function setIsPrivate($isPrivate = true) { - return $this->getSubject()->setIsPrivate($isPrivate); + return $this->_getSubject()->setIsPrivate($isPrivate); } /** - * Sets xml for this configuration - * - * @param \Magento\Framework\Simplexml\Element $node - * @return $this + * {@inheritdoc} + */ + public function getReaderContext() + { + return $this->_getSubject()->getReaderContext(); + } + + /** + * {@inheritdoc} */ public function setXml(\Magento\Framework\Simplexml\Element $node) { - return $this->getSubject()->setXml($node); + return $this->_getSubject()->setXml($node); } /** - * Returns node found by the $path - * - * @param string $path - * @return \Magento\Framework\Simplexml\Element|bool - * @see \Magento\Framework\Simplexml\Element::descend + * {@inheritdoc} */ public function getNode($path = null) { - return $this->getSubject()->getNode($path); + return $this->_getSubject()->getNode($path); } /** - * Returns nodes found by xpath expression - * - * @param string $xpath - * @return \SimpleXMLElement[]|bool + * {@inheritdoc} */ public function getXpath($xpath) { - return $this->getSubject()->getXpath($xpath); + return $this->_getSubject()->getXpath($xpath); } /** - * Set cache - * - * @param \Magento\Framework\Simplexml\Config\Cache\AbstractCache $cache - * @return $this + * {@inheritdoc} */ public function setCache($cache) { - return $this->getSubject()->setCache($cache); + return $this->_getSubject()->setCache($cache); } /** - * Return cache - * - * @return \Magento\Framework\Simplexml\Config\Cache\AbstractCache + * {@inheritdoc} */ public function getCache() { - return $this->getSubject()->getCache(); + return $this->_getSubject()->getCache(); } /** - * Set whether cache is saved - * - * @param boolean $flag - * @return $this + * {@inheritdoc} */ public function setCacheSaved($flag) { - return $this->getSubject()->setCacheSaved($flag); + return $this->_getSubject()->setCacheSaved($flag); } /** - * Return whether cache is saved - * - * @return bool - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) + * {@inheritdoc} */ public function getCacheSaved() { - return $this->getSubject()->getCacheSaved(); + return $this->_getSubject()->getCacheSaved(); } /** - * Set cache ID - * - * @param string $id - * @return $this + * {@inheritdoc} */ public function setCacheId($id) { - return $this->getSubject()->setCacheId($id); + return $this->_getSubject()->setCacheId($id); } /** - * Return cache ID - * - * @return string + * {@inheritdoc} */ public function getCacheId() { - return $this->getSubject()->getCacheId(); + return $this->_getSubject()->getCacheId(); } /** - * Set cache tags - * - * @param array $tags - * @return $this + * {@inheritdoc} */ public function setCacheTags($tags) { - return $this->getSubject()->setCacheTags($tags); + return $this->_getSubject()->setCacheTags($tags); } /** - * Return cache tags - * - * @return array + * {@inheritdoc} */ public function getCacheTags() { - return $this->getSubject()->getCacheTags(); + return $this->_getSubject()->getCacheTags(); } /** - * Set cache lifetime - * - * @param int $lifetime - * @return $this + * {@inheritdoc} */ public function setCacheLifetime($lifetime) { - return $this->getSubject()->setCacheLifetime($lifetime); + return $this->_getSubject()->setCacheLifetime($lifetime); } /** - * Return cache lifetime - * - * @return int + * {@inheritdoc} */ public function getCacheLifetime() { - return $this->getSubject()->getCacheLifetime(); + return $this->_getSubject()->getCacheLifetime(); } /** - * Set cache checksum - * - * @param string $data - * @return $this + * {@inheritdoc} */ public function setCacheChecksum($data) { - return $this->getSubject()->setCacheChecksum($data); + return $this->_getSubject()->setCacheChecksum($data); } /** - * Update cache checksum - * - * @param string $data - * @return $this + * {@inheritdoc} */ public function updateCacheChecksum($data) { - return $this->getSubject()->updateCacheChecksum($data); + return $this->_getSubject()->updateCacheChecksum($data); } /** - * Return cache checksum - * - * @return string + * {@inheritdoc} */ public function getCacheChecksum() { - return $this->getSubject()->getCacheChecksum(); + return $this->_getSubject()->getCacheChecksum(); } /** - * Get cache checksum ID - * - * @return string + * {@inheritdoc} */ public function getCacheChecksumId() { - return $this->getSubject()->getCacheChecksumId(); + return $this->_getSubject()->getCacheChecksumId(); } /** - * Fetch cache checksum - * - * @return boolean + * {@inheritdoc} */ public function fetchCacheChecksum() { - return $this->getSubject()->fetchCacheChecksum(); + return $this->_getSubject()->fetchCacheChecksum(); } /** - * Validate cache checksum - * - * @return boolean + * {@inheritdoc} */ public function validateCacheChecksum() { - return $this->getSubject()->validateCacheChecksum(); + return $this->_getSubject()->validateCacheChecksum(); } /** - * Load cache - * - * @return boolean + * {@inheritdoc} */ public function loadCache() { - return $this->getSubject()->loadCache(); + return $this->_getSubject()->loadCache(); } /** - * Save cache - * - * @param array $tags - * @return $this + * {@inheritdoc} */ public function saveCache($tags = null) { - return $this->getSubject()->saveCache($tags); + return $this->_getSubject()->saveCache($tags); } /** - * Return Xml of node as string - * - * @return string + * {@inheritdoc} */ public function getXmlString() { - return $this->getSubject()->getXmlString(); + return $this->_getSubject()->getXmlString(); } /** - * Remove cache - * - * @return $this + * {@inheritdoc} */ public function removeCache() { - return $this->getSubject()->removeCache(); + return $this->_getSubject()->removeCache(); } /** - * Imports XML file - * - * @param string $filePath - * @return boolean + * {@inheritdoc} */ public function loadFile($filePath) { - return $this->getSubject()->loadFile($filePath); + return $this->_getSubject()->loadFile($filePath); } /** - * Imports XML string - * - * @param string $string - * @return boolean + * {@inheritdoc} */ public function loadString($string) { - return $this->getSubject()->loadString($string); + return $this->_getSubject()->loadString($string); } /** - * Imports DOM node - * - * @param \DOMNode $dom - * @return bool + * {@inheritdoc} */ public function loadDom(\DOMNode $dom) { - return $this->getSubject()->loadDom($dom); + return $this->_getSubject()->loadDom($dom); } /** - * Create node by $path and set its value. - * - * @param string $path separated by slashes - * @param string $value - * @param boolean $overwrite - * @return $this + * {@inheritdoc} */ public function setNode($path, $value, $overwrite = true) { - return $this->getSubject()->setNode($path, $value, $overwrite); + return $this->_getSubject()->setNode($path, $value, $overwrite); } /** - * Process configuration xml - * - * @return $this + * {@inheritdoc} */ public function applyExtends() { - return $this->getSubject()->applyExtends(); + return $this->_getSubject()->applyExtends(); } /** - * Stub method for processing file data right after loading the file text - * - * @param string $text - * @return string + * {@inheritdoc} */ public function processFileData($text) { - return $this->getSubject()->processFileData($text); + return $this->_getSubject()->processFileData($text); } /** - * Extend configuration - * - * @param \Magento\Framework\Simplexml\Config $config - * @param boolean $overwrite - * @return $this + * {@inheritdoc} */ public function extend(\Magento\Framework\Simplexml\Config $config, $overwrite = true) { - return $this->getSubject()->extend($config, $overwrite); + return $this->_getSubject()->extend($config, $overwrite); } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php index bd18ebe344f884734c1b78233ed278c6c8b6b440..6f552656be534c3322b7f778a769fb55c83a366c 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php @@ -9,6 +9,7 @@ */ namespace Magento\Framework\View\Test\Unit\Element; +use Magento\Framework\Message\Manager; use \Magento\Framework\View\Element\Messages; use Magento\Framework\Message\ManagerInterface; @@ -229,7 +230,7 @@ class MessagesTest extends \PHPUnit_Framework_TestCase $this->assertEquals($emptyMessagesCacheKey, $this->messages->getCacheKeyInfo()); $messagesCacheKey = ['storage_types' => 'a:1:{i:0;s:7:"default";}']; - $this->messages->addStorageType(ManagerInterface::DEFAULT_GROUP); + $this->messages->addStorageType(Manager::DEFAULT_GROUP); $this->assertEquals($messagesCacheKey, $this->messages->getCacheKeyInfo()); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php index 45effcf449e43c4e7a05dcc77d37003195069eea..3d60c5e411300fd1ba642d03ed96720959434828 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php @@ -134,9 +134,6 @@ class LayoutTest extends \PHPUnit_Framework_TestCase $this->readerContextMock = $this->getMockBuilder('Magento\Framework\View\Layout\Reader\Context') ->disableOriginalConstructor() ->getMock(); - $this->readerContextFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($this->readerContextMock); $this->generatorContextFactoryMock = $this->getMockBuilder( 'Magento\Framework\View\Layout\Generator\ContextFactory' )->disableOriginalConstructor() @@ -683,6 +680,9 @@ class LayoutTest extends \PHPUnit_Framework_TestCase public function testGenerateElementsWithoutCache() { + $this->readerContextFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->readerContextMock); $layoutCacheId = 'layout_cache_id'; $handles = ['default', 'another']; /** @var \Magento\Framework\View\Layout\Element $xml */ @@ -807,4 +807,10 @@ class LayoutTest extends \PHPUnit_Framework_TestCase $this->model->generateElements(); } + + public function testGetXml() + { + $xml = '<layout/>'; + $this->assertSame($xml, \Magento\Framework\View\Layout::LAYOUT_NODE); + } } diff --git a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php index 79cd652f4b22e85a9abc8299e6d6180f4e6037d0..1bf5137218bf4de0d49039d61593c1730a9d871c 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php @@ -8,6 +8,7 @@ namespace Magento\Framework\Webapi; use Magento\Framework\Api\AbstractExtensibleObject; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Reflection\MethodsMap; /** * Data object converter for REST @@ -19,12 +20,21 @@ class ServiceOutputProcessor */ protected $dataObjectProcessor; + /** + * @var MethodsMap + */ + protected $methodsMapProcessor; + /** * @param DataObjectProcessor $dataObjectProcessor + * @param MethodsMap $methodsMapProcessor */ - public function __construct(DataObjectProcessor $dataObjectProcessor) - { + public function __construct( + DataObjectProcessor $dataObjectProcessor, + MethodsMap $methodsMapProcessor + ) { $this->dataObjectProcessor = $dataObjectProcessor; + $this->methodsMapProcessor = $methodsMapProcessor; } /** @@ -44,7 +54,7 @@ class ServiceOutputProcessor public function process($data, $serviceClassName, $serviceMethodName) { /** @var string $dataType */ - $dataType = $this->dataObjectProcessor->getMethodReturnType($serviceClassName, $serviceMethodName); + $dataType = $this->methodsMapProcessor->getMethodReturnType($serviceClassName, $serviceMethodName); if (is_array($data)) { $result = []; $arrayElementType = substr($dataType, 0, -2); diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index 5db3a546a66113ea49ff556c9f346281ec8afc44..414ef89f37a612ce5dcbbe14f6438d30e11a11a5 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -787,6 +787,9 @@ class Installer public function installUserConfig($data) { $userConfig = new StoreConfigurationDataMapper(); + /** @var \Magento\Framework\App\State $appState */ + $appState = $this->objectManagerProvider->get()->get('Magento\Framework\App\State'); + $appState->setAreaCode('setup'); $configData = $userConfig->getConfigData($data); if (count($configData) === 0) { return; diff --git a/setup/src/Magento/Setup/Model/Lists.php b/setup/src/Magento/Setup/Model/Lists.php index e898ed40c16c383ac2fa1ccadbdab2419aae793d..e52e921594caec60421e975f3782c8abf552ac13 100644 --- a/setup/src/Magento/Setup/Model/Lists.php +++ b/setup/src/Magento/Setup/Model/Lists.php @@ -10,6 +10,7 @@ use Magento\Framework\Locale\Bundle\CurrencyBundle; use Magento\Framework\Locale\Bundle\LanguageBundle; use Magento\Framework\Locale\Bundle\RegionBundle; use Magento\Framework\Locale\ConfigInterface; +use Magento\Framework\Locale\Resolver; use Magento\Framework\Locale\ResolverInterface; class Lists @@ -42,7 +43,7 @@ class Lists $list[$code] = \IntlTimeZone::createTimeZone($code)->getDisplayName( false, \IntlTimeZone::DISPLAY_LONG, - ResolverInterface::DEFAULT_LOCALE + Resolver::DEFAULT_LOCALE ) . ' (' . $code . ')'; } asort($list); @@ -56,7 +57,7 @@ class Lists */ public function getCurrencyList() { - $currencies = (new CurrencyBundle())->get(ResolverInterface::DEFAULT_LOCALE)['Currencies']; + $currencies = (new CurrencyBundle())->get(Resolver::DEFAULT_LOCALE)['Currencies']; $list = []; foreach ($currencies as $code => $data) { $list[$code] = $data[1] . ' (' . $code . ')'; @@ -72,8 +73,8 @@ class Lists */ public function getLocaleList() { - $languages = (new LanguageBundle())->get(ResolverInterface::DEFAULT_LOCALE)['Languages']; - $countries = (new RegionBundle())->get(ResolverInterface::DEFAULT_LOCALE)['Countries']; + $languages = (new LanguageBundle())->get(Resolver::DEFAULT_LOCALE)['Languages']; + $countries = (new RegionBundle())->get(Resolver::DEFAULT_LOCALE)['Countries']; $locales = \ResourceBundle::getLocales(null); $list = []; diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index 553c5c8ed4c1ec544a824a240237a1b0eb505dca..e3e44ebd20a3b43114e32d7df801c2c86591bf02 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -234,12 +234,21 @@ class InstallerTest extends \PHPUnit_Framework_TestCase $cacheManager = $this->getMock('Magento\Framework\App\Cache\Manager', [], [], '', false); $cacheManager->expects($this->once())->method('getAvailableTypes')->willReturn(['foo', 'bar']); $cacheManager->expects($this->once())->method('setEnabled')->willReturn(['foo', 'bar']); + $appState = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( + 'Magento\Framework\App\State' + ); $this->objectManager->expects($this->any()) ->method('create') ->will($this->returnValueMap([ ['Magento\Setup\Module\Setup', ['resource' => $resource], $setup], ['Magento\Setup\Module\DataSetup', [], $dataSetup], ['Magento\Framework\App\Cache\Manager', [], $cacheManager], + ['Magento\Framework\App\State', [], $appState], + ])); + $this->objectManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap([ + ['Magento\Framework\App\State', $appState], ])); $this->adminFactory->expects($this->once())->method('create')->willReturn( $this->getMock('Magento\Setup\Model\AdminAccount', [], [], '', false)