Completed
Push — master ( 7bb5f9...d11323 )
by Greg
08:31
created

HubphCommands   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 567
Duplicated Lines 11.11 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 11
dl 63
loc 567
rs 8.96
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A whoami() 0 8 1
A prCheck() 0 31 3
A prCreate() 0 44 4
A getMessage() 0 10 3
A getVids() 0 19 3
A projectWithOrg() 0 8 2
A getProjectWithOrgFromRemote() 0 6 1
A getProjectWithOrfFromUrl() 8 8 1
A getRemote() 0 7 2
A prFind() 0 19 2
A prClose() 12 12 3
A orgRepos() 13 13 1
A repoInfo() 14 14 1
A prShow() 0 18 3
A prStatuses() 16 16 3
A prList() 0 16 1
B addTableRenderFunction() 0 31 7
A keyById() 0 14 1
A api() 0 7 1

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

1
<?php
2
3
namespace Hubph\Cli;
4
5
use Consolidation\AnnotatedCommand\CommandData;
6
use Consolidation\Filter\FilterOutputData;
7
use Consolidation\Filter\LogicalOpFactory;
8
use Consolidation\OutputFormatters\Options\FormatterOptions;
9
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
10
use Consolidation\OutputFormatters\StructuredData\PropertyList;
11
use Psr\Log\LoggerAwareInterface;
12
use Psr\Log\LoggerAwareTrait;
13
use Robo\Common\ConfigAwareTrait;
14
use Robo\Contract\ConfigAwareInterface;
15
use Consolidation\AnnotatedCommand\CommandError;
16
use Hubph\HubphAPI;
17
use Hubph\VersionIdentifiers;
18
use Hubph\PullRequests;
19
20
class HubphCommands extends \Robo\Tasks implements ConfigAwareInterface, LoggerAwareInterface
21
{
22
    use ConfigAwareTrait;
23
    use LoggerAwareTrait;
24
25
    /**
26
     * Report who we have authenticated as
27
     *
28
     * @command whoami
29
     */
30
    public function whoami($options = ['as' => 'default'])
31
    {
32
        $api = $this->api($options['as']);
33
        $authenticated = $api->whoami();
34
        $authenticatedUser = $authenticated['login'];
35
36
        $this->say("Authenticated as $authenticatedUser.");
37
    }
38
39
    /**
40
     * @command pr:close
41
     */
42 View Code Duplication
    public function prClose($projectWithOrg = '', $number = '', $options = ['as' => 'default'])
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...
43
    {
44
        if (empty($number) && preg_match('#^[0-9]*$#', $projectWithOrg)) {
45
            $number = $projectWithOrg;
46
            $projectWithOrg = '';
47
        }
48
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
49
        list($org, $project) = explode('/', $projectWithOrg, 2);
50
51
        $api = $this->api($options['as']);
52
        $api->prClose($org, $project, $number);
0 ignored issues
show
Documentation introduced by
$number is of type string, but the function expects a object<Hubph\PullRequests>.

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...
53
    }
54
55
    /*
56
     * hubph pr:check --vid=php-7.0./31 --vid=php-7.1./20
57
     *
58
     * status 0 and csv with PR numbers to close
59
     *
60
     *  - or -
61
     *
62
     * status 1 if all vid/vvals exist and nothing more needs to be done
63
     */
64
65
    /**
66
     * @command pr:check
67
     */
68
    public function prCheck(
69
        $options = [
70
            'message|m' => '',
71
            'file|F' => '',
72
            'base' => '',
73
            'head' => '',
74
            'as' => 'default',
75
            'format' => 'yaml',
76
            'idempotent' => false
77
        ]
78
    ) {
79
        $projectWithOrg = $this->projectWithOrg();
80
81
        // Get the commit message from --message or --file
82
        $message = $this->getMessage($options);
83
84
        // Determine all of the vid/vval pairs if idempotent
85
        $vids = $this->getVids($options, $message);
86
87
        $api = $this->api($options['as']);
88
        list($status, $result) = $api->prCheck($projectWithOrg, $vids);
89
90
        if ($status) {
91
            return new CommandError($result, $status);
0 ignored issues
show
Documentation introduced by
$status is of type boolean, but the function expects a integer.

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...
92
        }
93
        if (is_string($result)) {
94
            $this->logger->notice("No open pull requests that need to be closed.");
95
            return;
96
        }
97
        return implode(',', (array)$result);
98
    }
99
100
    /**
101
     * @command pr:create
102
     * @aliases pull-request
103
     */
104
    public function prCreate(
105
        $options = [
106
            'message|m' => '',
107
            'body' => '',
108
            'file|F' => '',
109
            'base' => 'master',
110
            'head' => '',
111
            'as' => 'default',
112
            'format' => 'yaml',
113
            'idempotent' => false
114
        ]
115
    ) {
116
        $projectWithOrg = $this->projectWithOrg();
117
118
        // Get the commit message from --message or --file
119
        $message = $this->getMessage($options);
120
        list($org, $project) = explode('/', $projectWithOrg, 2);
121
122
        // Determine all of the vid/vval pairs if idempotent
123
        $vids = $this->getVids($options, $message);
124
125
        $api = $this->api($options['as']);
126
        list($status, $result) = $api->prCheck($projectWithOrg, $vids);
127
128
        if ($status) {
129
            return new CommandError($result, $status);
0 ignored issues
show
Documentation introduced by
$status is of type boolean, but the function expects a integer.

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...
130
        }
131
132
        // TODO: We could look up 'head' if it is not provided.
133
        if (empty($options['head'])) {
134
            throw new \Exceptions('Must provide --head');
135
        }
136
137
        // Create the requested PR
138
        $api->prCreate($org, $project, $message, $options['body'], $options['base'], $options['head']);
139
140
        // If $result is an array, it will contain
141
        // all of the pull request numbers to close.
142
        // TODO: We should make a wrapper object for $result
143
        if (is_array($result)) {
144
            list($org, $project) = explode('/', $projectWithOrg, 2);
145
            $api->prClose($org, $project, $result);
0 ignored issues
show
Documentation introduced by
$result is of type array, but the function expects a object<Hubph\PullRequests>.

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...
146
        }
147
    }
148
149
    protected function getMessage($options)
150
    {
151
        if (!empty($options['message'])) {
152
            return $options['message'];
153
        }
154
        if (!empty($options['file'])) {
155
            return file_get_contents($options['file']);
156
        }
157
        return '';
158
    }
159
160
    protected function getVids($options, $message)
161
    {
162
        $vids = new VersionIdentifiers();
163
164
        //if (empty($options['idempotent'])) {
165
        //    return $vids;
166
        //}
167
168
        // Allow the caller to define more specific vid / vval patterns
169
        if (!empty($options['vid'])) {
170
            $vids->setVidPattern($options['vid']);
171
        }
172
        if (!empty($options['vval'])) {
173
            $vids->setVvalPattern($options['vval']);
174
        }
175
176
        $vids->addVidsFromMessage($message);
177
        return $vids;
178
    }
179
180
    protected function projectWithOrg($projectWithOrg = '')
181
    {
182
        if (!empty($projectWithOrg)) {
183
            return $projectWithOrg;
184
        }
185
186
        return $this->getProjectWithOrgFromRemote();
187
    }
188
189
    protected function getProjectWithOrgFromRemote($remote = 'origin', $cwd = '')
190
    {
191
        $remote = $this->getRemote($remote, $cwd);
192
193
        return $this->getProjectWithOrfFromUrl($remote);
194
    }
195
196 View Code Duplication
    protected function getProjectWithOrfFromUrl($remote)
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...
197
    {
198
        $remote = preg_replace('#^git@[^:]*:#', '', $remote);
199
        $remote = preg_replace('#^[^:]*://[^/]/#', '', $remote);
200
        $remote = preg_replace('#\.git$#', '', $remote);
201
202
        return $remote;
203
    }
204
205
    protected function getRemote($remote = 'origin', $cwd = '')
206
    {
207
        if (!empty($cwd)) {
208
            $cwd = "-C $cwd";
209
        }
210
        return exec("git {$cwd} config --get remote.{$remote}.url");
211
    }
212
213
    /**
214
     * @command pr:find
215
     * @param $projectWithOrg The project to work on, e.g. org/project
216
     * @option $q Query term
217
     * @filter-output
218
     * @field-labels
219
     *   url: Url
220
     *   id: ID
221
     *   node_id: Node ID
222
     *   html_url: HTML Url
223
     *   diff_url: Diff Url
224
     *   patch_url: Patch Url
225
     *   issue_url: Issue Url
226
     *   number: Number
227
     *   state: State
228
     *   locked: Locked
229
     *   title: Title
230
     *   user: User
231
     *   body: Boday
232
     *   created_at: Created
233
     *   updated_at: Updated
234
     *   closed_at: Closed
235
     *   merged_at: Merged
236
     *   merge_commit_sha: Merge Commit
237
     *   assignee: Assignee
238
     *   assignees: Assignees
239
     *   requested_reviewers: Requested Reviewers
240
     *   requested_teams: Requested Teams
241
     *   labels: Labels
242
     *   milestone: Milestone
243
     *   commits_url: Commit Url
244
     *   review_comments_url: Review Comments Url
245
     *   review_comment_url: Review Comment Url
246
     *   comments_url: Comments Url
247
     *   statuses_url: Statuses Url
248
     *   head: Head
249
     *   base: Base
250
     *   _links: Links
251
     * @default-fields number,user,title
252
     * @default-string-field number
253
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
254
    */
255
    public function prFind($projectWithOrg = '', $options = ['as' => 'default', 'format' => 'yaml', 'q' => ''])
256
    {
257
        $api = $this->api($options['as']);
258
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
259
        $q = $options['q'];
260
261
        if (!empty($q)) {
262
            $q = $q . ' ';
263
        }
264
        $q = $q . 'repo:' . $projectWithOrg;
265
        $searchResults = $api->gitHubAPI()->api('search')->issues($q);
266
        $pullRequests = $searchResults['items'];
267
268
        $pullRequests = $this->keyById($pullRequests, 'number');
269
        $result = new RowsOfFields($pullRequests);
270
        $this->addTableRenderFunction($result);
271
272
        return $result;
273
    }
274
275
    /**
276
     * @command org:repos
277
     * @param $org The org to list
278
     * @filter-output
279
     * @field-labels
280
     *   url: Url
281
     *   id: ID
282
     *   owner: Owner
283
     *   name: Shortname
284
     *   full_name: Name
285
     *   private: Private
286
     *   fork: Fork
287
     *   created_at: Created
288
     *   updated_at: Updated
289
     *   pushed_at: Pushed
290
     *   git_url: Git URL
291
     *   ssh_url: SSH URL
292
     *   svn_url: SVN URL
293
     *   homepage: Homepage
294
     *   size: Size
295
     *   stargazers_count: Stargazers
296
     *   watchers_count: Watchers
297
     *   language: Language
298
     *   has_issues: Has Issues
299
     *   has_projects: Has Projects
300
     *   has_downloads: Has Downloads
301
     *   has_wiki: Has Wiki
302
     *   has_pages: Has Pages
303
     *   forks_count: Forks
304
     *   archived: Archived
305
     *   disabled: Disabled
306
     *   open_issues_count: Open Issues
307
     *   default_branch: Default Branch
308
     *   license: License
309
     *   permissions: Permissions
310
     * @default-fields full_name,language,default_branch
311
     * @default-string-field full_name
312
     *
313
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
314
     */
315 View Code Duplication
    public function orgRepos($org, $options = ['as' => 'default', 'format' => 'table'])
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...
316
    {
317
        $api = $this->api($options['as']);
318
        $pager = $api->resultPager();
319
320
        $repoApi = $api->gitHubAPI()->api('organization');
321
        $repos = $pager->fetchAll($repoApi, 'repositories', [$org]);
322
323
        $data = new \Consolidation\OutputFormatters\StructuredData\RowsOfFields($repos);
324
        $this->addTableRenderFunction($data);
325
326
        return $data;
327
    }
328
329
    /**
330
     * @command repo:info
331
     * @param $projectWithOrg The project to work on, e.g. org/project
332
     * @field-labels
333
     *   url: Url
334
     *   id: ID
335
     *   owner: Owner
336
     *   name: Shortname
337
     *   full_name: Name
338
     *   private: Private
339
     *   fork: Fork
340
     *   created_at: Created
341
     *   updated_at: Updated
342
     *   pushed_at: Pushed
343
     *   git_url: Git URL
344
     *   ssh_url: SSH URL
345
     *   svn_url: SVN URL
346
     *   homepage: Homepage
347
     *   size: Size
348
     *   stargazers_count: Stargazers
349
     *   watchers_count: Watchers
350
     *   language: Language
351
     *   has_issues: Has Issues
352
     *   has_projects: Has Projects
353
     *   has_downloads: Has Downloads
354
     *   has_wiki: Has Wiki
355
     *   has_pages: Has Pages
356
     *   forks_count: Forks
357
     *   archived: Archived
358
     *   disabled: Disabled
359
     *   open_issues_count: Open Issues
360
     *   default_branch: Default Branch
361
     *   license: License
362
     *   permissions: Permissions
363
     *
364
     * @return Consolidation\OutputFormatters\StructuredData\PropertyList
365
     */
366 View Code Duplication
    public function repoInfo($projectWithOrg, $options = ['as' => 'default', 'format' => 'table'])
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...
367
    {
368
        $api = $this->api($options['as']);
369
370
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
371
        list($org, $project) = explode('/', $projectWithOrg, 2);
372
373
        $info = $api->gitHubAPI()->api('repo')->show($org, $project);
374
375
        $data = new \Consolidation\OutputFormatters\StructuredData\PropertyList($info);
376
        $this->addTableRenderFunction($data);
377
378
        return $data;
379
    }
380
381
    /**
382
     * @command pr:show
383
     * @field-labels
384
     *   url: Url
385
     *   id: ID
386
     *   node_id: Node ID
387
     *   html_url: HTML Url
388
     *   diff_url: Diff Url
389
     *   patch_url: Patch Url
390
     *   issue_url: Issue Url
391
     *   number: Number
392
     *   state: State
393
     *   locked: Locked
394
     *   title: Title
395
     *   user: User
396
     *   body: Boday
397
     *   created_at: Created
398
     *   updated_at: Updated
399
     *   closed_at: Closed
400
     *   mergeable: Mergeable
401
     *   mergeable_state: Mergable State
402
     *   merged_at: Merged
403
     *   merge_commit_sha: Merge Commit
404
     *   assignee: Assignee
405
     *   assignees: Assignees
406
     *   requested_reviewers: Requested Reviewers
407
     *   requested_teams: Requested Teams
408
     *   labels: Labels
409
     *   milestone: Milestone
410
     *   commits_url: Commit Url
411
     *   review_comments_url: Review Comments Url
412
     *   review_comment_url: Review Comment Url
413
     *   comments_url: Comments Url
414
     *   statuses_url: Statuses Url
415
     *   head: Head
416
     *   base: Base
417
     *   _links: Links
418
     * @return Consolidation\OutputFormatters\StructuredData\PropertyList
419
     */
420
    public function prShow($projectWithOrg = '', $number = '', $options = ['as' => 'default', 'format' => 'table'])
421
    {
422
        if (empty($number) && preg_match('#^[0-9]*$#', $projectWithOrg)) {
423
            $number = $projectWithOrg;
424
            $projectWithOrg = '';
425
        }
426
        $api = $this->api($options['as']);
427
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
428
429
        list($org, $project) = explode('/', $projectWithOrg, 2);
430
431
        $pullRequest = $api->gitHubAPI()->api('pull_request')->show($org, $project, $number);
432
433
        $result = new PropertyList($pullRequest);
434
        $this->addTableRenderFunction($result);
435
436
        return $result;
437
    }
438
439
    /**
440
     * @command pr:statuses
441
     * @field-labels
442
     *   url: Url
443
     *   id: ID
444
     *   state: State
445
     *   description: Description
446
     *   node_id: Node ID
447
     *   context: Context
448
     *   avatar_url: Avatar URL
449
     *   target_url: Target URL
450
     *   creator: Creator
451
     *   created_at: Created
452
     *   updated_at: Updated
453
     * @default-fields id,creator,state,description
454
     * @default-string-field description
455
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
456
     */
457 View Code Duplication
    public function prStatuses($projectWithOrg = '', $number = '', $options = ['as' => 'default', 'format' => 'yaml'])
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...
458
    {
459
        if (empty($number) && preg_match('#^[0-9]*$#', $projectWithOrg)) {
460
            $number = $projectWithOrg;
461
            $projectWithOrg = '';
462
        }
463
        $api = $this->api($options['as']);
464
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
465
466
        $pullRequestStatus = $api->prStatuses($projectWithOrg, $number);
467
468
        $result = new RowsOfFields($pullRequestStatus);
469
        $this->addTableRenderFunction($result);
470
471
        return $result;
472
    }
473
474
    /**
475
     * @command pr:list
476
     * @param $projectWithOrg The project to work on, e.g. org/project
477
     * @filter-output
478
     * @field-labels
479
     *   url: Url
480
     *   id: ID
481
     *   node_id: Node ID
482
     *   html_url: HTML Url
483
     *   diff_url: Diff Url
484
     *   patch_url: Patch Url
485
     *   issue_url: Issue Url
486
     *   number: Number
487
     *   state: State
488
     *   locked: Locked
489
     *   title: Title
490
     *   user: User
491
     *   body: Boday
492
     *   created_at: Created
493
     *   updated_at: Updated
494
     *   closed_at: Closed
495
     *   merged_at: Merged
496
     *   merge_commit_sha: Merge Commit
497
     *   assignee: Assignee
498
     *   assignees: Assignees
499
     *   requested_reviewers: Requested Reviewers
500
     *   requested_teams: Requested Teams
501
     *   labels: Labels
502
     *   milestone: Milestone
503
     *   commits_url: Commit Url
504
     *   review_comments_url: Review Comments Url
505
     *   review_comment_url: Review Comment Url
506
     *   comments_url: Comments Url
507
     *   statuses_url: Statuses Url
508
     *   head: Head
509
     *   base: Base
510
     *   _links: Links
511
     * @default-fields number,user,title
512
     * @default-string-field number
513
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
514
     */
515
    public function prList($projectWithOrg = '', $options = ['state' => 'open', 'as' => 'default', 'format' => 'table'])
516
    {
517
        $api = $this->api($options['as']);
518
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
519
520
        list($org, $project) = explode('/', $projectWithOrg, 2);
521
522
        $pullRequests = $api->gitHubAPI()->api('pull_request')->all($org, $project, ['state' => $options['state']]);
523
524
        $pullRequests = $this->keyById($pullRequests, 'number');
525
526
        $result = new RowsOfFields($pullRequests);
527
        $this->addTableRenderFunction($result);
528
529
        return $result;
530
    }
531
532
    protected function addTableRenderFunction($data)
533
    {
534
        $data->addRendererFunction(
535
            function ($key, $cellData, FormatterOptions $options, $rowData) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $rowData is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
536
                if (empty($cellData)) {
537
                    return '';
538
                }
539
                if (is_array($cellData)) {
540
                    if ($key == 'permissions') {
541
                        return implode(',', array_filter(array_keys($cellData)));
542
                    }
543
                    foreach (['login', 'label', 'name'] as $k) {
544
                        if (isset($cellData[$k])) {
545
                            return $cellData[$k];
546
                        }
547
                    }
548
                    // TODO: simplify
549
                    //   assignees
550
                    //   requested_reviewers
551
                    //   requested_teams
552
                    //   labels
553
                    //   _links
554
                    return json_encode($cellData, true);
555
                }
556
                if (!is_string($cellData)) {
557
                    return var_export($cellData, true);
558
                }
559
                return $cellData;
560
            }
561
        );
562
    }
563
564
    protected function keyById($data, $field)
565
    {
566
        return
567
            array_column(
568
                array_map(
569
                    function ($k) use ($data, $field) {
570
                        return [$data[$k][$field], $data[$k]];
571
                    },
572
                    array_keys($data)
573
                ),
574
                1,
575
                0
576
            );
577
    }
578
579
    protected function api($as = 'default')
580
    {
581
        $api = new HubphAPI($this->getConfig());
582
        $api->setAs($as);
583
584
        return $api;
585
    }
586
}
587