1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace StoutLogic\AcfBuilder; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Manages an array of field configs |
7
|
|
|
*/ |
8
|
|
|
class FieldManager |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* Array of fields |
12
|
|
|
* @var array |
13
|
|
|
*/ |
14
|
|
|
private $fields; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @param array $fields optional default array of field configs |
18
|
|
|
*/ |
19
|
|
|
public function __construct($fields = []) |
20
|
|
|
{ |
21
|
|
|
$this->fields = $fields; |
22
|
|
|
} |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @return array field configs |
26
|
|
|
*/ |
27
|
|
|
public function getFields() |
28
|
|
|
{ |
29
|
|
|
return $this->fields; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Return int of fields |
34
|
|
|
* @return int field count |
35
|
|
|
*/ |
36
|
|
|
public function getCount() |
37
|
|
|
{ |
38
|
|
|
return count($this->getFields()); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Add field to end of array |
43
|
|
|
* @param array|Builder $field Field array config or Builder |
44
|
|
|
* @return void |
45
|
|
|
*/ |
46
|
|
|
public function pushField($field) |
47
|
|
|
{ |
48
|
|
|
$this->insertFields($field, $this->getCount()); |
|
|
|
|
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Remove last field from end of array |
53
|
|
|
* @throws \OutOfRangeException if array is empty |
54
|
|
|
* @return array|Builder Field array config or Builder |
55
|
|
|
*/ |
56
|
|
|
public function popField() |
57
|
|
|
{ |
58
|
|
|
if ($this->getCount() > 0) { |
59
|
|
|
$fields = $this->removeFieldAtIndex($this->getCount() - 1); |
60
|
|
|
return $fields[0]; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
throw new \OutOfRangeException("Can't call popField when the field count is 0"); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Insert of field at a specific index |
68
|
|
|
* @param array|NamedBuilder $fields a single field or an array of fields |
69
|
|
|
* @param int $index insertion point |
70
|
|
|
* @return void |
71
|
|
|
*/ |
72
|
|
|
public function insertFields($fields, $index) |
73
|
|
|
{ |
74
|
|
|
if (!$fields instanceof NamedBuilder && !is_array($fields)) { |
75
|
|
|
return; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
// If a singular field config, put into an array of fields |
79
|
|
|
if ($fields instanceof NamedBuilder || array_key_exists('name', $fields)) { |
80
|
|
|
$fields = [$fields]; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
foreach ($fields as $i => $field) { |
84
|
|
|
if ($this->validateField($field)) { |
85
|
|
|
array_splice($this->fields, $index + $i, 0, [$field]); |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Remove a field at a specific index |
92
|
|
|
* @param int $index |
93
|
|
|
* @return array removed field |
94
|
|
|
*/ |
95
|
|
|
private function removeFieldAtIndex($index) |
96
|
|
|
{ |
97
|
|
|
return array_splice($this->fields, $index, 1); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Remove a speicifc field by name |
102
|
|
|
* @param string $name name of the field |
103
|
|
|
* @return void |
104
|
|
|
*/ |
105
|
|
|
public function removeField($name) |
106
|
|
|
{ |
107
|
|
|
$index = $this->getFieldIndex($name); |
108
|
|
|
$this->removeFieldAtIndex($index); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Replace a field with a single field or array of fields |
113
|
|
|
* @param string $name name of field to replace |
114
|
|
|
* @param array|Builder $field single or array of fields |
115
|
|
|
* @return void |
116
|
|
|
*/ |
117
|
|
|
public function replaceField($name, $field) |
118
|
|
|
{ |
119
|
|
|
$index = $this->getFieldIndex($name); |
120
|
|
|
$this->removeFieldAtIndex($index); |
121
|
|
|
$this->insertFields($field, $index); |
|
|
|
|
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Check to see if a field name already exists |
126
|
|
|
* @param string $name field name |
127
|
|
|
* @return bool |
128
|
|
|
*/ |
129
|
|
|
public function fieldNameExists($name) |
130
|
|
|
{ |
131
|
|
|
try { |
132
|
|
|
$this->getFieldIndex($name); |
133
|
|
|
} catch (FieldNotFoundException $e) { |
134
|
|
|
return false; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
return true; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Return a field by name |
142
|
|
|
* @param string $name field name |
143
|
|
|
* @return array|Builder Field config array or Builder |
144
|
|
|
*/ |
145
|
|
|
public function getField($name) |
146
|
|
|
{ |
147
|
|
|
return $this->fields[$this->getFieldIndex($name)]; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Return the name given a field |
152
|
|
|
* @param array|NamedBuilder $field |
153
|
|
|
* @return string|false field name |
154
|
|
|
*/ |
155
|
|
|
public function getFieldName($field) |
156
|
|
|
{ |
157
|
|
|
if ($field instanceof NamedBuilder) { |
158
|
|
|
return $field->getName(); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
if (is_array($field) && array_key_exists('name', $field)) { |
162
|
|
|
return $field['name']; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
return false; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Modify the configuration of a field |
170
|
|
|
* @param string $name field name |
171
|
|
|
* @param array $modifications field configuration |
172
|
|
|
* @return void |
173
|
|
|
*/ |
174
|
|
|
public function modifyField($name, $modifications) |
175
|
|
|
{ |
176
|
|
|
$field = $this->getField($name); |
177
|
|
|
$field = array_merge($field, $modifications); |
178
|
|
|
$this->replaceField($name, $field); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Validate a field |
183
|
|
|
* @param array|Builder $field |
184
|
|
|
* @return bool |
185
|
|
|
*/ |
186
|
|
|
private function validateField($field) |
187
|
|
|
{ |
188
|
|
|
return $this->validateFieldName($field); |
|
|
|
|
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Validates that a field's name doesn't already exist |
193
|
|
|
* @param array|NamedBuilder $field |
194
|
|
|
* @throws FieldNameCollisionException when the name already exists |
195
|
|
|
* @return bool |
196
|
|
|
*/ |
197
|
|
|
private function validateFieldName($field) |
198
|
|
|
{ |
199
|
|
|
$fieldName = $this->getFieldName($field); |
200
|
|
|
if (!$fieldName || $this->fieldNameExists($fieldName)) { |
|
|
|
|
201
|
|
|
throw new FieldNameCollisionException("Field Name: `{$fieldName}` already exists."); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return true; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Return the index in the $this->fields array looked up by the field's name |
209
|
|
|
* @param string $name Field Name |
210
|
|
|
* @throws FieldNotFoundException if the field name doesn't exist |
211
|
|
|
* @return int Field Index |
212
|
|
|
*/ |
213
|
|
|
public function getFieldIndex($name) |
214
|
|
|
{ |
215
|
|
|
foreach ($this->getFields() as $index => $field) { |
216
|
|
|
if ($this->getFieldName($field) === $name) { |
217
|
|
|
return $index; |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
throw new FieldNotFoundException("Field `{$name}` not found."); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.