Test Setup Failed
Pull Request — master (#46)
by Steve
01:58
created

FieldsBuilder::fieldExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace StoutLogic\AcfBuilder;
4
5
/**
6
 * Builds configurations for ACF Field Groups
7
 */
8
class FieldsBuilder extends ParentDelegationBuilder implements NamedBuilder
9
{
10
    /**
11
     * Field Group Configuration
12
     * @var array
13
     */
14
    protected $config = [];
15
16
    /**
17
     * Manages the Field Configurations
18
     * @var FieldManager
19
     */
20
    protected $fieldManager;
21
22
    /**
23
     * Location configuration for Field Group
24
     * @var LocationBuilder
25
     */
26
    protected $location;
27
28
    /**
29
     * Field Group Name
30
     * @var string
31
     */
32
    protected $name;
33
34
    /**
35
     * @param string $name Field Group name
36
     * @param array $groupConfig Field Group configuration
37
     */
38
    public function __construct($name, array $groupConfig = [])
39
    {
40
        $this->fieldManager = new FieldManager();
41
        $this->name = $name;
42
        $this->setGroupConfig('key', $name);
43
        $this->setGroupConfig('title', $this->generateLabel($name));
44
45
        $this->config = array_merge($this->config, $groupConfig);
46
    }
47
48
    /**
49
     * Set a value for a particular key in the group config
50
     * @param string $key
51
     * @param mixed $value
52
     * @return $this
53
     */
54
    public function setGroupConfig($key, $value)
55
    {
56
        $this->config[$key] = $value;
57
58
        return $this;
59
    }
60
61
    /**
62
     * Get a value for a particular key in the group config.
63
     * Returns null if the key isn't defined in the config.
64
     * @param string $key
65
     * @return mixed|null
66
     */
67
    public function getGroupConfig($key)
68
    {
69
        if (array_key_exists($key, $this->config)) {
70
            return $this->config[$key];
71
        }
72
73
        return null;
74
    }
75
76
    /**
77
     * @return string
78
     */
79
    public function getName()
80
    {
81
        return $this->name;
82
    }
83
84
    /**
85
     * Namespace a group key
86
     * Append the namespace 'group' before the set key.
87
     *
88
     * @param  string $key Field Key
89
     * @return string      Field Key
90
     */
91
    private function namespaceGroupKey($key)
92
    {
93
        if (strpos($key, 'group_') !== 0) {
94
            $key = 'group_'.$key;
95
        }
96
        return $key;
97
    }
98
99
    /**
100
     * Build the final config array. Build any other builders that may exist
101
     * in the config.
102
     * @return array Final field config
103
     */
104
    public function build()
105
    {
106
        return array_merge($this->config, [
107
            'fields' => $this->buildFields(),
108
            'location' => $this->buildLocation(),
109
            'key' => $this->namespaceGroupKey($this->config['key']),
110
        ]);
111
    }
112
113
    /**
114
     * Return a fields config array
115
     * @return array
116
     */
117
    private function buildFields()
118
    {
119
        $fields = array_map(function($field) {
120
            return ($field instanceof Builder) ? $field->build() : $field;
121
        }, $this->getFields());
122
123
        return $this->transformFields($fields);
124
    }
125
126
    /**
127
     * Apply field transforms
128
     * @param  array $fields
129
     * @return array Transformed fields config
130
     */
131
    private function transformFields($fields)
132
    {
133
        $conditionalTransform = new Transform\ConditionalLogic($this);
134
        $namespaceFieldKeyTransform = new Transform\NamespaceFieldKey($this);
135
136
        return
137
            $namespaceFieldKeyTransform->transform(
138
                $conditionalTransform->transform($fields)
139
            );
140
    }
141
142
    /**
143
     * Return a locations config array
144
     * @return array|LocationBuilder
145
     */
146
    private function buildLocation()
147
    {
148
        $location = $this->getLocation();
149
        return ($location instanceof Builder) ? $location->build() : $location;
150
    }
151
152
    /**
153
     * Add multiple fields either via an array or from another builder
154
     * @param FieldsBuilder|array $fields
155
     * @return $this
156
     */
157
    public function addFields($fields)
158
    {
159
        if ($fields instanceof FieldsBuilder) {
160
            $builder = clone $fields;
161
            $fields = $builder->getFields();
162
        }
163
164
        foreach ($fields as $field) {
165
            $this->getFieldManager()->pushField($field);
166
        }
167
168
        return $this;
169
    }
170
171
    /**
172
     * Add a field of a specific type
173
     * @param string $name
174
     * @param string $type
175
     * @param array $args field configuration
176
     * @throws FieldNameCollisionException if name already exists.
177
     * @return FieldBuilder
178
     */
179
    public function addField($name, $type, array $args = [])
180
    {
181
        return $this->initializeField(new FieldBuilder($name, $type, $args));
182
    }
183
184
    /**
185
     * Add a field of a choice type, allows choices to be added.
186
     * @param string $name
187
     * @param string $type 'select', 'radio', 'checkbox'
188
     * @param array $args field configuration
189
     * @throws FieldNameCollisionException if name already exists.
190
     * @return FieldBuilder
191
     */
192
    public function addChoiceField($name, $type, array $args = [])
193
    {
194
        return $this->initializeField(new ChoiceFieldBuilder($name, $type, $args));
195
    }
196
197
    /**
198
     * Initialize the FieldBuilder, add to FieldManager
199
     * @param  FieldBuilder $field
200
     * @return FieldBuilder
201
     */
202
    protected function initializeField($field)
203
    {
204
        $field->setParentContext($this);
205
        $this->getFieldManager()->pushField($field);
206
        return $field;
207
    }
208
209
    /**
210
     * @param string $name
211
     * @param array $args field configuration
212
     * @throws FieldNameCollisionException if name already exists.
213
     * @return FieldBuilder
214
     */
215
    public function addText($name, array $args = [])
216
    {
217
        return $this->addField($name, 'text', $args);
218
    }
219
220
    /**
221
     * @param string $name
222
     * @param array $args field configuration
223
     * @throws FieldNameCollisionException if name already exists.
224
     * @return FieldBuilder
225
     */
226
    public function addTextarea($name, array $args = [])
227
    {
228
        return $this->addField($name, 'textarea', $args);
229
    }
230
231
    /**
232
     * @param string $name
233
     * @param array $args field configuration
234
     * @throws FieldNameCollisionException if name already exists.
235
     * @return FieldBuilder
236
     */
237
    public function addNumber($name, array $args = [])
238
    {
239
        return $this->addField($name, 'number', $args);
240
    }
241
242
    /**
243
     * @param string $name
244
     * @param array $args field configuration
245
     * @throws FieldNameCollisionException if name already exists.
246
     * @return FieldBuilder
247
     */
248
    public function addEmail($name, array $args = [])
249
    {
250
        return $this->addField($name, 'email', $args);
251
    }
252
253
    /**
254
     * @param string $name
255
     * @param array $args field configuration
256
     * @throws FieldNameCollisionException if name already exists.
257
     * @return FieldBuilder
258
     */
259
    public function addUrl($name, array $args = [])
260
    {
261
        return $this->addField($name, 'url', $args);
262
    }
263
264
    /**
265
     * @param string $name
266
     * @param array $args field configuration
267
     * @throws FieldNameCollisionException if name already exists.
268
     * @return FieldBuilder
269
     */
270
    public function addPassword($name, array $args = [])
271
    {
272
        return $this->addField($name, 'password', $args);
273
    }
274
275
    /**
276
     * @param string $name
277
     * @param array $args field configuration
278
     * @throws FieldNameCollisionException if name already exists.
279
     * @return FieldBuilder
280
     */
281
    public function addWysiwyg($name, array $args = [])
282
    {
283
        return $this->addField($name, 'wysiwyg', $args);
284
    }
285
286
    /**
287
     * @param string $name
288
     * @param array $args field configuration
289
     * @throws FieldNameCollisionException if name already exists.
290
     * @return FieldBuilder
291
     */
292
    public function addOembed($name, array $args = [])
293
    {
294
        return $this->addField($name, 'oembed', $args);
295
    }
296
297
    /**
298
     * @param string $name
299
     * @param array $args field configuration
300
     * @throws FieldNameCollisionException if name already exists.
301
     * @return FieldBuilder
302
     */
303
    public function addImage($name, array $args = [])
304
    {
305
        return $this->addField($name, 'image', $args);
306
    }
307
308
    /**
309
     * @param string $name
310
     * @param array $args field configuration
311
     * @throws FieldNameCollisionException if name already exists.
312
     * @return FieldBuilder
313
     */
314
    public function addFile($name, array $args = [])
315
    {
316
        return $this->addField($name, 'file', $args);
317
    }
318
319
    /**
320
     * @param string $name
321
     * @param array $args field configuration
322
     * @throws FieldNameCollisionException if name already exists.
323
     * @return FieldBuilder
324
     */
325
    public function addGallery($name, array $args = [])
326
    {
327
        return $this->addField($name, 'gallery', $args);
328
    }
329
330
    /**
331
     * @param string $name
332
     * @param array $args field configuration
333
     * @throws FieldNameCollisionException if name already exists.
334
     * @return FieldBuilder
335
     */
336
    public function addTrueFalse($name, array $args = [])
337
    {
338
        return $this->addField($name, 'true_false', $args);
339
    }
340
341
    /**
342
     * @param string $name
343
     * @param array $args field configuration
344
     * @throws FieldNameCollisionException if name already exists.
345
     * @return FieldBuilder
346
     */
347
    public function addSelect($name, array $args = [])
348
    {
349
        return $this->addChoiceField($name, 'select', $args);
350
    }
351
352
    /**
353
     * @param string $name
354
     * @param array $args field configuration
355
     * @throws FieldNameCollisionException if name already exists.
356
     * @return FieldBuilder
357
     */
358
    public function addRadio($name, array $args = [])
359
    {
360
        return $this->addChoiceField($name, 'radio', $args);
361
    }
362
363
    /**
364
     * @param string $name
365
     * @param array $args field configuration
366
     * @throws FieldNameCollisionException if name already exists.
367
     * @return FieldBuilder
368
     */
369
    public function addCheckbox($name, array $args = [])
370
    {
371
        return $this->addChoiceField($name, 'checkbox', $args);
372
    }
373
374
    /**
375
     * @param string $name
376
     * @param array $args field configuration
377
     * @throws FieldNameCollisionException if name already exists.
378
     * @return FieldBuilder
379
     */
380
    public function addPostObject($name, array $args = [])
381
    {
382
        return $this->addField($name, 'post_object', $args);
383
    }
384
385
    /**
386
     * @param string $name
387
     * @param array $args field configuration
388
     * @throws FieldNameCollisionException if name already exists.
389
     * @return FieldBuilder
390
     */
391
    public function addPageLink($name, array $args = [])
392
    {
393
        return $this->addField($name, 'page_link', $args);
394
    }
395
396
    /**
397
     * @param string $name
398
     * @param array $args field configuration
399
     * @throws FieldNameCollisionException if name already exists.
400
     * @return FieldBuilder
401
     */
402
    public function addRelationship($name, array $args = [])
403
    {
404
        return $this->addField($name, 'relationship', $args);
405
    }
406
407
    /**
408
     * @param string $name
409
     * @param array $args field configuration
410
     * @throws FieldNameCollisionException if name already exists.
411
     * @return FieldBuilder
412
     */
413
    public function addTaxonomy($name, array $args = [])
414
    {
415
        return $this->addField($name, 'taxonomy', $args);
416
    }
417
418
    /**
419
     * @param string $name
420
     * @param array $args field configuration
421
     * @throws FieldNameCollisionException if name already exists.
422
     * @return FieldBuilder
423
     */
424
    public function addUser($name, array $args = [])
425
    {
426
        return $this->addField($name, 'user', $args);
427
    }
428
429
    /**
430
     * @param string $name
431
     * @param array $args field configuration
432
     * @throws FieldNameCollisionException if name already exists.
433
     * @return FieldBuilder
434
     */
435
    public function addDatePicker($name, array $args = [])
436
    {
437
        return $this->addField($name, 'date_picker', $args);
438
    }
439
440
    /**
441
     * @param string $name
442
     * @param array $args field configuration
443
     * @throws FieldNameCollisionException if name already exists.
444
     * @return FieldBuilder
445
     */
446
    public function addTimePicker($name, array $args = [])
447
    {
448
        return $this->addField($name, 'time_picker', $args);
449
    }
450
451
    /**
452
     * @param string $name
453
     * @param array $args field configuration
454
     * @throws FieldNameCollisionException if name already exists.
455
     * @return FieldBuilder
456
     */
457
    public function addDateTimePicker($name, array $args = [])
458
    {
459
        return $this->addField($name, 'date_time_picker', $args);
460
    }
461
462
    /**
463
     * @param string $name
464
     * @param array $args field configuration
465
     * @throws FieldNameCollisionException if name already exists.
466
     * @return FieldBuilder
467
     */
468
    public function addColorPicker($name, array $args = [])
469
    {
470
        return $this->addField($name, 'color_picker', $args);
471
    }
472
473
    /**
474
     * @param string $name
475
     * @param array $args field configuration
476
     * @throws FieldNameCollisionException if name already exists.
477
     * @return FieldBuilder
478
     */
479
    public function addGoogleMap($name, array $args = [])
480
    {
481
        return $this->addField($name, 'google_map', $args);
482
    }
483
484
    /**
485
     * @param string $name
486
     * @param array $args field configuration
487
     * @throws FieldNameCollisionException if name already exists.
488
     * @return FieldBuilder
489
     */
490
    public function addLink($name, array $args = [])
491
    {
492
        return $this->addField($name, 'link', $args);
493
    }
494
495
    /**
496
     * @param string $name
497
     * @param array $args field configuration
498
     * @throws FieldNameCollisionException if name already exists.
499
     * @return FieldBuilder
500
     */
501
    public function addRange($name, array $args = [])
502
    {
503
        return $this->addField($name, 'range', $args);
504
    }
505
506
    /**
507
     * All fields added after will appear under this tab, until another tab
508
     * is added.
509
     * @param string $label Tab label
510
     * @param array $args field configuration
511
     * @throws FieldNameCollisionException if name already exists.
512
     * @return FieldBuilder
513
     */
514
    public function addTab($label, array $args = [])
515
    {
516
        return $this->initializeField(new TabBuilder($label, 'tab', $args));
517
    }
518
519
    /**
520
     * Addes a message field
521
     * @param string $label
522
     * @param string $message
523
     * @param array $args field configuration
524
     * @throws FieldNameCollisionException if name already exists.
525
     * @return FieldBuilder
526
     */
527
    public function addMessage($label, $message, array $args = [])
528
    {
529
        $name = $this->generateName($label).'_message';
530
        $args = array_merge([
531
            'label' => $label,
532
            'message' => $message,
533
        ], $args);
534
535
        return $this->addField($name, 'message', $args);
536
    }
537
538
    /**
539
     * @param string $name
540
     * @param array $args field configuration
541
     * @throws FieldNameCollisionException if name already exists.
542
     * @return GroupBuilder
543
     */
544
    public function addGroup($name, array $args = [])
545
    {
546
        return $this->initializeField(new GroupBuilder($name, 'group', $args));
547
    }
548
549
    /**
550
     * Add a repeater field. Any fields added after will be added to the repeater
551
     * until `endRepeater` is called.
552
     * @param string $name
553
     * @param array $args field configuration
554
     * @throws FieldNameCollisionException if name already exists.
555
     * @return RepeaterBuilder
556
     */
557
    public function addRepeater($name, array $args = [])
558
    {
559
        return $this->initializeField(new RepeaterBuilder($name, 'repeater', $args));
560
    }
561
562
    /**
563
     * Add a flexible content field. Once adding a layout with `addLayout`,
564
     * any fields added after will be added to that layout until another
565
     * `addLayout` call is made, or until `endFlexibleContent` is called.
566
     * @param string $name
567
     * @param array $args field configuration
568
     * @throws FieldNameCollisionException if name already exists.
569
     * @return FlexibleContentBuilder
570
     */
571
    public function addFlexibleContent($name, array $args = [])
572
    {
573
        return $this->initializeField(new FlexibleContentBuilder($name, 'flexible_content', $args));
574
    }
575
576
    /**
577
     * @return FieldManager
578
     */
579
    protected function getFieldManager()
580
    {
581
        return $this->fieldManager;
582
    }
583
584
    /**
585
     * @return NamedBuilder[]
586
     */
587
    public function getFields()
588
    {
589
        return $this->getFieldManager()->getFields();
590
    }
591
592
    /**
593
     * @param string $name [description]
594
     * @return FieldBuilder
595
     */
596
    public function getField($name)
597
    {
598
        return $this->getFieldManager()->getField($name);
599
    }
600
601
    public function fieldExists($name)
602
    {
603
        return $this->getFieldManager()->fieldNameExists($name);
604
    }
605
606
    /**
607
     * Modify an already defined field
608
     * @param  string $name   Name of the field
609
     * @param  array|\Closure  $modify Array of field configs or a closure that accepts
610
     * a FieldsBuilder and returns a FieldsBuilder.
611
     * @throws ModifyFieldReturnTypeException if $modify is a closure and doesn't
612
     * return a FieldsBuilder.
613
     * @throws FieldNotFoundException if the field name doesn't exist.
614
     * @return $this
615
     */
616
    public function modifyField($name, $modify)
617
    {
618
        if (is_array($modify)) {
619
            $this->getFieldManager()->modifyField($name, $modify);
620
        } elseif ($modify instanceof \Closure) {
621
            $field = $this->getField($name);
622
623
            // Initialize Modifying FieldsBuilder
624
            $modifyBuilder = new FieldsBuilder('');
625
            $modifyBuilder->addFields([$field]);
626
627
            /**
628
             * @var FieldsBuilder
629
             */
630
            $modifyBuilder = $modify($modifyBuilder);
631
632
            // Check if a FieldsBuilder is returned
633
            if (!$modifyBuilder instanceof FieldsBuilder) {
634
                throw new ModifyFieldReturnTypeException(gettype($modifyBuilder));
635
            }
636
637
            // Build Modifications
638
            $modifyConfig = $modifyBuilder->build();
0 ignored issues
show
Unused Code introduced by
$modifyConfig is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
639
640
            // Insert field(s)
641
            $this->getFieldManager()->replaceField($name, $modifyBuilder->getFields());
642
        }
643
644
        return $this;
645
    }
646
647
    /**
648
     * Remove a field by name
649
     * @param  string $name Field to remove
650
     * @return $this
651
     */
652
    public function removeField($name)
653
    {
654
        $this->getFieldManager()->removeField($name);
655
656
        return $this;
657
    }
658
659
    /**
660
     * Set the location of the field group. See
661
     * https://github.com/StoutLogic/acf-builder/wiki/location and
662
     * https://www.advancedcustomfields.com/resources/custom-location-rules/
663
     * for more details.
664
     * @param string $param
665
     * @param string $operator
666
     * @param string $value
667
     * @return LocationBuilder
668
     */
669
    public function setLocation($param, $operator, $value)
670
    {
671
        if ($this->getParentContext()) {
672
            return $this->getParentContext()->setLocation($param, $operator, $value);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface StoutLogic\AcfBuilder\Builder as the method setLocation() does only exist in the following implementations of said interface: StoutLogic\AcfBuilder\FieldsBuilder.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
673
        }
674
675
        $this->location = new LocationBuilder($param, $operator, $value);
676
        $this->location->setParentContext($this);
677
678
        return $this->location;
679
    }
680
681
    /**
682
     * @return LocationBuilder
683
     */
684
    public function getLocation()
685
    {
686
        return $this->location;
687
    }
688
689
    /**
690
     * Create a field label based on the field's name. Generates title case.
691
     * @param  string $name
692
     * @return string label
693
     */
694
    protected function generateLabel($name)
695
    {
696
        return ucwords(str_replace('_', ' ', $name));
697
    }
698
699
    /**
700
     * Generates a snaked cased name.
701
     * @param  string $name
702
     * @return string
703
     */
704
    protected function generateName($name)
705
    {
706
        return strtolower(str_replace(' ', '_', $name));
707
    }
708
709
}
710