Completed
Pull Request — develop (#151)
by
unknown
15:46
created

ArticleIndexer::getTypeTranslation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.7462

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 3
cts 7
cp 0.4286
rs 9.4285
cc 2
eloc 7
nc 2
nop 1
crap 2.7462
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\Service\Manager;
15
use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
16
use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery;
17
use Sulu\Bundle\ArticleBundle\Document\ArticleDocument;
18
use Sulu\Bundle\ArticleBundle\Document\ArticleViewDocument;
19
use Sulu\Bundle\ArticleBundle\Document\ArticleViewDocumentInterface;
20
use Sulu\Bundle\ArticleBundle\Document\Index\Factory\ExcerptFactory;
21
use Sulu\Bundle\ArticleBundle\Document\Index\Factory\SeoFactory;
22
use Sulu\Bundle\ArticleBundle\Document\LocalizationStateViewObject;
23
use Sulu\Bundle\ArticleBundle\Event\Events;
24
use Sulu\Bundle\ArticleBundle\Event\IndexEvent;
25
use Sulu\Bundle\ArticleBundle\Metadata\ArticleTypeTrait;
26
use Sulu\Bundle\ArticleBundle\Metadata\ArticleViewDocumentIdTrait;
27
use Sulu\Bundle\ContactBundle\Entity\ContactRepository;
28
use Sulu\Bundle\SecurityBundle\UserManager\UserManager;
29
use Sulu\Component\Content\Document\LocalizationState;
30
use Sulu\Component\Content\Document\WorkflowStage;
31
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;
32
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
33
use Symfony\Component\Translation\TranslatorInterface;
34
35
/**
36
 * Provides methods to index articles.
37
 */
38
class ArticleIndexer implements IndexerInterface
39
{
40
    use ArticleTypeTrait;
41
    use ArticleViewDocumentIdTrait;
42
43
    /**
44
     * @var StructureMetadataFactoryInterface
45
     */
46
    protected $structureMetadataFactory;
47
48
    /**
49
     * @var UserManager
50
     */
51
    protected $userManager;
52
53
    /**
54
     * @var ContactRepository
55
     */
56
    protected $contactRepository;
57
58
    /**
59
     * @var DocumentFactoryInterface
60
     */
61
    protected $documentFactory;
62
63
    /**
64
     * @var Manager
65
     */
66
    protected $manager;
67
68
    /**
69
     * @var ExcerptFactory
70
     */
71
    protected $excerptFactory;
72
73
    /**
74
     * @var SeoFactory
75
     */
76
    protected $seoFactory;
77
78
    /**
79
     * @var EventDispatcherInterface
80
     */
81
    protected $eventDispatcher;
82
83
    /**
84
     * @var TranslatorInterface
85
     */
86
    protected $translator;
87
88
    /**
89
     * @var array
90
     */
91
    protected $typeConfiguration;
92
93
    /**
94
     * @param StructureMetadataFactoryInterface $structureMetadataFactory
95
     * @param UserManager $userManager
96
     * @param ContactRepository $contactRepository
97
     * @param DocumentFactoryInterface $documentFactory
98
     * @param Manager $manager
99
     * @param ExcerptFactory $excerptFactory
100
     * @param SeoFactory $seoFactory
101
     * @param EventDispatcherInterface $eventDispatcher
102
     * @param TranslatorInterface $translator
103
     * @param array $typeConfiguration
104
     */
105 30
    public function __construct(
106
        StructureMetadataFactoryInterface $structureMetadataFactory,
107
        UserManager $userManager,
108
        ContactRepository $contactRepository,
109
        DocumentFactoryInterface $documentFactory,
110
        Manager $manager,
111
        ExcerptFactory $excerptFactory,
112
        SeoFactory $seoFactory,
113
        EventDispatcherInterface $eventDispatcher,
114
        TranslatorInterface $translator,
115
        array $typeConfiguration
116
    ) {
117 30
        $this->structureMetadataFactory = $structureMetadataFactory;
118 30
        $this->userManager = $userManager;
119 30
        $this->contactRepository = $contactRepository;
120 30
        $this->documentFactory = $documentFactory;
121 30
        $this->manager = $manager;
122 30
        $this->excerptFactory = $excerptFactory;
123 30
        $this->seoFactory = $seoFactory;
124 30
        $this->eventDispatcher = $eventDispatcher;
125 30
        $this->translator = $translator;
126 30
        $this->typeConfiguration = $typeConfiguration;
127 30
    }
128
129
    /**
130
     * Returns translation for given article type.
131
     *
132
     * @param string $type
133
     *
134
     * @return string
135
     */
136 30
    private function getTypeTranslation($type)
137
    {
138 30
        if (!array_key_exists($type, $this->typeConfiguration)) {
139 30
            return ucfirst($type);
140
        }
141
142
        $typeTranslationKey = $this->typeConfiguration[$type]['translation_key'];
143
144
        return $this->translator->trans(
145
            $typeTranslationKey,
146
            [],
147
            'backend'
148
        );
149
    }
150
151
    /**
152
     * @param ArticleDocument $document
153
     * @param ArticleViewDocument $article
154
     */
155 30
    protected function dispatchIndexEvent(ArticleDocument $document, ArticleViewDocument $article)
156
    {
157 30
        $this->eventDispatcher->dispatch(Events::INDEX_EVENT, new IndexEvent($document, $article));
158 30
    }
159
160
    /**
161
     * @param ArticleDocument $document
162
     * @param string $locale
163
     * @param string $localizationState
164
     *
165
     * @return ArticleViewDocumentInterface
166
     */
167 30
    protected function createOrUpdateArticle(
168
        ArticleDocument $document,
169
        $locale,
170
        $localizationState = LocalizationState::LOCALIZED
171
    ) {
172 30
        $articleId = $this->getViewDocumentId($document->getUuid(), $locale);
173
        /** @var ArticleViewDocument $article */
174 30
        $article = $this->manager->find($this->documentFactory->getClass('article'), $articleId);
175
176 30
        if (!$article) {
177 30
            $article = $this->documentFactory->create('article');
178 30
            $article->setId($articleId);
179 30
            $article->setUuid($document->getUuid());
180 30
            $article->setLocale($locale);
181
        } else {
182
            // Only index ghosts when the article isn't a ghost himself.
183 9
            if (LocalizationState::GHOST === $localizationState
184 9
                && LocalizationState::GHOST !== $article->getLocalizationState()->state
185
            ) {
186 4
                return null;
187
            }
188
        }
189
190 30
        $structureMetadata = $this->structureMetadataFactory->getStructureMetadata(
191 30
            'article',
192 30
            $document->getStructureType()
193
        );
194
195 30
        $article->setTitle($document->getTitle());
196 30
        $article->setRoutePath($document->getRoutePath());
197 30
        $article->setChanged($document->getChanged());
198 30
        $article->setCreated($document->getCreated());
199 30
        $article->setAuthored($document->getAuthored());
200 30
        if ($document->getAuthor() && $author = $this->contactRepository->find($document->getAuthor())) {
201 30
            $article->setAuthorFullName($author->getFullName());
202 30
            $article->setAuthorId($author->getId());
203
        }
204 30
        if ($document->getChanger() && $changer = $this->userManager->getUserById($document->getChanger())) {
205 30
            $article->setChangerFullName($changer->getFullName());
206 30
            $article->setChangerContactId($changer->getContact()->getId());
207
        }
208 30
        if ($document->getCreator() && $creator = $this->userManager->getUserById($document->getCreator())) {
209 30
            $article->setCreatorFullName($creator->getFullName());
210 30
            $article->setCreatorContactId($creator->getContact()->getId());
211
        }
212 30
        $article->setType($this->getType($structureMetadata));
0 ignored issues
show
Bug introduced by
It seems like $structureMetadata defined by $this->structureMetadata...nt->getStructureType()) on line 190 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...
213 30
        $article->setStructureType($document->getStructureType());
214 30
        $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...
215 30
        $article->setPublishedState($document->getWorkflowStage() === WorkflowStage::PUBLISHED);
216 30
        $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 190 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...
217 30
        $article->setLocalizationState(
218 30
            new LocalizationStateViewObject(
219
                $localizationState,
220 30
                (LocalizationState::LOCALIZED === $localizationState) ? null : $document->getLocale()
221
            )
222
        );
223
224 30
        $extensions = $document->getExtensionsData()->toArray();
225 30
        if (array_key_exists('excerpt', $extensions)) {
226 30
            $article->setExcerpt($this->excerptFactory->create($extensions['excerpt'], $document->getLocale()));
227
        }
228 30
        if (array_key_exists('seo', $extensions)) {
229 30
            $article->setSeo($this->seoFactory->create($extensions['seo']));
230
        }
231 30
        if ($structureMetadata->hasPropertyWithTagName('sulu.teaser.description')) {
232 1
            $descriptionProperty = $structureMetadata->getPropertyByTagName('sulu.teaser.description');
233 1
            $article->setTeaserDescription(
234 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...
235
            );
236
        }
237 30
        if ($structureMetadata->hasPropertyWithTagName('sulu.teaser.media')) {
238 1
            $mediaProperty = $structureMetadata->getPropertyByTagName('sulu.teaser.media');
239 1
            $mediaData = $document->getStructure()->getProperty($mediaProperty->getName())->getValue();
240 1
            if (null !== $mediaData && array_key_exists('ids', $mediaData)) {
241 1
                $article->setTeaserMediaId(reset($mediaData['ids']) ?: null);
242
            }
243
        }
244
245 30
        return $article;
246
    }
247
248
    /**
249
     * {@inheritdoc}
250
     */
251 2
    public function remove($document)
252
    {
253 2
        $repository = $this->manager->getRepository($this->documentFactory->getClass('article'));
254 2
        $search = $repository->createSearch()
255 2
            ->addQuery(new TermQuery('uuid', $document->getUuid()))
256 2
            ->setSize(1000);
257 2
        foreach ($repository->findDocuments($search) as $viewDocument) {
258 2
            $this->manager->remove($viewDocument);
0 ignored issues
show
Bug introduced by
It seems like $viewDocument defined by $viewDocument on line 257 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...
259
        }
260 2
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265 30
    public function flush()
266
    {
267 30
        $this->manager->commit();
268 30
    }
269
270
    /**
271
     * {@inheritdoc}
272
     */
273 10
    public function clear()
274
    {
275 10
        $pageSize = 500;
276 10
        $repository = $this->manager->getRepository($this->documentFactory->getClass('article'));
277 10
        $search = $repository->createSearch()
278 10
            ->addQuery(new MatchAllQuery())
279 10
            ->setSize($pageSize);
280
281
        do {
282 10
            $result = $repository->findDocuments($search);
283 10
            foreach ($result as $document) {
284 8
                $this->manager->remove($document);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $document on line 283 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...
285
            }
286
287 10
            $this->manager->commit();
288 10
        } while ($result->count() !== 0);
289
290 10
        $this->manager->clearCache();
291 10
        $this->manager->flush();
292 10
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    public function setUnpublished($uuid)
298
    {
299
        $article = $this->manager->find($this->documentFactory->getClass('article'), $uuid);
300
301
        if (!$article) {
302
            return;
303
        }
304
305
        $article->setPublished(null);
306
        $article->setPublishedState(false);
307
308
        $this->manager->persist($article);
309
    }
310
311
    /**
312
     * {@inheritdoc}
313
     */
314 11
    public function index(ArticleDocument $document)
315
    {
316 11
        $article = $this->createOrUpdateArticle($document, $document->getLocale());
317 11
        $this->dispatchIndexEvent($document, $article);
0 ignored issues
show
Documentation introduced by
$article is of type object<Sulu\Bundle\Artic...DocumentInterface>|null, but the function expects a object<Sulu\Bundle\Artic...nt\ArticleViewDocument>.

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...
318 11
        $this->manager->persist($article);
0 ignored issues
show
Bug introduced by
It seems like $article defined by $this->createOrUpdateArt...$document->getLocale()) on line 316 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...
319 11
    }
320
}
321