Completed
Pull Request — develop (#158)
by Wachter
37:42 queued 11:18
created

ArticleController::getAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 2
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\BoolQuery;
19
use ONGR\ElasticsearchDSL\Query\IdsQuery;
20
use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
21
use ONGR\ElasticsearchDSL\Query\MatchQuery;
22
use ONGR\ElasticsearchDSL\Query\MultiMatchQuery;
23
use ONGR\ElasticsearchDSL\Query\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 11
    public function cgetAction(Request $request)
97
    {
98 11
        $locale = $this->getRequestParameter($request, 'locale', true);
99
100 11
        $restHelper = $this->get('sulu_core.list_rest_helper');
101
102
        /** @var Manager $manager */
103 11
        $manager = $this->get('es.manager.default');
104 11
        $repository = $manager->getRepository($this->get('sulu_article.view_document.factory')->getClass('article'));
105 11
        $search = $repository->createSearch();
106
107 11
        $limit = (int) $restHelper->getLimit();
108 11
        $page = (int) $restHelper->getPage();
109
110 11
        if (null !== $locale) {
111 11
            $search->addQuery(new TermQuery('locale', $locale));
112
        }
113
114 11
        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 11
        if (!empty($searchPattern = $restHelper->getSearchPattern())
120 11
            && 0 < count($searchFields = $restHelper->getSearchFields())
121
        ) {
122 2
            $search->addQuery(new MultiMatchQuery($searchFields, $searchPattern));
123
        }
124
125 11
        if (null !== ($type = $request->get('type'))) {
126 10
            $search->addQuery(new TermQuery('type', $type));
127
        }
128
129 11
        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 11
        if (null !== ($categoryId = $request->get('categoryId'))) {
138
            $search->addQuery(new TermQuery('excerpt.categories.id', $categoryId), BoolQuery::MUST);
139
        }
140
141 11
        if (null === $search->getQueries()) {
142
            $search->addQuery(new MatchAllQuery());
143 11
        }
144 1
145 1
        $count = $repository->count($search);
146
147
        if (null !== $restHelper->getSortColumn()) {
148
            $search->addSort(
149 11
                new FieldSort($this->uncamelize($restHelper->getSortColumn()), $restHelper->getSortOrder())
150 11
            );
151
        }
152 11
153 11
        $search->setSize($limit);
154 9
        $search->setFrom(($page - 1) * $limit);
155 1
156
        $result = [];
157 11
        foreach ($repository->execute($search) as $document) {
0 ignored issues
show
Bug introduced by
The method execute() does not exist on ONGR\ElasticsearchBundle\Service\Repository. Did you maybe mean executeSearch()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
158
            if (false !== ($index = array_search($document->getUuid(), $ids))) {
159
                $result[$index] = $document;
160
            } else {
161 11
                $result[] = $document;
162 1
            }
163 1
        }
164
165
        if (count($ids)) {
166 11
            ksort($result);
167 11
            $result = array_values($result);
168 11
        }
169
170 11
        return $this->handleView(
171 11
            $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 11
                new ListRepresentation(
173
                    $result,
174
                    'articles',
175
                    'get_articles',
176
                    $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 2
     * @param Request $request
190
     *
191 2
     * @return Response
192 2
     */
193
    public function getAction($uuid, Request $request)
194
    {
195
        $locale = $this->getRequestParameter($request, 'locale', true);
196 2
        $document = $this->getDocumentManager()->find(
197
            $uuid,
198
            $locale,
199
            [
200
                'load_ghost_content' => true,
201 2
                'load_shadow_content' => false,
202 2
            ]
203 2
        );
204
205
        return $this->handleView(
206
            $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
                SerializationContext::create()->setSerializeNull(true)->setGroups(['defaultPage'])
208
            )
209
        );
210
    }
211
212
    /**
213
     * Create article.
214
     *
215 29
     * @param Request $request
216
     *
217 29
     * @return Response
218 29
     */
219 29
    public function postAction(Request $request)
220 29
    {
221
        $action = $request->get('action');
222 29
        $document = $this->getDocumentManager()->create(self::DOCUMENT_TYPE);
223 29
        $locale = $this->getRequestParameter($request, 'locale', true);
224 29
        $data = $request->request->all();
225
226 29
        $this->persistDocument($data, $document, $locale);
227 29
        $this->handleActionParameter($action, $document, $locale);
228 29
        $this->getDocumentManager()->flush();
229
230
        return $this->handleView(
231
            $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
                SerializationContext::create()->setSerializeNull(true)->setGroups(['defaultPage'])
233
            )
234
        );
235
    }
236
237
    /**
238
     * Update articles.
239
     *
240
     * @param Request $request
241 7
     * @param string $uuid
242
     *
243 7
     * @return Response
244 7
     */
245 7
    public function putAction(Request $request, $uuid)
246
    {
247 7
        $locale = $this->getRequestParameter($request, 'locale', true);
248
        $action = $request->get('action');
249
        $data = $request->request->all();
250
251 7
        $document = $this->getDocumentManager()->find(
252
            $uuid,
253
            $locale,
254
            [
255
                'load_ghost_content' => false,
256 7
                'load_shadow_content' => false,
257
            ]
258 7
        );
259 7
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
        return $this->handleView(
267
            $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
                SerializationContext::create()->setSerializeNull(true)->setGroups(['defaultPage'])
269
            )
270
        );
271
    }
272
273
    /**
274
     * Deletes multiple documents.
275
     *
276 1
     * @param Request $request
277
     *
278 1
     * @return Response
279
     */
280 1
    public function cdeleteAction(Request $request)
281 1
    {
282 1
        $ids = array_filter(explode(',', $request->get('ids', '')));
283 1
284 1
        $documentManager = $this->getDocumentManager();
285
        foreach ($ids as $id) {
286
            $document = $documentManager->find($id);
287 1
            $documentManager->remove($document);
288
            $documentManager->flush();
289
        }
290
291
        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 1
     * @param string $id
298
     *
299 1
     * @return Response
300 1
     */
301 1
    public function deleteAction($id)
302 1
    {
303
        $documentManager = $this->getDocumentManager();
304 1
        $document = $documentManager->find($id);
305
        $documentManager->remove($document);
306
        $documentManager->flush();
307
308
        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 1
     * @param Request $request
318
     *
319
     * @return Response
320 1
     */
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 1
327
        // prepare vars
328
        $view = null;
329
        $data = null;
330 1
        $userId = $this->getUser()->getId();
331
332
        try {
333
            switch ($action) {
334
                case 'unpublish':
335
                    $document = $this->getDocumentManager()->find($uuid, $locale);
336
                    $this->getDocumentManager()->unpublish($document, $locale);
337 1
                    $this->getDocumentManager()->flush();
338
339
                    $data = $this->getDocumentManager()->find($uuid, $locale);
340
                    break;
341
                case 'remove-draft':
342 1
                    $data = $this->getDocumentManager()->find($uuid, $locale);
343 1
                    $this->getDocumentManager()->removeDraft($data, $locale);
344 1
                    $this->getDocumentManager()->flush();
345 1
                    break;
346
                case 'copy-locale':
347
                    $destLocales = $this->getRequestParameter($request, 'dest', true);
348
                    $data = $this->getMapper()->copyLanguage($uuid, $userId, null, $locale, explode(',', $destLocales));
349
                    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 1
                    throw new RestException('Unrecognized action: ' . $action);
360 1
            }
361
362
            // prepare view
363
            $view = $this->view($data, $data !== null ? 200 : 204);
364
            $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 1
        } catch (RestException $exc) {
366
            $view = $this->view($exc->toArray(), 400);
367
        }
368
369
        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 30
372
    /**
373 30
     * {@inheritdoc}
374
     */
375
    public function getSecurityContext()
376
    {
377
        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 29
     *
387
     * @throws InvalidFormException
388 29
     * @throws MissingParameterException
389 29
     */
390
    private function persistDocument($data, $document, $locale)
391
    {
392
        $form = $this->createForm(
393 29
            ArticleDocumentType::class,
394
            $document,
395
            [
396 29
                // disable csrf protection, since we can't produce a token, because the form is cached on the client
397
                'csrf_protection' => false,
398 29
            ]
399
        );
400
        $form->submit($data, false);
401
402 29
        if (!$form->isValid()) {
403
            throw new InvalidFormException($form);
404
        }
405
406 29
        $this->getDocumentManager()->persist(
407
            $document,
408 29
            $locale,
409
            [
410
                'user' => $this->getUser()->getId(),
411 29
                'clear_missing_content' => false,
412
                'route_path' => array_key_exists('routePath', $data) ? $data['routePath'] : null,
413
            ]
414
        );
415
    }
416
417
    /**
418 29
     * Returns document-manager.
419
     *
420 29
     * @return DocumentManagerInterface
421
     */
422
    protected function getDocumentManager()
423
    {
424
        return $this->get('sulu_document_manager.document_manager');
425
    }
426 1
427
    /**
428 1
     * @return ContentMapperInterface
429
     */
430
    protected function getMapper()
431
    {
432
        return $this->get('sulu.content.mapper');
433
    }
434
435
    /**
436
     * Delegates actions by given actionParameter, which can be retrieved from the request.
437
     *
438 29
     * @param string $actionParameter
439
     * @param object $document
440
     * @param string $locale
441 29
     */
442 10
    private function handleActionParameter($actionParameter, $document, $locale)
443 10
    {
444
        switch ($actionParameter) {
445 29
            case 'publish':
446
                $this->getDocumentManager()->publish($document, $locale);
447
                break;
448
        }
449
    }
450
451
    /**
452
     * Converts camel case string into normalized string with underscore.
453
     *
454 1
     * @param string $camel
455
     *
456 1
     * @return string
457 1
     */
458 1
    private function uncamelize($camel)
459 1
    {
460
        $camel = preg_replace(
461
            '/(?!^)[[:upper:]][[:lower:]]/',
462 1
            '$0',
463
            preg_replace('/(?!^)[[:upper:]]+/', '_$0', $camel)
464
        );
465
466
        return strtolower($camel);
467
    }
468
}
469