Completed
Push — master ( 1698f0...2d1ea1 )
by Steve
04:16
created

FieldsBuilder::addLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
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
     * All fields added after will appear under this tab, until another tab
497
     * is added.
498
     * @param string $label Tab label
499
     * @param array $args field configuration
500
     * @throws FieldNameCollisionException if name already exists.
501
     * @return FieldBuilder
502
     */
503
    public function addTab($label, array $args = [])
504
    {
505
        return $this->initializeField(new TabBuilder($label, 'tab', $args));
506
    }
507
508
    /**
509
     * Addes a message field
510
     * @param string $label
511
     * @param string $message
512
     * @param array $args field configuration
513
     * @throws FieldNameCollisionException if name already exists.
514
     * @return FieldBuilder
515
     */
516
    public function addMessage($label, $message, array $args = [])
517
    {
518
        $name = $this->generateName($label).'_message';
519
        $args = array_merge([
520
            'label' => $label,
521
            'message' => $message,
522
        ], $args);
523
524
        return $this->addField($name, 'message', $args);
525
    }
526
527
    /**
528
     * Add a repeater field. Any fields added after will be added to the repeater
529
     * until `endRepeater` is called.
530
     * @param string $name
531
     * @param array $args field configuration
532
     * @throws FieldNameCollisionException if name already exists.
533
     * @return RepeaterBuilder
534
     */
535
    public function addRepeater($name, array $args = [])
536
    {
537
        return $this->initializeField(new RepeaterBuilder($name, 'repeater', $args));
538
    }
539
540
    /**
541
     * Add a flexible content field. Once adding a layout with `addLayout`,
542
     * any fields added after will be added to that layout until another
543
     * `addLayout` call is made, or until `endFlexibleContent` is called.
544
     * @param string $name
545
     * @param array $args field configuration
546
     * @throws FieldNameCollisionException if name already exists.
547
     * @return FlexibleContentBuilder
548
     */
549
    public function addFlexibleContent($name, array $args = [])
550
    {
551
        return $this->initializeField(new FlexibleContentBuilder($name, 'flexible_content', $args));
552
    }
553
554
    /**
555
     * @return FieldManager
556
     */
557
    protected function getFieldManager()
558
    {
559
        return $this->fieldManager;
560
    }
561
562
    /**
563
     * @return NamedBuilder[]
564
     */
565
    public function getFields()
566
    {
567
        return $this->getFieldManager()->getFields();
568
    }
569
570
    /**
571
     * @param string $name [description]
572
     * @return FieldBuilder
573
     */
574
    public function getField($name)
575
    {
576
        return $this->getFieldManager()->getField($name);
577
    }
578
579
    public function fieldExists($name)
580
    {
581
        return $this->getFieldManager()->fieldNameExists($name);
582
    }
583
584
    /**
585
     * Modify an already defined field
586
     * @param  string $name   Name of the field
587
     * @param  array|\Closure  $modify Array of field configs or a closure that accepts
588
     * a FieldsBuilder and returns a FieldsBuilder.
589
     * @throws ModifyFieldReturnTypeException if $modify is a closure and doesn't
590
     * return a FieldsBuilder.
591
     * @throws FieldNotFoundException if the field name doesn't exist.
592
     * @return $this
593
     */
594
    public function modifyField($name, $modify)
595
    {
596
        if (is_array($modify)) {
597
            $this->getFieldManager()->modifyField($name, $modify);
598
        } elseif ($modify instanceof \Closure) {
599
            $field = $this->getField($name);
600
601
            // Initialize Modifying FieldsBuilder
602
            $modifyBuilder = new FieldsBuilder('');
603
            $modifyBuilder->addFields([$field]);
604
605
            /**
606
             * @var FieldsBuilder
607
             */
608
            $modifyBuilder = $modify($modifyBuilder);
609
610
            // Check if a FieldsBuilder is returned
611
            if (!$modifyBuilder instanceof FieldsBuilder) {
612
                throw new ModifyFieldReturnTypeException(gettype($modifyBuilder));
613
            }
614
615
            // Build Modifications
616
            $modifyConfig = $modifyBuilder->build();
617
618
            // Insert field(s)
619
            $this->getFieldManager()->replaceField($name, $modifyConfig['fields']);
620
        }
621
622
        return $this;
623
    }
624
625
    /**
626
     * Remove a field by name
627
     * @param  string $name Field to remove
628
     * @return $this
629
     */
630
    public function removeField($name)
631
    {
632
        $this->getFieldManager()->removeField($name);
633
634
        return $this;
635
    }
636
637
    /**
638
     * Set the location of the field group. See
639
     * https://github.com/StoutLogic/acf-builder/wiki/location and
640
     * https://www.advancedcustomfields.com/resources/custom-location-rules/
641
     * for more details.
642
     * @param string $param
643
     * @param string $operator
644
     * @param string $value
645
     * @return LocationBuilder
646
     */
647
    public function setLocation($param, $operator, $value)
648
    {
649
        if ($this->getParentContext()) {
650
            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...
651
        }
652
653
        $this->location = new LocationBuilder($param, $operator, $value);
654
        $this->location->setParentContext($this);
655
656
        return $this->location;
657
    }
658
659
    /**
660
     * @return LocationBuilder
661
     */
662
    public function getLocation()
663
    {
664
        return $this->location;
665
    }
666
667
    /**
668
     * Create a field label based on the field's name. Generates title case.
669
     * @param  string $name
670
     * @return string label
671
     */
672
    protected function generateLabel($name)
673
    {
674
        return ucwords(str_replace('_', ' ', $name));
675
    }
676
677
    /**
678
     * Generates a snaked cased name.
679
     * @param  string $name
680
     * @return string
681
     */
682
    protected function generateName($name)
683
    {
684
        return strtolower(str_replace(' ', '_', $name));
685
    }
686
687
}
688