Completed
Push — master ( 5bb9e4...33b9a4 )
by Serhii
09:16 queued 09:08
created

PerformanceEventAdmin   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 417
Duplicated Lines 18.47 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 77
loc 417
ccs 18
cts 18
cp 1
rs 8.3999
c 0
b 0
f 0
wmc 46
lcom 1
cbo 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A configureRoutes() 0 7 1
B configureFormFields() 0 81 3
A configureListFields() 13 13 1
A configureDatagridFilters() 0 7 1
B preUpdate() 0 19 5
A postUpdate() 0 14 4
A getEm() 0 7 2
A enableTicketsForSale() 0 14 1
A inspectPriceCategories() 0 14 3
B getRows() 11 16 5
B getPlaces() 11 19 6
B getSeat() 41 44 6
B inspectSeatMoreThanOnePrice() 0 22 4
A inspectSeatWithoutPrice() 0 22 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PerformanceEventAdmin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PerformanceEventAdmin, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace AppBundle\Admin;
4
5
use AppBundle\Entity\PerformanceEvent;
6
use AppBundle\Entity\PriceCategory;
7
use AppBundle\Entity\RowsForSale;
8
use AppBundle\Entity\Seat;
9
use AppBundle\Entity\Ticket;
10
use AppBundle\Entity\Venue;
11
use AppBundle\Entity\VenueSector;
12
use AppBundle\Services\Ticket\GenerateSetHandler;
13
use Doctrine\ORM\EntityManager;
14
use Sonata\AdminBundle\Admin\Admin;
15
use Sonata\AdminBundle\Datagrid\ListMapper;
16
use Sonata\AdminBundle\Exception\ModelManagerException;
17
use Sonata\AdminBundle\Form\FormMapper;
18
use Sonata\AdminBundle\Route\RouteCollection;
19
use Symfony\Component\Form\Extension\Core\Type\TextType;
20
use Sonata\AdminBundle\Datagrid\DatagridMapper;
21
22
/**
23
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
24 1
 */
