Completed
Push — master ( c8a423...a23ea0 )
by Song
02:21
created

src/Actions/Interactor/Form.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Encore\Admin\Actions\Interactor;
4
5
use Encore\Admin\Actions\RowAction;
6
use Encore\Admin\Admin;
7
use Encore\Admin\Form\Field;
8
use Illuminate\Http\Request;
9
use Illuminate\Support\MessageBag;
10
use Illuminate\Validation\ValidationException;
11
use Illuminate\Validation\Validator;
12
use Symfony\Component\DomCrawler\Crawler;
13
14
class Form extends Interactor
15
{
16
    /**
17
     * @var array
18
     */
19
    protected $fields = [];
20
21
    /**
22
     * @var string
23
     */
24
    protected $modalId;
25
26
    /**
27
     * @param string $label
28
     *
29
     * @return array
30
     */
31
    protected function formatLabel($label)
32
    {
33
        return array_filter((array) $label);
34
    }
35
36
    /**
37
     * @param string $column
38
     * @param string $label
39
     *
40
     * @return Field\Text
41
     */
42
    public function text($column, $label = '')
43
    {
44
        $field = new Field\Text($column, $this->formatLabel($label));
45
46
        $this->addField($field);
47
48
        return $field;
49
    }
50
51
    /**
52
     * @param string $column
53
     * @param string $label
54
     *
55
     * @return Field\Text
56
     */
57
    public function email($column, $label = '')
58
    {
59
        $field = new Field\Email($column, $this->formatLabel($label));
60
61
        $this->addField($field)->setView('admin::actions.form.text');
62
63
        return $field->inputmask(['alias' => 'email']);
64
    }
65
66
    /**
67
     * @param string $column
68
     * @param string $label
69
     *
70
     * @return Field\Text
71
     */
72
    public function integer($column, $label = '')
73
    {
74
        return $this->text($column, $label)
75
            ->width('200px')
76
            ->inputmask(['alias' => 'integer']);
77
    }
78
79
    /**
80
     * @param string $column
81
     * @param string $label
82
     *
83
     * @return Field\Text
84
     */
85
    public function ip($column, $label = '')
86
    {
87
        return $this->text($column, $label)
88
            ->width('200px')
89
            ->inputmask(['alias' => 'ip']);
90
    }
91
92
    /**
93
     * @param string $column
94
     * @param string $label
95
     *
96
     * @return Field\Text
97
     */
98
    public function url($column, $label = '')
99
    {
100
        return $this->text($column, $label)
101
            ->inputmask(['alias' => 'url'])
102
            ->width('200px');
103
    }
104
105
    /**
106
     * @param string $column
107
     * @param string $label
108
     *
109
     * @return Field\Text
110
     */
111
    public function password($column, $label = '')
112
    {
113
        return $this->text($column, $label)
114
            ->attribute('type', 'password');
115
    }
116
117
    /**
118
     * @param string $column
119
     * @param string $label
120
     *
121
     * @return Field\Text
122
     */
123
    public function mobile($column, $label = '')
124
    {
125
        return $this->text($column, $label)
126
            ->inputmask(['mask' => '99999999999'])
127
            ->width('100px');
128
    }
129
130
    /**
131
     * @param string $column
132
     * @param string $label
133
     *
134
     * @return Field\Textarea
135
     */
136
    public function textarea($column, $label = '')
137
    {
138
        $field = new Field\Textarea($column, $this->formatLabel($label));
139
140
        $this->addField($field);
141
142
        return $field;
143
    }
144
145
    /**
146
     * @param string $column
147
     * @param string $label
148
     *
149
     * @return Field\Select
150
     */
151
    public function select($column, $label = '')
152
    {
153
        $field = new Field\Select($column, $this->formatLabel($label));
154
155
        $this->addField($field);
156
157
        return $field;
158
    }
159
160
    /**
161
     * @param string $column
162
     * @param string $label
163
     *
164
     * @return Field\MultipleSelect
165
     */
166
    public function multipleSelect($column, $label = '')
167
    {
168
        $field = new Field\MultipleSelect($column, $this->formatLabel($label));
169
170
        $this->addField($field);
171
172
        return $field;
173
    }
174
175
    /**
176
     * @param string $column
177
     * @param string $label
178
     *
179
     * @return Field\Checkbox
180
     */
181
    public function checkbox($column, $label = '')
182
    {
183
        $field = new Field\Checkbox($column, $this->formatLabel($label));
184
185
        $this->addField($field);
186
187
        return $field;
188
    }
189
190
    /**
191
     * @param string $column
192
     * @param string $label
193
     *
194
     * @return Field\Radio
195
     */
196
    public function radio($column, $label = '')
197
    {
198
        $field = new Field\Radio($column, $this->formatLabel($label));
199
200
        $this->addField($field);
201
202
        return $field;
203
    }
204
205
    /**
206
     * @param string $column
207
     * @param string $label
208
     *
209
     * @return Field\File
210
     */
211
    public function file($column, $label = '')
212
    {
213
        $field = new Field\File($column, $this->formatLabel($label));
214
215
        $this->addField($field);
216
217
        return $field;
218
    }
219
220
    /**
221
     * @param string $column
222
     * @param string $label
223
     *
224
     * @return Field\Image
225
     */
226
    public function image($column, $label = '')
227
    {
228
        $field = new Field\Image($column, $this->formatLabel($label));
229
230
        $this->addField($field)->setView('admin::actions.form.file');
231
232
        return $field;
233
    }
234
235
    /**
236
     * @param string $column
237
     * @param string $label
238
     *
239
     * @return Field\Date
240
     */
241
    public function date($column, $label = '')
242
    {
243
        $field = new Field\Date($column, $this->formatLabel($label));
244
245
        $this->addField($field);
246
247
        return $field;
248
    }
249
250
    /**
251
     * @param string $column
252
     * @param string $label
253
     *
254
     * @return Field\Date
255
     */
256
    public function datetime($column, $label = '')
257
    {
258
        return $this->date($column, $label)->format('YYYY-MM-DD HH:mm:ss');
259
    }
260
261
    /**
262
     * @param string $column
263
     * @param string $label
264
     *
265
     * @return Field\Date
266
     */
267
    public function time($column, $label = '')
268
    {
269
        return $this->date($column, $label)->format('HH:mm:ss');
270
    }
271
272
    /**
273
     * @param string $column
274
     * @param string $label
275
     *
276
     * @return Field\Hidden
277
     */
278
    public function hidden($column, $label = '')
279
    {
280
        $field = new Field\Hidden($column, $this->formatLabel($label));
281
282
        $this->addField($field);
283
284
        return $field;
285
    }
286
287
    /**
288
     * @param string $content
289
     * @param string $selector
290
     *
291
     * @return string
292
     */
293
    public function addElementAttr($content, $selector)
294
    {
295
        $crawler = new Crawler($content);
296
297
        $node = $crawler->filter($selector)->getNode(0);
298
        $node->setAttribute('modal', $this->getModalId());
299
300
        return $crawler->children()->html();
301
    }
302
303
    /**
304
     * @param Field $field
305
     *
306
     * @return Field
307
     */
308 View Code Duplication
    protected function addField(Field $field)
0 ignored issues
show
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...
309
    {
310
        $elementClass = array_merge(['action'], $field->getElementClass());
311
312
        $field->addElementClass($elementClass);
313
314
        $field->setView($this->resolveView(get_class($field)));
315
316
        array_push($this->fields, $field);
317
318
        return $field;
319
    }
320
321
    /**
322
     * @param Request $request
323
     *
324
     * @throws ValidationException
325
     * @throws \Exception
326
     *
327
     * @return void
328
     */
329
    public function validate(Request $request)
330
    {
331 View Code Duplication
        if ($this->action instanceof RowAction) {
0 ignored issues
show
This code seems to be duplicated across 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...
332
            call_user_func([$this->action, 'form'], $this->action->getRow());
333
        } else {
334
            call_user_func([$this->action, 'form']);
335
        }
336
337
        $failedValidators = [];
338
339
        /** @var Field $field */
340 View Code Duplication
        foreach ($this->fields as $field) {
0 ignored issues
show
This code seems to be duplicated across 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...
341
            if (!$validator = $field->getValidator($request->all())) {
342
                continue;
343
            }
344
345
            if (($validator instanceof Validator) && !$validator->passes()) {
346
                $failedValidators[] = $validator;
347
            }
348
        }
349
350
        $message = $this->mergeValidationMessages($failedValidators);
351
352
        if ($message->any()) {
353
            throw ValidationException::withMessages($message->toArray());
354
        }
355
    }
356
357
    /**
358
     * Merge validation messages from input validators.
359
     *
360
     * @param \Illuminate\Validation\Validator[] $validators
361
     *
362
     * @return MessageBag
363
     */
364
    protected function mergeValidationMessages($validators)
365
    {
366
        $messageBag = new MessageBag();
367
368
        foreach ($validators as $validator) {
369
            $messageBag = $messageBag->merge($validator->messages());
370
        }
371
372
        return $messageBag;
373
    }
374
375
    /**
376
     * @param string $class
377
     *
378
     * @return string
379
     */
380 View Code Duplication
    protected function resolveView($class)
0 ignored issues
show
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...
381
    {
382
        $path = explode('\\', $class);
383
384
        $name = strtolower(array_pop($path));
385
386
        return "admin::actions.form.{$name}";
387
    }
388
389
    /**
390
     * @return void
391
     */
392
    public function addModalHtml()
393
    {
394
        $data = [
395
            'fields'   => $this->fields,
396
            'title'    => $this->action->name(),
397
            'modal_id' => $this->getModalId(),
398
        ];
399
400
        $modal = view('admin::actions.form.modal', $data)->render();
0 ignored issues
show
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
401
402
        Admin::html($modal);
403
    }
404
405
    /**
406
     * @return string
407
     */
408
    public function getModalId()
409
    {
410
        if (!$this->modalId) {
411
            if ($this->action instanceof RowAction) {
412
                $this->modalId = uniqid('row-action-modal-').mt_rand(1000, 9999);
413
            } else {
414
                $this->modalId = strtolower(str_replace('\\', '-', get_class($this->action)));
415
            }
416
        }
417
418
        return $this->modalId;
419
    }
420
421
    /**
422
     * @return void
423
     */
424
    public function addScript()
425
    {
426
        $this->action->attribute('modal', $this->getModalId());
427
428
        $parameters = json_encode($this->action->parameters());
429
430
        $script = <<<SCRIPT
431
432
(function ($) {
433
    $('{$this->action->selector($this->action->selectorPrefix)}').off('{$this->action->event}').on('{$this->action->event}', function() {
434
        var data = $(this).data();
435
        var modalId = $(this).attr('modal');
436
        Object.assign(data, {$parameters});
437
        {$this->action->actionScript()}
438
        $('#'+modalId).modal('show');
439
        $('#'+modalId+' form').off('submit').on('submit', function (e) {
440
            e.preventDefault();
441
            var form = this;
442
            {$this->buildActionPromise()}
443
            {$this->action->handleActionPromise()}
444
        });
445
    });
446
})(jQuery);
447
448
SCRIPT;
449
450
        Admin::script($script);
451
    }
452
453
    /**
454
     * @throws \Exception
455
     *
456
     * @return string
457
     */
458
    protected function buildActionPromise()
459
    {
460 View Code Duplication
        if ($this->action instanceof RowAction) {
0 ignored issues
show
This code seems to be duplicated across 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...
461
            call_user_func([$this->action, 'form'], $this->action->getRow());
462
        } else {
463
            call_user_func([$this->action, 'form']);
464
        }
465
466
        $this->addModalHtml();
467
468
        return <<<SCRIPT
469
            var process = new Promise(function (resolve,reject) {
470
                Object.assign(data, {
471
                    _token: $.admin.token,
472
                    _action: '{$this->action->getCalledClass()}',
473
                });
474
                
475
                var formData = new FormData(form);
476
                for (var key in data) {
477
                    formData.append(key, data[key]);
478
                }
479
                
480
                $.ajax({
481
                    method: '{$this->action->getMethod()}',
482
                    url: '{$this->action->getHandleRoute()}',
483
                    data: formData,
484
                    cache: false,
485
                    contentType: false,
486
                    processData: false,
487
                    success: function (data) {
488
                        resolve(data);
489
                        if (data.status === true) {
490
                            $('#'+modalId).modal('hide');
491
                        }
492
                    },
493
                    error:function(request){
494
                        reject(request);
495
                    }
496
                });
497
            });
498
SCRIPT;
499
    }
500
}
501