Completed
Branch develop (6833e5)
by Steve
04:32
created

FieldsBuilder::addPageLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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