View.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Mview;
  7. use Magento\Framework\Mview\View\ChangelogTableNotExistsException;
  8. use Magento\Framework\Mview\View\SubscriptionFactory;
  9. /**
  10. * Mview
  11. *
  12. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  13. */
  14. class View extends \Magento\Framework\DataObject implements ViewInterface
  15. {
  16. /**
  17. * Default batch size for partial reindex
  18. */
  19. const DEFAULT_BATCH_SIZE = 1000;
  20. /**
  21. * Max versions to load from database at a time
  22. */
  23. private static $maxVersionQueryBatch = 100000;
  24. /**
  25. * @var string
  26. */
  27. protected $_idFieldName = 'view_id';
  28. /**
  29. * @var ConfigInterface
  30. */
  31. protected $config;
  32. /**
  33. * @var ActionFactory
  34. */
  35. protected $actionFactory;
  36. /**
  37. * @var View\ChangelogInterface
  38. */
  39. protected $changelog;
  40. /**
  41. * @var View\SubscriptionFactory
  42. */
  43. protected $subscriptionFactory;
  44. /**
  45. * @var \Magento\Framework\Mview\View\StateInterface
  46. */
  47. protected $state;
  48. /**
  49. * @var array
  50. */
  51. private $changelogBatchSize;
  52. /**
  53. * @param ConfigInterface $config
  54. * @param ActionFactory $actionFactory
  55. * @param View\StateInterface $state
  56. * @param View\ChangelogInterface $changelog
  57. * @param SubscriptionFactory $subscriptionFactory
  58. * @param array $data
  59. * @param array $changelogBatchSize
  60. */
  61. public function __construct(
  62. ConfigInterface $config,
  63. ActionFactory $actionFactory,
  64. View\StateInterface $state,
  65. View\ChangelogInterface $changelog,
  66. SubscriptionFactory $subscriptionFactory,
  67. array $data = [],
  68. array $changelogBatchSize = []
  69. ) {
  70. $this->config = $config;
  71. $this->actionFactory = $actionFactory;
  72. $this->state = $state;
  73. $this->changelog = $changelog;
  74. $this->subscriptionFactory = $subscriptionFactory;
  75. $this->changelogBatchSize = $changelogBatchSize;
  76. parent::__construct($data);
  77. }
  78. /**
  79. * Return ID
  80. *
  81. * @return string
  82. */
  83. public function getId()
  84. {
  85. return $this->getData($this->_idFieldName);
  86. }
  87. /**
  88. * Set ID
  89. *
  90. * @param string $id
  91. * @return $this
  92. */
  93. public function setId($id)
  94. {
  95. $this->setData($this->_idFieldName, $id);
  96. return $this;
  97. }
  98. /**
  99. * Id field name setter
  100. *
  101. * @param string $name
  102. * @return $this
  103. */
  104. public function setIdFieldName($name)
  105. {
  106. $this->_idFieldName = $name;
  107. return $this;
  108. }
  109. /**
  110. * Id field name getter
  111. *
  112. * @return string
  113. */
  114. public function getIdFieldName()
  115. {
  116. return $this->_idFieldName;
  117. }
  118. /**
  119. * Return view action class
  120. *
  121. * @return string
  122. */
  123. public function getActionClass()
  124. {
  125. return $this->getData('action_class');
  126. }
  127. /**
  128. * Return view group
  129. *
  130. * @return string
  131. */
  132. public function getGroup()
  133. {
  134. return $this->getData('group');
  135. }
  136. /**
  137. * Return view subscriptions
  138. *
  139. * @return array
  140. */
  141. public function getSubscriptions()
  142. {
  143. return $this->getData('subscriptions');
  144. }
  145. /**
  146. * Fill view data from config
  147. *
  148. * @param string $viewId
  149. * @return ViewInterface
  150. * @throws \InvalidArgumentException
  151. */
  152. public function load($viewId)
  153. {
  154. $view = $this->config->getView($viewId);
  155. if (empty($view) || empty($view['view_id']) || $view['view_id'] != $viewId) {
  156. throw new \InvalidArgumentException("{$viewId} view does not exist.");
  157. }
  158. $this->setId($viewId);
  159. $this->setData($view);
  160. return $this;
  161. }
  162. /**
  163. * Create subscriptions
  164. *
  165. * @throws \Exception
  166. * @return ViewInterface
  167. */
  168. public function subscribe()
  169. {
  170. if ($this->getState()->getMode() != View\StateInterface::MODE_ENABLED) {
  171. try {
  172. // Create changelog table
  173. $this->getChangelog()->create();
  174. // Create subscriptions
  175. foreach ($this->getSubscriptions() as $subscriptionConfig) {
  176. /** @var \Magento\Framework\Mview\View\SubscriptionInterface $subscription */
  177. $subscriptionInstance = $this->subscriptionFactory->create(
  178. [
  179. 'view' => $this,
  180. 'tableName' => $subscriptionConfig['name'],
  181. 'columnName' => $subscriptionConfig['column'],
  182. 'subscriptionModel' => !empty($subscriptionConfig['subscription_model'])
  183. ? $subscriptionConfig['subscription_model']
  184. : SubscriptionFactory::INSTANCE_NAME,
  185. ]
  186. );
  187. $subscriptionInstance->create();
  188. }
  189. // Update view state
  190. $this->getState()->setMode(View\StateInterface::MODE_ENABLED)->save();
  191. } catch (\Exception $e) {
  192. throw $e;
  193. }
  194. }
  195. return $this;
  196. }
  197. /**
  198. * Remove subscriptions
  199. *
  200. * @throws \Exception
  201. * @return ViewInterface
  202. */
  203. public function unsubscribe()
  204. {
  205. if ($this->getState()->getMode() != View\StateInterface::MODE_DISABLED) {
  206. try {
  207. // Remove subscriptions
  208. foreach ($this->getSubscriptions() as $subscriptionConfig) {
  209. /** @var \Magento\Framework\Mview\View\SubscriptionInterface $subscription */
  210. $subscriptionInstance = $this->subscriptionFactory->create(
  211. [
  212. 'view' => $this,
  213. 'tableName' => $subscriptionConfig['name'],
  214. 'columnName' => $subscriptionConfig['column'],
  215. 'subscriptionModel' => !empty($subscriptionConfig['subscriptionModel'])
  216. ? $subscriptionConfig['subscriptionModel']
  217. : SubscriptionFactory::INSTANCE_NAME,
  218. ]
  219. );
  220. $subscriptionInstance->remove();
  221. }
  222. // Update view state
  223. $this->getState()->setMode(View\StateInterface::MODE_DISABLED)->save();
  224. } catch (\Exception $e) {
  225. throw $e;
  226. }
  227. }
  228. return $this;
  229. }
  230. /**
  231. * Materialize view by IDs in changelog
  232. *
  233. * @return void
  234. * @throws \Exception
  235. */
  236. public function update()
  237. {
  238. if ($this->getState()->getStatus() == View\StateInterface::STATUS_IDLE) {
  239. try {
  240. $currentVersionId = $this->getChangelog()->getVersion();
  241. } catch (ChangelogTableNotExistsException $e) {
  242. return;
  243. }
  244. $lastVersionId = (int) $this->getState()->getVersionId();
  245. $action = $this->actionFactory->get($this->getActionClass());
  246. try {
  247. $this->getState()->setStatus(View\StateInterface::STATUS_WORKING)->save();
  248. $versionBatchSize = self::$maxVersionQueryBatch;
  249. $batchSize = isset($this->changelogBatchSize[$this->getChangelog()->getViewId()])
  250. ? $this->changelogBatchSize[$this->getChangelog()->getViewId()]
  251. : self::DEFAULT_BATCH_SIZE;
  252. for ($vsFrom = $lastVersionId; $vsFrom < $currentVersionId; $vsFrom += $versionBatchSize) {
  253. // Don't go past the current version for atomicy.
  254. $versionTo = min($currentVersionId, $vsFrom + $versionBatchSize);
  255. $ids = array_map('intval', $this->getChangelog()->getList($vsFrom, $versionTo));
  256. // We run the actual indexer in batches.
  257. // Chunked AFTER loading to avoid duplicates in separate chunks.
  258. $chunks = array_chunk($ids, $batchSize);
  259. foreach ($chunks as $ids) {
  260. $action->execute($ids);
  261. }
  262. }
  263. $this->getState()->loadByView($this->getId());
  264. $statusToRestore = $this->getState()->getStatus() == View\StateInterface::STATUS_SUSPENDED
  265. ? View\StateInterface::STATUS_SUSPENDED
  266. : View\StateInterface::STATUS_IDLE;
  267. $this->getState()->setVersionId($currentVersionId)->setStatus($statusToRestore)->save();
  268. } catch (\Exception $exception) {
  269. $this->getState()->loadByView($this->getId());
  270. $statusToRestore = $this->getState()->getStatus() == View\StateInterface::STATUS_SUSPENDED
  271. ? View\StateInterface::STATUS_SUSPENDED
  272. : View\StateInterface::STATUS_IDLE;
  273. $this->getState()->setStatus($statusToRestore)->save();
  274. throw $exception;
  275. }
  276. }
  277. }
  278. /**
  279. * Suspend view updates and set version ID to changelog's end
  280. *
  281. * @return void
  282. */
  283. public function suspend()
  284. {
  285. if ($this->getState()->getMode() == View\StateInterface::MODE_ENABLED) {
  286. $state = $this->getState();
  287. $state->setVersionId($this->getChangelog()->getVersion());
  288. $state->setStatus(View\StateInterface::STATUS_SUSPENDED);
  289. $state->save();
  290. }
  291. }
  292. /**
  293. * Resume view updates
  294. *
  295. * @return void
  296. */
  297. public function resume()
  298. {
  299. $state = $this->getState();
  300. if ($state->getStatus() == View\StateInterface::STATUS_SUSPENDED) {
  301. $state->setStatus(View\StateInterface::STATUS_IDLE);
  302. $state->save();
  303. }
  304. }
  305. /**
  306. * Clear precessed changelog entries
  307. *
  308. * @return void
  309. */
  310. public function clearChangelog()
  311. {
  312. if ($this->getState()->getMode() == View\StateInterface::MODE_ENABLED) {
  313. $this->getChangelog()->clear($this->getState()->getVersionId());
  314. }
  315. }
  316. /**
  317. * Return related state object
  318. *
  319. * @return View\StateInterface
  320. */
  321. public function getState()
  322. {
  323. if (!$this->state->getViewId()) {
  324. $this->state->loadByView($this->getId());
  325. }
  326. return $this->state;
  327. }
  328. /**
  329. * Set view state object
  330. *
  331. * @param View\StateInterface $state
  332. * @return ViewInterface
  333. */
  334. public function setState(View\StateInterface $state)
  335. {
  336. $this->state = $state;
  337. return $this;
  338. }
  339. /**
  340. * Check whether view is enabled
  341. *
  342. * @return bool
  343. */
  344. public function isEnabled()
  345. {
  346. return $this->getState()->getMode() == View\StateInterface::MODE_ENABLED;
  347. }
  348. /**
  349. * Check whether view is idle
  350. *
  351. * @return bool
  352. */
  353. public function isIdle()
  354. {
  355. return $this->getState()->getStatus() == \Magento\Framework\Mview\View\StateInterface::STATUS_IDLE;
  356. }
  357. /**
  358. * Check whether view is working
  359. *
  360. * @return bool
  361. */
  362. public function isWorking()
  363. {
  364. return $this->getState()->getStatus() == \Magento\Framework\Mview\View\StateInterface::STATUS_WORKING;
  365. }
  366. /**
  367. * Check whether view is suspended
  368. *
  369. * @return bool
  370. */
  371. public function isSuspended()
  372. {
  373. return $this->getState()->getStatus() == \Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED;
  374. }
  375. /**
  376. * Return view updated datetime
  377. *
  378. * @return string
  379. */
  380. public function getUpdated()
  381. {
  382. return $this->getState()->getUpdated();
  383. }
  384. /**
  385. * Retrieve linked changelog
  386. *
  387. * @return View\ChangelogInterface
  388. */
  389. public function getChangelog()
  390. {
  391. if (!$this->changelog->getViewId()) {
  392. $this->changelog->setViewId($this->getId());
  393. }
  394. return $this->changelog;
  395. }
  396. }