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

ArticleController::getMapper()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
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\Controller;
13
14
use FOS\RestBundle\Controller\Annotations\Post;
15
use FOS\RestBundle\Routing\ClassResourceInterface;
16
use JMS\Serializer\SerializationContext;
17
use ONGR\ElasticsearchBundle\Service\Manager;
18
use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery;
19
use ONGR\ElasticsearchDSL\Query\FullText\MatchQuery;
20
use ONGR\ElasticsearchDSL\Query\FullText\MultiMatchQuery;
21
use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
22
use ONGR\ElasticsearchDSL\Query\TermLevel\IdsQuery;
23
use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery;
24
use ONGR\ElasticsearchDSL\Sort\FieldSort;
25
use Sulu\Bundle\ArticleBundle\Admin\ArticleAdmin;
26
use Sulu\Bundle\ArticleBundle\Document\ArticleDocument;
27
use Sulu\Bundle\ArticleBundle\Document\Form\ArticleDocumentType;
28
use Sulu\Bundle\ArticleBundle\Metadata\ArticleViewDocumentIdTrait;
29
use Sulu\Component\Content\Form\Exception\InvalidFormException;
30
use Sulu\Component\Content\Mapper\ContentMapperInterface;
31
use Sulu\Component\DocumentManager\DocumentManagerInterface;
32
use Sulu\Component\Rest\Exception\MissingParameterException;
33
use Sulu\Component\Rest\Exception\RestException;
34
use Sulu\Component\Rest\ListBuilder\FieldDescriptor;
35
use Sulu\Component\Rest\ListBuilder\ListRepresentation;
36
use Sulu\Component\Rest\RequestParametersTrait;
37
use Sulu\Component\Rest\RestController;
38
use Sulu\Component\Security\SecuredControllerInterface;
39
use Symfony\Component\HttpFoundation\Request;
40
use Symfony\Component\HttpFoundation\Response;
41
42
/**
43
 * Provides API for articles.
44
 */
