Completed
Pull Request — master (#405)
by Stefan
03:39
created

Update::run()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 19
nc 1
nop 0
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
/**
3
 * (c) shopware AG <[email protected]>
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
namespace ShopwarePlugins\Connect\Bootstrap;
9
10
use Shopware\CustomModels\Connect\Attribute;
11
use Shopware\Components\Model\ModelManager;
12
use Enlight_Components_Db_Adapter_Pdo_Mysql as Pdo;
13
use Shopware\Models\Attribute\Configuration;
14
use Shopware\Models\Order\Status;
15
use ShopwarePlugins\Connect\Components\ProductQuery\BaseProductQuery;
16
use ShopwarePlugins\Connect\Components\Utils\ConnectOrderUtil;
17
use ShopwarePlugins\Connect\Components\Logger;
18
19
/**
20
 * Updates existing versions of the plugin
21
 *
22
 * Class Update
23
 * @package ShopwarePlugins\Connect\Bootstrap
24
 */
25
class Update
26
{
27
    /**
28
     * @var \Shopware_Plugins_Backend_SwagConnect_Bootstrap
29
     */
30
    protected $bootstrap;
31
32
    /**
33
     * @var Pdo
34
     */
35
    protected $db;
36
37
    /**
38
     * @var ModelManager
39
     */
40
    protected $modelManager;
41
42
    /**
43
     * @var string
44
     */
45
    protected $version;
46
47
    /**
48
     * @var Logger
49
     */
50
    private $logger;
51
52
    /**
53
     * Setup constructor.
54
     * @param \Shopware_Plugins_Backend_SwagConnect_Bootstrap $bootstrap
55
     * @param ModelManager $modelManager
56
     * @param Pdo $db
57
     * @param $version
58
     * @param Logger|null $logger
59
     */
60
    public function __construct(
61
        \Shopware_Plugins_Backend_SwagConnect_Bootstrap $bootstrap,
62
        ModelManager $modelManager,
63
        Pdo $db,
64
        Logger $logger,
65
        $version
66
    ) {
67
        $this->bootstrap = $bootstrap;
68
        $this->modelManager = $modelManager;
69
        $this->db = $db;
70
        $this->logger = $logger;
71
        $this->version = $version;
72
    }
73
74
    public function run()
75
    {
76
        // Force an SDK re-verify
77
        $this->reVerifySDK();
78
79
        $this->createExportedFlag();
80
        $this->removeRedirectMenu();
81
        $this->updateConnectAttribute();
82
        $this->addConnectDescriptionElement();
83
        $this->updateProductDescriptionSetting();
84
        $this->createUpdateAdditionalDescriptionColumn();
85
        $this->createDynamicStreamTable();
86
        $this->addOrderStatus();
87
        $this->fixExportDescriptionSettings();
88
        $this->fixMarketplaceUrl();
89
        $this->addIndexToChangeTable();
90
        $this->removeDuplicatedMenuItems();
91
        $this->addConnectItemsIndex();
92
        $this->createRemoteToLocalCategoriesTable();
93
        $this->recreateRemoteCategoriesAndProductAssignments();
94
        $this->addProductToCategoryIndex();
95
96
        return true;
97
    }
98
99
    /**
100
     * Forces the SDK to re-verify the API key
101
     */
102
    public function reVerifySDK()
103
    {
104
        $this->db->query('
105
            UPDATE sw_connect_shop_config
106
            SET s_config = ?
107
            WHERE s_shop = "_last_update_"
108
            LIMIT 1; ',
109
            [time() - 8 * 60 * 60 * 24]
110
        );
111
    }
112
113
    private function createExportedFlag()
114
    {
115
        if (version_compare($this->version, '1.0.1', '<=')) {
116
            $this->db->query('
117
                ALTER TABLE `s_plugin_connect_items`
118
                ADD COLUMN `exported` TINYINT(1) DEFAULT 0
119
            ');
120
121
            $this->db->query('
122
                UPDATE `s_plugin_connect_items`
123
                SET `exported` = 1
124
                WHERE (`export_status` = ? OR `export_status` = ? OR `export_status` = ?) AND `shop_id` IS NULL',
125
                [Attribute::STATUS_INSERT, Attribute::STATUS_UPDATE, Attribute::STATUS_SYNCED]
126
            );
127
        }
128
    }
129
130
    private function removeRedirectMenu()
131
    {
132
        if (version_compare($this->version, '1.0.4', '<=')) {
133
            $connectItem = $this->bootstrap->Menu()->findOneBy(['label' => 'Open Connect', 'action' => '']);
134
            if ($connectItem) {
135
                $this->modelManager->remove($connectItem);
136
                $this->modelManager->flush();
137
            }
138
        }
139
    }
140
141
    private function updateConnectAttribute()
142
    {
143
        if (version_compare($this->version, '1.0.6', '<=')) {
144
            $result = $this->db->query("SELECT value FROM s_plugin_connect_config WHERE name = 'connectAttribute'");
145
            $row = $result->fetch();
146
            $attr = 19;
147
            if ($row) {
148
                $attr = $row['value'];
149
            }
150
151
            $this->db->query('
152
                    UPDATE `s_articles_attributes` 
153
                    SET `connect_reference` = `attr' . $attr . '` 
154
                    WHERE connect_reference IS NULL;
155
                ');
156
157
            $this->db->query("DELETE FROM s_plugin_connect_config WHERE name = 'connectAttribute'");
158
        }
159
    }
160
161
    private function addConnectDescriptionElement()
162
    {
163
        if (version_compare($this->version, '1.0.9', '<=')) {
164
            $tableName = $this->modelManager->getClassMetadata('Shopware\Models\Attribute\Article')->getTableName();
165
            $columnName = 'connect_product_description';
166
167
            $repo = $this->modelManager->getRepository('Shopware\Models\Attribute\Configuration');
168
            $element = $repo->findOneBy([
169
                'tableName' => $tableName,
170
                'columnName' => $columnName,
171
            ]);
172
173
            if (!$element) {
174
                $element = new Configuration();
175
                $element->setTableName($tableName);
176
                $element->setColumnName($columnName);
177
            }
178
179
            $element->setColumnType('html');
180
            $element->setTranslatable(true);
181
            $element->setLabel('Connect Beschreibung');
182
            $element->setDisplayInBackend(true);
183
184
            $this->modelManager->persist($element);
185
            $this->modelManager->flush();
186
        }
187
    }
188
189
    private function updateProductDescriptionSetting()
190
    {
191
        if (version_compare($this->version, '1.0.9', '<=')) {
192
            //migrates to the new export settings
193
            $result = $this->db->query("SELECT `value` FROM s_plugin_connect_config WHERE name = 'alternateDescriptionField'");
194
            $row = $result->fetch();
195
196
            if ($row) {
197
                $mapper = [
198
                    'a.description' => BaseProductQuery::SHORT_DESCRIPTION_FIELD,
199
                    'a.descriptionLong' => BaseProductQuery::LONG_DESCRIPTION_FIELD,
200
                    'attribute.connectProductDescription' => BaseProductQuery::CONNECT_DESCRIPTION_FIELD,
201
                ];
202
203
                if ($name = $mapper[$row['value']]) {
204
                    $result = $this->db->query("SELECT `id` FROM s_plugin_connect_config WHERE name = '$name'");
205
                    $row = $result->fetch();
206
207
                    $id = null;
208
                    if (isset($row['id'])) {
209
                        $id = $row['id'];
210
                    }
211
212
                    $this->db->query(
213
                        "REPLACE INTO `s_plugin_connect_config`
214
                        (`id`, `name`, `value`, `shopId`, `groupName`)
215
                        VALUES
216
                        (?, ?, 1, null, 'export')",
217
                        [$id, $name]
218
                    );
219
                }
220
            }
221
222
            $this->db->query("
223
                ALTER TABLE `s_plugin_connect_items`
224
                ADD `update_additional_description` VARCHAR(255) NULL DEFAULT 'inherit' AFTER `update_short_description`;
225
            ");
226
        }
227
    }
228
229
    private function createUpdateAdditionalDescriptionColumn()
230
    {
231
        // for some reason update_additional_description column is missing in 1.0.11
232
        if (version_compare($this->version, '1.0.11', '<=')) {
233
            try {
234
                $this->db->query("
235
                        ALTER TABLE `s_plugin_connect_items`
236
                        ADD `update_additional_description` VARCHAR(255) NULL DEFAULT 'inherit' AFTER `update_short_description`;
237
                    ");
238
            } catch (\Exception $e) {
239
                // ignore it if the column already exists
240
            }
241
        }
242
    }
243
244
    private function createDynamicStreamTable()
245
    {
246
        if (version_compare($this->version, '1.0.12', '<=')) {
247
            $query = "CREATE TABLE IF NOT EXISTS `s_plugin_connect_streams_relation` (
248
                `stream_id` int(11) unsigned NOT NULL,
249
                `article_id` int(11) unsigned NOT NULL,
250
                `deleted` int(1) NOT NULL DEFAULT '0',
251
                UNIQUE KEY `stream_id` (`stream_id`,`article_id`),
252
                CONSTRAINT s_plugin_connect_streams_selection_fk_stream_id FOREIGN KEY (stream_id) REFERENCES s_product_streams (id) ON DELETE CASCADE,
253
                CONSTRAINT s_plugin_connect_streams_selection_fk_article_id FOREIGN KEY (article_id) REFERENCES s_articles (id) ON DELETE CASCADE
254
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;";
255
256
            $this->db->exec($query);
257
        }
258
    }
259
260
    private function addOrderStatus()
261
    {
262
        if (version_compare($this->version, '1.0.12', '<=')) {
263
            $query = $this->modelManager->getRepository('Shopware\Models\Order\Status')->createQueryBuilder('s');
264
            $query->select('MAX(s.id)');
265
            $result = $query->getQuery()->getOneOrNullResult();
266
267
            if (count($result) > 0) {
268
                $currentId = (int) reset($result);
269
            } else {
270
                $currentId = 0;
271
            }
272
273
            $name = ConnectOrderUtil::ORDER_STATUS_ERROR;
274
            $group = Status::GROUP_STATE;
275
276
            $isExists = $this->db->query('
277
                SELECT `id` FROM `s_core_states`
278
                WHERE `name` = ? AND `group` = ?
279
                ', [$name, $group]
280
            )->fetch();
281
282
            if ($isExists) {
283
                return;
284
            }
285
286
            ++$currentId;
287
            $this->db->query('
288
                INSERT INTO `s_core_states`
289
                (`id`, `name`, `description`, `position`, `group`, `mail`)
290
                VALUES (?, ?, ?, ?, ?, ?)
291
                ', [$currentId, $name, 'SC error', $currentId, $group, 0]
292
            );
293
        }
294
    }
295
296
    /**
297
     * Replace longDescriptionField and shortDescription values,
298
     * because of wrong snippets in previous versions.
299
     *
300
     * ExtJs view show longDescription label, but the value was stored as shortDescription
301
     */
302
    private function fixExportDescriptionSettings()
303
    {
304
        if (version_compare($this->version, '1.0.12', '<=')) {
305
            $rows = $this->db->fetchPairs(
306
                'SELECT `name`, `value` FROM s_plugin_connect_config WHERE name = ? OR name = ?',
307
                ['longDescriptionField', 'shortDescriptionField']
308
            );
309
310
            if (!array_key_exists('longDescriptionField', $rows) || !array_key_exists('shortDescriptionField', $rows)) {
311
                return;
312
            }
313
314
            if (($rows['longDescriptionField'] == 1 && $rows['shortDescriptionField'] == 1)
315
                || ($rows['longDescriptionField'] == 0 && $rows['shortDescriptionField'] == 0)) {
316
                return;
317
            }
318
319
            $newValues = [
320
                'longDescriptionField' => $rows['shortDescriptionField'],
321
                'shortDescriptionField' => $rows['longDescriptionField'],
322
            ];
323
324
            $this->db->query('
325
                UPDATE `s_plugin_connect_config`
326
                SET `value` = ?
327
                WHERE `name` = ?',
328
                [$newValues['longDescriptionField'], 'longDescriptionField']
329
            );
330
331
            $this->db->query('
332
                UPDATE `s_plugin_connect_config`
333
                SET `value` = ?
334
                WHERE `name` = ?',
335
                [$newValues['shortDescriptionField'], 'shortDescriptionField']
336
            );
337
        }
338
    }
339
340
    private function fixMarketplaceUrl()
341
    {
342
        if (version_compare($this->version, '1.0.12', '<=')) {
343
            $repo = $this->modelManager->getRepository('Shopware\Models\Config\Form');
344
            /** @var \Shopware\Models\Config\Form $form */
345
            $form = $repo->findOneBy([
346
                'name' => 'SwagConnect',
347
            ]);
348
349
            if (!$form) {
350
                return;
351
            }
352
353
            /** @var \Shopware\Models\Config\Element $element */
354
            foreach ($form->getElements() as $element) {
355
                if ($element->getName() != 'connectDebugHost') {
356
                    continue;
357
                }
358
359 View Code Duplication
                if (strlen($element->getValue()) > 0 && strpos($element->getValue(), 'sn.') === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
360
                    $element->setValue('sn.' . $element->getValue());
361
                    $this->modelManager->persist($element);
362
                }
363
364
                $values = $element->getValues();
365
                if (count($values) > 0) {
366
                    /** @var \Shopware\Models\Config\Value $element */
367
                    $value = $values[0];
368 View Code Duplication
                    if (strlen($value->getValue()) > 0 && strpos($value->getValue(), 'sn.') === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
369
                        $value->setValue('sn.' . $value->getValue());
370
                        $this->modelManager->persist($value);
371
                    }
372
                }
373
374
                $this->modelManager->flush();
375
            }
376
        }
377
    }
378
379
    private function addIndexToChangeTable()
380
    {
381
        if (version_compare($this->version, '1.0.16', '<=')) {
382
            $this->db->query('
383
              ALTER TABLE `sw_connect_change`
384
              ADD INDEX `c_operation` (`c_operation`)
385
             ');
386
        }
387
    }
388
389
    /**
390
     * In some cases Connect main menu was duplicated
391
     * when shop is connected to SEM project. All not needed menu items must be removed.
392
     */
393
    private function removeDuplicatedMenuItems()
394
    {
395
        if (version_compare($this->version, '1.0.16', '<=')) {
396
            $mainMenuItems = $this->bootstrap->Menu()->findBy([
397
                'class' => Menu::CONNECT_CLASS,
398
                'parent' => null,
399
            ], ['id' => 'ASC']);
400
401
            foreach (array_slice($mainMenuItems, 1) as $menuItem) {
402
                foreach ($menuItem->getChildren() as $children) {
403
                    $this->modelManager->remove($children);
404
                }
405
406
                $this->modelManager->remove($menuItem);
407
            }
408
            $this->modelManager->flush();
409
        }
410
    }
411
412
    /**
413
     * Create most used indexes in s_plugin_connect_items table.
414
     */
415
    private function addConnectItemsIndex()
416
    {
417
        if (version_compare($this->version, '1.1.1', '<=')) {
418
            try {
419
                $this->db->query('ALTER TABLE s_plugin_connect_items ADD INDEX stream(shop_id, stream)');
420
                $this->db->query('ALTER TABLE s_plugin_connect_items MODIFY group_id VARCHAR(64)');
421
                $this->db->query('ALTER TABLE s_plugin_connect_items ADD INDEX source_id (source_id, shop_id)');
422
                $this->db->query('ALTER TABLE s_plugin_connect_items ADD INDEX group_id (group_id, shop_id)');
423
            } catch (\Exception $e) {
424
                // ignore it if exists
425
                $this->logger->write(
426
                    true,
427
                    sprintf('An error occurred during update to version %s stacktrace: %s', $this->version, $e->getTraceAsString()),
428
                    $e->getMessage()
429
                );
430
            }
431
        }
432
    }
433
434
    /**
435
     * Create the mapping table between connect remote categories and local categories.
436
     */
437
    private function createRemoteToLocalCategoriesTable()
438
    {
439
        if (version_compare($this->version, '1.1.3', '<=')) {
440
            try {
441
                $this->db->query('CREATE TABLE IF NOT EXISTS `s_plugin_connect_categories_to_local_categories` (
442
                  `remote_category_id` int(11) NOT NULL,
443
                  `local_category_id` int(11) unsigned NOT NULL,
444
                  PRIMARY KEY (`remote_category_id`, `local_category_id`),
445
                  CONSTRAINT s_plugin_connect_remote_categories_fk_remote_category_id FOREIGN KEY (remote_category_id) REFERENCES s_plugin_connect_categories (id) ON DELETE CASCADE,
446
                  CONSTRAINT s_plugin_connect_remote_categories_fk_local_category_id FOREIGN KEY (local_category_id) REFERENCES s_categories (id) ON DELETE CASCADE
447
                  ) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;'
448
                );
449
                $result = $this->db->query('SELECT pcc.id, pcc.local_category_id
450
                                FROM s_plugin_connect_categories pcc
451
                                WHERE pcc.local_category_id IS NOT NULL');
452
453
                while ($row = $result->fetch()) {
454
                    $this->db->query(
455
                        'INSERT INTO `s_plugin_connect_categories_to_local_categories`
456
                        (`remote_category_id`, `local_category_id`)
457
                        VALUES (?, ?)',
458
                        [$row['id'],$row['local_category_id']]
459
                    );
460
                }
461
            } catch (\Exception $e) {
462
                // ignore it if exists
463
                $this->logger->write(
464
                    true,
465
                    sprintf('An error occurred during update to version %s stacktrace: %s', $this->version, $e->getTraceAsString()),
466
                    $e->getMessage()
467
                );
468
            }
469
        }
470
    }
471
472 View Code Duplication
    private function recreateRemoteCategoriesAndProductAssignments()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
473
    {
474
        if (version_compare($this->version, '1.1.4', '<=')) {
475
            try {
476
                $this->db->query('INSERT INTO `s_plugin_connect_config` (`name`, `value`) VALUES ("recreateConnectCategories", "0")');
477
            } catch (\Exception $e) {
478
                // ignore it if exists
479
                $this->logger->write(
480
                    true,
481
                    sprintf('An error occurred during update to version %s stacktrace: %s', $this->version, $e->getTraceAsString()),
482
                    $e->getMessage()
483
                );
484
            }
485
        }
486
    }
487
488
    /**
489
     * Create index by articleID in s_plugin_connect_product_to_categories table.
490
     */
491 View Code Duplication
    private function addProductToCategoryIndex()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
492
    {
493
        if (version_compare($this->version, '1.1.6', '<=')) {
494
            try {
495
                $this->db->query('ALTER TABLE s_plugin_connect_product_to_categories ADD INDEX article_id(articleID)');
496
            } catch (\Exception $e) {
497
                // ignore it if exists
498
                $this->logger->write(
499
                    true,
500
                    sprintf('An error occurred during update to version %s stacktrace: %s', $this->version, $e->getTraceAsString()),
501
                    $e->getMessage()
502
                );
503
            }
504
        }
505
    }
506
}
507