Passed
Branch master (bbfb46)
by Jan
22:41 queued 14:44
created

WikiApiService   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 689
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 360
dl 0
loc 689
rs 3.2
c 0
b 0
f 0
wmc 65

How to fix   Complexity   

Complex Class

Complex classes like WikiApiService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WikiApiService, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Docs\Convert;
4
5
use GuzzleHttp\Client;
6
7
class WikiApiService
8
{
9
    private const INITIAL_VERSION = '6.0.0';
10
    private const DOC_VERSION = '1.0.0';
11
12
    /**
13
     * @var string
14
     */
15
    private $sbpToken;
16
    /**
17
     * @var string
18
     */
19
    private $serverAddress;
20
    /**
21
     * @var string
22
     */
23
    private $rootCategoryId;
24
25
    public function __construct(string $sbpToken, string $serverAddress, int $rootCategoryId)
26
    {
27
        $this->sbpToken = $sbpToken;
28
        $this->serverAddress = $serverAddress;
29
        $this->rootCategoryId = $rootCategoryId;
30
31
        $this->client = new Client(['base_uri' => $this->serverAddress]);
32
    }
33
34
    public function getRootCategoryId()
35
    {
36
        return $this->rootCategoryId;
37
    }
38
39
    public function syncFilesWithServer(DocumentTree $tree): void
40
    {
41
        echo 'Syncing markdownfiles ...' . PHP_EOL;
42
        [$globalCategoryList, $articleList] = $this->gatherCategoryChildrenAndArticles($this->rootCategoryId, $this->getAllCategories());
43
44
        echo 'Deleting ' . count($articleList) . ' old articles ...' . PHP_EOL;
45
        foreach ($articleList as $article) {
46
            $this->disableArticle($article);
47
            $this->deleteArticle($article);
48
        }
49
50
        $this->deleteCategoryChildren();
51
52
        $this->syncArticles($tree);
53
        $this->syncCategories($tree);
54
        $this->syncRoot($tree);
55
    }
56
57
    private function insertGermanStubArticle(array $articleInfoDe, Document $document): void
58
    {
59
        $this->updateArticleLocale($articleInfoDe,
60
            [
61
                'seoUrl' => $document->getMetadata()->getUrlDe(),
62
                'searchableInAllLanguages' => true,
63
            ]
64
        );
65
66
        $this->updateArticleVersion($articleInfoDe,
67
            [
68
                'title' => $document->getMetadata()->getTitleDe(),
69
                'navigationTitle' => $document->getMetadata()->getTitleDe(),
70
                'content' => '<p>Die Entwicklerdokumentation ist nur auf Englisch verfügbar.</p>',
71
                'searchableInAllLanguages' => true,
72
                'fromProductVersion' => self::INITIAL_VERSION,
73
                'active' => $document->getMetadata()->isActive(),
74
                'metaTitle' => $document->getMetadata()->getMetaTitleDe(),
75
                'metaDescription' => $document->getMetadata()->getMetaDescriptionDe(),
76
            ]);
77
78
        $this->updateArticlePriority($articleInfoDe, $document->getPriority());
79
    }
80
81
    private function buildArticleVersionUrl(array $articleInfo)
82
    {
83
        return vsprintf('/wiki/entries/%d/localizations/%d/versions/%d',
84
            [
85
                $articleInfo['articleId'],
86
                $articleInfo['localeId'],
87
                $articleInfo['versionId'],
88
            ]);
89
    }
90
91
    private function getLocalizedVersionedArticle(array $articleInfo): array
92
    {
93
        $articleInLocaleWithVersionUrl = $this->buildArticleVersionUrl($articleInfo);
94
        $response = $this->client->get($articleInLocaleWithVersionUrl, ['headers' => $this->getBasicHeaders()]);
95
        $responseContents = $response->getBody()->getContents();
96
97
        return json_decode($responseContents, true);
98
    }
99
100
    private function updateArticleVersion(array $articleInfo, array $payload): void
101
    {
102
        $currentArticleVersionContents = $this->getLocalizedVersionedArticle($articleInfo);
103
        $articleInLocaleWithVersionUrl = $this->buildArticleVersionUrl($articleInfo);
104
105
        $versionString = self::DOC_VERSION;
106
107
        $requiredContents = [
108
            'id' => $articleInfo['versionId'],
109
            'version' => $versionString,
110
            'selectedVersion' => $currentArticleVersionContents,
111
        ];
112
113
        $articleContents = array_merge($currentArticleVersionContents, $requiredContents, $payload);
114
115
        $this->client->put(
116
            $articleInLocaleWithVersionUrl,
117
            [
118
                'json' => $articleContents,
119
                'headers' => $this->getBasicHeaders(),
120
            ]
121
        );
122
    }
123
124
    private function updateArticleLocale(array $articleInfo, array $payload): void
125
    {
126
        // create english lang
127
        $articleLocalUrl = vsprintf('/wiki/entries/%d/localizations/%d',
128
            [
129
                $articleInfo['articleId'],
130
                $articleInfo['localeId'],
131
            ]
132
        );
133
134
        $response = $this->client->get(
135
            $articleLocalUrl, ['headers' => $this->getBasicHeaders()]
136
        );
137
        $responseJson = $response->getBody()->getContents();
138
139
        $articleContents = array_merge(json_decode($responseJson, true), $payload);
140
141
        $this->client->put(
142
            $articleLocalUrl,
143
            [
144
                'json' => $articleContents,
145
                'headers' => $this->getBasicHeaders(),
146
            ]
147
        );
148
    }
149
150
    private function updateArticlePriority(array $articleInfo, int $priority): void
151
    {
152
        $priorityUrl = vsprintf('/wiki/entries/%d/orderPriority/%d', [$articleInfo['articleId'], $priority]);
153
154
        $this->client->put($priorityUrl, ['headers' => $this->getBasicHeaders()]);
155
    }
156
157
    private function uploadArticleMedia(array $articleInfo, string $filePath): string
158
    {
159
        $mediaEndpoint = $this->buildArticleVersionUrl($articleInfo) . '/media';
160
161
        $body = fopen($filePath, 'rb');
162
        $response = $this->client->post(
163
            $mediaEndpoint,
164
            [
165
                'multipart' => [
166
                    [
167
                        'name' => $filePath,
168
                        'contents' => $body,
169
                    ],
170
                ],
171
                'headers' => $this->getBasicHeaders(),
172
            ]
173
        );
174
175
        $responseContents = $response->getBody()->getContents();
176
177
        return json_decode($responseContents, true)[0]['fileLink'];
178
    }
179
180
    private function uploadCategoryMedia(int $categoryId, int $localizationId, string $filePath): string
181
    {
182
        $mediaEndpoint = vsprintf('wiki/categories/%d/localizations/%d/media', [$categoryId, $localizationId]);
183
184
        $body = fopen($filePath, 'rb');
185
        $response = $this->client->post(
186
            $mediaEndpoint,
187
            [
188
                'multipart' => [
189
                    [
190
                        'name' => $filePath,
191
                        'contents' => $body,
192
                    ],
193
                ],
194
                'headers' => $this->getBasicHeaders(),
195
            ]
196
        );
197
198
        $responseContents = $response->getBody()->getContents();
199
200
        return json_decode($responseContents, true)[0]['fileLink'];
201
    }
202
203
    private function getAllCategories(): array
204
    {
205
        $response = $this->client->get('/wiki/categories',
206
            ['headers' => $this->getBasicHeaders()]
207
        )->getBody()->getContents();
208
209
        return json_decode($response, true);
210
    }
211
212
    private function updateCategory(
213
        int $categoryId,
214
        int $parentId,
215
        array $oldContents,
216
        Document $document,
217
        DocumentTree $tree
218
    ): void {
219
        $oldLocalizations = $oldContents['localizations'];
220
        $oldContentDe = [];
221
        $oldContentEn = [];
222
        foreach ($oldLocalizations as $oldLocalization) {
223
            if ($oldLocalization['locale']['name'] === 'de_DE') {
224
                $oldContentDe = $oldLocalization;
225
            } elseif ($oldLocalization['locale']['name'] === 'en_GB') {
226
                $oldContentEn = $oldLocalization;
227
            }
228
        }
229
230
        $images = $document->getHtml()->render($tree)->getImages();
231
        $imageMap = [];
232
        if (count($images)) {
233
            echo '=> Uploading ' . count($images) . ' mediafile(s) ...' . PHP_EOL;
234
            foreach ($images as $key => $mediaFile) {
235
                $mediaLink = $this->uploadCategoryMedia($categoryId, $oldContentEn['id'], $mediaFile);
236
                $imageMap[$key] = $mediaLink;
237
            }
238
        }
239
240
        $payloadGlobal = [
241
            'orderPriority' => $document->getPriority(),
242
            'active' => $document->getMetadata()->isActive(),
243
        ];
244
245
        $payloadEn = [
246
            'title' => $document->getMetadata()->getTitleEn(),
247
            'navigationTitle' => $document->getMetadata()->getTitleEn(),
248
            'content' => $document->getHtml()->render($tree)->getContents($imageMap),
249
            'searchableInAllLanguages' => true,
250
            'seoUrl' => $document->getMetadata()->getUrlEn(),
251
            'metaDescription' => $document->getMetadata()->getMetaDescriptionEn(),
252
        ];
253
254
        $payloadDe = [
255
            'title' => $document->getMetadata()->getTitleDe(),
256
            'navigationTitle' => $document->getMetadata()->getTitleDe(),
257
            'content' => '<p>Die Entwicklerdokumentation ist nur auf Englisch verfügbar.</p>',
258
            'searchableInAllLanguages' => true,
259
            'seoUrl' => $document->getMetadata()->getUrlDe(),
260
            'metaDescription' => $document->getMetadata()->getMetaDescriptionDe(),
261
        ];
262
263
        $payloadDe = array_merge($oldContentDe, $payloadDe);
264
        $payloadEn = array_merge($oldContentEn, $payloadEn);
265
266
        $contents = [
267
            'id' => $categoryId,
268
            'parent' => ['id' => $parentId],
269
            'localizations' => [
270
                $payloadDe,
271
                $payloadEn,
272
            ],
273
        ];
274
275
        $contents = array_merge($oldContents, $contents, $payloadGlobal);
276
277
        $this->client->put(
278
            vsprintf('/wiki/categories/%d', [$categoryId]),
279
            [
280
                'json' => $contents,
281
                'headers' => $this->getBasicHeaders(),
282
            ]
283
        );
284
    }
285
286
    private function getBasicHeaders(): array
287
    {
288
        return ['X-Shopware-Token' => $this->sbpToken];
289
    }
290
291
    private function addArticleToCategory(array $articleInfo, int $categoryId): void
292
    {
293
        $this->client->post(
294
            vsprintf('/wiki/categories/%s/entries', [$categoryId]),
295
            [
296
                'json' => [
297
                    'id' => $articleInfo['articleId'],
298
                    'orderPriority' => '',
299
                    'excludeFromSearch' => false,
300
                    'categories' => [],
301
                ],
302
                'headers' => $this->getBasicHeaders(),
303
            ]
304
        );
305
    }
306
307
    private function getOrCreateMissingCategoryTree(Document $document): int
308
    {
309
        $prevEntryId = $this->rootCategoryId;
310
311
        $chain = array_filter($document->createParentChain(), function (Document $document): bool {
312
            return $document->isCategory();
313
        });
314
315
        /** @var Document $parentCategory */
316
        foreach ($chain as $parentCategory) {
317
            if ($parentCategory->getCategoryId()) {
318
                $prevEntryId = $parentCategory->getCategoryId();
319
                continue;
320
            }
321
322
            $prevEntryId = $this->createCategory(
323
                $parentCategory->getMetadata()->getTitleEn(),
324
                $parentCategory->getMetadata()->getUrlEn(),
325
                $parentCategory->getMetadata()->getTitleDe(),
326
                $parentCategory->getMetadata()->getUrlDe(),
327
                $prevEntryId
328
            );
329
            $parentCategory->setCategoryId($prevEntryId);
330
        }
331
332
        return $prevEntryId;
333
    }
334
335
    private function createCategory($titleEn, $seoEn, $titleDe, $seoDe, $parentCategoryId = 50): int
336
    {
337
        $response = $this->client->post(
338
            '/wiki/categories',
339
            [
340
                'json' => [
341
                    'id' => null,
342
                    'orderPriority' => null,
343
                    'active' => null,
344
                    'parent' => ['id' => $parentCategoryId],
345
                    'localizations' => [
346
                        [
347
                            'id' => null,
348
                            'locale' => [
349
                                'name' => 'de_DE',
350
                            ],
351
                            'title' => $titleDe,
352
                            'navigationTitle' => $titleDe,
353
                            'seoUrl' => $seoDe,
354
                            'content' => '',
355
                            'metaTitle' => '',
356
                            'metaDescription' => '',
357
                            'media' => null,
358
                            'searchableInAllLanguages' => false,
359
                        ],
360
                        [
361
                            'id' => null,
362
                            'locale' => [
363
                                'name' => 'en_GB',
364
                            ],
365
                            'title' => $titleEn,
366
                            'navigationTitle' => $titleEn,
367
                            'seoUrl' => $seoEn,
368
                            'content' => '',
369
                            'metaTitle' => '',
370
                            'metaDescription' => '',
371
                            'media' => null,
372
                            'searchableInAllLanguages' => false,
373
                        ],
374
                    ],
375
                ],
376
                'headers' => $this->getBasicHeaders(),
377
            ]
378
        );
379
380
        $responseContents = $response->getBody()->getContents();
381
        $responseJson = json_decode($responseContents, true);
382
383
        return $responseJson['id'];
384
    }
385
386
    private function createLocalizedVersionedArticle($seoEn, $seoDe): array
387
    {
388
        $response = $this->client->post(
389
            '/wiki/entries',
390
            [
391
                'json' => [
392
                    'product' => ['id' => 4, 'name' => 'PF', 'label' => 'Shopware Platform'],
393
                ],
394
                'headers' => $this->getBasicHeaders(),
395
            ]
396
        );
397
398
        $responseContents = $response->getBody()->getContents();
399
        $articleId = json_decode($responseContents, true)['id'];
400
401
        // create english lang
402
        $articleUrl = vsprintf('/wiki/entries/%d', [$articleId]);
403
        $articleLocalizationUrl = vsprintf('%s/localizations', [$articleUrl]);
404
405
        [$localeIdEn, $versionIdEn, $articleUrlEn] = $this->createArticleLocale($seoEn, $articleLocalizationUrl, ['id' => 2, 'name' => 'en_GB']);
406
        [$localeIdDe, $versionIdDe, $articleUrlDe] = $this->createArticleLocale($seoDe, $articleLocalizationUrl, ['name' => 'de_DE']);
407
408
        return [
409
            'en_GB' => [
410
                'articleId' => $articleId,
411
                'localeId' => $localeIdEn,
412
                'versionId' => $versionIdEn,
413
            ],
414
            'de_DE' => [
415
                'articleId' => $articleId,
416
                'localeId' => $localeIdDe,
417
                'versionId' => $versionIdDe,
418
            ],
419
        ];
420
    }
421
422
    private function createArticleLocale($seo, $articleLocalizationUrl, $locale): array
423
    {
424
        $response = $this->client->post(
425
            $articleLocalizationUrl,
426
            [
427
                'json' => [
428
                    'locale' => $locale, 'seoUrl' => $seo,
429
                ],
430
                'headers' => $this->getBasicHeaders(),
431
            ]
432
        );
433
434
        $responseContents = $response->getBody()->getContents();
435
        $localeId = json_decode($responseContents, true)['id'];
436
        $articleInLocaleUrl = $articleLocalizationUrl . '/' . $localeId;
437
        $articleVersioningUrl = $articleInLocaleUrl . '/versions';
438
439
        // create version
440
        $response = $this->client->post(
441
            $articleVersioningUrl,
442
            [
443
                'json' => [
444
                    'version' => '1.0.0',
445
                ],
446
                'headers' => $this->getBasicHeaders(),
447
            ]
448
        );
449
450
        $responseContents = $response->getBody()->getContents();
451
        $versionId = json_decode($responseContents, true)['id'];
452
        $articleInLocaleWithVersionUrl = $articleVersioningUrl . '/' . $versionId;
453
454
        return [$localeId, $versionId, $articleInLocaleWithVersionUrl];
455
    }
456
457
    private function deleteCategoryChildren(int $categoryId = -1): void
458
    {
459
        if ($categoryId === -1) {
460
            $categoryId = $this->rootCategoryId;
461
        }
462
463
        $categories = $this->getAllCategories();
464
465
        [$categoriesToDelete, $articlesToDelete] = $this->gatherCategoryChildrenAndArticles(
466
            $categoryId,
467
            $categories);
468
469
        foreach ($articlesToDelete as $article) {
470
            $this->disableArticle($article);
471
            $this->deleteArticle($article);
472
        }
473
474
        foreach (array_keys($categoriesToDelete) as $category) {
475
            $this->deleteCategory($category);
476
        }
477
    }
478
479
    private function gatherCategoryChildrenAndArticles($rootId, $categoryJson): array
480
    {
481
        $articleList = [];
482
        $categoryList = [];
483
        $idStack = [$rootId];
484
485
        while (\count($idStack) > 0) {
486
            $parentId = array_shift($idStack);
487
488
            foreach ($categoryJson as $category) {
489
                $parent = $category['parent'];
490
                if ($parent !== null && $parent['id'] === $parentId) {
491
                    $localizations = $category['localizations'];
492
493
                    $seo = '';
494
                    foreach ($localizations as $locale) {
495
                        if ($locale['locale']['name'] === 'en_GB') {
496
                            $seo = $locale['seoUrl'];
497
                        }
498
                    }
499
                    $categoryList[$category['id']] = $seo;
500
                    $articleList[] = $category['entryIds'];
501
                    $idStack[] = $category['id'];
502
                }
503
            }
504
        }
505
506
        $categoryList = array_reverse($categoryList, true);
507
508
        // also delete articles in the root category
509
        $idColumn = array_column($categoryJson, 'id');
510
        $rootCategoryIndex = array_search($rootId, $idColumn, true);
511
        if ($rootCategoryIndex !== false) {
512
            $articleList[] = $categoryJson[$rootCategoryIndex]['entryIds'];
513
        }
514
515
        $articleList = array_merge(...$articleList);
516
        $articleList = array_unique($articleList);
517
518
        return [$categoryList, $articleList];
519
    }
520
521
    private function deleteArticle($articleId): void
522
    {
523
        $this->client->delete(
524
            vsprintf('/wiki/entries/%s', [$articleId]),
525
            ['headers' => $this->getBasicHeaders()]
526
        );
527
    }
528
529
    private function disableArticle($articleId): void
530
    {
531
        $response = $this->client->get(
532
            vsprintf('/wiki/entries/%s', [$articleId]),
533
            ['headers' => $this->getBasicHeaders()]
534
        )->getBody()->getContents();
535
        $reponseJson = json_decode($response, true);
536
537
        if (!array_key_exists('localizations', $reponseJson) && $reponseJson['localizations'] === null) {
538
            return;
539
        }
540
541
        foreach ($reponseJson['localizations'] as $locale) {
542
            $localId = $locale['id'];
543
544
            if (!array_key_exists('versions', $locale) && $locale['versions'] === null) {
545
                continue;
546
            }
547
548
            foreach ($locale['versions'] as $version) {
549
                $versionId = $version['id'];
550
                $this->updateArticleVersion(['articleId' => $articleId, 'localeId' => $localId, 'versionId' => $versionId],
551
                    ['active' => false]);
552
            }
553
        }
554
    }
555
556
    private function deleteCategory($categoryId): void
557
    {
558
        $this->client->delete(
559
            vsprintf('/wiki/categories/%s', [$categoryId]),
560
            ['headers' => $this->getBasicHeaders()]
561
        );
562
    }
563
564
    private function syncArticles(DocumentTree $tree)
565
    {
566
        $i = 0;
567
        /** @var Document $document */
568
        foreach ($tree->getArticles() as $document) {
569
            ++$i;
570
            echo 'Syncing article (' . $i . '/' . count($tree->getArticles()) . ') ' . $document->getFile()->getRelativePathname() . ' with prio ' . $document->getPriority() . PHP_EOL;
571
572
            $articleInfo = $this->createLocalizedVersionedArticle($document->getMetadata()->getUrlEn(), $document->getMetadata()->getUrlDe());
573
            $categoryId = $this->getOrCreateMissingCategoryTree($document);
574
            $this->addArticleToCategory($articleInfo['en_GB'], $categoryId);
575
576
            // handle media files for articles
577
            $images = $document->getHtml()->render($tree)->getImages();
578
            $imageMap = [];
579
            if (count($images)) {
580
                echo '=> Uploading ' . count($images) . ' mediafile(s) ...' . PHP_EOL;
581
                foreach ($images as $key => $mediaFile) {
582
                    $mediaLink = $this->uploadArticleMedia($articleInfo['en_GB'], $mediaFile);
583
                    $imageMap[$key] = $mediaLink;
584
                }
585
            }
586
587
            $this->updateArticleLocale($articleInfo['en_GB'],
588
                [
589
                    'seoUrl' => $document->getMetadata()->getUrlEn(),
590
                    'searchableInAllLanguages' => true,
591
                ]
592
            );
593
            $this->updateArticleVersion($articleInfo['en_GB'],
594
                [
595
                    'content' => $document->getHtml()->render($tree)->getContents($imageMap),
596
                    'title' => $document->getMetadata()->getTitleEn(),
597
                    'navigationTitle' => $document->getMetadata()->getTitleEn(),
598
                    'searchableInAllLanguages' => true,
599
                    'active' => $document->getMetadata()->isActive(),
600
                    'fromProductVersion' => self::INITIAL_VERSION,
601
                    'metaTitle' => $document->getMetadata()->getMetaTitleEn(),
602
                    'metaDescription' => $document->getMetadata()->getMetaDescriptionEn(),
603
                ]
604
            );
605
606
            $this->updateArticlePriority($articleInfo['en_GB'], $document->getPriority());
607
            $this->insertGermanStubArticle($articleInfo['de_DE'], $document);
608
        }
609
    }
610
611
    private function syncCategories(DocumentTree $tree): void
612
    {
613
        echo 'Syncing ' . count($tree->getCategories()) . ' categories ...' . PHP_EOL;
614
615
        $this->addEmptyCategories($tree);
616
        $this->syncCategoryContents($tree);
617
    }
618
619
    private function addEmptyCategories(DocumentTree $tree)
620
    {
621
        foreach ($tree->getCategories() as $document) {
622
            if ($document->getCategoryId()) {
623
                continue;
624
            }
625
626
            $this->getOrCreateMissingCategoryTree($document);
627
        }
628
    }
629
630
    private function syncCategoryContents(DocumentTree $tree): void
631
    {
632
        $oldCategories = $this->getAllCategories();
633
        $categoryIds = array_column($oldCategories, 'id');
634
635
        /** @var Document $document */
636
        foreach ($tree->getCategories() as $document) {
637
            echo 'Syncing category ' . $document->getFile()->getRelativePathname() . ' with prio ' . $document->getPriority() . ' ... ' . PHP_EOL;
638
            $parentId = $this->rootCategoryId;
639
            $categoryId = $document->getCategoryId();
640
641
            if ($document->getParent()) {
642
                $parentId = $document->getParent()->getCategoryId();
643
            }
644
645
            if (!$categoryId) {
646
                echo 'Skipping category ' . $document->getFile()->getRelativePathname() . " - no sync reason found\n";
647
                continue;
648
            }
649
650
            if (!$parentId) {
651
                echo 'Skipping category ' . $document->getFile()->getRelativePathname() . " - parent not synced\n";
652
                continue;
653
            }
654
655
            $baseContents = $oldCategories[array_search($categoryId, $categoryIds, true)];
656
657
            if (!$baseContents) {
658
                throw new \RuntimeException('Unable to update category, no contents found');
659
            }
660
661
            $this->updateCategory(
662
                $categoryId,
663
                $parentId,
664
                $baseContents,
665
                $document,
666
                $tree
667
            );
668
        }
669
    }
670
671
    private function syncRoot(DocumentTree $tree)
672
    {
673
        $root = $tree->getRoot();
674
675
        $oldCategories = $this->getAllCategories();
676
        $categoryIds = array_column($oldCategories, 'id');
677
678
        $index = array_search($this->rootCategoryId, $categoryIds, true);
679
        $category = $oldCategories[$index];
680
681
        $enIndex = -1;
682
683
        foreach ($category['localizations'] as $index => $localization) {
684
            if ($localization['locale']['name'] === 'en_GB') {
685
                $enIndex = $index;
686
            }
687
        }
688
689
        $category['localizations'][$enIndex]['content'] = $root->getHtml()->render($tree)->getContents();
690
691
        $this->client->put(
692
            vsprintf('/wiki/categories/%d', [$this->rootCategoryId]),
693
            [
694
                'json' => $category,
695
                'headers' => $this->getBasicHeaders(),
696
            ]
697
        );
698
    }
699
}
700