Skip to content

Commit d923e49

Browse files
author
Viktor Paladiychuk
committed
MAGETWO-62660: [GitHub] Overly aggressive performance optimizations of the static content deployment #7862
1 parent ca2ade1 commit d923e49

File tree

2 files changed

+177
-25
lines changed

2 files changed

+177
-25
lines changed

app/code/Magento/Deploy/Model/Deploy/LocaleQuickDeploy.php

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66

77
namespace Magento\Deploy\Model\Deploy;
88

9-
use Magento\Deploy\Model\DeployManager;
109
use Magento\Framework\App\Filesystem\DirectoryList;
11-
use Magento\Framework\App\Utility\Files;
1210
use Magento\Framework\Filesystem;
1311
use Magento\Framework\Filesystem\Directory\WriteInterface;
1412
use Symfony\Component\Console\Output\OutputInterface;
1513
use Magento\Framework\Console\Cli;
1614
use Magento\Deploy\Console\Command\DeployStaticOptionsInterface as Options;
17-
use \Magento\Framework\RequireJs\Config as RequireJsConfig;
15+
use Magento\Framework\RequireJs\Config as RequireJsConfig;
16+
use Magento\Framework\App\View\Asset\Publisher;
17+
use Magento\Framework\View\Asset\Repository;
18+
use Magento\Framework\App\ObjectManager;
19+
use Magento\Translation\Model\Js\Config as TranslationJsConfig;
20+
use Magento\Framework\TranslateInterface;
1821

