Completed
Push — master ( 86bee8...f7a629 )
by Grégoire
04:38
created

GalleryController::handleWriteGalleryhasmedia()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
cc 2
eloc 16
c 2
b 2
f 0
nc 2
nop 4
dl 0
loc 26
rs 8.8571

1 Method

Rating   Name   Duplication   Size   Complexity  
A GalleryController::deleteGalleryAction() 0 8 1
1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\MediaBundle\Controller\Api;
13
14
use FOS\RestBundle\Controller\Annotations\QueryParam;
15
use FOS\RestBundle\Controller\Annotations\View;
16
use FOS\RestBundle\Request\ParamFetcherInterface;
17
use FOS\RestBundle\View\View as FOSRestView;
18
use JMS\Serializer\SerializationContext;
19
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
20
use Sonata\DatagridBundle\Pager\PagerInterface;
21
use Sonata\MediaBundle\Model\GalleryInterface;
22
use Sonata\MediaBundle\Model\GalleryItemInterface;
23
use Sonata\MediaBundle\Model\GalleryManagerInterface;
24
use Sonata\MediaBundle\Model\MediaInterface;
25
use Sonata\MediaBundle\Model\MediaManagerInterface;
26
use Symfony\Component\Form\FormFactoryInterface;
27
use Symfony\Component\Form\FormInterface;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
30
31
/**
32
 * Class GalleryController.
33
 *
34
 * @author Hugo Briand <[email protected]>
35
 */
