Completed
Pull Request — develop (#161)
by Wachter
22:54 queued 09:56
created

ArticleIndexer::getTypeTranslation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.2559

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 3
cts 5
cp 0.6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2.2559
1
<?php
2
3
/*
4
 * This file is part of Sulu.
5
 *
6
 * (c) MASSIVE ART WebServices 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\ArticlePageViewObject;
20
use Sulu\Bundle\ArticleBundle\Document\ArticleViewDocumentInterface;
21
use Sulu\Bundle\ArticleBundle\Document\Index\Factory\ExcerptFactory;
22
use Sulu\Bundle\ArticleBundle\Document\Index\Factory\SeoFactory;
23
use Sulu\Bundle\ArticleBundle\Document\LocalizationStateViewObject;
24
use Sulu\Bundle\ArticleBundle\Event\Events;
25
use Sulu\Bundle\ArticleBundle\Event\IndexEvent;
26
use Sulu\Bundle\ArticleBundle\Metadata\ArticleTypeTrait;
27
use Sulu\Bundle\ArticleBundle\Metadata\ArticleViewDocumentIdTrait;
28
use Sulu\Bundle\ContactBundle\Entity\ContactRepository;
29
use Sulu\Bundle\SecurityBundle\UserManager\UserManager;
30
use Sulu\Component\Content\Document\LocalizationState;
31
use Sulu\Component\Content\Document\WorkflowStage;
32
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;
33
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
34
use Symfony\Component\Translation\TranslatorInterface;
35
36
/**
37
 * Provides methods to index articles.
38
 */
39
class ArticleIndexer implements IndexerInterface
40
{
41
    use ArticleTypeTrait;
42
    use ArticleViewDocumentIdTrait;
43
44
    /**
45
     * @var StructureMetadataFactoryInterface
46
     */
47
    protected $structureMetadataFactory;
48
49
    /**
50
     * @var UserManager
51
     */
52
    protected $userManager;
53
54
    /**
55
     * @var ContactRepository
56
     */
57
    protected $contactRepository;
58
59
    /**
60
     * @var DocumentFactoryInterface
61
     */
62
    protected $documentFactory;
63
64
    /**
65
     * @var Manager
66
     */
67
    protected $manager;
68
69
    /**
70
     * @var ExcerptFactory
71
     */
72
    protected $excerptFactory;
73
74
    /**
75
     * @var SeoFactory
76
     */
77
    protected $seoFactory;
78
79
    /**
80
     * @var EventDispatcherInterface
81
     */
82
    protected $eventDispatcher;
83
84
    /**
85
     * @var TranslatorInterface
86
     */
87
    protected $translator;
88
89
    /**
90
     * @var array
91
     */
92
    protected $typeConfiguration;
93
94
    /**
95
     * @param StructureMetadataFactoryInterface $structureMetadataFactory
96
     * @param UserManager $userManager
97
     * @param ContactRepository $contactRepository
98
     * @param DocumentFactoryInterface $documentFactory
99
     * @param Manager $manager
100
     * @param ExcerptFactory $excerptFactory
101
     * @param SeoFactory $seoFactory
102
     * @param EventDispatcherInterface $eventDispatcher
103
     * @param TranslatorInterface $translator
104
     * @param array $typeConfiguration
105
     */
106 44
    public function __construct(
107
        StructureMetadataFactoryInterface $structureMetadataFactory,
108
        UserManager $userManager,
109
        ContactRepository $contactRepository,
110
        DocumentFactoryInterface $documentFactory,
111
        Manager $manager,
112
        ExcerptFactory $excerptFactory,
113
        SeoFactory $seoFactory,
114
        EventDispatcherInterface $eventDispatcher,
115
        TranslatorInterface $translator,
116
        array $typeConfiguration
117
    ) {
118 44
        $this->structureMetadataFactory = $structureMetadataFactory;
119 44
        $this->userManager = $userManager;
120 44
        $this->contactRepository = $contactRepository;
121 44
        $this->documentFactory = $documentFactory;
122 44
        $this->manager = $manager;
123 44
        $this->excerptFactory = $excerptFactory;
124 44
        $this->seoFactory = $seoFactory;
125 44
        $this->eventDispatcher = $eventDispatcher;
126 44
        $this->translator = $translator;
127 44
        $this->typeConfiguration = $typeConfiguration;
128 44
    }
129
130
    /**
131
     * Returns translation for given article type.
132
     *
133
     * @param string $type
134
     *
135
     * @return string
136
     */
137 44
    private function getTypeTranslation($type)
138
    {
139 44
        if (!array_key_exists($type, $this->typeConfiguration)) {
140 44
            return ucfirst($type);
141
        }
142
143
        $typeTranslationKey = $this->typeConfiguration[$type]['translation_key'];
144
145
        return $this->translator->trans($typeTranslationKey, [], 'backend');
146
    }
147
148
    /**
149
     * @param ArticleDocument $document
150
     * @param ArticleViewDocumentInterface $article
151
     */
152 44
    protected function dispatchIndexEvent(ArticleDocument $document, ArticleViewDocumentInterface $article)
153
    {
154 44
        $this->eventDispatcher->dispatch(Events::INDEX_EVENT, new IndexEvent($document, $article));
155 44
    }
156
157
    /**
158
     * @param ArticleDocument $document
159
     * @param string $locale
160
     * @param string $localizationState
161
     *
162
     * @return ArticleViewDocumentInterface
163
     */
164 44
    protected function createOrUpdateArticle(
165
        ArticleDocument $document,
166
        $locale,
167
        $localizationState = LocalizationState::LOCALIZED
168
    ) {
169 44
        $article = $this->findOrCreateViewDocument($document, $locale, $localizationState);
170 44
        if (!$article) {
171 5
            return;
172
        }
173
174 44
        $structureMetadata = $this->structureMetadataFactory->getStructureMetadata(
175 44
            'article',
176 44
            $document->getStructureType()
177
        );
178
179 44
        $article->setTitle($document->getTitle());
180 44
        $article->setRoutePath($document->getRoutePath());
181 44
        $article->setChanged($document->getChanged());
182 44
        $article->setCreated($document->getCreated());
183 44
        $article->setAuthored($document->getAuthored());
184 44
        if ($document->getAuthor() && $author = $this->contactRepository->find($document->getAuthor())) {
185 44
            $article->setAuthorFullName($author->getFullName());
186 44
            $article->setAuthorId($author->getId());
187
        }
188 44
        if ($document->getChanger() && $changer = $this->userManager->getUserById($document->getChanger())) {
189 44
            $article->setChangerFullName($changer->getFullName());
190 44
            $article->setChangerContactId($changer->getContact()->getId());
191
        }
192 44
        if ($document->getCreator() && $creator = $this->userManager->getUserById($document->getCreator())) {
193 44
            $article->setCreatorFullName($creator->getFullName());
194 44
            $article->setCreatorContactId($creator->getContact()->getId());
195
        }
196 44
        $article->setType($this->getType($structureMetadata));
0 ignored issues
show
Bug introduced by
It seems like $structureMetadata defined by $this->structureMetadata...nt->getStructureType()) on line 174 can be null; however, Sulu\Bundle\ArticleBundl...cleTypeTrait::getType() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
197 44
        $article->setStructureType($document->getStructureType());
198 44
        $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...
199 44
        $article->setPublishedState($document->getWorkflowStage() === WorkflowStage::PUBLISHED);
200 44
        $article->setTypeTranslation($this->getTypeTranslation($this->getType($structureMetadata)));
0 ignored issues
show
Bug introduced by
It seems like $structureMetadata defined by $this->structureMetadata...nt->getStructureType()) on line 174 can be null; however, Sulu\Bundle\ArticleBundl...cleTypeTrait::getType() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
201 44
        $article->setLocalizationState(
202 44
            new LocalizationStateViewObject(
203
                $localizationState,
204 44
                (LocalizationState::LOCALIZED === $localizationState) ? null : $document->getLocale()
205
            )
206
        );
207
208 44
        $extensions = $document->getExtensionsData()->toArray();
209 44
        if (array_key_exists('excerpt', $extensions)) {
210 44
            $article->setExcerpt($this->excerptFactory->create($extensions['excerpt'], $document->getLocale()));
211
        }
212 44
        if (array_key_exists('seo', $extensions)) {
213 44
            $article->setSeo($this->seoFactory->create($extensions['seo']));
214
        }
215 44
        if ($structureMetadata->hasPropertyWithTagName('sulu.teaser.description')) {
216 1
            $descriptionProperty = $structureMetadata->getPropertyByTagName('sulu.teaser.description');
217 1
            $article->setTeaserDescription(
218 1
                $document->getStructure()->getProperty($descriptionProperty->getName())->getValue()
0 ignored issues
show
Documentation introduced by
$document->getStructure(...>getName())->getValue() is of type array<integer|string,*>, but the function expects a string.

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...
219
            );
220
        }
221 44
        if ($structureMetadata->hasPropertyWithTagName('sulu.teaser.media')) {
222 1
            $mediaProperty = $structureMetadata->getPropertyByTagName('sulu.teaser.media');
223 1
            $mediaData = $document->getStructure()->getProperty($mediaProperty->getName())->getValue();
224 1
            if (null !== $mediaData && array_key_exists('ids', $mediaData)) {
225 1
                $article->setTeaserMediaId(reset($mediaData['ids']) ?: null);
226
            }
227
        }
228
229 44
        $this->mapPages($document, $article);
230
231 44
        return $article;
232
    }
