Completed
Pull Request — develop (#151)
by
unknown
27:07
created

ArticleIndexer::index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
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 31
    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 31
        $this->structureMetadataFactory = $structureMetadataFactory;
118 31
        $this->userManager = $userManager;
119 31
        $this->contactRepository = $contactRepository;
120 31
        $this->documentFactory = $documentFactory;
121 31
        $this->manager = $manager;
122 31
        $this->excerptFactory = $excerptFactory;
123 31
        $this->seoFactory = $seoFactory;
124 31
        $this->eventDispatcher = $eventDispatcher;
125 31
        $this->translator = $translator;
126 31
        $this->typeConfiguration = $typeConfiguration;
127 31
    }
128
129
    /**
130
     * Returns translation for given article type.
131
     *
132
     * @param string $type
133
     *
134
     * @return string
135
     */
136 31
    private function getTypeTranslation($type)
137
    {
138 31
        if (!array_key_exists($type, $this->typeConfiguration)) {
139 31
            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 31
    protected function dispatchIndexEvent(ArticleDocument $document, ArticleViewDocument $article)
156
    {
157 31
        $this->eventDispatcher->dispatch(Events::INDEX_EVENT, new IndexEvent($document, $article));
158 31
    }
159
160
    /**
161
     * @param ArticleDocument $document
162
     * @param string $locale
163
     * @param string $localizationState
164
     *
165
     * @return ArticleViewDocumentInterface
166
     */
167 31
    protected function createOrUpdateArticle(
168
        ArticleDocument $document,
169
        $locale,
170
        $localizationState = LocalizationState::LOCALIZED
171
    ) {
172 31
        $articleId = $this->getViewDocumentId($document->getUuid(), $locale);
173
        /** @var ArticleViewDocument $article */
174 31
        $article = $this->manager->find($this->documentFactory->getClass('article'), $articleId);
175
176 31
        if (!$article) {
177 31
            $article = $this->documentFactory->create('article');
178 31
            $article->setId($articleId);
179 31
            $article->setUuid($document->getUuid());
180 31
            $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 31
        $structureMetadata = $this->structureMetadataFactory->getStructureMetadata(
191 31
            'article',
192 31
            $document->getStructureType()
193
        );
194
195 31
        $article->setTitle($document->getTitle());
196 31
        $article->setRoutePath($document->getRoutePath());
197 31
        $article->setChanged($document->getChanged());
198 31
        $article->setCreated($document->getCreated());
199 31
        $article->setAuthored($document->getAuthored());
200 31
        if ($document->getAuthor() && $author = $this->contactRepository->find($document->getAuthor())) {
201 31
            $article->setAuthorFullName($author->getFullName());
202 31
            $article->setAuthorId($author->getId());
203
        }
204 31
        if ($document->getChanger() && $changer = $this->userManager->getUserById($document->getChanger())) {
205 31
            $article->setChangerFullName($changer->getFullName());
206 31
            $article->setChangerContactId($changer->getContact()->getId());
207
        }
208 31
        if ($document->getCreator() && $creator = $this->userManager->getUserById($document->getCreator())) {
209 31
            $article->setCreatorFullName($creator->getFullName());
210 31
            $article->setCreatorContactId($creator->getContact()->getId());
211
        }
212 31
        $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 31
        $article->setStructureType($document->getStructureType());
214 31
        $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 31
        $article->setPublishedState($document->getWorkflowStage() === WorkflowStage::PUBLISHED);
216 31
        $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 31
        $article->setLocalizationState(
218 31
            new LocalizationStateViewObject(
219
                $localizationState,
220 31
                (LocalizationState::LOCALIZED === $localizationState) ? null : $document->getLocale()
221
            )
222
        );
223
224 31
        $extensions = $document->getExtensionsData()->toArray();
225 31
        if (array_key_exists('excerpt', $extensions)) {
226 31
            $article->setExcerpt($this->excerptFactory->create($extensions['excerpt'], $document->getLocale()));
227
        }
228 31
        if (array_key_exists('seo', $extensions)) {
229 31
            $article->setSeo($this->seoFactory->create($extensions['seo']));
230
        }
231 31
        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 31
        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 31
        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 31
    public function flush()
266
    {
267 31
        $this->manager->commit();
268 31
    }
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