45
class ArticleController extends RestController implements ClassResourceInterface, SecuredControllerInterface
46
{
47
    const DOCUMENT_TYPE = 'article';
48
49
    use RequestParametersTrait;
50
    use ArticleViewDocumentIdTrait;
51
52
    /**
53
     * Create field-descriptor array.
54
     *
55
     * @return FieldDescriptor[]
56
     */
57
    private function getFieldDescriptors()
58
    {
59
        return [
60
            'uuid' => new FieldDescriptor('uuid', 'public.id', true),
61
            'typeTranslation' => new FieldDescriptor(
62
                'typeTranslation',
63
                'sulu_article.list.type',
64
                !$this->getParameter('sulu_article.display_tab_all'),
65
                false
66
            ),
67
            'title' => new FieldDescriptor('title', 'public.title', false, true),
68
            'creatorFullName' => new FieldDescriptor('creatorFullName', 'sulu_article.list.creator', true, false),
69
            'changerFullName' => new FieldDescriptor('changerFullName', 'sulu_article.list.changer', false, false),
70
            'authorFullName' => new FieldDescriptor('authorFullName', 'sulu_article.author', false, false),
71
            'created' => new FieldDescriptor('created', 'public.created', true, false, 'datetime'),
72
            'changed' => new FieldDescriptor('changed', 'public.changed', false, false, 'datetime'),
73
            'authored' => new FieldDescriptor('authored', 'sulu_article.authored', false, false, 'date'),
74
        ];
75
    }
76
77
    /**
78
     * Returns fields.
79
     *
80
     * @return Response
81
     */
82
    public function cgetFieldsAction()
83
    {
84
        $fieldDescriptors = $this->getFieldDescriptors();
85
86
        return $this->handleView($this->view(array_values($fieldDescriptors)));
0 ignored issues
show
Documentation introduced by
$this->view(array_values($fieldDescriptors)) is of type this<Sulu\Bundle\Article...ller\ArticleController>, but the function expects a object<FOS\RestBundle\View\View>.

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...
87
    }
88
89
    /**
90
     * Returns list of articles.
91
     *
92
     * @param Request $request
93
     *
94
     * @return Response
95
     */
96 12
    public function cgetAction(Request $request)
97
    {
98 12
        $locale = $this->getRequestParameter($request, 'locale', true);
99
100 12
        $restHelper = $this->get('sulu_core.list_rest_helper');
101
102
        /** @var Manager $manager */
103 12
        $manager = $this->get('es.manager.default');
104 12
        $repository = $manager->getRepository($this->get('sulu_article.view_document.factory')->getClass('article'));
105 12
        $search = $repository->createSearch();
106
107 12
        $limit = (int) $restHelper->getLimit();
108 12
        $page = (int) $restHelper->getPage();
109
110 12
        if (null !== $locale) {
111 12
            $search->addQuery(new TermQuery('locale', $locale));
112
        }
113
114 12
        if (count($ids = array_filter(explode(',', $request->get('ids', ''))))) {
115 1
            $search->addQuery(new IdsQuery($this->getViewDocumentIds($ids, $locale)));
116 1
            $limit = count($ids);
117
        }
118
119 12
        if (!empty($searchPattern = $restHelper->getSearchPattern())
120 12
            && 0 < count($searchFields = $restHelper->getSearchFields())
121
        ) {
122 2
            $search->addQuery(new MultiMatchQuery($searchFields, $searchPattern));
123
        }
124
125 12
        if (null !== ($type = $request->get('type'))) {
126 10
            $search->addQuery(new TermQuery('type', $type));
127
        }
128
129 12
        if (null !== ($contactId = $request->get('contactId'))) {
130 1
            $boolQuery = new BoolQuery();
131 1
            $boolQuery->add(new MatchQuery('changer_contact_id', $contactId), BoolQuery::SHOULD);
132 1
            $boolQuery->add(new MatchQuery('creator_contact_id', $contactId), BoolQuery::SHOULD);
133 1
            $boolQuery->add(new MatchQuery('author_id', $contactId), BoolQuery::SHOULD);
134 1
            $search->addQuery($boolQuery);
135
        }
136
137 12
        if (null !== ($categoryId = $request->get('categoryId'))) {
138 1
            $search->addQuery(new TermQuery('excerpt.categories.id', $categoryId), BoolQuery::MUST);
139
        }
140
141 12
        if (null === $search->getQueries()) {
142
            $search->addQuery(new MatchAllQuery());
143
        }
144
145 12
        $count = $repository->count($search);
146
147 12
        if (null !== $restHelper->getSortColumn()) {
148 1
            $search->addSort(
149 1
                new FieldSort($this->uncamelize($restHelper->getSortColumn()), $restHelper->getSortOrder())
150
            );
151
        }
152
153 12
        $search->setSize($limit);
154 12
        $search->setFrom(($page - 1) * $limit);
155
156 12
        $result = [];
157 12
        foreach ($repository->findDocuments($search) as $document) {
158 10
            if (false !== ($index = array_search($document->getUuid(), $ids))) {
159 1
                $result[$index] = $document;
160
            } else {
161 12
                $result[] = $document;
162
            }
163
        }
164
165 12
        if (count($ids)) {
166 1
            ksort($result);
167 1
            $result = array_values($result);
168
        }
169
170 12
        return $this->handleView(
171 12
            $this->view(
0 ignored issues
show
Documentation introduced by
$this->view(new \Sulu\Co...$page, $limit, $count)) is of type this<Sulu\Bundle\Article...ller\ArticleController>, but the function expects a object<FOS\RestBundle\View\View>.

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...
172 12
                new ListRepresentation(
173
                    $result,
174 12
                    'articles',
175 12
                    'get_articles',
176 12
                    $request->query->all(),
177
                    $page,
178
                    $limit,
179
                    $count
0 ignored issues
show
Bug introduced by
It seems like $count defined by $repository->count($search) on line 145 can also be of type array; however, Sulu\Component\Rest\List...entation::__construct() does only seem to accept integer, 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...
180
                )
181
            )
182
        );
183
    }
184
185
    /**
186
     * Returns single article.
187
     *
188
     * @param string  $uuid
189
     * @param Request $request
190
     *
191
     * @return Response
192
     */
193 2
    public function getAction($uuid, Request $request)
194
    {
195 2
        $locale = $this->getRequestParameter($request, 'locale', true);
196 2
        $document = $this->getDocumentManager()->find(
197
            $uuid,
198
            $locale,
199
            [
200 2
                'load_ghost_content' => true,
201
                'load_shadow_content' => false,
202
            ]
203
        );
204
205 2
        return $this->handleView(
206 2
            $this->view($document)->setSerializationContext(
0 ignored issues
show
Bug introduced by
The method setSerializationContext() does not seem to exist on object<Sulu\Bundle\Artic...ller\ArticleController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
207 2
                SerializationContext::create()->setSerializeNull(true)->setGroups(['defaultPage'])
208
            )
209
        );
210
    }
211
212
    /**
213
     * Create article.
214
     *
215
     * @param Request $request
216
     *
217
     * @return Response
218
     */
219 30
    public function postAction(Request $request)
