Skip to content

Commit 7f6e9a8

Browse files
committed
MC-41534: [On-Premise] Product themes don't apply as expected
1 parent b5c918b commit 7f6e9a8

File tree

2 files changed

+216
-16
lines changed

2 files changed

+216
-16
lines changed

app/code/Magento/Catalog/Controller/Product/View.php

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55
*/
66
namespace Magento\Catalog\Controller\Product;
77

8+
use Magento\Catalog\Model\Design;
9+
use Magento\Catalog\Model\ProductRepository;
810
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
911
use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface;
1012
use Magento\Framework\App\Action\Context;
1113
use Magento\Framework\App\ObjectManager;
14+
use Magento\Framework\Controller\Result\Forward;
15+
use Magento\Framework\Controller\Result\ForwardFactory;
16+
use Magento\Framework\Controller\Result\Redirect;
17+
use Magento\Framework\DataObject;
18+
use Magento\Framework\Exception\NoSuchEntityException;
19+
use Magento\Framework\Json\Helper\Data;
1220
use Magento\Framework\View\Result\PageFactory;
1321
use Magento\Catalog\Controller\Product as ProductAction;
22+
use Magento\Store\Model\StoreManagerInterface;
23+
use Psr\Log\LoggerInterface;
1424

1525
/**
1626
* View a product on storefront. Needs to be accessible by POST because of the store switching
@@ -25,57 +35,84 @@ class View extends ProductAction implements HttpGetActionInterface, HttpPostActi
2535
protected $viewHelper;
2636

2737
/**
28-
* @var \Magento\Framework\Controller\Result\ForwardFactory
38+
* @var ForwardFactory
2939
*/
3040
protected $resultForwardFactory;
3141

3242
/**
33-
* @var \Magento\Framework\View\Result\PageFactory
43+
* @var PageFactory
3444
*/
3545
protected $resultPageFactory;
3646

3747
/**
38-
* @var \Psr\Log\LoggerInterface
48+
* @var LoggerInterface
3949
*/
4050
private $logger;
4151

4252
/**
43-
* @var \Magento\Framework\Json\Helper\Data
53+
* @var Data
4454
*/
4555
private $jsonHelper;
4656

57+
/**
58+
* @var Design
59+
*/
60+
private $catalogDesign;
61+
62+
/**
63+
* @var ProductRepository
64+
*/
65+
private $productRepository;
66+
67+
/**
68+
* @var StoreManagerInterface
69+
*/
70+
protected $storeManager;
71+
4772
/**
4873
* Constructor
4974
*
5075
* @param Context $context
5176
* @param \Magento\Catalog\Helper\Product\View $viewHelper
52-
* @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
77+
* @param ForwardFactory $resultForwardFactory
5378
* @param PageFactory $resultPageFactory
54-
* @param \Psr\Log\LoggerInterface $logger
55-
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
79+
* @param LoggerInterface|null $logger
80+
* @param Data|null $jsonHelper
81+
* @param Design|null $catalogDesign
82+
* @param ProductRepository|null $productRepository
83+
* @param StoreManagerInterface|null $storeManager
5684
*/
5785
public function __construct(
5886
Context $context,
5987
\Magento\Catalog\Helper\Product\View $viewHelper,
60-
\Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory,
88+
ForwardFactory $resultForwardFactory,
6189
PageFactory $resultPageFactory,
62-
\Psr\Log\LoggerInterface $logger = null,
63-
\Magento\Framework\Json\Helper\Data $jsonHelper = null
90+
?LoggerInterface $logger = null,
91+
?Data $jsonHelper = null,
92+
?Design $catalogDesign = null,
93+
?ProductRepository $productRepository = null,
94+
?StoreManagerInterface $storeManager = null
6495
) {
6596
parent::__construct($context);
6697
$this->viewHelper = $viewHelper;
6798
$this->resultForwardFactory = $resultForwardFactory;
6899
$this->resultPageFactory = $resultPageFactory;
69100
$this->logger = $logger ?: ObjectManager::getInstance()
70-
->get(\Psr\Log\LoggerInterface::class);
101+
->get(LoggerInterface::class);
71102
$this->jsonHelper = $jsonHelper ?: ObjectManager::getInstance()
72-
->get(\Magento\Framework\Json\Helper\Data::class);
103+
->get(Data::class);
104+
$this->catalogDesign = $catalogDesign ?: ObjectManager::getInstance()
105+
->get(Design::class);
106+
$this->productRepository = $productRepository ?: ObjectManager::getInstance()
107+
->get(ProductRepository::class);
108+
$this->storeManager = $storeManager ?: ObjectManager::getInstance()
109+
->get(StoreManagerInterface::class);
73110
}
74111