233
234
    /**
235
     * Returns view-document from index or create a new one.
236
     *
237
     * @param ArticleDocument $document
238
     * @param string $locale
239
     * @param string $localizationState
240
     *
241
     * @return ArticleViewDocumentInterface
242
     */
243 44
    protected function findOrCreateViewDocument(ArticleDocument $document, $locale, $localizationState)
244
    {
245 44
        $articleId = $this->getViewDocumentId($document->getUuid(), $locale);
246
        /** @var ArticleViewDocumentInterface $article */
247 44
        $article = $this->manager->find($this->documentFactory->getClass('article'), $articleId);
248
249 44
        if ($article) {
250
            // Only index ghosts when the article isn't a ghost himself.
251 17
            if (LocalizationState::GHOST === $localizationState
252 17
                && LocalizationState::GHOST !== $article->getLocalizationState()->state
253
            ) {
254 5
                return null;
255
            }
256
257 17
            return $article;
258
        }
259
260 44
        $article = $this->documentFactory->create('article');
261 44
        $article->setId($articleId);
262 44
        $article->setUuid($document->getUuid());
263 44
        $article->setLocale($locale);
264
265 44
        return $article;
266
    }
267
268
    /**
269
     * Maps pages from document to view-document.
270
     *
271
     * @param ArticleDocument $document
272
     * @param ArticleViewDocumentInterface $article
273
     */