25
class PerformanceEventAdmin extends Admin
26
{
27 1
    protected $baseRouteName = 'AppBundle\Entity\PerformanceEvent';
28 1
    protected $baseRoutePattern = 'PerformanceEvent';
29 1
    protected $datagridValues = [
30 1
        '_sort_order' => 'DESC',
31
        '_sort_by'    => 'dateTime',
32 1
    ];
33
    protected $seatPrice = [];
34
35
    /** @var GenerateSetHandler */
36
    protected $ticketGenerateSet;
37
38 1
    /**
39
     * PerformanceEventAdmin constructor.
40 1
     * @param string $code
41
     * @param string $class
42
     * @param string $baseControllerName
43
     * @param GenerateSetHandler $ticketGenerateSet
44
     */
45
    public function __construct($code, $class, $baseControllerName, GenerateSetHandler $ticketGenerateSet)
46
    {
47 2
        parent::__construct($code, $class, $baseControllerName);
48
        $this->ticketGenerateSet = $ticketGenerateSet;
49
    }
50 2
51 2
    /**
52 2
     * @var EntityManager
53 2
     */
54
    protected $em;
55
56
    /**
57 2
     * @param RouteCollection $collection
58
     */
59
    protected function configureRoutes(RouteCollection $collection)
60 2
    {
61
        $collection
62
            ->add('getVenue')
63
            ->add('deletePriceCategories')
64
        ;
65
    }
66
67 2
    /**
68
     * @param FormMapper $formMapper
69
     *
70 2
     * @return void
71 2
     */
72
    protected function configureFormFields(FormMapper $formMapper)
73 2
    {
74
        $queryRowsForSale = $this->getEm()->getRepository(RowsForSale::class)
75
            ->findVenueSectorsByPerformanceEventQueryBuilder($this->getSubject());
76
77
        $formMapper
78
            ->with('PerformanceEvents')
79
            ->add('performance', 'sonata_type_model')
80
            ->add(
81
                'dateTime',
82
                'sonata_type_datetime_picker',
83
                [
84
                    'dp_side_by_side'       => true,
85
                    'dp_use_current'        => false,
86
                    'dp_use_seconds'        => false,
87
                    'format' => "dd/MM/yyyy HH:mm",
88
                ]
89
            )
90
            ->add('venue')
91
            ->end()
92
            ->with('PriceCategory')
93
            ->add('priceCategories', 'sonata_type_collection', [
94
                'by_reference' => true,
95
                'required' => false,
96
                'cascade_validation' => true,
97
                'type_options'       => [
98
                    'delete' => true,
99
                ],
100
                'label' => false,
101
            ], [
102
                'inline'  => 'table',
103
                'edit' => 'inline',
104
                'sortable' => 'position',
105
                'link_parameters'       => [
106
                    'performanceEvent_id' => $this->getSubject()->getId(),
107
                ],
108
            ])
109
            ->end()
110
            ->with('EnableSale')
111
            ->add('seriesDate', 'sonata_type_datetime_picker', [
112
                'dp_side_by_side'       => true,
113
                'dp_use_current'        => true,
114
                'dp_use_seconds'        => false,
115
                'format' => "dd/MM/yyyy HH:mm",
116
                'required' => true,
117
            ])
118
            ->add('rowsForSale', 'sonata_type_model', [
119
                'class' => RowsForSale::class,
120
                'required' => false,
121
                'multiple' => true,
122
                'query' => $queryRowsForSale,
123
            ])
124
        ;
125
        if ($this->getSubject()->isEnableSale() !== true) {
126
            $formMapper
127
                ->add('seriesNumber', null, [
128
                    'required' => true,
129
                ])
130
                ->add('sale', 'checkbox', [
131
                    'required' => false,
132
                    'label' => 'Enable Sale',
133
                ])
134
                ->end()
135
            ;
136
        }
137
        if ($this->getSubject()->isEnableSale()) {
138
            $formMapper
139
                ->add('seriesNumber', null, [
140
                    'required' => true,
141
                    'attr' => ['class' => 'hidden'],
142
                    'label' => false,
143
                ])
144
                ->add('enableSale', TextType::class, [
145
                    'required' => false,
146
                    'attr' => ['class' => 'hidden'],
147
                    'label' => false,
148
                ])
149
                ->end()
150
            ;
151
        }
152
    }
153
154
    /**
155
     * @param ListMapper $listMapper
156
     *
157
     * @return void
158
     */
159 View Code Duplication
    protected function configureListFields(ListMapper $listMapper)
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...
160
    {
161
        $listMapper
162
            ->add('performance')
163
            ->add('dateTime')
164
            ->add('venue')
165
            ->add('_action', 'actions', [
166
                'actions' => [
167
                    'edit' => [],
168
                    'delete' => [],
169
                ],
170
            ]);
171
    }
172
173
    /**
174
     * @param DatagridMapper $datagridMapper
175
     *
176
     * @return void
177
     */
178
    protected function configureDatagridFilters(DatagridMapper $datagridMapper)
179
    {
180
        $datagridMapper
181
            ->add('performance')
182
            ->add('venue')
183
        ;
184
    }
185
186
    public function preUpdate($object)
187
    {
188
        $this->seatPrice = [];
189
        if (!self::inspectPriceCategories($object)) {
190
            return null;
191
        }
192
        if ($object->isEnableSale() === null) {
193
            $object->setEnableSale(false);
194
            $this->getEm()->persist($object);
195
        }
196
        if (($object->isEnableSale() === false) && ($object->isSale() === true)) {
197
            /** @var Ticket[] $tickets */
198
            $tickets = $this->ticketGenerateSet->handle($object);
199
            $this->getEm()->getRepository(Ticket::class)->batchSave($tickets);
200
            $object->setEnableSale(true);
201
            $this->getEm()->persist($object);
202
        }
203
        return true;
204
    }
205
206
    public function postUpdate($object)
207
    {
208
        $this->seatPrice = [];
209
        if (!self::inspectPriceCategories($object)) {
210
            return null;
211
        }
212
        if (!self::inspectSeatWithoutPrice($object->getVenue())) {
213
            return null;
214
        }
215
        if ($object->isEnableSale()) {
216
            self::enableTicketsForSale($object);
217
        }
218
        return true;
219
    }
220
221
    public function getEm()
222
    {
223
        if (!$this->em) {
224
            $this->em = $this->getConfigurationPool()->getContainer()->get('Doctrine')->getManager();
225
        }
226
        return $this->em;
227
    }
228
229
    /**
230
     * Change Status in Ticket
231
     * form STATUS_OFFLINE to STATUS_FREE if Ticket is in RowsForSale
232
     * and vice versa
233
     *
234
     * @param PerformanceEvent $performanceEvent
235
     * @return int
236
     */
237
    public function enableTicketsForSale(PerformanceEvent $performanceEvent)
238
    {
239
        $count = $this->getEm()->getRepository(Ticket::class)->enableTicketsForSale($performanceEvent);
240
            $this
241
                ->getConfigurationPool()
242
                ->getContainer()
243
                ->get('session')
244
                ->getFlashBag()
245
                ->add(
246
                    'success',
247
                    "До продажу вiдкрито $count квиткiв!"
248
                );
249
        return $count;
250
    }
251
252
    /**
253
     * Inspect PriceCategory. Search errors
254
     *
255
     * @param PerformanceEvent $performanceEvent
256
     * @return bool
257
     */
258
    public function inspectPriceCategories(PerformanceEvent $performanceEvent)
259
    {
260
        $categories = $this->getEm()->getRepository('AppBundle:PriceCategory')
261
            ->findBy(['performanceEvent' => $performanceEvent]);
262
        $venue = $performanceEvent->getVenue();
263
        /** @var PriceCategory $category*/
264
        foreach ($categories as $category) {
265
            self::getRows($venue, $category->getRows(), $category->getVenueSector(), $category->getPlaces());
266
        }
267
        if (!$categories) {
268
            return false;
269
        }
270
        return true;
271
    }
272
273
    /**
274
     * Parse string rows in PriceCategory
275
     *
276
     * @param Venue $venue
277
     * @param $strRows
278
     * @param VenueSector $venueSector
279
     * @param $strPlaces
280
     * @return bool|null
281
     */
282
    public function getRows(Venue $venue, $strRows, VenueSector $venueSector, $strPlaces = null)
283
    {
284
        $dataRows = explode(',', $strRows);
285 View Code Duplication
        foreach ($dataRows as $rows) {
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...
286
            if (substr_count($rows, '-') === 1) {
287
                list($begin, $end) = explode('-', $rows);
288
                for ($row = $begin; $row <= $end; $row++) {
289
                    self::getPlaces($venue, $row, $venueSector, $strPlaces);
290
                }
291
            }
292
            if (substr_count($rows, '-') === 0) {
293
                self::getPlaces($venue, $rows, $venueSector, $strPlaces);
294
            }
295
        }
296
        return true;
297
    }
298
299
    /**
300
     * Parse string places in PriceCategory
301
     *
302
     * @param Venue $venue
303
     * @param $row
304
     * @param VenueSector $venueSector
305
     * @param $strPlaces
306
     */
307
    public function getPlaces(Venue $venue, $row, VenueSector $venueSector, $strPlaces = null)
308
    {
309
        if ($strPlaces === null) {
310
            self::getSeat($venue, $row, $venueSector);
311
            return;
312
        }
313
        $dataPlaces = explode(',', $strPlaces);
314 View Code Duplication
        foreach ($dataPlaces as $places) {
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...
315
            if (substr_count($places, '-') === 1) {
316
                list($begin, $end) = explode('-', $places);
317
                for ($place = $begin; $place <= $end; $place++) {
318
                    self::getSeat($venue, $row, $venueSector, $place);
319
                }
320
            }
321
            if (substr_count($places, '-') === 0) {
322
                self::getSeat($venue, $row, $venueSector, $places);
323
            }
324
        }
325
    }
326
327
    /**
328
     * Research existing Seat with row-place - $row-$place
329
     *
330
     * @param Venue $venue
331
     * @param $row
332
     * @param VenueSector $venueSector
333
     * @param null $place
334
     * @throws ModelManagerException
335
     */
336
    public function getSeat(Venue $venue, $row, VenueSector $venueSector, $place = null)
337
    {
338 View Code Duplication
        if ($place === null) {
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...
339
            $seat = $this->getEm()->getRepository('AppBundle:Seat')->findBy([
340
                'row' => $row,
341
                'venueSector' => $venueSector,
342
            ]);
343
            if (!$seat) {
344
                $this
345
                    ->getConfigurationPool()
346
                    ->getContainer()
347
                    ->get('session')
348
                    ->getFlashBag()
349
                    ->add(
350
                        'error',
351
                        "Помилка. В залi $venue немає $row ряда в секторі $venueSector!"
352
                    );
353
                throw new ModelManagerException('Error row!');
354
            }
355
            foreach ($seat as $placeAllInRow) {
356
                self::inspectSeatMoreThanOnePrice($row, $placeAllInRow->getPlace(), $venueSector);
357
            }
358
        }
359 View Code Duplication
        if ($place !== null) {
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...
360
            $seat = $this->getEm()->getRepository('AppBundle:Seat')->findOneBy([
361
                'row' => $row,
362
                'place' => $place,
363
                'venueSector' => $venueSector,
364
            ]);
365
            if (!$seat) {
366
                $this
367
                    ->getConfigurationPool()
368
                    ->getContainer()
369
                    ->get('session')
370
                    ->getFlashBag()
371
                    ->add(
372
                        'error',
373
                        "Помилка. В залi $venue немає $row - $place в секторі $venueSector!"
374
                    );
375
                throw new ModelManagerException('Error row-place!');
376
            }
377
            self::inspectSeatMoreThanOnePrice($row, $place, $venueSector);
378
        }
379
    }
380
381
    /**
382
     * Search Seat with more than one price
383
     *
384
     * @param $row
385
     * @param $place
386
     * @param VenueSector $venueSector
387
     * @throws ModelManagerException
388
     */
389
    public function inspectSeatMoreThanOnePrice($row, $place, VenueSector $venueSector)
390
    {
391
        $seats = $this->seatPrice;
392
        foreach ($this->seatPrice as $sector => $key) {
393
            if ($sector === $venueSector->getId()) {
394
                if ($key === $row.'-'.$place) {
395
                    $this
396
                        ->getConfigurationPool()
397
                        ->getContainer()
398
                        ->get('session')
399
                        ->getFlashBag()
400
                        ->add(
401
                            'error',
402
                            "Помилка. $row - $place в секторі $venueSector вже має цiну!"
403
                        );
404
                    throw new ModelManagerException('Error Seat with more than one price!');
405
                }
406
            }
407
        }
408
        $seats[$venueSector->getId()][] = $row.'-'.$place;
409
        $this->seatPrice = $seats;
410
    }
411
412
    /**
413
     * Search Seat without price
414
     *
415
     * @param Venue $venue
416
     * @return bool
417
     * @throws ModelManagerException
418
     */
419
    public function inspectSeatWithoutPrice(Venue $venue)
420
    {
421
        foreach ($this->seatPrice as $sector => $key) {
422
            $venueSector = $this->getEm()->getRepository(VenueSector::class)->find($sector);
423
            $seat = $this->getEm()->getRepository(Seat::class)->findBy(['venueSector' => $venueSector]);
424
            if (count($seat) != count($key)) {
425
                $this
426
                    ->getConfigurationPool()
427
                    ->getContainer()
428
                    ->get('session')
429
                    ->getFlashBag()
430
                    ->add(
431
                        'error',
432
                        "Помилка. В залі 
433
                        $venue в секторі 
434
                        $venueSector ціна проставлена не на всі місця!"
435
                    );
436
                throw new ModelManagerException('In the hall not all places have price!');
437
            }
438
        }
439
        return true;
440
    }
441
}
442