1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace App\Services\Html; |
4
|
|
|
|
5
|
|
|
use Form; |
6
|
|
|
use Html; |
7
|
|
|
use Illuminate\Support\Collection; |
8
|
|
|
use Illuminate\Support\ViewErrorBag; |
9
|
|
|
use Illuminate\Database\Eloquent\Model; |
10
|
|
|
|
11
|
|
|
class BlenderFormBuilder |
12
|
|
|
{ |
13
|
|
|
/** @var string */ |
14
|
|
|
protected $module; |
15
|
|
|
|
16
|
|
|
/** @var \Illuminate\Database\Eloquent\Model */ |
17
|
|
|
protected $model; |
18
|
|
|
|
19
|
|
|
/** @var \Illuminate\Support\ViewErrorBag */ |
20
|
|
|
protected $errors; |
21
|
|
|
|
22
|
|
|
public function init(string $module, Model $model, ViewErrorBag $errors) |
23
|
|
|
{ |
24
|
|
|
$this->module = $module; |
25
|
|
|
$this->model = $model; |
26
|
|
|
$this->errors = $errors; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
public function label(string $name, bool $required = false, array $options = []): string |
30
|
|
|
{ |
31
|
|
|
return Form::label($name, fragment("back.{$this->module}.{$name}").($required ? '*' : ''), $options); |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
public function error(string $name): string |
35
|
|
|
{ |
36
|
|
|
return Html::error($this->errors->first($name)); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
View Code Duplication |
public function text(string $name, bool $required = false, string $locale = ''): string |
|
|
|
|
40
|
|
|
{ |
41
|
|
|
$fieldName = $this->fieldName($name, $locale); |
42
|
|
|
|
43
|
|
|
return $this->group([ |
44
|
|
|
$this->label($name, $required), |
45
|
|
|
Form::text($fieldName, Form::useInitialValue($this->model, $name, $locale)), |
46
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
47
|
|
|
]); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
View Code Duplication |
public function textarea(string $name, bool $required = false, string $locale = ''): string |
|
|
|
|
51
|
|
|
{ |
52
|
|
|
$fieldName = $this->fieldName($name, $locale); |
53
|
|
|
|
54
|
|
|
return $this->group([ |
55
|
|
|
$this->label($name, $required), |
56
|
|
|
Form::textarea($fieldName, Form::useInitialValue($this->model, $name, $locale), ['data-autosize']), |
57
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
58
|
|
|
]); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
public function redactor(string $name, bool $required = false, string $locale = ''): string |
62
|
|
|
{ |
63
|
|
|
$fieldName = $this->fieldName($name, $locale); |
64
|
|
|
|
65
|
|
|
$options = [ |
66
|
|
|
'data-editor' => '', |
67
|
|
|
'data-editor-medialibrary-url' => action( |
68
|
|
|
'Back\Api\MediaLibraryController@index', |
69
|
|
|
[ |
70
|
|
|
'model_name' => ucfirst(get_class($this->model)), |
71
|
|
|
'model_id' => $this->model->id, |
72
|
|
|
'redactor=true', |
73
|
|
|
] |
74
|
|
|
), |
75
|
|
|
]; |
76
|
|
|
|
77
|
|
|
return $this->group([ |
78
|
|
|
$this->label($name, $required), |
79
|
|
|
Form::textarea($fieldName, Form::useInitialValue($this->model, $name, $locale), $options), |
80
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
81
|
|
|
]); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
public function checkbox(string $name, string $locale = ''): string |
85
|
|
|
{ |
86
|
|
|
$fieldName = $this->fieldName($name, $locale); |
87
|
|
|
|
88
|
|
|
$contents = Form::checkbox( |
89
|
|
|
$fieldName, |
90
|
|
|
1, |
91
|
|
|
Form::useInitialValue($this->model, $name, $locale), |
92
|
|
|
['class' => 'form-control'] |
93
|
|
|
).' '.fragment("back.{$this->module}.{$name}"); |
94
|
|
|
|
95
|
|
|
return $this->group([el('label.-checkbox', $contents)]); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
View Code Duplication |
public function date(string $name, bool $required = false, string $locale = ''): string |
|
|
|
|
99
|
|
|
{ |
100
|
|
|
$fieldName = $this->fieldName($name, $locale); |
101
|
|
|
|
102
|
|
|
return $this->group([ |
103
|
|
|
$this->label($name, $required), |
104
|
|
|
Form::datePicker($fieldName, Form::useInitialValue($this->model, $name, $locale)), |
105
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
106
|
|
|
]); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
View Code Duplication |
public function select(string $name, $options, string $locale = ''): string |
|
|
|
|
110
|
|
|
{ |
111
|
|
|
$fieldName = $this->fieldName($name, $locale); |
112
|
|
|
|
113
|
|
|
return $this->group([ |
114
|
|
|
$this->label($name, true), |
115
|
|
|
Form::select( |
116
|
|
|
$fieldName, |
117
|
|
|
$options, |
118
|
|
|
Form::useInitialValue($this->model, $name, $locale), |
119
|
|
|
['data-select' => 'select'] |
120
|
|
|
), |
121
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
122
|
|
|
]); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
View Code Duplication |
public function searchableSelect(string $name, $options, string $locale = ''): string |
|
|
|
|
126
|
|
|
{ |
127
|
|
|
$fieldName = $this->fieldName($name, $locale); |
128
|
|
|
|
129
|
|
|
return $this->group([ |
130
|
|
|
$this->label($name), |
131
|
|
|
Form::select( |
132
|
|
|
$fieldName, |
133
|
|
|
$options, |
134
|
|
|
Form::useInitialValue($this->model, $name, $locale), |
135
|
|
|
['data-select' => 'search'] |
136
|
|
|
), |
137
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
138
|
|
|
]); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
View Code Duplication |
public function searchableMultiSelect(string $name, $options, string $locale = ''): string |
|
|
|
|
142
|
|
|
{ |
143
|
|
|
$fieldName = $this->fieldName($name, $locale); |
144
|
|
|
|
145
|
|
|
return $this->group([ |
146
|
|
|
$this->label($name), |
147
|
|
|
Form::select( |
148
|
|
|
$fieldName, |
149
|
|
|
$options, |
150
|
|
|
Form::useInitialValue($this->model, $name, $locale), |
151
|
|
|
['data-select' => 'search', 'multiple' => true] |
152
|
|
|
), |
153
|
|
|
$this->error($fieldName, $this->errors), |
|
|
|
|
154
|
|
|
]); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
View Code Duplication |
public function tags(string $type): string |
|
|
|
|
158
|
|
|
{ |
159
|
|
|
return $this->group([ |
160
|
|
|
Form::label($type.'_tags[]', fragment("back.{$this->module}.{$type}").'*'), |
161
|
|
|
Form::tags($this->model, $type), |
162
|
|
|
]); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
View Code Duplication |
public function category(string $type): string |
|
|
|
|
166
|
|
|
{ |
167
|
|
|
return $this->group([ |
168
|
|
|
Form::label($type.'_tags[]', fragment("back.{$this->module}.{$type}").'*'), |
169
|
|
|
Form::category($this->model, $type, ['data-select' => 'select']), |
170
|
|
|
]); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function media(string $collection, string $type, array $associated = []): string |
174
|
|
|
{ |
175
|
|
|
return $this->group([ |
176
|
|
|
$this->label($collection), |
177
|
|
|
Form::media($this->model, $collection, $type, $associated), |
178
|
|
|
]); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
public function contentBlocks(string $collection = 'default', string $editor = 'default'): string |
182
|
|
|
{ |
183
|
|
|
return $this->group([ |
184
|
|
|
$this->label('contentBlocks.'.$collection), |
185
|
|
|
Form::contentBlocks($this->model, $collection, $editor), |
186
|
|
|
]); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
public function map(string $name): string |
190
|
|
|
{ |
191
|
|
|
$form = []; |
192
|
|
|
|
193
|
|
|
$form[] = el('div', ['class' => 'locationpicker_tools :align-right'], [ |
194
|
|
|
el('input', [ |
195
|
|
|
'type' => 'text', |
196
|
|
|
'class' => 'locationpicker_search', |
197
|
|
|
'placeholder' => fragment('back.locationpicker.search'), |
198
|
|
|
'data-locationpicker-search', |
199
|
|
|
], ''), |
200
|
|
|
el('button', [ |
201
|
|
|
'class' => 'locationpicker_button', |
202
|
|
|
'type' => 'button', |
203
|
|
|
'data-locationpicker-button', |
204
|
|
|
], fragment('back.locationpicker.submit')), |
205
|
|
|
]); |
206
|
|
|
|
207
|
|
|
$form[] = el('div', [ |
208
|
|
|
'class' => 'locationpicker_map', |
209
|
|
|
'data-locationpicker-map', |
210
|
|
|
], ''); |
211
|
|
|
|
212
|
|
|
$form[] = Form::hidden( |
213
|
|
|
"{$name}_lat", |
214
|
|
|
Form::useInitialValue($this->model, "{$name}_lat"), |
215
|
|
|
['data-locationpicker-lat'] |
216
|
|
|
); |
217
|
|
|
|
218
|
|
|
$form[] = Form::hidden( |
219
|
|
|
"{$name}_lng", |
220
|
|
|
Form::useInitialValue($this->model, "{$name}_lng"), |
221
|
|
|
['data-locationpicker-lng'] |
222
|
|
|
); |
223
|
|
|
|
224
|
|
|
$form[] = Form::hidden( |
225
|
|
|
"{$name}_zoom", |
226
|
|
|
Form::useInitialValue($this->model, "{$name}_zoom"), |
227
|
|
|
['data-locationpicker-zoom'] |
228
|
|
|
); |
229
|
|
|
|
230
|
|
|
return $this->group([ |
231
|
|
|
$this->label($name), |
232
|
|
|
el('div.locationpicker', ['data-locationpicker', 'data-api-key' => env('GOOGLE_MAPS_API_KEY')], $form), |
233
|
|
|
]); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
public function translated(array $fields): string |
237
|
|
|
{ |
238
|
|
|
// Ex. ['name' => 'text', 'contents' => 'redactor'] |
|
|
|
|
239
|
|
|
|
240
|
|
|
$translatedFields = []; |
241
|
|
|
|
242
|
|
|
foreach (locales() as $locale) { |
243
|
|
|
$fieldset = []; |
244
|
|
|
|
245
|
|
|
foreach ($fields as $name => $type) { |
246
|
|
|
$fieldset[] = $this->$type($name, false, $locale); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
if (locales()->count() === 1) { |
250
|
|
|
$translatedFields[] = $this->group($fieldset); |
251
|
|
|
continue; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
$translatedFields[] = $this->languageFieldSet($locale, $fieldset); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
return implode('', $translatedFields); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
public function seo(): string |
261
|
|
|
{ |
262
|
|
|
return locales()->map(function ($locale) { |
263
|
|
|
return collect($this->model->defaultSeoValues()) |
264
|
|
|
->keys() |
265
|
|
|
->map(function ($attribute) use ($locale) { |
266
|
|
|
$fieldName = translate_field_name("seo.{$attribute}", $locale); |
267
|
|
|
|
268
|
|
|
return $this->group([ |
269
|
|
|
Form::label($fieldName, $this->getSeoLabel($attribute)), |
270
|
|
|
Form::text( |
271
|
|
|
$fieldName, |
272
|
|
|
old($fieldName, $this->model->getTranslation('seo_values', $locale)[$attribute] ?? ''), |
273
|
|
|
['placeholder' => $this->model->defaultSeoValues()[$attribute]] |
274
|
|
|
), |
275
|
|
|
]); |
276
|
|
|
}) |
277
|
|
|
->pipe(function (Collection $fields) use ($locale) { |
278
|
|
|
return $this->languageFieldSet($locale, $fields->toArray()); |
279
|
|
|
}); |
280
|
|
|
})->implode(''); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
protected function getSeoLabel(string $attribute): string |
284
|
|
|
{ |
285
|
|
|
if (starts_with($attribute, 'meta_')) { |
286
|
|
|
return 'Meta: '.substr($attribute, 5); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
return fragment("back.seo.{$attribute}"); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
public function submit(): string |
293
|
|
|
{ |
294
|
|
|
return el( |
295
|
|
|
'div.form__group.-buttons', |
296
|
|
|
Form::submit(fragment("back.{$this->module}.save"), ['class' => 'button -default']) |
297
|
|
|
); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
protected function languageFieldSet($locale, array $elements) |
301
|
|
|
{ |
302
|
|
|
return el( |
303
|
|
|
'fieldset', |
304
|
|
|
array_merge([el('legend', el('div.legend_lang', $locale))], $elements) |
305
|
|
|
); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
protected function group(array $elements): string |
309
|
|
|
{ |
310
|
|
|
return el('div.form__group', $elements); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
protected function parts(array $elements): string |
314
|
|
|
{ |
315
|
|
|
return el('div.parts', $elements); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
protected function fieldName(string $name, string $locale = ''): string |
319
|
|
|
{ |
320
|
|
|
return $locale ? translate_field_name($name, $locale) : $name; |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
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.