220
    {
221 30
        $action = $request->get('action');
222 30
        $document = $this->getDocumentManager()->create(self::DOCUMENT_TYPE);
223 30
        $locale = $this->getRequestParameter($request, 'locale', true);
224 30
        $data = $request->request->all();
225
226 30
        $this->persistDocument($data, $document, $locale);
227 30
        $this->handleActionParameter($action, $document, $locale);
228 30
        $this->getDocumentManager()->flush();
229
230 30
        return $this->handleView(
231 30
            $this->view($document)->setSerializationContext(
0 ignored issues
show
Bug introduced by
The method setSerializationContext() does not seem to exist on object<Sulu\Bundle\Artic...ller\ArticleController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
232 30
                SerializationContext::create()->setSerializeNull(true)->setGroups(['defaultPage'])
233
            )
234
        );
235
    }
236
237
    /**
238
     * Update articles.
239
     *
240
     * @param Request $request
241
     * @param string  $uuid
242
     *
243
     * @return Response
244
     */
245 7
    public function putAction(Request $request, $uuid)
246
    {
247 7
        $locale = $this->getRequestParameter($request, 'locale', true);
248 7
        $action = $request->get('action');
249 7
        $data = $request->request->all();
250
251 7
        $document = $this->getDocumentManager()->find(
252
            $uuid,
253
            $locale,
254
            [
255 7
                'load_ghost_content' => false,
256
                'load_shadow_content' => false,
257
            ]
258
        );
259
260 7
        $this->get('sulu_hash.request_hash_checker')->checkHash($request, $document, $document->getUuid());
261
262 7
        $this->persistDocument($data, $document, $locale);
263 7
        $this->handleActionParameter($action, $document, $locale);
264 7
        $this->getDocumentManager()->flush();
265
266 7
        return $this->handleView(
267 7
            $this->view($document)->setSerializationContext(
0 ignored issues
show
Bug introduced by
The method setSerializationContext() does not seem to exist on object<Sulu\Bundle\Artic...ller\ArticleController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
268 7
                SerializationContext::create()->setSerializeNull(true)->setGroups(['defaultPage'])
269
            )
270
        );
271
    }
272
273
    /**
274
     * Deletes multiple documents.
275
     *
276
     * @param Request $request
277
     *
278
     * @return Response
279
     */
280 1
    public function cdeleteAction(Request $request)
281
    {
282 1
        $ids = array_filter(explode(',', $request->get('ids', '')));
283
284 1
        $documentManager = $this->getDocumentManager();
285 1
        foreach ($ids as $id) {
286 1
            $document = $documentManager->find($id);
287 1
            $documentManager->remove($document);
288 1
            $documentManager->flush();
289
        }
290
291 1
        return $this->handleView($this->view(null));
0 ignored issues
show
Documentation introduced by
$this->view(null) is of type this<Sulu\Bundle\Article...ller\ArticleController>, but the function expects a object<FOS\RestBundle\View\View>.

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...
292
    }
293
294
    /**
295
     * Deletes multiple documents.
296
     *
297
     * @param string $id
298
     *
299
     * @return Response
300
     */
301 1
    public function deleteAction($id)
302
    {
303 1
        $documentManager = $this->getDocumentManager();
304 1
        $document = $documentManager->find($id);
305 1
        $documentManager->remove($document);
306 1
        $documentManager->flush();
307
308 1
        return $this->handleView($this->view(null));
0 ignored issues
show
Documentation introduced by
$this->view(null) is of type this<Sulu\Bundle\Article...ller\ArticleController>, but the function expects a object<FOS\RestBundle\View\View>.

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...
309
    }
310
311
    /**
312
     * Trigger a action for given article specified over get-action parameter.
313
     *
314
     * @Post("/articles/{uuid}")
315
     *
316
     * @param string  $uuid
317
     * @param Request $request
318
     *
319
     * @return Response
320
     */
321 1
    public function postTriggerAction($uuid, Request $request)
