Completed
Push — 2.0 ( f46ccd...153639 )
by Paweł
10:00
created

ContentListItemController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
/*
4
 * This file is part of the Superdesk Web Publisher Core Bundle.
5
 *
6
 * Copyright 2016 Sourcefabric z.ú. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2016 Sourcefabric z.ú
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace SWP\Bundle\CoreBundle\Controller;
16
17
use DateTime;
18
use DateTimeZone;
19
use Doctrine\ORM\EntityManagerInterface;
20
use Nelmio\ApiDocBundle\Annotation\Model;
21
use Nelmio\ApiDocBundle\Annotation\Operation;
22
use Swagger\Annotations as SWG;
23
use SWP\Bundle\ContentBundle\ArticleEvents;
24
use SWP\Bundle\ContentBundle\Event\ArticleEvent;
25
use SWP\Bundle\ContentListBundle\Form\Type\ContentListItemsType;
26
use SWP\Bundle\ContentListBundle\Services\ContentListServiceInterface;
27
use SWP\Bundle\CoreBundle\Form\Type\ContentListItemType;
28
use SWP\Bundle\CoreBundle\Model\ContentListInterface;
29
use SWP\Bundle\CoreBundle\Model\ContentListItemInterface;
30
use SWP\Bundle\CoreBundle\Repository\ArticleRepositoryInterface;
31
use SWP\Bundle\CoreBundle\Repository\ContentListItemRepositoryInterface;
32
use SWP\Component\Common\Criteria\Criteria;
33
use SWP\Component\Common\Pagination\PaginationData;
34
use SWP\Component\Common\Response\ResourcesListResponse;
35
use SWP\Component\Common\Response\ResourcesListResponseInterface;
36
use SWP\Component\Common\Response\ResponseContext;
37
use SWP\Component\Common\Response\SingleResourceResponse;
38
use SWP\Component\Common\Response\SingleResourceResponseInterface;
39
use SWP\Component\ContentList\Repository\ContentListRepositoryInterface;
40
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
41
use Symfony\Component\Form\FormFactoryInterface;
42
use Symfony\Component\HttpFoundation\Request;
43
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
44
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
45
use Symfony\Component\Routing\Annotation\Route;
46
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
47
48
class ContentListItemController extends AbstractController
49
{
50
    private $contentListItemRepository;
51
52
    private $entityManager;
53
54
    public function __construct(ContentListItemRepositoryInterface $contentListItemRepository, EntityManagerInterface $entityManager)
55
    {
56
        $this->contentListItemRepository = $contentListItemRepository;
57
        $this->entityManager = $entityManager;
58
    }
59
60
    /**
61
     * List all items of content list.
62
     *
63
     * @Operation(
64
     *     tags={"content list"},
65
     *     summary="Lists content list items",
66
     *     @SWG\Parameter(
67
     *         name="sticky",
68
     *         in="query",
69
     *         description="Keep item on top of the list",
70
     *         required=false,
71
     *         type="boolean"
72
     *     ),
73
     *     @SWG\Parameter(
74
     *         name="sorting",
75
     *         in="query",
76
     *         description="example: [updatedAt]=asc|desc",
77
     *         required=false,
78
     *         type="string"
79
     *     ),
80
     *     @SWG\Response(
81
     *         response="200",
82
     *         description="Returned on success.",
83
     *         @SWG\Schema(
84
     *             type="array",
85
     *             @SWG\Items(ref=@Model(type=\SWP\Bundle\CoreBundle\Model\ContentListItem::class, groups={"api"}))
86
     *         )
87
     *     ),
88
     *     @SWG\Response(
89
     *         response="404",
90
     *         description="Content list item not found."
91
     *     ),
92
     *     @SWG\Response(
93
     *         response="500",
94
     *         description="Unexpected error."
95
     *     )
96
     * )
97
     *
98
     * @Route("/api/{version}/content/lists/{id}/items/", options={"expose"=true}, defaults={"version"="v2"}, methods={"GET"}, name="swp_api_core_list_items", requirements={"id"="\d+"})
99
     */
100
    public function listAction(Request $request, int $id): ResourcesListResponseInterface
