Completed
Push — master ( 422987...324f23 )
by Steve
31s
created

FieldsBuilder::modifyField()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 28
rs 8.8571
cc 3
eloc 14
nc 3
nop 2
1
<?php
2
3
namespace StoutLogic\AcfBuilder;
4
5
class FieldsBuilder extends Builder
6
{
7
    protected $config = [];
8
    protected $fields = [];
9
    protected $location = null;
10
    protected $name;
11
12
    public function __construct($name, $groupConfig = [])
13
    {
14
        $this->name = $name;
15
        $this->setGroupConfig('key', 'group_'.$name);
16
        $this->setGroupConfig('title', $this->generateLabel($name));
17
18
        $this->config = array_merge($this->config, $groupConfig);
19
    }
20
21
    public function setGroupConfig($key, $value)
22
    {
23
        $this->config[$key] = $value;
24
25
        return $this;
26
    }
27
28
    public function getName()
29
    {
30
        return $this->name;
31
    }
32
33
    /**
34
     * Build the final config array. Build any other builders that may exist
35
     * in the config.
36
     * @return array    final field config
37
     */
38
    public function build()
39
    {
40
        $fields = $this->getFields();
41
42
        $fields = $this->buildFields($fields);
43
44
        array_walk_recursive($fields, function(&$value, $key) {
45
46
            switch ($key) {
47
                case 'conditional_logic':
48
                    $value = $value->build();
49
                    $value = $this->transformConditionalConfig($value);
50
                    break;
51
            }
52
        });
53
54
        $location = $this->getLocation();
55
        if (is_subclass_of($location, Builder::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \StoutLogic\AcfBuilder\Builder::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
56
            $location = $location->build();
57
        }
58
59
        return array_merge($this->config, [
60
            'fields' => $fields,
61
            'location' => $location,
62
        ]);
63
    }
64
65
    private function buildFields($fields)
66
    {
67
        $builtFields = [];
68
69
        foreach ($fields as $i => $field) {
70
            if (is_subclass_of($field, Builder::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \StoutLogic\AcfBuilder\Builder::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
71
                $builtFields[] = $field->build();
72
            } else {
73
                $builtFields[] = $field;
74
            }
75
        }
76
77
        return $builtFields;
78
    }
79
80
    /**
81
     * Replace field values with the field's respective key
82
     * @param  array $config
83
     * @return array
84
     */
85
    protected function transformConditionalConfig($config)
86
    {
87
        // Replace field name with the field's key, default: field_$name
88
        array_walk_recursive($config, function(&$value, $key) {
89
            switch ($key) {
90
                case 'field':
91
                    if ($field = $this->getFieldByName($value)) {
92
                        $value = $field['key'];
93
                    } else {
94
                        $value = 'field_'.$value;
95
                    }
96
                    break;
97
            }
98
        });
99
100
        return $config;
101
    }
102
103
    /**
104
     * Add multiple fields either via an array or from another builder
105
     * @param mixed $fields array of fields or a FieldBuilder
106
     */
107
    public function addFields($fields)
108
    {
109
        if (is_a($fields, FieldsBuilder::class)) {
110
            $fields = clone $fields;
111
            foreach ($fields->getFields() as $field) {
112
                $this->pushField($field);
113
            }
114
        } else {
115
            foreach ($fields as $field) {
116
                $this->pushField($field);
117
            }
118
        }
119
120
        return $this;
121
    }
122
123
    public function addField($name, $args = [])
124
    {
125
        $field = array_merge([
126
            'key' => 'field_'.$name,
127
            'name' => $name,
128
            'label' => $this->generateLabel($name),
129
        ], $args);
130
131
        $this->pushField($field);
132
133
        return $this;
134
    }
135
136
    protected function addFieldType($name, $type, $args = [])
137
    {
138
        return $this->addField($name, array_merge([
139
            'type' => $type,
140
        ], $args));
141
    }
142
143
    public function addText($name, $args = [])
144
    {
145
        return $this->addFieldType($name, 'text', $args);
146
    }
147
148
    public function addTextarea($name, $args = [])
149
    {
150
        return $this->addFieldType($name, 'textarea', $args);
151
    }
152
153
    public function addNumber($name, $args = [])
154
    {
155
        return $this->addFieldType($name, 'number', $args);
156
    }
157
158
    public function addEmail($name, $args = [])
159
    {
160
        return $this->addFieldType($name, 'email', $args);
161
    }
162
163
    public function addUrl($name, $args = [])
164
    {
165
        return $this->addFieldType($name, 'url', $args);
166
    }
167
168
    public function addPassword($name, $args = [])
169
    {
170
        return $this->addFieldType($name, 'password', $args);
171
    }
172
173
    public function addWysiwyg($name, $args = [])
174
    {
175
        return $this->addFieldType($name, 'wysiwyg', $args);
176
    }
177
178
    public function addOembed($name, $args = [])
179
    {
180
        return $this->addFieldType($name, 'oembed', $args);
181
    }
182
183
    public function addImage($name, $args = [])
184
    {
185
        return $this->addFieldType($name, 'image', $args);
186
    }
187
188
    public function addFile($name, $args = [])
189
    {
190
        return $this->addFieldType($name, 'file', $args);
191
    }
192
193
    public function addGallery($name, $args = [])
194
    {
195
        return $this->addFieldType($name, 'gallery', $args);
196
    }
197
198
    public function addTrueFalse($name, $args = [])
199
    {
200
        return $this->addFieldType($name, 'true_false', $args);
201
    }
202
203
    public function addSelect($name, $args = [])
204
    {
205
        return $this->addFieldType($name, 'select', $args);
206
    }
207
208
    public function addRadio($name, $args = [])
209
    {
210
        return $this->addFieldType($name, 'radio', $args);
211
    }
212
213
    public function addCheckbox($name, $args = [])
214
    {
215
        return $this->addFieldType($name, 'checkbox', $args);
216
    }
217
218
    public function addPostObject($name, $args = [])
219
    {
220
        return $this->addFieldType($name, 'post_object', $args);
221
    }
222
223
    public function addPostLink($name, $args = [])
224
    {
225
        return $this->addFieldType($name, 'post_link', $args);
226
    }
227
228
    public function addRelationship($name, $args = [])
229
    {
230
        return $this->addFieldType($name, 'relationship', $args);
231
    }
232
233
    public function addTaxonomy($name, $args = [])
234
    {
235
        return $this->addFieldType($name, 'taxonomy', $args);
236
    }
237
238
    public function addUser($name, $args = [])
239
    {
240
        return $this->addFieldType($name, 'user', $args);
241
    }
242
243
    public function addDatePicker($name, $args = [])
244
    {
245
        return $this->addFieldType($name, 'date_picker', $args);
246
    }
247
248
    public function addTimePicker($name, $args = [])
249
    {
250
        return $this->addFieldType($name, 'time_picker', $args);
251
    }
252
253
    public function addDateTimePicker($name, $args = [])
254
    {
255
        return $this->addFieldType($name, 'date_time_picker', $args);
256
    }
257
258
    public function addColorPicker($name, $args = [])
259
    {
260
        return $this->addFieldType($name, 'color_picker', $args);
261
    }
262
263 View Code Duplication
    public function addTab($label, $args = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
264
    {
265
        $name = $this->generateName($label).'_tab';
266
        $args = array_merge([
267
            'label' => $label,
268
        ], $args);
269
270
        return $this->addFieldType($name, 'tab', $args);
271
    }
272
273
    public function endpoint($value = 1)
274
    {
275
        return $this->setConfig('endpoint', $value);
276
    }
277
278 View Code Duplication
    public function addMessage($label, $message, $args = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
    {
280
        $name = $this->generateName($label).'_message';
281
        $args = array_merge([
282
            'label' => $label,
283
            'message' => $message,
284
        ], $args);
285
286
        return $this->addFieldType($name, 'message', $args);
287
    }
288
289
    public function addRepeater($name, $args = [])
290
    {
291
        $repeaterBuilder = new RepeaterBuilder($name, $args);
292
        $repeaterBuilder->setParentContext($this);
293
        $this->pushField($repeaterBuilder);
294
295
        return $repeaterBuilder;
296
    }
297
298
    public function addFlexibleContent($name, $args = [])
299
    {
300
        $flexibleContentBuilder = new FlexibleContentBuilder($name, $args);
301
        $flexibleContentBuilder->setParentContext($this);
302
        $this->pushField($flexibleContentBuilder);
303
304
        return $flexibleContentBuilder;
305
    }
306
307
    public function addChoice($choice, $label = null)
308
    {
309
        $field = $this->popLastField();
310
311
        array_key_exists('choices', $field) ?: $field['choices'] = [];
312
        $label ?: $label = $choice;
313
314
        $field['choices'][$choice] = $label;
315
        $this->pushField($field);
316
317
        return $this;
318
    }
319
320
    public function addChoices()
321
    {
322
        foreach (func_get_args() as $choice) {
323
            if (is_array($choice)) {
324
                $values = each($choice);
325
                $this->addChoice($values['key'], $values['value']);
326
            } else {
327
                $this->addChoice($choice);
328
            }
329
        }
330
331
        return $this;
332
    }
333
334
    public function conditional($name, $operator, $value)
335
    {
336
        $field = $this->popLastField();
337
        $conditionalBuilder = new ConditionalBuilder($name, $operator, $value);
338
        $conditionalBuilder->setParentContext($this);
339
340
        $field['conditional_logic'] = $conditionalBuilder;
341
        $this->pushField($field);
342
343
        return $conditionalBuilder;
344
    }
345
346
    public function getFields()
347
    {
348
        return $this->fields;
349
    }
350
351
    /**
352
     * Return the index in the $this->fields array looked up by the field's name
353
     * @param  string $name Field Name
354
     *
355
     * @throws FieldNotFoundException if the field name doesn't exist
356
     *
357
     * @return integer Field Index
358
     */
359
    protected function getFieldIndexByName($name)
360
    {
361
        foreach ($this->fields as $index => $field) {
362
            if ($field['name'] === $name) {
363
                return $index;
364
            }
365
        }
366
367
        throw new FieldNotFoundException("Field name '{$name}' not found.");
368
    }
369
370
    protected function getFieldByName($name)
371
    {
372
        return $this->fields[$this->getFieldIndexByName($name)];
373
    }
374
375
    /**
376
     * Modify an already defined field
377
     * @param  string $name   Name of the field
378
     * @param  mixed  $modify Array of field configs or a closure that accepts
379
     *                        a FieldsBuilder and returns a FieldsBuilder.
380
     *
381
     * @throws ModifyFieldReturnTypeException if $modify is a closure and doesn't
382
     *                                        return a FieldsBuilder.
383
     *
384
     * @return FieldsBuilder  $this
385
     */
386
    public function modifyField($name, $modify)
387
    {
388
        $index = $this->getFieldIndexByName($name);
389
390
        if ($modify instanceof \Closure) {
391
            // Initialize Modifying FieldsBuilder
392
            $modifyBuilder = new FieldsBuilder($name);
393
            $modifyBuilder->addFields([$this->fields[$index]]);
394
395
            // Modify Field
396
            $modifyBuilder = $modify($modifyBuilder);
397
398
            // Check if a FieldsBuilder is returned
399
            if (!is_a($modifyBuilder, FieldsBuilder::class)) {
400
                throw new ModifyFieldReturnTypeException(gettype($modifyBuilder));
401
            } else {
402
                // Build Modifcations
403
                $modifyConfig = $modifyBuilder->build();
404
405
                // Build Modifcations
406
                array_splice($this->fields, $index, 1, $modifyConfig['fields']);
407
            }
408
        } else {
409
            $this->fields[$index] = array_merge($this->fields[$index], $modify);
410
        }
411
412
        return $this;
413
    }
414
415
    /**
416
     * Remove a field by name
417
     * @param  string $name Field to remove
418
     *
419
     * @return FieldsBuilder  $this
420
     */
421
    public function removeField($name)
422
    {
423
        $index = $this->getFieldIndexByName($name);
424
        unset($this->fields[$index]);
425
426
        return $this;
427
    }
428
429
    public function defaultValue($value)
430
    {
431
        return $this->setConfig('default_value', $value);
432
    }
433
434
    public function required($value = true)
435
    {
436
        return $this->setConfig('required', $value ? 1 : 0);
437
    }
438
439
    public function instructions($value)
440
    {
441
        return $this->setConfig('instructions', $value);
442
    }
443
444
    public function setConfig($key, $value)
445
    {
446
        $field = $this->popLastField();
447
        $field[$key] = $value;
448
        $this->pushField($field);
449
450
        return $this;
451
    }
452
453
    public function setLocation($param, $operator, $value)
454
    {
455
        if ($this->getParentContext()) {
456
            return $this->getParentContext()->setLocation($param, $operator, $value);
0 ignored issues
show
Documentation Bug introduced by
The method setLocation does not exist on object<StoutLogic\AcfBuilder\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
457
        }
458
459
        $this->location = new LocationBuilder($param, $operator, $value);
460
        $this->location->setParentContext($this);
461
462
        return $this->location;
463
    }
464
465
    public function getLocation()
466
    {
467
        return $this->location;
468
    }
469
470
    protected function popLastField()
471
    {
472
        return array_pop($this->fields);
473
    }
474
475
    protected function pushField($field)
476
    {
477
        $this->fields[] = $field;
478
    }
479
480
    protected function generateLabel($name)
481
    {
482
        return ucwords(str_replace("_", " ", $name));
483
    }
484
485
    protected function generateName($name)
486
    {
487
        return strtolower(str_replace(" ", "_", $name));
488
    }
489
}
490