274 44
    private function mapPages(ArticleDocument $document, ArticleViewDocumentInterface $article)
275
    {
276 44
        $pages = [];
277 44
        foreach ($document->getChildren() as $child) {
278 7
            $pages[] = $page = new ArticlePageViewObject();
279 7
            $page->uuid = $child->getUuid();
280 7
            $page->pageNumber = $child->getPageNumber();
281 7
            $page->title = $child->getPageTitle();
282 7
            $page->routePath = $child->getRoutePath();
283
        }
284
285 44
        $article->setPages(new Collection($pages));
286 44
    }
287
288
    /**
289
     * @param string $id
290
     */
291
    protected function removeArticle($id)
292
    {
293
        $article = $this->manager->find(
294
            $this->documentFactory->getClass('article'),
295
            $id
296
        );
297
        if (null === $article) {
298
            return;
299
        }
300
301
        $this->manager->remove($article);
302
    }
303
304
    /**
305
     * {@inheritdoc}
306
     */
307 2
    public function remove($document)
308
    {
309 2
        $repository = $this->manager->getRepository($this->documentFactory->getClass('article'));
310 2
        $search = $repository->createSearch()
311 2
            ->addQuery(new TermQuery('uuid', $document->getUuid()))
312 2
            ->setSize(1000);
313 2
        foreach ($repository->findDocuments($search) as $viewDocument) {
314 2
            $this->manager->remove($viewDocument);
0 ignored issues
show
Bug introduced by
It seems like $viewDocument defined by $viewDocument on line 313 can also be of type array or null; however, ONGR\ElasticsearchBundle\Service\Manager::remove() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
315
        }
316 2
    }
317
318
    /**
319
     * {@inheritdoc}
320
     */
321 44
    public function flush()
322
    {
323 44
        $this->manager->commit();
324 44
    }
325
326
    /**
327
     * {@inheritdoc}
328
     */
329 17
    public function clear()
330
    {
331 17
        $pageSize = 500;
332 17
        $repository = $this->manager->getRepository($this->documentFactory->getClass('article'));
333 17
        $search = $repository->createSearch()
334 17
            ->addQuery(new MatchAllQuery())
335 17
            ->setSize($pageSize);
336
337
        do {
338 17
            $result = $repository->findDocuments($search);
339 17
            foreach ($result as $document) {
340 15
                $this->manager->remove($document);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $document on line 339 can also be of type array or null; however, ONGR\ElasticsearchBundle\Service\Manager::remove() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
341
            }
342
343 17
            $this->manager->commit();
344 17
        } while ($result->count() !== 0);
345
346 17
        $this->manager->clearCache();
347 17
        $this->manager->flush();
348 17
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353
    public function setUnpublished($uuid)
354
    {
355
        $article = $this->manager->find($this->documentFactory->getClass('article'), $uuid);
356
357
        if (!$article) {
358
            return;
359
        }
360
361
        $article->setPublished(null);
362
        $article->setPublishedState(false);
363
364
        $this->manager->persist($article);
365
    }
366
367
    /**
368
     * {@inheritdoc}
369
     */
370 12
    public function index(ArticleDocument $document)
371
    {
372 12
        $article = $this->createOrUpdateArticle($document, $document->getLocale());
373 12
        $this->dispatchIndexEvent($document, $article);
0 ignored issues
show
Bug introduced by
It seems like $article defined by $this->createOrUpdateArt...$document->getLocale()) on line 372 can be null; however, Sulu\Bundle\ArticleBundl...r::dispatchIndexEvent() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
374 12
        $this->manager->persist($article);
0 ignored issues
show
Bug introduced by
It seems like $article defined by $this->createOrUpdateArt...$document->getLocale()) on line 372 can be null; however, ONGR\ElasticsearchBundle...vice\Manager::persist() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
375 12
    }
376
}
377