UpgradeSchema.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. <?php
  2. namespace Dotdigitalgroup\Email\Setup;
  3. use Magento\Framework\DB\Ddl\Table;
  4. use Magento\Framework\Setup\UpgradeSchemaInterface;
  5. use Magento\Framework\Setup\ModuleContextInterface;
  6. use Magento\Framework\Setup\SchemaSetupInterface;
  7. use \Magento\Framework\DB\Adapter\AdapterInterface;
  8. /**
  9. * @codeCoverageIgnore
  10. */
  11. class UpgradeSchema implements UpgradeSchemaInterface
  12. {
  13. /**
  14. * @var \Dotdigitalgroup\Email\Model\Config\Json
  15. */
  16. public $json;
  17. /**
  18. * @var Schema\Shared
  19. */
  20. private $shared;
  21. /**
  22. * UpgradeSchema constructor.
  23. *
  24. * @param \Dotdigitalgroup\Email\Model\Config\Json $json
  25. * @param Schema\Shared $shared
  26. */
  27. public function __construct(
  28. \Dotdigitalgroup\Email\Model\Config\Json $json,
  29. Schema\Shared $shared
  30. ) {
  31. $this->shared = $shared;
  32. $this->json = $json;
  33. }
  34. /**
  35. * {@inheritdoc}
  36. */
  37. public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
  38. {
  39. $setup->startSetup();
  40. $connection = $setup->getConnection();
  41. $this->upgradeOneOneZeoToTwoTwoOne($setup, $context, $connection);
  42. $this->upgradeTwoThreeSixToTwoFiveFour($setup, $context);
  43. $this->upgradeTwoFiveFourToThreeZeroThree($setup, $context);
  44. $setup->endSetup();
  45. }
  46. /**
  47. * @param AdapterInterface $connection
  48. * @param SchemaSetupInterface $setup
  49. *
  50. * @return void
  51. */
  52. private function upgradeTwoOSix($connection, $setup)
  53. {
  54. //modify email_campaign table
  55. $campaignTable = $setup->getTable(Schema::EMAIL_CAMPAIGN_TABLE);
  56. //add columns
  57. $connection->addColumn(
  58. $campaignTable,
  59. 'send_id',
  60. [
  61. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
  62. 'nullable' => false,
  63. 'default' => '',
  64. 'comment' => 'Campaign Send Id'
  65. ]
  66. );
  67. $connection->addColumn(
  68. $campaignTable,
  69. 'send_status',
  70. [
  71. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
  72. 'nullable' => false,
  73. 'default' => 0,
  74. 'comment' => 'Send Status'
  75. ]
  76. );
  77. if ($connection->tableColumnExists($campaignTable, 'is_sent')) {
  78. //update table with historical send values
  79. $select = $connection->select();
  80. //join
  81. $select->joinLeft(
  82. ['oc' => $campaignTable],
  83. "oc.id = nc.id",
  84. [
  85. 'send_status' => new \Zend_Db_Expr(\Dotdigitalgroup\Email\Model\Campaign::SENT)
  86. ]
  87. )->where('oc.is_sent =?', 1);
  88. //update query from select
  89. $updateSql = $select->crossUpdateFromSelect(['nc' => $campaignTable]);
  90. //run query
  91. $connection->query($updateSql);
  92. //remove column
  93. $connection->dropColumn($campaignTable, 'is_sent');
  94. }
  95. //add index
  96. $connection->addIndex(
  97. $campaignTable,
  98. $setup->getIdxName($campaignTable, ['send_status']),
  99. ['send_status']
  100. );
  101. }
  102. /**
  103. * @param SchemaSetupInterface $setup
  104. * @param AdapterInterface $connection
  105. *
  106. * @return null
  107. */
  108. private function convertDataForConfig(SchemaSetupInterface $setup, $connection)
  109. {
  110. $configTable = $setup->getTable('core_config_data');
  111. //customer and order custom attributes from config
  112. $select = $connection->select()->from(
  113. $configTable
  114. )->where(
  115. 'path IN (?)',
  116. [
  117. 'connector_automation/order_status_automation/program',
  118. 'connector_data_mapping/customer_data/custom_attributes'
  119. ]
  120. );
  121. $rows = $setup->getConnection()->fetchAssoc($select);
  122. $serializedRows = array_filter($rows, function ($row) {
  123. return $this->isSerialized($row['value']);
  124. });
  125. foreach ($serializedRows as $id => $serializedRow) {
  126. $convertedValue = $this->json->serialize($this->unserialize($serializedRow['value']));
  127. $bind = ['value' => $convertedValue];
  128. $where = [$connection->quoteIdentifier('config_id') . '=?' => $id];
  129. $connection->update($configTable, $bind, $where);
  130. }
  131. }
  132. /**
  133. * @param SchemaSetupInterface $setup
  134. * @param AdapterInterface $connection
  135. *
  136. * @return null
  137. */
  138. private function convertDataForRules(SchemaSetupInterface $setup, $connection)
  139. {
  140. $rulesTable = $setup->getTable(Schema::EMAIL_RULES_TABLE);
  141. //rules data
  142. $select = $connection->select()->from($rulesTable);
  143. $rows = $setup->getConnection()->fetchAssoc($select);
  144. $serializedRows = array_filter($rows, function ($row) {
  145. return $this->isSerialized($row['conditions']);
  146. });
  147. foreach ($serializedRows as $id => $serializedRow) {
  148. $convertedValue = $this->json->serialize($this->unserialize($serializedRow['conditions']));
  149. $bind = ['conditions' => $convertedValue];
  150. $where = [$connection->quoteIdentifier('id') . '=?' => $id];
  151. $connection->update($rulesTable, $bind, $where);
  152. }
  153. }
  154. /**
  155. * @param SchemaSetupInterface $setup
  156. * @param AdapterInterface $connection
  157. *
  158. * @return null
  159. */
  160. private function convertDataForImporter(SchemaSetupInterface $setup, $connection)
  161. {
  162. $importerTable = $setup->getTable(Schema::EMAIL_IMPORTER_TABLE);
  163. //imports that are not imported and has TD data
  164. $select = $connection->select()
  165. ->from($importerTable)
  166. ->where('import_status =?', 0)
  167. ->where('import_type IN (?)', ['Catalog_Default', 'Orders' ])
  168. ;
  169. $rows = $setup->getConnection()->fetchAssoc($select);
  170. $serializedRows = array_filter($rows, function ($row) {
  171. return $this->isSerialized($row['import_data']);
  172. });
  173. foreach ($serializedRows as $id => $serializedRow) {
  174. $convertedValue = $this->json->serialize($this->unserialize($serializedRow['import_data']));
  175. $bind = ['import_data' => $convertedValue];
  176. $where = [$connection->quoteIdentifier('id') . '=?' => $id];
  177. $connection->update($importerTable, $bind, $where);
  178. }
  179. }
  180. /**
  181. * Check if value is a serialized string
  182. *
  183. * @param string $value
  184. * @return boolean
  185. */
  186. private function isSerialized($value)
  187. {
  188. return (boolean) preg_match('/^((s|i|d|b|a|O|C):|N;)/', $value);
  189. }
  190. /**
  191. * @param string $string
  192. * @return mixed
  193. */
  194. private function unserialize($string)
  195. {
  196. if (false === $string || null === $string || '' === $string) {
  197. throw new \InvalidArgumentException('Unable to unserialize value.');
  198. }
  199. set_error_handler(
  200. function () {
  201. restore_error_handler();
  202. throw new \InvalidArgumentException('Unable to unserialize value, string is corrupted.');
  203. },
  204. E_NOTICE
  205. );
  206. $result = unserialize($string, ['allowed_classes' => false]);
  207. restore_error_handler();
  208. return $result;
  209. }
  210. /**
  211. * @param SchemaSetupInterface $setup
  212. * @param AdapterInterface $connection
  213. *
  214. * @return null
  215. */
  216. private function addIndexKeyForCatalog(
  217. SchemaSetupInterface $setup,
  218. \Magento\Framework\DB\Adapter\AdapterInterface $connection
  219. ) {
  220. $connection->addForeignKey(
  221. $setup->getFkName(
  222. Schema::EMAIL_CATALOG_TABLE,
  223. 'product_id',
  224. 'catalog_product_entity',
  225. 'entity_id'
  226. ),
  227. $setup->getTable(Schema::EMAIL_CATALOG_TABLE),
  228. 'product_id',
  229. $setup->getTable('catalog_product_entity'),
  230. 'entity_id',
  231. \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
  232. );
  233. }
  234. /**
  235. * @param SchemaSetupInterface $setup
  236. * @param AdapterInterface $connection
  237. *
  238. * @return null
  239. */
  240. private function addIndexKeyForOrder(
  241. SchemaSetupInterface $setup,
  242. \Magento\Framework\DB\Adapter\AdapterInterface $connection
  243. ) {
  244. //Only add foreign key if table exist in default connection
  245. if ($connection->isTableExists($setup->getTable('sales_order'))) {
  246. $connection->addForeignKey(
  247. $setup->getFkName(
  248. Schema::EMAIL_ORDER_TABLE,
  249. 'order_id',
  250. 'sales_order',
  251. 'entity_id'
  252. ),
  253. $setup->getTable(Schema::EMAIL_ORDER_TABLE),
  254. 'order_id',
  255. $setup->getTable('sales_order'),
  256. 'entity_id',
  257. \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
  258. );
  259. }
  260. }
  261. /**
  262. * @param SchemaSetupInterface $setup
  263. * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
  264. */
  265. private function addColumnToCouponTable(SchemaSetupInterface $setup, $connection)
  266. {
  267. $couponTable = $setup->getTable('salesrule_coupon');
  268. $connection->addColumn(
  269. $couponTable,
  270. 'generated_by_dotmailer',
  271. [
  272. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
  273. 'nullable' => true,
  274. 'default' => null,
  275. 'comment' => '1 = Generated by dotmailer'
  276. ]
  277. );
  278. }
  279. /**
  280. * @param SchemaSetupInterface $setup
  281. * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
  282. */
  283. private function convertDataAndAddIndexes(SchemaSetupInterface $setup, $connection)
  284. {
  285. //modify the condition column name for the email_rules table - reserved name for mysql
  286. $rulesTable = $setup->getTable(Schema::EMAIL_RULES_TABLE);
  287. if ($connection->tableColumnExists($rulesTable, 'condition')) {
  288. $connection->changeColumn(
  289. $rulesTable,
  290. 'condition',
  291. 'conditions',
  292. [
  293. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_BLOB,
  294. 'nullable' => false,
  295. 'comment' => 'Rule Conditions'
  296. ]
  297. );
  298. }
  299. /**
  300. * Core config data.
  301. */
  302. $this->convertDataForConfig($setup, $connection);
  303. /**
  304. * Importer data.
  305. */
  306. $this->convertDataForImporter($setup, $connection);
  307. /**
  308. * Rules conditions.
  309. */
  310. $this->convertDataForRules($setup, $connection);
  311. /**
  312. * Index foreign key for email catalog.
  313. */
  314. $this->addIndexKeyForCatalog($setup, $connection);
  315. /**
  316. * Add index foreign key for email order.
  317. */
  318. $this->addIndexKeyForOrder($setup, $connection);
  319. }
  320. /**
  321. * @param SchemaSetupInterface $setup
  322. */
  323. private function modifyWishlistTable(SchemaSetupInterface $setup)
  324. {
  325. $connection = $setup->getConnection();
  326. $emailWishlistTable = $setup->getTable(Schema::EMAIL_WISHLIST_TABLE);
  327. if ($connection->tableColumnExists($emailWishlistTable, 'customer_id')) {
  328. $connection->changeColumn(
  329. $emailWishlistTable,
  330. 'customer_id',
  331. 'customer_id',
  332. [
  333. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
  334. 'unsigned' => true,
  335. 'nullable' => true,
  336. 'comment' => 'Customer ID'
  337. ]
  338. );
  339. }
  340. $connection->addForeignKey(
  341. $setup->getFkName(
  342. Schema::EMAIL_WISHLIST_TABLE,
  343. 'customer_id',
  344. 'customer_entity',
  345. 'entity_id'
  346. ),
  347. $setup->getTable(Schema::EMAIL_WISHLIST_TABLE),
  348. 'customer_id',
  349. $setup->getTable('customer_entity'),
  350. 'entity_id',
  351. \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
  352. );
  353. $connection->addForeignKey(
  354. $setup->getFkName(
  355. Schema::EMAIL_WISHLIST_TABLE,
  356. 'wishlist_id',
  357. 'wishlist',
  358. 'wishlist_id'
  359. ),
  360. $setup->getTable(Schema::EMAIL_WISHLIST_TABLE),
  361. 'wishlist_id',
  362. $setup->getTable('wishlist'),
  363. 'wishlist_id',
  364. \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
  365. );
  366. }
  367. /**
  368. * @param SchemaSetupInterface $setup
  369. * @param ModuleContextInterface $context
  370. * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
  371. */
  372. private function upgradeOneOneZeoToTwoTwoOne(
  373. SchemaSetupInterface $setup,
  374. ModuleContextInterface $context,
  375. $connection
  376. ) {
  377. if (version_compare($context->getVersion(), '1.1.0', '<')) {
  378. //remove quote table
  379. $connection->dropTable($setup->getTable('email_quote'));
  380. }
  381. if (version_compare($context->getVersion(), '2.0.6', '<')) {
  382. $this->upgradeTwoOSix($connection, $setup);
  383. }
  384. if (version_compare($context->getVersion(), '2.1.0', '<')) {
  385. $this->addColumnToCouponTable($setup, $connection);
  386. }
  387. //replace serialize with json_encode
  388. if (version_compare($context->getVersion(), '2.2.1', '<')) {
  389. $this->convertDataAndAddIndexes($setup, $connection);
  390. }
  391. }
  392. /**
  393. * @param SchemaSetupInterface $setup
  394. * @param ModuleContextInterface $context
  395. */
  396. private function upgradeTwoThreeSixToTwoFiveFour(
  397. SchemaSetupInterface $setup,
  398. ModuleContextInterface $context
  399. ) {
  400. if (version_compare($context->getVersion(), '2.3.6', '<')) {
  401. $tableName = $setup->getTable(Schema::EMAIL_ABANDONED_CART_TABLE);
  402. $this->shared->createAbandonedCartTable($setup, $tableName);
  403. }
  404. if (version_compare($context->getVersion(), '2.5.2', '<')) {
  405. $tableName = $setup->getTable(Schema::EMAIL_CONTACT_CONSENT_TABLE);
  406. $this->shared->createConsentTable($setup, $tableName);
  407. }
  408. if (version_compare($context->getVersion(), '2.5.3', '<')) {
  409. $tableName = $setup->getTable(Schema::EMAIL_FAILED_AUTH_TABLE);
  410. $this->shared->createFailedAuthTable($setup, $tableName);
  411. }
  412. if (version_compare($context->getVersion(), '2.5.4', '<')) {
  413. $this->modifyWishlistTable($setup);
  414. }
  415. }
  416. /**
  417. * @param SchemaSetupInterface $setup
  418. * @param ModuleContextInterface $context
  419. */
  420. private function upgradeTwoFiveFourToThreeZeroThree(
  421. SchemaSetupInterface $setup,
  422. ModuleContextInterface $context
  423. ) {
  424. if (version_compare($context->getVersion(), '3.0.3', '<')) {
  425. $definition = [
  426. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
  427. 'size' => 255,
  428. 'nullable' => false,
  429. 'default' => '',
  430. 'comment' => 'Contact Status'
  431. ];
  432. $setup->getConnection()->addColumn(
  433. $setup->getTable(Schema::EMAIL_ABANDONED_CART_TABLE),
  434. 'status',
  435. $definition
  436. );
  437. }
  438. }
  439. }