75112
/**
76113
* Redirect if product failed to load
77114
*
78-
* @return \Magento\Framework\Controller\Result\Redirect|\Magento\Framework\Controller\Result\Forward
115+
* @return Redirect|Forward
79116
*/
80117
protected function noProductRedirect()
81118
{
@@ -93,7 +130,7 @@ protected function noProductRedirect()
93130
/**
94131
* Product view action
95132
*
96-
* @return \Magento\Framework\Controller\Result\Forward|\Magento\Framework\Controller\Result\Redirect
133+
* @return Forward|Redirect
97134
*/
98135
public function execute()
99136
{
@@ -130,16 +167,17 @@ public function execute()
130167
}
131168

132169
// Prepare helper and params
133-
$params = new \Magento\Framework\DataObject();
170+
$params = new DataObject();
134171
$params->setCategoryId($categoryId);
135172
$params->setSpecifyOptions($specifyOptions);
136173

137174
// Render page
138175
try {
176+
$this->applyCustomDesign($productId);
139177
$page = $this->resultPageFactory->create();
140178
$this->viewHelper->prepareAndRender($page, $productId, $this, $params);
141179
return $page;
142-
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
180+
} catch (NoSuchEntityException $e) {
143181
return $this->noProductRedirect();
144182
} catch (\Exception $e) {
145183
$this->logger->critical($e);
@@ -148,4 +186,19 @@ public function execute()
148186
return $resultForward;
149187
}
150188
}
189+
190+
/**
191+
* Apply custom design from product design settings
192+
*
193+
* @param int $productId
194+
* @throws NoSuchEntityException
195+
*/
196+
private function applyCustomDesign(int $productId): void
197+
{
198+
$product = $this->productRepository->getById($productId, false, $this->storeManager->getStore()->getId());
199+
$settings = $this->catalogDesign->getDesignSettings($product);
200+
if ($settings->getCustomDesign()) {
201+
$this->catalogDesign->applyCustomDesign($settings->getCustomDesign());
202+
}
203+
}
151204
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Test\Unit\Controller\Product;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Controller\Product\View;
12+
use Magento\Catalog\Helper\Product\View as ViewHelper;
13+
use Magento\Catalog\Model\Design;
14+
use Magento\Catalog\Model\ProductRepository;
15+
use Magento\Framework\App\Action\Context;
16+
use Magento\Framework\App\RequestInterface;
17+
use Magento\Framework\Controller\Result\ForwardFactory;
18+
use Magento\Framework\DataObject;
19+
use Magento\Framework\View\Result\Page;
20+
use Magento\Framework\View\Result\PageFactory;
21+
use Magento\Store\Model\Store;
22+
use Magento\Store\Model\StoreManagerInterface;
23+
use PHPUnit\Framework\MockObject\MockObject;
24+
use PHPUnit\Framework\TestCase;
25+
26+
/**
27+
* Responsible for testing product view action on a strorefront.
28+
*/
29+
class ViewTest extends TestCase
30+
{
31+
/**
32+
* @var View
33+
*/
34+
private $view;
35+
36+
/**
37+
* @var RequestInterface|MockObject
38+
*/
39+
private $requestMock;
40+
41+
/**
42+
* @var PageFactory|MockObject
43+
*/
44+
private $resultPageFactoryMock;
45+
46+
/**
47+
* @var Design|MockObject
48+
*/
49+
private $catalogDesignMock;
50+
51+
/**
52+
* @var ProductRepository|MockObject
53+
*/
54+
private $productRepositoryMock;
55+
56+
/**
57+
* @var ProductInterface|MockObject
58+
*/
59+
private $productInterfaceMock;
60+
61+
/**
62+
* @var StoreManagerInterface|MockObject
63+
*/
64+
protected $storeManagerMock;
65+
66+
/**
67+
* @inheritDoc
68+
*/
69+
protected function setUp(): void
70+
{
71+
$contextMock = $this->getMockBuilder(Context::class)
72+
->disableOriginalConstructor()
73+
->getMock();
74+
$this->requestMock = $this->getMockBuilder(RequestInterface::class)
75+
->disableOriginalConstructor()
76+
->addMethods(['isPost'])
77+
->getMockForAbstractClass();
78+
$contextMock->expects($this->any())
79+
->method('getRequest')
80+
->willReturn($this->requestMock);
81+
$viewHelperMock = $this->getMockBuilder(ViewHelper::class)
82+
->disableOriginalConstructor()
83+
->getMock();
84+
$resultForwardFactoryMock = $this->getMockBuilder(ForwardFactory::class)
85+
->disableOriginalConstructor()
86+
->getMock();
87+
$this->resultPageFactoryMock = $this->getMockBuilder(PageFactory::class)
88+
->disableOriginalConstructor()
89+
->getMock();
90+
$this->resultPageFactoryMock = $this->getMockBuilder(PageFactory::class)
91+
->disableOriginalConstructor()
92+
->getMock();
93+
$this->catalogDesignMock = $this->getMockBuilder(Design::class)
94+
->disableOriginalConstructor()
95+
->getMock();
96+
$this->productRepositoryMock = $this->getMockBuilder(ProductRepository::class)
97+
->disableOriginalConstructor()
98+
->getMock();
99+
$this->productInterfaceMock = $this->getMockBuilder(ProductInterface::class)
100+
->disableOriginalConstructor()
101+
->getMock();
102+
$this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class);
103+
$storeMock = $this->createMock(Store::class);
104+
$this->storeManagerMock->method('getStore')->willReturn($storeMock);
105+
106+
$this->view = new View(
107+
$contextMock,
108+
$viewHelperMock,
109+
$resultForwardFactoryMock,
110+
$this->resultPageFactoryMock,
111+
null,
112+
null,
113+
$this->catalogDesignMock,
114+
$this->productRepositoryMock,
115+
$this->storeManagerMock
116+
);
117+
}
118+
119+
/**
120+
* Verify that product custom design theme is applied before product rendering
121+
*/
122+
public function testExecute(): void
123+
{
124+
$themeId = 3;
125+
$this->requestMock->method('isPost')
126+
->willReturn(false);
127+
$this->productRepositoryMock->method('getById')
128+
->willReturn($this->productInterfaceMock);
129+
$dataObjectMock = $this->getMockBuilder(DataObject::class)
130+
->disableOriginalConstructor()
131+
->addMethods(['getCustomDesign'])
132+
->getMock();
133+
$dataObjectMock->method('getCustomDesign')
134+
->willReturn($themeId);
135+
$this->catalogDesignMock->method('getDesignSettings')
136+
->willReturn($dataObjectMock);
137+
$this->catalogDesignMock->expects($this->once())
138+
->method('applyCustomDesign')
139+
->with($themeId);
140+
$viewResultPageMock = $this->getMockBuilder(Page::class)
141+
->disableOriginalConstructor()
142+
->getMock();
143+
$this->resultPageFactoryMock->method('create')
144+
->willReturn($viewResultPageMock);
145+
$this->view->execute();
146+
}
147+
}

0 commit comments

Comments
 (0)