36
class GalleryController
37
{
38
    /**
39
     * @var GalleryManagerInterface
40
     */
41
    protected $galleryManager;
42
43
    /**
44
     * @var MediaManagerInterface
45
     */
46
    protected $mediaManager;
47
48
    /**
49
     * @var FormFactoryInterface
50
     */
51
    protected $formFactory;
52
53
    /**
54
     * @var string
55
     */
56
    protected $galleryItemClass;
57
58
    /**
59
     * Constructor.
60
     *
61
     * @param GalleryManagerInterface $galleryManager
62
     * @param MediaManagerInterface   $mediaManager
63
     * @param FormFactoryInterface    $formFactory
64
     * @param string                  $galleryItemClass
65
     */
66
    public function __construct(GalleryManagerInterface $galleryManager, MediaManagerInterface $mediaManager, FormFactoryInterface $formFactory, $galleryItemClass)
67
    {
68
        $this->galleryManager = $galleryManager;
69
        $this->mediaManager = $mediaManager;
70
        $this->formFactory = $formFactory;
71
        $this->galleryItemClass = $galleryItemClass;
72
    }
73
74
    /**
75
     * Retrieves the list of galleries (paginated).
76
     *
77
     * @ApiDoc(
78
     *  resource=true,
79
     *  output={"class"="Sonata\DatagridBundle\Pager\PagerInterface", "groups"="sonata_api_read"}
80
     * )
81
     *
82
     * @QueryParam(
83
     *     name="page",
84
     *     requirements="\d+",
85
     *     default="1",
86
     *     description="Page for gallery list pagination"
87
     * )
88
     * @QueryParam(
89
     *     name="count",
90
     *     requirements="\d+",
91
     *     default="10",
92
     *     description="Number of galleries by page"
93
     * )
94
     * @QueryParam(
95
     *     name="enabled",
96
     *     requirements="0|1",
97
     *     nullable=true,
98
     *     strict=true,
99
     *     description="Enabled/Disabled galleries filter"
100
     * )
101
     * @QueryParam(
102
     *     name="orderBy",
103
     *     array=true,
104
     *     requirements="ASC|DESC",
105
     *     nullable=true,
106
     *     strict=true,
107
     *     description="Order by array (key is field, value is direction)"
108
     * )
109
     *
110
     * @View(serializerGroups="sonata_api_read", serializerEnableMaxDepthChecks=true)
111
     *
112
     * @param ParamFetcherInterface $paramFetcher
113
     *
114
     * @return PagerInterface
115
     */
116
    public function getGalleriesAction(ParamFetcherInterface $paramFetcher)
117
    {
118
        $supportedCriteria = array(
119
            'enabled' => '',
120
        );
121
122
        $page = $paramFetcher->get('page');
123
        $limit = $paramFetcher->get('count');
124
        $sort = $paramFetcher->get('orderBy');
125
        $criteria = array_intersect_key($paramFetcher->all(), $supportedCriteria);
126
127
        foreach ($criteria as $key => $value) {
128
            if (null === $value) {
129
                unset($criteria[$key]);
130
            }
131
        }
132
133
        if (!$sort) {
134
            $sort = array();
135
        } elseif (!is_array($sort)) {
136
            $sort = array($sort => 'asc');
137
        }
138
139
        return $this->getGalleryManager()->getPager($criteria, $page, $limit, $sort);
140
    }
141
142
    /**
143
     * Retrieves a specific gallery.
144
     *
145
     * @ApiDoc(
146
     *  requirements={
147
     *      {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="gallery id"}
148
     *  },
149
     *  output={"class"="sonata_media_api_form_gallery", "groups"="sonata_api_read"},
150
     *  statusCodes={
151
     *      200="Returned when successful",
152
     *      404="Returned when gallery is not found"
153
     *  }
154
     * )
155
     *
156
     * @View(serializerGroups="sonata_api_read", serializerEnableMaxDepthChecks=true)
157
     *
158
     * @param $id
159
     *
160
     * @return GalleryInterface
161
     */
162
    public function getGalleryAction($id)
163
    {
164
        return $this->getGallery($id);
165
    }
166
167
    /**
168
     * Retrieves the medias of specified gallery.
169
     *
170
     * @ApiDoc(
171
     *  requirements={
172
     *      {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="gallery id"}
173
     *  },
174
     *  output={"class"="Sonata\MediaBundle\Model\Media", "groups"="sonata_api_read"},
175
     *  statusCodes={
176
     *      200="Returned when successful",
177
     *      404="Returned when gallery is not found"
178
     *  }
179
     * )
180
     *
181
     * @View(serializerGroups="sonata_api_read", serializerEnableMaxDepthChecks=true)
182
     *
183
     * @param $id
184
     *
185
     * @return MediaInterface[]
186
     */
187
    public function getGalleryMediasAction($id)
188
    {
189
        $galleryItems = $this->getGallery($id)->getGalleryItems();
190
191
        $media = array();
192
        foreach ($galleryItems as $galleryItem) {
193
            $media[] = $galleryItem->getMedia();
194
        }
195
196
        return $media;
197
    }
198
199
    /**
200
     * Retrieves the gallery items of specified gallery.
201
     *
202
     * @ApiDoc(
203
     *  requirements={
204
     *      {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="gallery id"}
205
     *  },
206
     *  output={"class"="Sonata\MediaBundle\Model\GalleryItem", "groups"="sonata_api_read"},
207
     *  statusCodes={
208
     *      200="Returned when successful",
209
     *      404="Returned when gallery is not found"
210
     *  }
211
     * )
212
     *
213
     * @View(serializerGroups="sonata_api_read", serializerEnableMaxDepthChecks=true)
214
     *
215
     * @param $id
216
     *
217
     * @return GalleryItemInterface[]
218
     */
219
    public function getGalleryGalleryItemAction($id)
220
    {
221
        return $this->getGallery($id)->getGalleryItems();
222
    }
223
224
    /**
225
     * Adds a gallery.
226
     *
227
     * @ApiDoc(
228
     *  input={"class"="sonata_media_api_form_gallery", "name"="", "groups"={"sonata_api_write"}},
229
     *  output={"class"="sonata_media_api_form_gallery", "groups"={"sonata_api_read"}},
230
     *  statusCodes={
231
     *      200="Returned when successful",
232
     *      400="Returned when an error has occurred while gallery creation",
233
     *  }
234
     * )
235
     *
236
     * @param Request $request A Symfony request
237
     *
238
     * @return GalleryInterface
239
     *
240
     * @throws NotFoundHttpException
241
     */
242
    public function postGalleryAction(Request $request)
243
    {
244
        return $this->handleWriteGallery($request);
245
    }
246
247
    /**
248
     * Updates a gallery.
249
     *
250
     * @ApiDoc(
251
     *  requirements={
252
     *      {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="gallery identifier"}
253
     *  },
254
     *  input={"class"="sonata_media_api_form_gallery", "name"="", "groups"={"sonata_api_write"}},
255
     *  output={"class"="sonata_media_api_form_gallery", "groups"={"sonata_api_read"}},
256
     *  statusCodes={
257
     *      200="Returned when successful",
258
     *      400="Returned when an error has occurred while gallery creation",
259
     *      404="Returned when unable to find gallery"
260
     *  }
261
     * )
262
     *
263
     * @param int     $id      User id
264
     * @param Request $request A Symfony request
265
     *
266
     * @return GalleryInterface
267
     *
268
     * @throws NotFoundHttpException
269
     */
270
    public function putGalleryAction($id, Request $request)
271
    {
272
        return $this->handleWriteGallery($request, $id);
273
    }
274
275
    /**
276
     * Adds a media to a gallery.
277
     *
278
     * @ApiDoc(
279
     *  requirements={
280
     *      {"name"="galleryId", "dataType"="integer", "requirement"="\d+", "description"="gallery identifier"},
281
     *      {"name"="mediaId", "dataType"="integer", "requirement"="\d+", "description"="media identifier"}
282
     *  },
283
     *  input={"class"="sonata_media_api_form_gallery_item", "name"="", "groups"={"sonata_api_write"}},
284
     *  output={"class"="sonata_media_api_form_gallery", "groups"={"sonata_api_read"}},
285
     *  statusCodes={
286
     *      200="Returned when successful",
287
     *      400="Returned when an error has occurred while gallery/media attachment",
288
     *  }
289
     * )
290
     *
291
     * @param int     $galleryId A gallery identifier
292
     * @param int     $mediaId   A media identifier
293
     * @param Request $request   A Symfony request
294
     *
295
     * @return GalleryInterface
296
     *
297
     * @throws NotFoundHttpException
298
     */
299
    public function postGalleryMediaGalleryItemAction($galleryId, $mediaId, Request $request)
300
    {
301
        $gallery = $this->getGallery($galleryId);
302
        $media = $this->getMedia($mediaId);
303
304
        foreach ($gallery->getGalleryItems() as $galleryItem) {
305
            if ($galleryItem->getMedia()->getId() == $media->getId()) {
306
                return FOSRestView::create(array(
307
                    'error' => sprintf('Gallery "%s" already has media "%s"', $galleryId, $mediaId),
308
                ), 400);
309
            }
310
        }
311
312
        return $this->handleWriteGalleryItem($gallery, $media, null, $request);
313
    }
314
315
    /**
316
     * Updates a media to a gallery.
317
     *
318
     * @ApiDoc(
319
     *  requirements={
320
     *      {"name"="galleryId", "dataType"="integer", "requirement"="\d+", "description"="gallery identifier"},
321
     *      {"name"="mediaId", "dataType"="integer", "requirement"="\d+", "description"="media identifier"}
322
     *  },
323
     *  input={"class"="sonata_media_api_form_gallery_item", "name"="", "groups"={"sonata_api_write"}},
324
     *  output={"class"="sonata_media_api_form_gallery", "groups"={"sonata_api_read"}},
325
     *  statusCodes={
326
     *      200="Returned when successful",
327
     *      404="Returned when an error if media cannot be found in gallery",
328
     *  }
329
     * )
330
     *
331
     * @param int     $galleryId A gallery identifier
332
     * @param int     $mediaId   A media identifier
333
     * @param Request $request   A Symfony request
334
     *
335
     * @return GalleryInterface
336
     *
337
     * @throws NotFoundHttpException
338
     */
339
    public function putGalleryMediaGalleryItemAction($galleryId, $mediaId, Request $request)
340
    {
341
        $gallery = $this->getGallery($galleryId);
342
        $media = $this->getMedia($mediaId);
343
344
        foreach ($gallery->getGalleryItems() as $galleryItem) {
345
            if ($galleryItem->getMedia()->getId() == $media->getId()) {
346
                return $this->handleWriteGalleryItem($gallery, $media, $galleryItem, $request);
347
            }
348
        }
349
350
        throw new NotFoundHttpException(sprintf('Gallery "%s" does not have media "%s"', $galleryId, $mediaId));
351
    }
352
353
    /**
354
     * Deletes a media association to a gallery.
355
     *
356
     * @ApiDoc(
357
     *  requirements={
358
     *      {"name"="galleryId", "dataType"="integer", "requirement"="\d+", "description"="gallery identifier"},
359
     *      {"name"="mediaId", "dataType"="integer", "requirement"="\d+", "description"="media identifier"}
360
     *  },
361
     *  statusCodes={
362
     *      200="Returned when media is successfully deleted from gallery",
363
     *      400="Returned when an error has occurred while media deletion of gallery",
364
     *      404="Returned when unable to find gallery or media"
365
     *  }
366
     * )
367
     *
368
     * @param int $galleryId A gallery identifier
369
     * @param int $mediaId   A media identifier
370
     *
371
     * @return View
372
     *
373
     * @throws NotFoundHttpException
374
     */
375
    public function deleteGalleryMediaGalleryItemAction($galleryId, $mediaId)
376
    {
377
        $gallery = $this->getGallery($galleryId);
378
        $media = $this->getMedia($mediaId);
379
380
        foreach ($gallery->getGalleryItems() as $key => $galleryItem) {
381
            if ($galleryItem->getMedia()->getId() == $media->getId()) {
382
                $gallery->getGalleryItems()->remove($key);
0 ignored issues
show
Bug introduced by
The method remove cannot be called on $gallery->getGalleryItems() (of type array<integer,object<Son...\GalleryItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
383
                $this->getGalleryManager()->save($gallery);
384
385
                return array('deleted' => true);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('deleted' => true); (array<string,boolean>) is incompatible with the return type documented by Sonata\MediaBundle\Contr...yMediaGalleryItemAction of type FOS\RestBundle\Controller\Annotations\View.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
386
            }
387
        }
388
389
        return FOSRestView::create(array(
390
            'error' => sprintf('Gallery "%s" does not have media "%s" associated', $galleryId, $mediaId),
391
        ), 400);
392
    }
393
394
    /**
395
     * Deletes a gallery.
396
     *
397
     * @ApiDoc(
398
     *  requirements={
399
     *      {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="gallery identifier"}
400
     *  },
401
     *  statusCodes={
402
     *      200="Returned when gallery is successfully deleted",
403
     *      400="Returned when an error has occurred while gallery deletion",
404
     *      404="Returned when unable to find gallery"
405
     *  }
406
     * )
407
     *
408
     * @param int $id A Gallery identifier
409
     *
410
     * @return View
411
     *
412
     * @throws NotFoundHttpException
413
     */
414
    public function deleteGalleryAction($id)
415
    {
416
        $gallery = $this->getGallery($id);
417
418
        $this->galleryManager->delete($gallery);
419
420
        return array('deleted' => true);
421
    }
422
423
    /**
424
     * Write a GalleryItem, this method is used by both POST and PUT action methods.
425
     *
426
     * @param GalleryInterface     $gallery
427
     * @param MediaInterface       $media
428
     * @param GalleryItemInterface $galleryItem
429
     * @param Request              $request
430
     *
431
     * @return FormInterface
432
     */
433
    protected function handleWriteGalleryItem(GalleryInterface $gallery, MediaInterface $media, GalleryItemInterface $galleryItem = null, Request $request)
434
    {
435
        $form = $this->formFactory->createNamed(null, 'sonata_media_api_form_gallery_item', $galleryItem, array(
436
            'csrf_protection' => false,
437
        ));
438
439
        $form->handleRequest($request);
440
441
        if ($form->isValid()) {
442
            $galleryItem = $form->getData();
443
            $galleryItem->setMedia($media);
444
445
            $gallery->addGalleryItem($galleryItem);
446
            $this->galleryManager->save($gallery);
447
448
            $view = FOSRestView::create($galleryItem);
449
            $serializationContext = SerializationContext::create();
450
            $serializationContext->setGroups(array('sonata_api_read'));
451
            $serializationContext->enableMaxDepthChecks();
452
            $view->setSerializationContext($serializationContext);
453
454
            return $view;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $view; (FOS\RestBundle\View\View) is incompatible with the return type documented by Sonata\MediaBundle\Contr...:handleWriteGalleryItem of type Symfony\Component\Form\FormInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
455
        }
456
457
        return $form;
458
    }
459
460
    /**
461
     * Retrieves gallery with id $id or throws an exception if it doesn't exist.
462
     *
463
     * @param $id
464
     *
465
     * @return GalleryInterface
466
     *
467
     * @throws NotFoundHttpException
468
     */
469
    protected function getGallery($id)
470
    {
471
        $gallery = $this->getGalleryManager()->findOneBy(array('id' => $id));
472
473
        if (null === $gallery) {
474
            throw new NotFoundHttpException(sprintf('Gallery (%d) not found', $id));
475
        }
476
477
        return $gallery;
478
    }
479
480
    /**
481
     * Retrieves media with id $id or throws an exception if it doesn't exist.
482
     *
483
     * @param $id
484
     *
485
     * @return MediaInterface
486
     *
487
     * @throws NotFoundHttpException
488
     */
489
    protected function getMedia($id)
490
    {
491
        $media = $this->getMediaManager()->findOneBy(array('id' => $id));
492
493
        if (null === $media) {
494
            throw new NotFoundHttpException(sprintf('Media (%d) not found', $id));
495
        }
496
497
        return $media;
498
    }
499
500
    /**
501
     * @return GalleryManagerInterface
502
     */
503
    protected function getGalleryManager()
504
    {
505
        return $this->galleryManager;
506
    }
507
508
    /**
509
     * @return MediaManagerInterface
510
     */
511
    protected function getMediaManager()
512
    {
513
        return $this->mediaManager;
514
    }
515
516
    /**
517
     * Write a Gallery, this method is used by both POST and PUT action methods.
518
     *
519
     * @param Request  $request Symfony request
520
     * @param int|null $id      A Gallery identifier
521
     *
522
     * @return View|FormInterface
523
     */
524
    protected function handleWriteGallery($request, $id = null)
525
    {
526
        $gallery = $id ? $this->getGallery($id) : null;
527
528
        $form = $this->formFactory->createNamed(null, 'sonata_media_api_form_gallery', $gallery, array(
529
            'csrf_protection' => false,
530
        ));
531
532
        $form->handleRequest($request);
533
534
        if ($form->isValid()) {
535
            $gallery = $form->getData();
536
            $this->galleryManager->save($gallery);
537
538
            $view = FOSRestView::create($gallery);
539
            $serializationContext = SerializationContext::create();
540
            $serializationContext->setGroups(array('sonata_api_read'));
541
            $serializationContext->enableMaxDepthChecks();
542
            $view->setSerializationContext($serializationContext);
543
544
            return $view;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $view; (FOS\RestBundle\View\View) is incompatible with the return type documented by Sonata\MediaBundle\Contr...ler::handleWriteGallery of type Symfony\Component\Form\FormInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
545
        }
546
547
        return $form;
548
    }
549
}
550