Completed
Branch EDTR/master (d4741d)
by
unknown
64:56 queued 54:35
created

AdvancedEditorData   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 582
Duplicated Lines 41.41 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 241
loc 582
rs 6.96
c 0
b 0
f 0
wmc 53
lcom 1
cbo 8

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 1
B loadScriptsStyles() 0 41 9
A getEditorData() 0 14 2
B getEventGraphQLData() 0 31 8
A addDefaultEntities() 0 19 6
A getGraphQLDatetimes() 43 43 2
A getGraphQLTickets() 45 45 2
A getGraphQLPrices() 40 40 2
A getGraphQLPriceTypes() 31 31 2
A getGraphQLCurrentUser() 29 29 2
A getGraphQLGeneralSettings() 21 21 2
A makeGraphQLRequest() 0 13 3
C getRelationalData() 32 71 10
A convertToGlobalId() 0 9 2

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 AdvancedEditorData 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 AdvancedEditorData, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace EventEspresso\core\domain\services\admin\events\editor;
4
5
use DomainException;
6
use EE_Admin_Config;
7
use EE_Error;
8
use EE_Event;
9
use EEM_Datetime;
10
use EE_Datetime;
11
use EEM_Price;
12
use EEM_Price_Type;
13
use EEM_Ticket;
14
use EEM_Venue;
15
use EventEspresso\core\domain\services\assets\EspressoEditorAssetManager;
16
use EventEspresso\core\exceptions\InvalidDataTypeException;
17
use EventEspresso\core\exceptions\InvalidInterfaceException;
18
use EventEspresso\core\exceptions\ModelConfigurationException;
19
use EventEspresso\core\exceptions\UnexpectedEntityException;
20
use InvalidArgumentException;
21
use ReflectionException;
22
use WP_Post;
23
use WPGraphQL\Router;
24
use GraphQLRelay\Relay;
25
26
/**
27
 * Class AdvancedEditorData
28
 * Description
29
 *
30
 * @package EventEspresso\core\domain\services\admin\events\editor
31
 * @author  Brent Christensen
32
 * @since   $VID:$
33
 */
