Passed
Branch develop (4c9db4)
by Steve
27:39
created

FieldsBuilder::addChoices()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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