101
    {
102
        $items = $this->contentListItemRepository->getPaginatedByCriteria(
103
            new Criteria([
104
                'contentList' => $id,
105
                'sticky' => $request->query->get('sticky', ''),
106
            ]),
107
            $request->query->get('sorting', ['sticky' => 'desc']),
108
            new PaginationData($request)
109
        );
110
111
        $responseContext = new ResponseContext();
112
        $responseContext->setSerializationGroups(
113
            [
114
                'Default',
115
                'api_packages_list',
116
                'api_content_list_item_details',
117
                'api_articles_list',
118
                'api_articles_featuremedia',
119
                'api_article_media_list',
120
                'api_article_media_renditions',
121
                'api_articles_statistics_list',
122
                'api_image_details',
123
                'api_routes_list',
124
                'api_tenant_list',
125
            ]
126
        );
127
128
        return new ResourcesListResponse($items, $responseContext);
129
    }
130
131
    /**
132
     * @Operation(
133
     *     tags={"content list"},
134
     *     summary="Get single content list item",
135
     *     @SWG\Response(
136
     *         response="200",
137
     *         description="Returned on success.",
138
     *         @Model(type=\SWP\Bundle\CoreBundle\Model\ContentListItem::class, groups={"api"})
139
     *     )
140
     * )
141
     *
142
     * @Route("/api/{version}/content/lists/{listId}/items/{id}", options={"expose"=true}, defaults={"version"="v2"}, methods={"GET"}, name="swp_api_core_show_lists_item", requirements={"id"="\d+"})
143
     */
144
    public function getAction($listId, $id)
145
    {
146
        return new SingleResourceResponse($this->findOr404($listId, $id));
147
    }
148
149
    /**
150
     * @Operation(
151
     *     tags={"content list"},
152
     *     summary="Update single content list item",
153
     *     @SWG\Parameter(
154
     *         name="body",
155
     *         in="body",
156
     *         @SWG\Schema(
157
     *             ref=@Model(type=ContentListItemType::class)
158
     *         )
159
     *     ),
160
     *     @SWG\Response(
161
     *         response="200",
162
     *         description="Returned on success.",
163
     *         @Model(type=\SWP\Bundle\CoreBundle\Model\ContentListItem::class, groups={"api"})
164
     *     ),
165
     *     @SWG\Response(
166
     *         response="400",
167
     *         description="Returned when not valid data."
168
     *     ),
169
     *     @SWG\Response(
170
     *         response="404",
171
     *         description="Returned when not found."
172
     *     )
173
     * )
174
     *
175
     * @Route("/api/{version}/content/lists/{listId}/items/{id}", options={"expose"=true}, defaults={"version"="v2"}, methods={"PATCH"}, name="swp_api_core_update_lists_item", requirements={"id"="\d+", "listId"="\d+"})
176
     */
177 View Code Duplication
    public function updateAction(Request $request, FormFactoryInterface $formFactory, $listId, $id): SingleResourceResponseInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
    {
179
        $contentListItem = $this->findOr404($listId, $id);
180
        $form = $formFactory->createNamed('',
181
            ContentListItemType::class,
182
            $contentListItem,
183
            ['method' => $request->getMethod()]
184
        );
185
186
        $form->handleRequest($request);
187
188
        if ($form->isSubmitted() && $form->isValid()) {
189
            $contentListItem->getContentList()->setUpdatedAt(new DateTime());
190
            $this->entityManager->flush();
191
192
            return new SingleResourceResponse($contentListItem);
193
        }
194
195
        return new SingleResourceResponse($form, new ResponseContext(400));
196
    }
197
198
    /**
199
     * Tips:
200
     *  - position "-1" will place element at end of list.
201
     *  - make sure that "updated_at" value is filled with value fetched from list.
202
     *
203
     * Possible actions: move, add, delete
204
     *
205
     * @Operation(
206
     *     tags={"content list"},
207
     *     summary="Update many content list items",
208
     *     @SWG\Parameter(
209
     *         name="body",
210
     *         in="body",
211
     *         @SWG\Schema(
212
     *             ref=@Model(type=ContentListItemsType::class)
213
     *         )
214
     *     ),
215
     *     @SWG\Response(
216
     *         response="200",
217
     *         description="Returned on success.",
218
     *         @Model(type=\SWP\Bundle\CoreBundle\Model\ContentList::class, groups={"api"})
219
     *     ),
220
     *     @SWG\Response(
221
     *         response="400",
222
     *         description="Returned when not valid data."
223
     *     ),
224
     *     @SWG\Response(
225
     *         response="404",
226
     *         description="Returned when not found."
227
     *     )
228
     * )
229
     *
230
     * @Route("/api/{version}/content/lists/{listId}/items/", options={"expose"=true}, defaults={"version"="v2"}, methods={"PATCH"}, name="swp_api_core_batch_update_lists_item", requirements={"listId"="\d+"})
231
     */