322
    {
323
        // extract parameter
324 1
        $action = $this->getRequestParameter($request, 'action', true);
325 1
        $locale = $this->getRequestParameter($request, 'locale', true);
326
327
        // prepare vars
328 1
        $view = null;
329 1
        $data = null;
330 1
        $userId = $this->getUser()->getId();
331
332
        try {
333
            switch ($action) {
334 1
                case 'unpublish':
335
                    $document = $this->getDocumentManager()->find($uuid, $locale);
336
                    $this->getDocumentManager()->unpublish($document, $locale);
337
                    $this->getDocumentManager()->flush();
338
339
                    $data = $this->getDocumentManager()->find($uuid, $locale);
340
                    break;
341 1
                case 'remove-draft':
342
                    $data = $this->getDocumentManager()->find($uuid, $locale);
343
                    $this->getDocumentManager()->removeDraft($data, $locale);
344
                    $this->getDocumentManager()->flush();
345
                    break;
346 1
                case 'copy-locale':
347 1
                    $destLocales = $this->getRequestParameter($request, 'dest', true);
348 1
                    $data = $this->getMapper()->copyLanguage($uuid, $userId, null, $locale, explode(',', $destLocales));
349 1
                    break;
350
                case 'copy':
351
                    /** @var ArticleDocument $document */
352
                    $document = $this->getDocumentManager()->find($uuid, $locale);
353
                    $copiedPath = $this->getDocumentManager()->copy($document, dirname($document->getPath()));
354
                    $this->getDocumentManager()->flush();
355
356
                    $data = $this->getDocumentManager()->find($copiedPath, $locale);
357
                    break;
358
                default:
359
                    throw new RestException('Unrecognized action: ' . $action);
360
            }
361
362
            // prepare view
363 1
            $view = $this->view($data, $data !== null ? 200 : 204);
364 1
            $view->setSerializationContext(SerializationContext::create()->setGroups(['defaultPage']));
0 ignored issues
show
Bug introduced by
The method setSerializationContext() does not seem to exist on object<Sulu\Bundle\Artic...ller\ArticleController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
365
        } catch (RestException $exc) {
366
            $view = $this->view($exc->toArray(), 400);
367
        }
368
369 1
        return $this->handleView($view);
0 ignored issues
show
Documentation introduced by
$view is of type this<Sulu\Bundle\Article...ller\ArticleController>, but the function expects a object<FOS\RestBundle\View\View>.

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...
370
    }
371
372
    /**
373
     * {@inheritdoc}
374
     */
375 31
    public function getSecurityContext()
376
    {
377 31
        return ArticleAdmin::SECURITY_CONTEXT;
378
    }
379
380
    /**
381
     * Persists the document using the given information.
382
     *
383
     * @param array  $data
384
     * @param object $document
385
     * @param string $locale
386
     *
387
     * @throws InvalidFormException
388
     * @throws MissingParameterException
389
     */
390 30
    private function persistDocument($data, $document, $locale)
391
    {
392 30
        $form = $this->createForm(
393 30
            ArticleDocumentType::class,
394
            $document,
395
            [
396
                // disable csrf protection, since we can't produce a token, because the form is cached on the client
397 30
                'csrf_protection' => false,
398
            ]
399
        );
400 30
        $form->submit($data, false);
401
402 30
        if (!$form->isValid()) {
403
            throw new InvalidFormException($form);
404
        }
405
406 30
        $this->getDocumentManager()->persist(
407
            $document,
408
            $locale,
409
            [
410 30
                'user' => $this->getUser()->getId(),
411
                'clear_missing_content' => false,
412 30
                'route_path' => array_key_exists('routePath', $data) ? $data['routePath'] : null,
413
            ]
414
        );
415 30
    }
416
417
    /**
418
     * Returns document-manager.
419
     *
420
     * @return DocumentManagerInterface
421
     */
422 30
    protected function getDocumentManager()
423
    {
424 30
        return $this->get('sulu_document_manager.document_manager');
425
    }
426
427
    /**
428
     * @return ContentMapperInterface
429
     */
430 1
    protected function getMapper()
431
    {
432 1
        return $this->get('sulu.content.mapper');
433
    }
434
435
    /**
436
     * Delegates actions by given actionParameter, which can be retrieved from the request.
437
     *
438
     * @param string $actionParameter
439
     * @param object $document
440
     * @param string $locale
441
     */
442 30
    private function handleActionParameter($actionParameter, $document, $locale)
443
    {
444
        switch ($actionParameter) {
445 30
            case 'publish':
446 10
                $this->getDocumentManager()->publish($document, $locale);
447 10
                break;
448
        }
449 30
    }
450
451
    /**
452
     * Converts camel case string into normalized string with underscore.
453
     *
454
     * @param string $camel
455
     *
456
     * @return string
457
     */
458 1
    private function uncamelize($camel)
459
    {
460 1
        $camel = preg_replace(
461 1
            '/(?!^)[[:upper:]][[:lower:]]/',
462 1
            '$0',
463 1
            preg_replace('/(?!^)[[:upper:]]+/', '_$0', $camel)
464
        );
465
466 1
        return strtolower($camel);
467
    }
468
}
469