34
class AdvancedEditorData
35
{
36
37
    /**
38
     * @var string $namespace The graphql namespace/prefix.
39
     */
40
    protected $namespace = 'Espresso';
41
42
    /**
43
     * @var EE_Event
44
     */
45
    protected $event;
46
47
    /**
48
     * @var EE_Admin_Config
49
     */
50
    protected $admin_config;
51
    /**
52
     * @var EEM_Datetime $datetime_model
53
     */
54
    protected $datetime_model;
55
    /**
56
     * @var EEM_Price $price_model
57
     */
58
    protected $price_model;
59
    /**
60
     * @var EEM_Ticket $ticket_model
61
     */
62
    protected $ticket_model;
63
64
65
    /**
66
     * AdvancedEditorAdminForm constructor.
67
     *
68
     * @param EE_Event        $event
69
     * @param EE_Admin_Config $admin_config
70
     * @param EEM_Datetime    $datetime_model
71
     * @param EEM_Price       $price_model
72
     * @param EEM_Ticket      $ticket_model
73
     */
74
    public function __construct(
75
        EE_Event $event,
76
        EE_Admin_Config $admin_config,
77
        EEM_Datetime $datetime_model,
78
        EEM_Price $price_model,
79
        EEM_Ticket $ticket_model
80
    ) {
81
        $this->event = $event;
82
        $this->admin_config = $admin_config;
83
        $this->datetime_model = $datetime_model;
84
        $this->price_model = $price_model;
85
        $this->ticket_model = $ticket_model;
86
        add_action('admin_enqueue_scripts', [$this, 'loadScriptsStyles']);
87
    }
88
89
90
    /**
91
     * @throws EE_Error
92
     * @throws InvalidArgumentException
93
     * @throws InvalidDataTypeException
94
     * @throws InvalidInterfaceException
95
     * @throws ModelConfigurationException
96
     * @throws ReflectionException
97
     * @throws UnexpectedEntityException
98
     * @throws DomainException
99
     * @since $VID:$
100
     */
101
    public function loadScriptsStyles()
102
    {
103
        if ($this->admin_config->useAdvancedEditor()) {
104
            $eventId = $this->event instanceof EE_Event ? $this->event->ID() : 0;
105
            if (! $eventId) {
106
                global $post;
107
                $eventId = isset($_REQUEST['post']) ? absint($_REQUEST['post']) : 0;
108
                $eventId = $eventId === 0 && $post instanceof WP_Post && $post->post_type === 'espresso_events'
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
109
                    ? $post->ID
110
                    : $eventId;
111
            }
112
            if ($eventId) {
113
                $data = $this->getEditorData($eventId);
114
                $data = wp_json_encode($data);
115
                add_action(
116
                    'admin_footer',
117
                    static function () use ($data) {
118
                        wp_add_inline_script(
119
                            EspressoEditorAssetManager::JS_HANDLE_EDITOR,
120
                            "
121
var eeEditorData={$data};
122
",
123
                            'before'
124
                        );
125
                    }
126
                );
127
                add_action(
128
                    'admin_footer',
129
                    static function () use ($data) {
130
                        wp_add_inline_script(
131
                            EspressoEditorAssetManager::JS_HANDLE_EDITOR_PROTOTYPE,
132
                            "
133
var eeEditorData={$data};
134
",
135
                            'before'
136
                        );
137
                    }
138
                );
139
            }
140
        }
141
    }
142
143
144
    /**
145
     * @param int $eventId
146
     * @return array
147
     * @throws EE_Error
148
     * @throws InvalidDataTypeException
149
     * @throws InvalidInterfaceException
150
     * @throws ModelConfigurationException
151
     * @throws UnexpectedEntityException
152
     * @throws InvalidArgumentException
153
     * @throws ReflectionException
154
     * @throws DomainException
155
     * @since $VID:$
156
     */
157
    protected function getEditorData($eventId)
158
    {
159
        $event = $this->getEventGraphQLData($eventId);
160
        $event['dbId'] = $eventId;
161
162
        $graphqlEndpoint = class_exists('WPGraphQL') ? trailingslashit(site_url()) . Router::$route : '';
163
        $graphqlEndpoint = esc_url($graphqlEndpoint);
164
165
        $currentUser = $this->getGraphQLCurrentUser();
166
167
        $generalSettings = $this->getGraphQLGeneralSettings();
168
169
        return compact('event', 'graphqlEndpoint', 'currentUser', 'generalSettings');
170
    }
171
172
173
    /**
174
     * @param int $eventId
175
     * @return array
176
     * @since $VID:$
177
     */
178
    protected function getEventGraphQLData($eventId)
179
    {
180
        $datetimes = $this->getGraphQLDatetimes($eventId);
181
182
        if (empty($datetimes['nodes']) || (isset($_REQUEST['action']) && $_REQUEST['action'] === 'create_new')) {
183
            $this->addDefaultEntities($eventId);
184
            $datetimes = $this->getGraphQLDatetimes($eventId);
185
        }
186
187
        if (! empty($datetimes['nodes'])) {
188
            $datetimeIn = wp_list_pluck($datetimes['nodes'], 'id');
189
190
            if (! empty($datetimeIn)) {
191
                $tickets = $this->getGraphQLTickets($datetimeIn);
192
            }
193
        }
194
195
        if (! empty($tickets['nodes'])) {
196
            $ticketIn = wp_list_pluck($tickets['nodes'], 'id');
197
198
            if (! empty($ticketIn)) {
199
                $prices = $this->getGraphQLPrices($ticketIn);
200
            }
201
        }
202
203
        $priceTypes = $this->getGraphQLPriceTypes();
204
205
        $relations = $this->getRelationalData($eventId);
206
207
        return compact('datetimes', 'tickets', 'prices', 'priceTypes', 'relations');
208
    }
209
210
    /**
211
     * @param int $eventId
212
     * @throws DomainException
213
     * @throws EE_Error
214
     * @throws InvalidArgumentException
215
     * @throws InvalidDataTypeException
216
     * @throws InvalidInterfaceException
217
     * @throws ModelConfigurationException
218
     * @throws ReflectionException
219
     * @throws UnexpectedEntityException
220
     * @since $VID:$
221
     */
222
    protected function addDefaultEntities($eventId)
223
    {
224
        $default_dates = $this->datetime_model->create_new_blank_datetime();
225
        if (is_array($default_dates) && isset($default_dates[0]) && $default_dates[0] instanceof EE_Datetime) {
226
            $default_date = $default_dates[0];
227
            $default_date->save();
228
            $default_date->_add_relation_to($eventId, 'Event');
229
            $default_tickets = $this->ticket_model->get_all_default_tickets();
230
            $default_prices = $this->price_model->get_all_default_prices();
231
            foreach ($default_tickets as $default_ticket) {
232
                $default_ticket->save();
233
                $default_ticket->_add_relation_to($default_date, 'Datetime');
234
                foreach ($default_prices as $default_price) {
0 ignored issues
show
Bug introduced by
The expression $default_prices of type integer|array<integer,object<EE_Base_Class>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
235
                    $default_price->save();
236
                    $default_price->_add_relation_to($default_ticket, 'Ticket');
237
                }
238
            }
239
        }
240
    }
241
242
243
    /**
244
     * @param int $eventId
245
     * @return array|null
246
     * @since $VID:$
247
     */
248 View Code Duplication
    protected function getGraphQLDatetimes($eventId)
249
    {
250
        $field_key = lcfirst($this->namespace) . 'Datetimes';
251
        $query = <<<QUERY
252
        query GET_DATETIMES(\$where: {$this->namespace}RootQueryDatetimesConnectionWhereArgs) {
253
            {$field_key}(where: \$where) {
254
                nodes {
255
                    id
256
                    dbId
257
                    name
258
                    description
259
                    startDate
260
                    endDate
261
                    capacity
262
                    isActive
263
                    isExpired
264
                    isPrimary
265
                    isSoldOut
266
                    isUpcoming
267
                    length
268
                    order
269
                    reserved
270
                    sold
271
                    __typename
272
                }
273
                __typename
274
            }
275
        }
276
QUERY;
277
            $data = [
278
                'operation_name' => 'GET_DATETIMES',
279
                'variables' => [
280
                    'first' => 50,
281
                    'where' => [
282
                        'eventId' => $eventId,
283
                    ],
284
                ],
285
                'query' => $query,
286
            ];
287
288
            $responseData = $this->makeGraphQLRequest($data);
289
            return !empty($responseData[ $field_key ]) ? $responseData[ $field_key ] : null;
290
    }
291
292
293
    /**
294
     * @param array $datetimeIn
295
     * @return array|null
296
     * @since $VID:$
297
     */
298 View Code Duplication
    protected function getGraphQLTickets(array $datetimeIn)
299
    {
300
        $field_key = lcfirst($this->namespace) . 'Tickets';
301
        $query = <<<QUERY
302
        query GET_TICKETS(\$where: {$this->namespace}RootQueryTicketsConnectionWhereArgs) {
303
            {$field_key}(where: \$where) {
304
                nodes {
305
                    id
306
                    dbId
307
                    description
308
                    endDate
309
                    isDefault
310
                    isFree
311
                    isRequired
312
                    isTaxable
313
                    max
314
                    min
315
                    name
316
                    order
317
                    price
318
                    quantity
319
                    reserved
320
                    reverseCalculate
321
                    sold
322
                    startDate
323
                    uses
324
                    __typename
325
                }
326
                __typename
327
            }
328
        }
329
QUERY;
330
            $data = [
331
                'operation_name' => 'GET_TICKETS',
332
                'variables' => [
333
                    'where' => [
334
                        'datetimeIn' => $datetimeIn,
335
                    ],
336
                ],
337
                'query' => $query,
338
            ];
339
340
            $responseData = $this->makeGraphQLRequest($data);
341
            return !empty($responseData[ $field_key ]) ? $responseData[ $field_key ] : null;
342
    }
343
344
345
    /**
346
     * @param array $ticketIn
347
     * @return array|null
348
     * @since $VID:$
349
     */
350 View Code Duplication
    protected function getGraphQLPrices(array $ticketIn)
351
    {
352
        $field_key = lcfirst($this->namespace) . 'Prices';
353
        $query = <<<QUERY
354
        query GET_PRICES(\$where: {$this->namespace}RootQueryPricesConnectionWhereArgs) {
355
            {$field_key}(where: \$where) {
356
                nodes {
357
                    id
358
                    dbId
359
                    amount
360
                    desc
361
                    isBasePrice
362
                    isDefault
363
                    isDeleted
364
                    isDiscount
365
                    isPercent
366
                    isTax
367
                    name
368
                    order
369
                    overrides
370
                    priceTypeOrder
371
                    __typename
372
                }
373
                __typename
374
            }
375
        }
376
QUERY;
377
            $data = [
378
                'operation_name' => 'GET_PRICES',
379
                'variables' => [
380
                    'where' => [
381
                        'ticketIn' => $ticketIn,
382
                    ],
383
                ],
384
                'query' => $query,
385
            ];
386
387
            $responseData = $this->makeGraphQLRequest($data);
388
            return !empty($responseData[ $field_key ]) ? $responseData[ $field_key ] : null;
389
    }
390
391
392
    /**
393
     * @return array|null
394
     * @since $VID:$
395
     */
396 View Code Duplication
    protected function getGraphQLPriceTypes()
397
    {
398
        $field_key = lcfirst($this->namespace) . 'PriceTypes';
399
        $query = <<<QUERY
400
        query GET_PRICES {
401
            {$field_key} {
402
                nodes {
403
                    id
404
                    dbId
405
                    baseType
406
                    isBasePrice
407
                    isDeleted
408
                    isDiscount
409
                    isPercent
410
                    isTax
411
                    name
412
                    order
413
                    __typename
414
                }
415
                __typename
416
            }
417
        }
418
QUERY;
419
            $data = [
420
                'operation_name' => 'GET_PRICES',
421
                'query' => $query,
422
            ];
423
424
            $responseData = $this->makeGraphQLRequest($data);
425
            return !empty($responseData[ $field_key ]) ? $responseData[ $field_key ] : null;
426
    }
427
428
429
    /**
430
     * @return array|null
431
     * @since $VID:$
432
     */
433 View Code Duplication
    protected function getGraphQLCurrentUser()
434
    {
435
        $field_key = 'viewer';
436
        $query = <<<QUERY
437
        query GET_CURRENT_USER {
438
            {$field_key} {
439
                description
440
                email
441
                firstName
442
                id
443
                name
444
                nicename
445
                nickname
446
                lastName
447
                locale
448
                userId
449
                username
450
                __typename
451
            }
452
        }
453
QUERY;
454
            $data = [
455
                'operation_name' => 'GET_CURRENT_USER',
456
                'query' => $query,
457
            ];
458
459
            $responseData = $this->makeGraphQLRequest($data);
460
            return !empty($responseData[ $field_key ]) ? $responseData[ $field_key ] : null;
461
    }
462
463
464
    /**
465
     * @return array|null
466
     * @since $VID:$
467
     */
468 View Code Duplication
    protected function getGraphQLGeneralSettings()
469
    {
470
        $field_key = 'generalSettings';
471
        $query = <<<QUERY
472
        query GET_GENERAL_SETTINGS {
473
            {$field_key} {
474
                dateFormat
475
                timeFormat
476
                timezone
477
                __typename
478
            }
479
        }
480
QUERY;
481
            $data = [
482
                'operation_name' => 'GET_CURRENT_USER',
483
                'query' => $query,
484
            ];
485
486
            $responseData = $this->makeGraphQLRequest($data);
487
            return !empty($responseData[ $field_key ]) ? $responseData[ $field_key ] : null;
488
    }
489
490
491
    /**
492
     * @param array $data
493
     * @return array
494
     * @since $VID:$
495
     */
496
    protected function makeGraphQLRequest($data)
497
    {
498
        try {
499
            $response = graphql($data);
500
            if (!empty($response['data'])) {
501
                return $response['data'];
502
            }
503
            return null;
504
        } catch (\Exception $e) {
505
            // do something with the errors thrown
506
            return null;
507
        }
508
    }
509
510
511
    /**
512
     * @param mixed       $source  The source that's passed down the GraphQL queries
0 ignored issues
show
Bug introduced by
There is no parameter named $source. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
513
     * @param array       $args    The inputArgs on the field
0 ignored issues
show
Bug introduced by
There is no parameter named $args. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
514
     * @param AppContext  $context The AppContext passed down the GraphQL tree
0 ignored issues
show
Bug introduced by
There is no parameter named $context. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
515
     * @param ResolveInfo $info    The ResolveInfo passed down the GraphQL tree
0 ignored issues
show
Bug introduced by
There is no parameter named $info. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
516
     * @return string
517
     * @throws EE_Error
518
     * @throws Exception
519
     * @throws InvalidArgumentException
520
     * @throws InvalidDataTypeException
521
     * @throws InvalidInterfaceException
522
     * @throws ReflectionException
523
     * @throws UserError
524
     * @throws UnexpectedEntityException
525
     * @since $VID:$
526
     */
527
    public static function getRelationalData($eventId)
528
    {
529
530
        $data = [
531
            'datetimes'  => [],
532
            'tickets'    => [],
533
            'prices'     => [],
534
        ];
535
536
        $eem_datetime   = EEM_Datetime::instance();
537
        $eem_ticket     = EEM_Ticket::instance();
538
        $eem_price      = EEM_Price::instance();
539
        $eem_price_type = EEM_Price_Type::instance();
540
541
        // PROCESS DATETIMES
542
        $related_models = [
543
            'tickets' => $eem_ticket,
544
        ];
545
        // Get the IDs of event datetimes.
546
        $datetimeIds = $eem_datetime->get_col([['EVT_ID' => $eventId]]);
547 View Code Duplication
        foreach ($datetimeIds as $datetimeId) {
548
            $GID = self::convertToGlobalId($eem_datetime->item_name(), $datetimeId);
549
            foreach ($related_models as $key => $model) {
550
                // Get the IDs of related entities for the datetime ID.
551
                $Ids = $model->get_col([['Datetime.DTT_ID' => $datetimeId]]);
552
                if (! empty($Ids)) {
553
                    $data['datetimes'][ $GID ][ $key ] = self::convertToGlobalId($model->item_name(), $Ids);
554
                }
555
            }
556
        }
557
558
        // PROCESS TICKETS
559
        $related_models = [
560
            'datetimes' => $eem_datetime,
561
            'prices'    => $eem_price,
562
        ];
563
        // Get the IDs of all datetime tickets.
564
        $ticketIds = $eem_ticket->get_col([['Datetime.DTT_ID' => ['in', $datetimeIds]]]);
565 View Code Duplication
        foreach ($ticketIds as $ticketId) {
566
            $GID = self::convertToGlobalId($eem_ticket->item_name(), $ticketId);
567
568
            foreach ($related_models as $key => $model) {
569
                // Get the IDs of related entities for the ticket ID.
570
                $Ids = $model->get_col([['Ticket.TKT_ID' => $ticketId]]);
571
                if (! empty($Ids)) {
572
                    $data['tickets'][ $GID ][ $key ] = self::convertToGlobalId($model->item_name(), $Ids);
573
                }
574
            }
575
        }
576
577
        // PROCESS PRICES
578
        $related_models = [
579
            'tickets'    => $eem_ticket,
580
            'priceTypes' => $eem_price_type,
581
        ];
582
        // Get the IDs of all ticket prices.
583
        $priceIds = $eem_price->get_col([['Ticket.TKT_ID' => ['in', $ticketIds]]]);
584 View Code Duplication
        foreach ($priceIds as $priceId) {
585
            $GID = self::convertToGlobalId($eem_price->item_name(), $priceId);
586
587
            foreach ($related_models as $key => $model) {
588
                // Get the IDs of related entities for the price ID.
589
                $Ids = $model->get_col([['Price.PRC_ID' => $priceId]]);
590
                if (! empty($Ids)) {
591
                    $data['prices'][ $GID ][ $key ] = self::convertToGlobalId($model->item_name(), $Ids);
592
                }
593
            }
594
        }
595
596
        return $data;
597
    }
598
599
    /**
600
     * Convert the DB ID into GID
601
     *
602
     * @param string    $type
603
     * @param int|int[] $ID
604
     * @return mixed
605
     */
606
    public static function convertToGlobalId($type, $ID)
607
    {
608
        if (is_array($ID)) {
609
            return array_map(function ($id) use ($type) {
610
                return self::convertToGlobalId($type, $id);
611
            }, $ID);
612
        }
613
        return Relay::toGlobalId($type, $ID);
614
    }
615
}
616