232
    public function batchUpdateAction(
233
        Request $request,
234
        FormFactoryInterface $formFactory,
235
        ContentListRepositoryInterface $contentListRepository,
236
        ArticleRepositoryInterface $articleRepository,
237
        ContentListServiceInterface $contentListService,
238
        EventDispatcherInterface $eventDispatcher,
239
        int $listId
240
    ): SingleResourceResponseInterface {
241
        /** @var ContentListInterface $list */
242
        $list = $contentListRepository->findOneBy(['id' => $listId]);
243
        if (null === $list) {
244
            throw new NotFoundHttpException(sprintf('Content list with id "%s" was not found.', $list));
245
        }
246
247
        $form = $formFactory->createNamed('', ContentListItemsType::class, [], ['method' => $request->getMethod()]);
248
249
        $form->handleRequest($request);
250
        if ($form->isSubmitted() && $form->isValid()) {
251
            $data = $form->getData();
252
            $updatedAt = DateTime::createFromFormat(DateTime::RFC3339, $data['updatedAt'], new DateTimeZone('UTC'));
253
            $updatedAt->setTimezone(new DateTimeZone('UTC'));
254
            $listUpdatedAt = $list->getUpdatedAt();
255
            $listUpdatedAt->setTimezone(new DateTimeZone('UTC'));
256
            if ($updatedAt < $listUpdatedAt) {
257
                throw new ConflictHttpException('List was already updated');
258
            }
259
260
            $updatedArticles = [];
261
            foreach ($data['items'] as $item) {
262
                if (!is_array($item)) {
263
                    continue;
264
                }
265
266
                if (!isset($item['position']) || !is_numeric($item['position'])) {
267
                    $item['position'] = 0;
268
                }
269
270
                switch ($item['action']) {
271 View Code Duplication
                    case 'move':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272
                        $contentListItem = $this->findByContentOr404($list, $item['contentId']);
273
                        $contentListItem->setPosition($item['position']);
274
                        $list->setUpdatedAt(new DateTime('now'));
275
                        $this->entityManager->flush();
276
                        $updatedArticles[$item['contentId']] = $contentListItem->getContent();
277
278
                        break;
279
                    case 'add':
280
                        $object = $articleRepository->findOneById($item['contentId']);
0 ignored issues
show
Bug introduced by
The method findOneById() does not exist on SWP\Bundle\CoreBundle\Re...icleRepositoryInterface. Did you maybe mean findOneBy()?

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...
281
                        $contentListItem = $contentListService->addArticleToContentList($list, $object, $item['position']);
282
                        $updatedArticles[$item['contentId']] = $contentListItem->getContent();
283
284
                        break;
285 View Code Duplication
                    case 'delete':
286
                        $contentListItem = $this->findByContentOr404($list, $item['contentId']);
287
                        $this->entityManager->remove($contentListItem);
288
                        $list->setUpdatedAt(new DateTime('now'));
289
                        $this->entityManager->flush();
290
                        $updatedArticles[$item['contentId']] = $contentListItem->getContent();
291
292
                        break;
293
                }
294
            }
295
296
            foreach ($updatedArticles as $updatedArticle) {
297
                $eventDispatcher->dispatch(ArticleEvents::POST_UPDATE, new ArticleEvent(
298
                    $updatedArticle,
299
                    $updatedArticle->getPackage(),
300
                    ArticleEvents::POST_UPDATE
301
                ));
302
            }
303
304
            return new SingleResourceResponse($list, new ResponseContext(201));
305
        }
306
307
        return new SingleResourceResponse($form, new ResponseContext(400));
308
    }
309
310 View Code Duplication
    private function findByContentOr404($listId, $contentId): ContentListItemInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
    {
312
        /** @var ContentListItemInterface $listItem */
313
        $listItem = $this->contentListItemRepository->findOneBy([
314
            'contentList' => $listId,
315
            'content' => $contentId,
316
        ]);
317
318
        if (null === $listItem) {
319
            throw new NotFoundHttpException(sprintf('Content list item with content_id "%s" was not found on that list. If You want to add new item - use action type "add".', $contentId));
320
        }
321
322
        return $listItem;
323
    }
324
325 View Code Duplication
    private function findOr404($listId, $id): ContentListItemInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
    {
327
        /** @var ContentListItemInterface $listItem */
328
        $listItem = $this->contentListItemRepository->findOneBy([
329
            'contentList' => $listId,
330
            'id' => $id,
331
        ]);
332
333
        if (null === $listItem) {
334
            throw new NotFoundHttpException(sprintf('Content list item with id "%s" was not found.', $id));
335
        }
336
337
        return $listItem;
338
    }
339
}
340