Completed
Pull Request — develop (#423)
by
unknown
13:26
created

ArticleIndexer::getDocumentInspector()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/*
4
 * This file is part of Sulu.
5
 *
6
 * (c) Sulu GmbH
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Sulu\Bundle\ArticleBundle\Document\Index;
13
14
use ONGR\ElasticsearchBundle\Collection\Collection;
15
use ONGR\ElasticsearchBundle\Service\Manager;
16
use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
17
use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery;
18
use Sulu\Bundle\ArticleBundle\Document\ArticleDocument;
19
use Sulu\Bundle\ArticleBundle\Document\ArticlePageDocument;
20
use Sulu\Bundle\ArticleBundle\Document\ArticlePageViewObject;
21
use Sulu\Bundle\ArticleBundle\Document\ArticleViewDocumentInterface;
22
use Sulu\Bundle\ArticleBundle\Document\Index\Factory\ExcerptFactory;
23
use Sulu\Bundle\ArticleBundle\Document\Index\Factory\SeoFactory;
24
use Sulu\Bundle\ArticleBundle\Document\LocalizationStateViewObject;
25
use Sulu\Bundle\ArticleBundle\Document\Resolver\WebspaceResolver;
26
use Sulu\Bundle\ArticleBundle\Event\Events;
27
use Sulu\Bundle\ArticleBundle\Event\IndexEvent;
28
use Sulu\Bundle\ArticleBundle\Metadata\ArticleViewDocumentIdTrait;
29
use Sulu\Bundle\ArticleBundle\Metadata\StructureTagTrait;
30
use Sulu\Bundle\ContactBundle\Entity\ContactRepository;
31
use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector;
32
use Sulu\Bundle\RouteBundle\PageTree\PageTreeTrait;
33
use Sulu\Bundle\SecurityBundle\UserManager\UserManager;
34
use Sulu\Component\Content\Document\LocalizationState;
35
use Sulu\Component\Content\Document\WorkflowStage;
36
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;
37
use Sulu\Component\DocumentManager\DocumentManagerInterface;
38
use Sulu\Component\DocumentManager\Exception\DocumentManagerException;
39
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
40
use Symfony\Component\Translation\TranslatorInterface;
41
42
/**
43
 * Provides methods to index articles.
44
 */
45
class ArticleIndexer implements IndexerInterface
46
{
47
    use StructureTagTrait;
48
    use ArticleViewDocumentIdTrait;
49
    use PageTreeTrait;
50
51
    /**
52
     * @var StructureMetadataFactoryInterface
53
     */
54
    protected $structureMetadataFactory;
55
56
    /**
57
     * @var UserManager
58
     */
59
    protected $userManager;
60
61
    /**
62
     * @var ContactRepository
63
     */
64
    protected $contactRepository;
65
66
    /**
67
     * @var DocumentFactoryInterface
68
     */
69
    protected $documentFactory;
70
71
    /**
72
     * @var Manager
73
     */
74
    protected $manager;
75
76
    /**
77
     * @var ExcerptFactory
78
     */
79
    protected $excerptFactory;
80
81
    /**
82
     * @var SeoFactory
83
     */
84
    protected $seoFactory;
85
86
    /**
87
     * @var EventDispatcherInterface
88
     */
89
    protected $eventDispatcher;
90
91
    /**
92
     * @var TranslatorInterface
93
     */
94
    protected $translator;
95
96
    /**
97
     * @var DocumentManagerInterface
98
     */
99
    protected $documentManager;
100
101
    /**
102
     * @var DocumentInspector
103
     */
104
    protected $inspector;
105
106
    /**
107
     * @var WebspaceResolver
108
     */
109 53
    protected $webspaceResolver;
110
111
    /**
112
     * @var array
113
     */
114
    protected $typeConfiguration;
115
116
    /**
117
     * @param StructureMetadataFactoryInterface $structureMetadataFactory
118
     * @param UserManager $userManager
119
     * @param ContactRepository $contactRepository
120
     * @param DocumentFactoryInterface $documentFactory
121 53
     * @param Manager $manager
122 53
     * @param ExcerptFactory $excerptFactory
123 53
     * @param SeoFactory $seoFactory
124 53
     * @param EventDispatcherInterface $eventDispatcher
125 53
     * @param TranslatorInterface $translator
126 53
     * @param DocumentManagerInterface $documentManager
127 53
     * @param DocumentInspector $inspector
128 53
     * @param WebspaceResolver $webspaceResolver
129 53
     * @param array $typeConfiguration
130 53
     */
131 53
    public function __construct(
132
        StructureMetadataFactoryInterface $structureMetadataFactory,
133
        UserManager $userManager,
134
        ContactRepository $contactRepository,
135
        DocumentFactoryInterface $documentFactory,
136
        Manager $manager,
137
        ExcerptFactory $excerptFactory,
138
        SeoFactory $seoFactory,
139
        EventDispatcherInterface $eventDispatcher,
140 52
        TranslatorInterface $translator,
141
        DocumentManagerInterface $documentManager,
142 52
        DocumentInspector $inspector,
143 52
        WebspaceResolver $webspaceResolver,
144
        array $typeConfiguration
145
    ) {
146
        $this->structureMetadataFactory = $structureMetadataFactory;
147
        $this->userManager = $userManager;
148
        $this->contactRepository = $contactRepository;
149
        $this->documentFactory = $documentFactory;
150
        $this->manager = $manager;
151
        $this->excerptFactory = $excerptFactory;
152
        $this->seoFactory = $seoFactory;
153
        $this->eventDispatcher = $eventDispatcher;
154
        $this->translator = $translator;
155 52
        $this->documentManager = $documentManager;
156
        $this->inspector = $inspector;
157 52
        $this->webspaceResolver = $webspaceResolver;
158 52
        $this->typeConfiguration = $typeConfiguration;
159
    }
160
161
    /**
162
     * Returns translation for given article type.
163
     *
164
     * @param string $type
165
     *
166
     * @return string
167 52
     */
168
    private function getTypeTranslation($type)
169
    {
170
        if (!array_key_exists($type, $this->typeConfiguration)) {
171
            return ucfirst($type);
172 52
        }
173 52
174 5
        $typeTranslationKey = $this->typeConfiguration[$type]['translation_key'];
175
176
        return $this->translator->trans($typeTranslationKey, [], 'backend');
177 52
    }
178 52
179 52
    /**
180
     * @param ArticleDocument $document
181
     * @param ArticleViewDocumentInterface $article
182 52
     */
183 52
    protected function dispatchIndexEvent(ArticleDocument $document, ArticleViewDocumentInterface $article)
184 52
    {
185 52
        $this->eventDispatcher->dispatch(Events::INDEX_EVENT, new IndexEvent($document, $article));
186 52
    }
187 52
188 52
    /**
189 52
     * @param ArticleDocument $document
190 52
     * @param string $locale
191
     * @param string $localizationState
192 52
     *
193 52
     * @return ArticleViewDocumentInterface
194 52
     */
195
    protected function createOrUpdateArticle(
196 52
        ArticleDocument $document,
197 52
        $locale,
198 52
        $localizationState = LocalizationState::LOCALIZED
199
    ) {
200 52
        $article = $this->findOrCreateViewDocument($document, $locale, $localizationState);
201 52
        if (!$article) {
202 52
            return;
203 52
        }
204 52
205 52
        $structureMetadata = $this->structureMetadataFactory->getStructureMetadata(
206 52
            'article',
207 52
            $document->getStructureType()
208 52
        );
209
210
        $article->setTitle($document->getTitle());
211
        $article->setRoutePath($document->getRoutePath());
212 52
        $this->setParentPageUuid($document, $article);
213 52
        $article->setChanged($document->getChanged());
214 52
        $article->setCreated($document->getCreated());
215
        $article->setAuthored($document->getAuthored());
216 52
        if ($document->getAuthor() && $author = $this->contactRepository->find($document->getAuthor())) {
217 52
            $article->setAuthorFullName($author->getFullName());
218
            $article->setAuthorId($author->getId());
219 52
        }
220 1
        if ($document->getChanger() && $changer = $this->userManager->getUserById($document->getChanger())) {
221 1
            $article->setChangerFullName($changer->getFullName());
222 1
            $article->setChangerContactId($changer->getContact()->getId());
223
        }
224
        if ($document->getCreator() && $creator = $this->userManager->getUserById($document->getCreator())) {
225 52
            $article->setCreatorFullName($creator->getFullName());
226 1
            $article->setCreatorContactId($creator->getContact()->getId());
227 1
        }
228 1
        $article->setType($this->getType($structureMetadata));
229 1
        $article->setStructureType($document->getStructureType());
230
        $article->setPublished($document->getPublished());
0 ignored issues
show
Documentation introduced by
$document->getPublished() is of type boolean, but the function expects a null|object<DateTime>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
231
        $article->setPublishedState(WorkflowStage::PUBLISHED === $document->getWorkflowStage());
232
        $article->setTypeTranslation($this->getTypeTranslation($this->getType($structureMetadata)));
233 52
        $article->setLocalizationState(
234
            new LocalizationStateViewObject(
235 52
                $localizationState,
236
                (LocalizationState::LOCALIZED === $localizationState) ? null : $document->getLocale()
237
            )
238
        );
239
240
        $extensions = $document->getExtensionsData()->toArray();
241
        if (array_key_exists('excerpt', $extensions)) {
242
            $article->setExcerpt($this->excerptFactory->create($extensions['excerpt'], $document->getLocale()));
243
        }
244
        if (array_key_exists('seo', $extensions)) {
245
            $article->setSeo($this->seoFactory->create($extensions['seo']));
246
        }
247 52
        if ($structureMetadata->hasPropertyWithTagName('sulu.teaser.description')) {
248
            $descriptionProperty = $structureMetadata->getPropertyByTagName('sulu.teaser.description');
249 52
            $article->setTeaserDescription(
250
                $document->getStructure()->getProperty($descriptionProperty->getName())->getValue()
251 52
            );
252
        }
253 52
        if ($structureMetadata->hasPropertyWithTagName('sulu.teaser.media')) {
254
            $mediaProperty = $structureMetadata->getPropertyByTagName('sulu.teaser.media');
255 19
            $mediaData = $document->getStructure()->getProperty($mediaProperty->getName())->getValue();
256 19
            if (null !== $mediaData && array_key_exists('ids', $mediaData)) {
257
                $article->setTeaserMediaId(reset($mediaData['ids']) ?: null);
258 5
            }
259
        }
260
261 19
        $article->setContentData(json_encode($document->getStructure()->toArray()));
262
263
        $article->setMainWebspace($this->webspaceResolver->resolveMainWebspace($document));
264 52
        $article->setAdditionalWebspaces($this->webspaceResolver->resolveAdditionalWebspaces($document));
265 52
266 52
        $this->mapPages($document, $article);
267 52
268
        return $article;
269 52
    }
270
271
    /**
272
     * Returns view-document from index or create a new one.
273
     *
274
     * @param ArticleDocument $document
275
     * @param string $locale
276
     * @param string $localizationState
277
     *
278 52
     * @return ArticleViewDocumentInterface
279
     */
280 52
    protected function findOrCreateViewDocument(ArticleDocument $document, $locale, $localizationState)
281 52
    {
282 8
        $articleId = $this->getViewDocumentId($document->getUuid(), $locale);
283 8
        /** @var ArticleViewDocumentInterface $article */
284 8
        $article = $this->manager->find($this->documentFactory->getClass('article'), $articleId);
285 8
286 8
        if ($article) {
287
            // Only index ghosts when the article isn't a ghost himself.
288
            if (LocalizationState::GHOST === $localizationState
289 52
                && LocalizationState::GHOST !== $article->getLocalizationState()->state
290 52
            ) {
291
                return null;
292
            }
293
294
            return $article;
295
        }
296
297
        $article = $this->documentFactory->create('article');
298
        $article->setId($articleId);
299 52
        $article->setUuid($document->getUuid());
300
        $article->setLocale($locale);
301
302
        return $article;
303
    }
304 52
305 52
    /**
306 38
     * Maps pages from document to view-document.
307
     *
308
     * @param ArticleDocument $document
309 14
     * @param ArticleViewDocumentInterface $article
310 14
     */
311 7
    private function mapPages(ArticleDocument $document, ArticleViewDocumentInterface $article)
312
    {
313
        $pages = [];
314 7
        /** @var ArticlePageDocument $child */
315 7
        foreach ($document->getChildren() as $child) {
316
            if (!$child instanceof ArticlePageDocument) {
317
                continue;
318
            }
319 7
320 7
            /* @var ArticlePageViewObject $page */
321
            $pages[] = $page = $this->documentFactory->create('article_page');
322
            $page->uuid = $child->getUuid();
0 ignored issues
show
Bug introduced by
Accessing uuid on the interface Sulu\Bundle\ArticleBundl...leViewDocumentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
323
            $page->pageNumber = $child->getPageNumber();
0 ignored issues
show
Bug introduced by
Accessing pageNumber on the interface Sulu\Bundle\ArticleBundl...leViewDocumentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
324
            $page->title = $child->getPageTitle();
0 ignored issues
show
Bug introduced by
Accessing title on the interface Sulu\Bundle\ArticleBundl...leViewDocumentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
325
            $page->routePath = $child->getRoutePath();
0 ignored issues
show
Bug introduced by
Accessing routePath on the interface Sulu\Bundle\ArticleBundl...leViewDocumentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
326
            $page->contentData = json_encode($child->getStructure()->toArray());
0 ignored issues
show
Bug introduced by
Accessing contentData on the interface Sulu\Bundle\ArticleBundl...leViewDocumentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
327
        }
328
329 52
        $article->setPages(new Collection($pages));
330
    }
331 52
332
    /**
333
     * Set parent-page-uuid to view-document.
334
     *
335 52
     * @param ArticleDocument $document
336 38
     * @param ArticleViewDocumentInterface $article
337
     */
338
    private function setParentPageUuid(
339 14
        ArticleDocument $document,
340
        ArticleViewDocumentInterface $article
341
    ) {
342
        $parentPageUuid = $this->getParentPageUuidFromPageTree($document);
343
344
        if (!$parentPageUuid) {
345
            return;
346
        }
347
348
        $article->setParentPageUuid($parentPageUuid);
349
    }
350
351
    /**
352
     * @param string $id
353
     */
354
    protected function removeArticle($id)
355
    {
356
        $article = $this->manager->find(
357
            $this->documentFactory->getClass('article'),
358
            $id
359
        );
360
        if (null === $article) {
361 2
            return;
362
        }
363 2
364 2
        $this->manager->remove($article);
365 2
    }
366 2
367 2
    /**
368 2
     * {@inheritdoc}
369
     */
370 2
    public function remove($document)
371
    {
372
        $repository = $this->manager->getRepository($this->documentFactory->getClass('article'));
373
        $search = $repository->createSearch()
374
            ->addQuery(new TermQuery('uuid', $document->getUuid()))
375 52
            ->setSize(1000);
376
        foreach ($repository->findDocuments($search) as $viewDocument) {
377 52
            $this->manager->remove($viewDocument);
378 52
        }
379
    }
380
381
    /**
382
     * {@inheritdoc}
383 18
     */
384
    public function flush()
385 18
    {
386 18
        $this->manager->commit();
387 18
    }
388 18
389 18
    /**
390
     * {@inheritdoc}
391
     */
392 18
    public function clear()
393 18
    {
394 16
        $pageSize = 500;
395
        $repository = $this->manager->getRepository($this->documentFactory->getClass('article'));
396
        $search = $repository->createSearch()
397 18
            ->addQuery(new MatchAllQuery())
398 18
            ->setSize($pageSize);
399
400 18
        do {
401 18
            $result = $repository->findDocuments($search);
402 18
            foreach ($result as $document) {
403
                $this->manager->remove($document);
404
            }
405
406
            $this->manager->commit();
407
        } while (0 !== $result->count());
408
409
        $this->manager->clearCache();
410
        $this->manager->flush();
411
    }
412
413
    /**
414
     * {@inheritdoc}
415
     */
416
    public function setUnpublished($uuid, $locale)
417
    {
418
        $articleId = $this->getViewDocumentId($uuid, $locale);
419
        $article = $this->manager->find($this->documentFactory->getClass('article'), $articleId);
420
        if (!$article) {
421
            return;
422
        }
423
424 19
        $article->setPublished(null);
425
        $article->setPublishedState(false);
426 19
427 19
        $this->manager->persist($article);
428 19
429 19
        return $article;
430
    }
431
432
    /**
433
     * {@inheritdoc}
434
     */
435 View Code Duplication
    public function index(ArticleDocument $document)
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...
436
    {
437
        if ($document->isShadowLocaleEnabled()) {
438
            $this->indexShadow($document);
439
440
            return;
441
        }
442
443
        $article = $this->createOrUpdateArticle($document, $document->getLocale());
444
445
        $this->dispatchIndexEvent($document, $article);
446
        $this->manager->persist($article);
447
448
        $this->createOrUpdateShadows($document);
449
    }
450
451
    /**
452
     * @param ArticleDocument $document
453
     */
454
    protected function indexShadow(ArticleDocument $document)
455
    {
456
        $shadowDocument = $this->documentManager->find(
457
            $document->getUuid(),
458
            $document->getOriginalLocale(),
459
            [
460
                'rehydrate' => true,
461
            ]
462
        );
463
464
        $article = $this->createOrUpdateArticle($shadowDocument, $document->getOriginalLocale(), LocalizationState::SHADOW);
465
466
        $this->dispatchIndexEvent($shadowDocument, $article);
467
        $this->manager->persist($article);
468
    }
469
470
    /**
471
     * @param ArticleDocument $document
472
     */
473
    protected function createOrUpdateShadows(ArticleDocument $document)
474
    {
475
        if ($document->isShadowLocaleEnabled()) {
476
            return;
477
        }
478
479
        foreach (array_keys($this->inspector->getShadowLocales($document)) as $shadowLocale) {
480
            try {
481
                /** @var ArticleDocument $shadowDocument */
482
                $shadowDocument = $this->documentManager->find($document->getUuid(), $shadowLocale);
483
                $this->indexShadow($shadowDocument);
484
            } catch (DocumentManagerException $documentManagerException) {
0 ignored issues
show
Bug introduced by
The class Sulu\Component\DocumentM...ocumentManagerException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
485
                // do nothing
486
            }
487
        }
488
    }
489
490
    /**
491
     * {@inheritdoc}
492
     */
493
    public function dropIndex()
494
    {
495
        if (!$this->manager->indexExists()) {
496
            return;
497
        }
498
499
        $this->manager->dropIndex();
500
    }
501
502
    /**
503
     * {@inheritdoc}
504
     */
505
    public function createIndex()
506
    {
507
        if ($this->manager->indexExists()) {
508
            return;
509
        }
510
511
        $this->manager->createIndex();
512
    }
513
514
    /**
515
     * {@inheritdoc}
516
     */
517
    protected function getDocumentInspector()
518
    {
519
        return $this->inspector;
520
    }
521
}
522