1922
class LocaleQuickDeploy implements DeployInterface
2023
{
@@ -38,16 +41,52 @@ class LocaleQuickDeploy implements DeployInterface
3841
*/
3942
private $options = [];
4043

44+
/**
45+
* @var Repository
46+
*/
47+
private $assetRepo;
48+
49+
/**
50+
* @var Publisher
51+
*/
52+
private $assetPublisher;
53+
54+
/**
55+
* @var TranslationJsConfig
56+
*/
57+
private $translationJsConfig;
58+
59+
/**
60+
* @var TranslateInterface
61+
*/
62+
private $translator;
63+
4164
/**
4265
* @param Filesystem $filesystem
4366
* @param OutputInterface $output
4467
* @param array $options
68+
* @param Repository $assetRepo
69+
* @param Publisher $assetPublisher
70+
* @param TranslationJsConfig $translationJsConfig
71+
* @param TranslateInterface $translator
4572
*/
46-
public function __construct(\Magento\Framework\Filesystem $filesystem, OutputInterface $output, $options = [])
47-
{
73+
public function __construct(
74+
Filesystem $filesystem,
75+
OutputInterface $output,
76+
$options = [],
77+
Repository $assetRepo = null,
78+
Publisher $assetPublisher = null,
79+
TranslationJsConfig $translationJsConfig = null,
80+
TranslateInterface $translator = null
81+
) {
4882
$this->filesystem = $filesystem;
4983
$this->output = $output;
5084
$this->options = $options;
85+
$this->assetRepo = $assetRepo ?: ObjectManager::getInstance()->get(Repository::class);
86+
$this->assetPublisher = $assetPublisher ?: ObjectManager::getInstance()->get(Publisher::class);
87+
$this->translationJsConfig = $translationJsConfig
88+
?: ObjectManager::getInstance()->get(TranslationJsConfig::class);
89+
$this->translator = $translator ?: ObjectManager::getInstance()->get(TranslateInterface::class);
5190
}
5291

5392
/**
@@ -67,13 +106,13 @@ private function getStaticDirectory()
67106
*/
68107
public function deploy($area, $themePath, $locale)
69108
{
70-
if (isset($this->options[Options::DRY_RUN]) && $this->options[Options::DRY_RUN]) {
109+
if (!empty($this->options[Options::DRY_RUN])) {
71110
return Cli::RETURN_SUCCESS;
72111
}
73112

74113
$this->output->writeln("=== {$area} -> {$themePath} -> {$locale} ===");
75114

76-
if (!isset($this->options[self::DEPLOY_BASE_LOCALE])) {
115+
if (empty($this->options[self::DEPLOY_BASE_LOCALE])) {
77116
throw new \InvalidArgumentException('Deploy base locale must be set for Quick Deploy');
78117
}
79118
$processedFiles = 0;
@@ -88,7 +127,7 @@ public function deploy($area, $themePath, $locale)
88127
$this->deleteLocaleResource($newLocalePath);
89128
$this->deleteLocaleResource($newRequireJsPath);
90129

91-
if (isset($this->options[Options::SYMLINK_LOCALE]) && $this->options[Options::SYMLINK_LOCALE]) {
130+
if (!empty($this->options[Options::SYMLINK_LOCALE])) {
92131
$this->getStaticDirectory()->createSymlink($baseLocalePath, $newLocalePath);
93132
$this->getStaticDirectory()->createSymlink($baseRequireJsPath, $newRequireJsPath);
94133

@@ -98,20 +137,65 @@ public function deploy($area, $themePath, $locale)
98137
$this->getStaticDirectory()->readRecursively($baseLocalePath),
99138
$this->getStaticDirectory()->readRecursively($baseRequireJsPath)
100139
);
140+
$jsDictionaryEnabled = $this->translationJsConfig->dictionaryEnabled();
101141
foreach ($localeFiles as $path) {
102142
if ($this->getStaticDirectory()->isFile($path)) {
103143
$destination = $this->replaceLocaleInPath($path, $baseLocale, $locale);
104-
$this->getStaticDirectory()->copyFile($path, $destination);
144+
if (!$jsDictionaryEnabled || !$this->isJsDictionary($path)) {
145+
$this->getStaticDirectory()->copyFile($path, $destination);
146+
}
105147
$processedFiles++;
106148
}
107149
}
108150

151+
if ($jsDictionaryEnabled) {
152+
$this->deployJsDictionary($area, $themePath, $locale);
153+
$processedFiles++;
154+
}
155+
109156
$this->output->writeln("\nSuccessful copied: {$processedFiles} files; errors: {$errorAmount}\n---\n");
110157
}
111158

112159
return Cli::RETURN_SUCCESS;
113160
}
114161

162+
/**
163+
* Define if provided path is js dictionary
164+
*
165+
* @param string $path
166+
* @return bool
167+
*/
168+
private function isJsDictionary($path)
169+
{
170+
return strpos($path, $this->translationJsConfig->getDictionaryFileName()) !== false;
171+
}
172+
173+
/**
174+
* Deploy js-dictionary for specific locale, theme and area
175+
*
176+
* @param string $area
177+
* @param string $themePath
178+
* @param string $locale
179+
* @return void
180+
*/
181+
private function deployJsDictionary($area, $themePath, $locale)
182+
{
183+
$this->translator->setLocale($locale);
184+
$this->translator->loadData($area, true);
185+
186+
$asset = $this->assetRepo->createAsset(
187+
$this->translationJsConfig->getDictionaryFileName(),
188+
['area' => $area, 'theme' => $themePath, 'locale' => $locale]
189+
);
190+
if ($this->output->isVeryVerbose()) {
191+
$this->output->writeln("\tDeploying the file to '{$asset->getPath()}'");
192+
} else {
193+
$this->output->write('.');
194+
}
195+
196+
$this->assetPublisher->publish($asset);
197+
}
198+
115199
/**
116200
* @param string $path
117201
* @return void

app/code/Magento/Deploy/Test/Unit/Model/Deploy/LocaleQuickDeployTest.php

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
1212
use Symfony\Component\Console\Output\OutputInterface;
1313
use Magento\Deploy\Console\Command\DeployStaticOptionsInterface as Options;
14-
use \Magento\Framework\RequireJs\Config as RequireJsConfig;
14+
use Magento\Framework\RequireJs\Config as RequireJsConfig;
15+
use Magento\Translation\Model\Js\Config as TranslationJsConfig;
16+
use Magento\Framework\TranslateInterface;
17+
use Magento\Framework\View\Asset\Repository;
18+
use Magento\Framework\View\Asset\LocalInterface as Asset;
19+
use Magento\Framework\App\View\Asset\Publisher;
1520

1621
class LocaleQuickDeployTest extends \PHPUnit_Framework_TestCase
1722
{
@@ -25,15 +30,46 @@ class LocaleQuickDeployTest extends \PHPUnit_Framework_TestCase
2530
*/
2631
private $staticDirectoryMock;
2732

33+
/**
34+
* @var TranslationJsConfig|\PHPUnit_Framework_MockObject_MockObject
35+
*/
36+
private $translationJsConfig;
37+
38+
/**
39+
* @var TranslateInterface|\PHPUnit_Framework_MockObject_MockObject
40+
*/
41+
private $translator;
42+
43+
/**
44+
* @var Repository|\PHPUnit_Framework_MockObject_MockObject
45+
*/
46+
private $assetRepo;
47+
48+
/**
49+
* @var Asset|\PHPUnit_Framework_MockObject_MockObject
50+
*/
51+
private $asset;
52+
53+
/**
54+
* @var Publisher|\PHPUnit_Framework_MockObject_MockObject
55+
*/
56+
private $assetPublisher;
57+
2858
protected function setUp()
2959
{
3060
$this->outputMock = $this->getMockBuilder(OutputInterface::class)
31-
->setMethods(['writeln'])
61+
->setMethods(['writeln', 'isVeryVerbose'])
3262
->getMockForAbstractClass();
3363

3464
$this->staticDirectoryMock = $this->getMockBuilder(WriteInterface::class)
3565
->setMethods(['createSymlink', 'getAbsolutePath', 'getRelativePath', 'copyFile', 'readRecursively'])
3666
->getMockForAbstractClass();
67+
68+
$this->translationJsConfig = $this->getMock(TranslationJsConfig::class, [], [], '', false);
69+
$this->translator = $this->getMockForAbstractClass(TranslateInterface::class, [], '', false, false, true);
70+
$this->assetRepo = $this->getMock(Repository::class, [], [], '', false);
71+
$this->asset = $this->getMockForAbstractClass(Asset::class, [], '', false, false, true);;
72+
$this->assetPublisher = $this->getMock(Publisher::class, [], [], '', false);
3773
}
3874

3975
/**
@@ -68,29 +104,57 @@ public function testDeployWithSymlinkStrategy()
68104

69105
public function testDeployWithCopyStrategy()
70106
{
71-
72107
$area = 'adminhtml';
73108
$themePath = 'Magento/backend';
74109
$locale = 'uk_UA';
75-
$baseLocal = 'en_US';
110+
$baseLocale = 'en_US';
111+
$baseDir = $baseLocale . 'dir';
112+
$file1 = 'file1';
113+
$file2 = 'file2';
114+
$baseFile1 = $baseLocale . $file1;
115+
$baseFile2 = $baseLocale . $file2;
116+
117+
$dictionary = TranslationJsConfig::DICTIONARY_FILE_NAME;
118+
$baseDictionary = $baseLocale . $dictionary;
119+
76120

77121
$this->staticDirectoryMock->expects(self::never())->method('createSymlink');
78-
$this->staticDirectoryMock->expects(self::exactly(2))->method('readRecursively')->willReturnMap([
79-
['adminhtml/Magento/backend/en_US', [$baseLocal . 'file1', $baseLocal . 'dir']],
80-
[RequireJsConfig::DIR_NAME . '/adminhtml/Magento/backend/en_US', [$baseLocal . 'file2']]
81-
]);
82-
$this->staticDirectoryMock->expects(self::exactly(3))->method('isFile')->willReturnMap([
83-
[$baseLocal . 'file1', true],
84-
[$baseLocal . 'dir', false],
85-
[$baseLocal . 'file2', true],
122+
$this->staticDirectoryMock->expects(self::exactly(2))->method('readRecursively')->willReturnMap(
123+
[
124+
['adminhtml/Magento/backend/en_US', [$baseFile1, $baseDir]],
125+
[RequireJsConfig::DIR_NAME . '/adminhtml/Magento/backend/en_US', [$baseFile2, $baseDictionary]]
126+
]
127+
);
128+
$this->staticDirectoryMock->expects(self::exactly(4))->method('isFile')->willReturnMap([
129+
[$baseFile1, true],
130+
[$baseDir, false],
131+
[$baseFile2, true],
132+
[$baseDictionary, true]
86133
]);
87134
$this->staticDirectoryMock->expects(self::exactly(2))->method('copyFile')->withConsecutive(
88-
[$baseLocal . 'file1', $locale . 'file1', null],
89-
[$baseLocal . 'file2', $locale . 'file2', null]
135+
[$baseFile1, $locale . $file1, null],
136+
[$baseFile2, $locale . $file2, null]
90137
);
91138

139+
$this->translationJsConfig->expects(self::exactly(4))->method('getDictionaryFileName')
140+
->willReturn($dictionary);
141+
142+
$this->translationJsConfig->expects($this->once())->method('dictionaryEnabled')->willReturn(true);
143+
144+
$this->translator->expects($this->once())->method('setLocale')->with($locale);
145+
$this->translator->expects($this->once())->method('loadData')->with($area, true);
146+
147+
$this->assetRepo->expects($this->once())->method('createAsset')
148+
->with(
149+
$dictionary,
150+
['area' => $area, 'theme' => $themePath, 'locale' => $locale]
151+
)
152+
->willReturn($this->asset);
153+
154+
$this->assetPublisher->expects($this->once())->method('publish');
155+
92156
$model = $this->getModel([
93-
DeployInterface::DEPLOY_BASE_LOCALE => $baseLocal,
157+
DeployInterface::DEPLOY_BASE_LOCALE => $baseLocale,
94158
Options::SYMLINK_LOCALE => 0,
95159
]);
96160
$model->deploy($area, $themePath, $locale);
@@ -107,7 +171,11 @@ private function getModel($options = [])
107171
[
108172
'output' => $this->outputMock,
109173
'staticDirectory' => $this->staticDirectoryMock,
110-
'options' => $options
174+
'options' => $options,
175+
'translationJsConfig' => $this->translationJsConfig,
176+
'translator' => $this->translator,
177+
'assetRepo' => $this->assetRepo,
178+
'assetPublisher' => $this->assetPublisher
111179
]
112180
);
113181
}

0 commit comments

Comments
 (0)