SynonymGroupRepository.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Search\Model;
  7. use Magento\Framework\Exception\CouldNotDeleteException;
  8. use Magento\Search\Api\Data\SynonymGroupInterface;
  9. use Magento\Search\Api\SynonymGroupRepositoryInterface;
  10. use Magento\Search\Model\ResourceModel\SynonymGroup as SynonymGroupResourceModel;
  11. /**
  12. * Synonym Group repository, provides implementation of saving and deleting synonym groups
  13. */
  14. class SynonymGroupRepository implements SynonymGroupRepositoryInterface
  15. {
  16. /**
  17. * SynonymGroup Factory
  18. *
  19. * @var SynonymGroupFactory
  20. */
  21. protected $synonymGroupFactory;
  22. /**
  23. * SynonymGroup resource model
  24. *
  25. * @var SynonymGroupResourceModel
  26. */
  27. protected $resourceModel;
  28. /**
  29. * Constructor
  30. *
  31. * @param SynonymGroupFactory $synonymGroupFactory
  32. * @param SynonymGroupResourceModel $resourceModel
  33. */
  34. public function __construct(
  35. \Magento\Search\Model\SynonymGroupFactory $synonymGroupFactory,
  36. SynonymGroupResourceModel $resourceModel
  37. ) {
  38. $this->synonymGroupFactory = $synonymGroupFactory;
  39. $this->resourceModel = $resourceModel;
  40. }
  41. /**
  42. * @inheritdoc
  43. */
  44. public function save(SynonymGroupInterface $synonymGroup, $errorOnMergeConflict = false)
  45. {
  46. /** @var SynonymGroup $synonymGroupModel */
  47. $synonymGroupModel = $this->synonymGroupFactory->create();
  48. $synonymGroupModel->load($synonymGroup->getGroupId());
  49. $isCreate = $synonymGroupModel->getSynonymGroup() === null;
  50. if ($isCreate) {
  51. return $this->create($synonymGroup, $errorOnMergeConflict);
  52. } else {
  53. return $this->update($synonymGroupModel, $synonymGroup, $errorOnMergeConflict);
  54. }
  55. }
  56. /**
  57. * Deletes a synonym group
  58. *
  59. * @param SynonymGroupInterface $synonymGroup
  60. * @throws CouldNotDeleteException
  61. * @return bool
  62. */
  63. public function delete(SynonymGroupInterface $synonymGroup)
  64. {
  65. try {
  66. $this->resourceModel->delete($synonymGroup);
  67. } catch (\Exception $exception) {
  68. throw new CouldNotDeleteException(
  69. __(
  70. 'The synonym group with the "%1" ID can\'t be deleted. %2',
  71. $synonymGroup->getGroupId(),
  72. $exception->getMessage()
  73. )
  74. );
  75. }
  76. return true;
  77. }
  78. /**
  79. * Return a particular synonym group interface instance based on passed in synonym group id
  80. *
  81. * @param int $synonymGroupId
  82. * @return \Magento\Search\Api\Data\SynonymGroupInterface
  83. */
  84. public function get($synonymGroupId)
  85. {
  86. /** @var SynonymGroup $synonymGroup */
  87. $synonymGroup = $this->synonymGroupFactory->create();
  88. if ($synonymGroupId !== null) {
  89. $synonymGroup->load($synonymGroupId);
  90. }
  91. return $synonymGroup;
  92. }
  93. /**
  94. * Private helper to create a synonym group, throw exception on merge conflict
  95. *
  96. * @param SynonymGroupInterface $synonymGroup
  97. * @param bool $errorOnMergeConflict
  98. * @return SynonymGroupInterface
  99. * @throws Synonym\MergeConflictException
  100. * @throws \Magento\Framework\Exception\AlreadyExistsException
  101. */
  102. private function create(SynonymGroupInterface $synonymGroup, $errorOnMergeConflict)
  103. {
  104. $matchingSynonymGroups = $this->getMatchingSynonymGroups($synonymGroup);
  105. if ($matchingSynonymGroups) {
  106. if ($errorOnMergeConflict) {
  107. throw new Synonym\MergeConflictException(
  108. $this->parseToArray($matchingSynonymGroups),
  109. $this->getExceptionMessage($matchingSynonymGroups)
  110. );
  111. }
  112. // merge matching synonyms before creating a new row
  113. $mergedSynonyms = $this->merge($synonymGroup, array_keys($matchingSynonymGroups));
  114. /** @var SynonymGroup $newSynonymGroupModel */
  115. $newSynonymGroupModel = $this->synonymGroupFactory->create();
  116. $newSynonymGroupModel->setSynonymGroup(implode(',', $mergedSynonyms));
  117. $newSynonymGroupModel->setWebsiteId($synonymGroup->getWebsiteId());
  118. $newSynonymGroupModel->setStoreId($synonymGroup->getStoreId());
  119. $this->resourceModel->save($newSynonymGroupModel);
  120. $synonymGroup->setSynonymGroup($newSynonymGroupModel->getSynonymGroup());
  121. $synonymGroup->setGroupId($newSynonymGroupModel->getGroupId());
  122. } else {
  123. // no merge conflict, perform simple insert
  124. /** @var SynonymGroup $synonymGroupModel */
  125. $synonymGroupModel = $this->synonymGroupFactory->create();
  126. $this->populateSynonymGroupModel($synonymGroupModel, $synonymGroup);
  127. $this->resourceModel->save($synonymGroupModel);
  128. $synonymGroup->setGroupId($synonymGroupModel->getGroupId());
  129. }
  130. return $synonymGroup;
  131. }
  132. /**
  133. * Perform synonyms merge
  134. *
  135. * @param SynonymGroupInterface $synonymGroupToMerge
  136. * @param array $matchingGroupIds
  137. * @return array
  138. * @throws \Exception
  139. */
  140. private function merge(SynonymGroupInterface $synonymGroupToMerge, array $matchingGroupIds)
  141. {
  142. $mergedSynonyms = [];
  143. foreach ($matchingGroupIds as $groupId) {
  144. /** @var SynonymGroup $synonymGroupModel */
  145. $synonymGroupModel = $this->synonymGroupFactory->create();
  146. $synonymGroupModel->load($groupId);
  147. $mergedSynonyms = array_merge($mergedSynonyms, explode(',', $synonymGroupModel->getSynonymGroup()));
  148. $synonymGroupModel->delete();
  149. }
  150. $mergedSynonyms = array_merge($mergedSynonyms, explode(',', $synonymGroupToMerge->getSynonymGroup()));
  151. $mergedSynonyms = array_unique($mergedSynonyms);
  152. return $mergedSynonyms;
  153. }
  154. /**
  155. * Private helper to populate SynonymGroup model with data
  156. *
  157. * @param SynonymGroup $modelToPopulate
  158. * @param SynonymGroupInterface $synonymGroupData
  159. * @return void
  160. */
  161. private function populateSynonymGroupModel(SynonymGroup $modelToPopulate, SynonymGroupInterface $synonymGroupData)
  162. {
  163. $modelToPopulate->setWebsiteId($synonymGroupData->getWebsiteId());
  164. $modelToPopulate->setStoreId($synonymGroupData->getStoreId());
  165. $modelToPopulate->setSynonymGroup($synonymGroupData->getSynonymGroup());
  166. }
  167. /**
  168. * Private helper to update a synonym group, throw exception on merge conflict
  169. *
  170. * @param SynonymGroup $oldSynonymGroup
  171. * @param SynonymGroupInterface $newSynonymGroup
  172. * @param bool $errorOnMergeConflict
  173. * @return SynonymGroupInterface
  174. * @throws Synonym\MergeConflictException
  175. * @throws \Magento\Framework\Exception\AlreadyExistsException
  176. */
  177. private function update(
  178. SynonymGroup $oldSynonymGroup,
  179. SynonymGroupInterface $newSynonymGroup,
  180. $errorOnMergeConflict
  181. ) {
  182. $matchingSynonymGroups = $this->getMatchingSynonymGroups($newSynonymGroup);
  183. // ignore existing synonym group as it's value will be discarded
  184. $matchingSynonymGroups = array_diff_key(
  185. $matchingSynonymGroups,
  186. [$oldSynonymGroup->getGroupId() => $oldSynonymGroup->getSynonymGroup()]
  187. );
  188. if ($matchingSynonymGroups) {
  189. if ($errorOnMergeConflict) {
  190. throw new Synonym\MergeConflictException(
  191. $this->parseToArray($matchingSynonymGroups),
  192. $this->getExceptionMessage($matchingSynonymGroups)
  193. );
  194. }
  195. // merge matching synonyms before updating a row
  196. $mergedSynonyms = $this->merge($newSynonymGroup, array_keys($matchingSynonymGroups));
  197. $oldSynonymGroup->setSynonymGroup(implode(',', $mergedSynonyms));
  198. $oldSynonymGroup->setWebsiteId($newSynonymGroup->getWebsiteId());
  199. $oldSynonymGroup->setStoreId($newSynonymGroup->getStoreId());
  200. $this->resourceModel->save($oldSynonymGroup);
  201. } else {
  202. // no merge conflict, perform simple update
  203. $this->populateSynonymGroupModel($oldSynonymGroup, $newSynonymGroup);
  204. $this->resourceModel->save($oldSynonymGroup);
  205. }
  206. return $oldSynonymGroup;
  207. }
  208. /**
  209. * Gets merge conflict exception message
  210. *
  211. * @param string[] $matchingSynonymGroups
  212. * @return \Magento\Framework\Phrase
  213. */
  214. private function getExceptionMessage($matchingSynonymGroups)
  215. {
  216. $displayString = '(';
  217. $displayString .= implode('), (', $matchingSynonymGroups);
  218. $displayString .= ')';
  219. return __('Merge conflict with existing synonym group(s): %1', $displayString);
  220. }
  221. /**
  222. * Parse the matching synonym groups into array
  223. *
  224. * @param string[] $matchingSynonymGroups
  225. * @return array
  226. */
  227. private function parseToArray($matchingSynonymGroups)
  228. {
  229. $parsedArray = [];
  230. foreach ($matchingSynonymGroups as $matchingSynonymGroup) {
  231. $parsedArray[] = explode(',', $matchingSynonymGroup);
  232. }
  233. return $parsedArray;
  234. }
  235. /**
  236. * Gets all other matching synonym groups in the same scope
  237. *
  238. * @param SynonymGroupInterface $synonymGroup
  239. * @return string[]
  240. */
  241. private function getMatchingSynonymGroups(SynonymGroupInterface $synonymGroup)
  242. {
  243. $synonymGroupsInScope = $this->resourceModel->getByScope(
  244. $synonymGroup->getWebsiteId(),
  245. $synonymGroup->getStoreId()
  246. );
  247. $matchingSynonymGroups = [];
  248. foreach ($synonymGroupsInScope as $synonymGroupInScope) {
  249. if (array_intersect(
  250. explode(',', $synonymGroup->getSynonymGroup()),
  251. explode(',', $synonymGroupInScope['synonyms'])
  252. )) {
  253. $matchingSynonymGroups[$synonymGroupInScope['group_id']] = $synonymGroupInScope['synonyms'];
  254. }
  255. }
  256. return $matchingSynonymGroups;
  257. }
  258. }