Completed
Push — master ( 29b277...2120d8 )
by Song
02:40
created

Form::integer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Actions\Interactor;
4
5
use Encore\Admin\Actions\Action;
6
use Encore\Admin\Actions\RowAction;
7
use Encore\Admin\Admin;
8
use Encore\Admin\Form\Field;
9
use Illuminate\Http\Request;
10
use Illuminate\Support\MessageBag;
11
use Illuminate\Validation\ValidationException;
12
use Illuminate\Validation\Validator;
13
use Symfony\Component\DomCrawler\Crawler;
14
15
class Form extends Interactor
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $fields = [];
21
22
    /**
23
     * @var string
24
     */
25
    protected $modalId;
26
27
    /**
28
     * @var string
29
     */
30
    protected $confirm = '';
31
32
    /**
33
     * @param string $label
34
     *
35
     * @return array
36
     */
37
    protected function formatLabel($label)
38
    {
39
        return array_filter((array) $label);
40
    }
41
42
    /**
43
     * @param string $column
44
     * @param string $label
45
     *
46
     * @return Field\Text
47
     */
48
    public function text($column, $label = '')
49
    {
50
        $field = new Field\Text($column, $this->formatLabel($label));
51
52
        $this->addField($field);
53
54
        return $field;
55
    }
56
57
    /**
58
     * @param $column
59
     * @param string   $label
60
     * @param \Closure $builder
61
     *
62
     * @return Field\Table
63
     */
64
    public function table($column, $label = '', $builder = null)
65
    {
66
        $field = new Field\Table($column, [$label, $builder]);
67
68
        $this->addField($field);
69
70
        return $field;
71
    }
72
73
    /**
74
     * @param string $column
75
     * @param string $label
76
     *
77
     * @return Field\Text
78
     */
79
    public function email($column, $label = '')
80
    {
81
        $field = new Field\Email($column, $this->formatLabel($label));
82
83
        $this->addField($field)->setView('admin::actions.form.text');
84
85
        return $field->inputmask(['alias' => 'email']);
86
    }
87
88
    /**
89
     * @param string $column
90
     * @param string $label
91
     *
92
     * @return Field\Text
93
     */
94
    public function integer($column, $label = '')
95
    {
96
        return $this->text($column, $label)
97
            ->width('200px')
98
            ->inputmask(['alias' => 'integer']);
99
    }
100
101
    /**
102
     * @param string $column
103
     * @param string $label
104
     *
105
     * @return Field\Text
106
     */
107
    public function ip($column, $label = '')
108
    {
109
        return $this->text($column, $label)
110
            ->width('200px')
111
            ->inputmask(['alias' => 'ip']);
112
    }
113
114
    /**
115
     * @param string $column
116
     * @param string $label
117
     *
118
     * @return Field\Text
119
     */
120
    public function url($column, $label = '')
121
    {
122
        return $this->text($column, $label)
123
            ->inputmask(['alias' => 'url'])
124
            ->width('200px');
125
    }
126
127
    /**
128
     * @param string $column
129
     * @param string $label
130
     *
131
     * @return Field\Text
132
     */
133
    public function password($column, $label = '')
134
    {
135
        return $this->text($column, $label)
136
            ->attribute('type', 'password');
137
    }
138
139
    /**
140
     * @param string $column
141
     * @param string $label
142
     *
143
     * @return Field\Text
144
     */
145
    public function mobile($column, $label = '')
146
    {
147
        return $this->text($column, $label)
148
            ->inputmask(['mask' => '99999999999'])
149
            ->width('100px');
150
    }
151
152
    /**
153
     * @param string $column
154
     * @param string $label
155
     *
156
     * @return Field\Textarea
157
     */
158
    public function textarea($column, $label = '')
159
    {
160
        $field = new Field\Textarea($column, $this->formatLabel($label));
161
162
        $this->addField($field);
163
164
        return $field;
165
    }
166
167
    /**
168
     * @param string $column
169
     * @param string $label
170
     *
171
     * @return Field\Select
172
     */
173
    public function select($column, $label = '')
174
    {
175
        $field = new Field\Select($column, $this->formatLabel($label));
176
177
        $this->addField($field);
178
179
        return $field;
180
    }
181
182
    /**
183
     * @param string $column
184
     * @param string $label
185
     *
186
     * @return Field\MultipleSelect
187
     */
188
    public function multipleSelect($column, $label = '')
189
    {
190
        $field = new Field\MultipleSelect($column, $this->formatLabel($label));
191
192
        $this->addField($field);
193
194
        return $field;
195
    }
196
197
    /**
198
     * @param string $column
199
     * @param string $label
200
     *
201
     * @return Field\Checkbox
202
     */
203
    public function checkbox($column, $label = '')
204
    {
205
        $field = new Field\Checkbox($column, $this->formatLabel($label));
206
207
        $this->addField($field);
208
209
        return $field;
210
    }
211
212
    /**
213
     * @param string $column
214
     * @param string $label
215
     *
216
     * @return Field\Radio
217
     */
218
    public function radio($column, $label = '')
219
    {
220
        $field = new Field\Radio($column, $this->formatLabel($label));
221
222
        $this->addField($field);
223
224
        return $field;
225
    }
226
227
    /**
228
     * @param string $column
229
     * @param string $label
230
     *
231
     * @return Field\File
232
     */
233
    public function file($column, $label = '')
234
    {
235
        $field = new Field\File($column, $this->formatLabel($label));
236
237
        $this->addField($field);
238
239
        return $field;
240
    }
241
242
    /**
243
     * @param string $column
244
     * @param string $label
245
     *
246
     * @return Field\MultipleFile
247
     */
248
    public function multipleFile($column, $label = '')
249
    {
250
        $field = new Field\MultipleFile($column, $this->formatLabel($label));
251
252
        $this->addField($field);
253
254
        return $field;
255
    }
256
257
    /**
258
     * @param string $column
259
     * @param string $label
260
     *
261
     * @return Field\Image
262
     */
263
    public function image($column, $label = '')
264
    {
265
        $field = new Field\Image($column, $this->formatLabel($label));
266
267
        $this->addField($field)->setView('admin::actions.form.file');
268
269
        return $field;
270
    }
271
272
    /**
273
     * @param string $column
274
     * @param string $label
275
     *
276
     * @return Field\MultipleImage
277
     */
278
    public function multipleImage($column, $label = '')
279
    {
280
        $field = new Field\MultipleImage($column, $this->formatLabel($label));
281
282
        $this->addField($field)->setView('admin::actions.form.muitplefile');
283
284
        return $field;
285
    }
286
287
    /**
288
     * @param string $column
289
     * @param string $label
290
     *
291
     * @return Field\Date
292
     */
293
    public function date($column, $label = '')
294
    {
295
        $field = new Field\Date($column, $this->formatLabel($label));
296
297
        $this->addField($field);
298
299
        return $field;
300
    }
301
302
    /**
303
     * @param string $column
304
     * @param string $label
305
     *
306
     * @return Field\Date
307
     */
308
    public function datetime($column, $label = '')
309
    {
310
        return $this->date($column, $label)->format('YYYY-MM-DD HH:mm:ss');
311
    }
312
313
    /**
314
     * @param string $column
315
     * @param string $label
316
     *
317
     * @return Field\Date
318
     */
319
    public function time($column, $label = '')
320
    {
321
        return $this->date($column, $label)->format('HH:mm:ss');
322
    }
323
324
    /**
325
     * @param string $column
326
     * @param string $label
327
     *
328
     * @return Field\Hidden
329
     */
330
    public function hidden($column, $label = '')
331
    {
332
        $field = new Field\Hidden($column, $this->formatLabel($label));
333
334
        $this->addField($field);
335
336
        return $field;
337
    }
338
339
    /**
340
     * @param $message
341
     * @return $this
342
     */
343
    public function confirm($message)
344
    {
345
        $this->confirm = $message;
346
347
        return $this;
348
    }
349
350
    /**
351
     * @param string $content
352
     * @param string $selector
353
     *
354
     * @return string
355
     */
356
    public function addElementAttr($content, $selector)
357
    {
358
        $crawler = new Crawler($content);
359
360
        $node = $crawler->filter($selector)->getNode(0);
361
        $node->setAttribute('modal', $this->getModalId());
362
363
        return $crawler->children()->html();
364
    }
365
366
    /**
367
     * @param Field $field
368
     *
369
     * @return Field
370
     */
371 View Code Duplication
    protected function addField(Field $field)
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...
372
    {
373
        $elementClass = array_merge(['action'], $field->getElementClass());
374
375
        $field->addElementClass($elementClass);
376
377
        $field->setView($this->resolveView(get_class($field)));
378
379
        array_push($this->fields, $field);
380
381
        return $field;
382
    }
383
384
    /**
385
     * @param Request $request
386
     *
387
     * @throws ValidationException
388
     * @throws \Exception
389
     *
390
     * @return void
391
     */
392
    public function validate(Request $request)
393
    {
394 View Code Duplication
        if ($this->action instanceof RowAction) {
0 ignored issues
show
Duplication introduced by
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...
395
            call_user_func([$this->action, 'form'], $this->action->getRow());
396
        } else {
397
            call_user_func([$this->action, 'form']);
398
        }
399
400
        $failedValidators = [];
401
402
        /** @var Field $field */
403 View Code Duplication
        foreach ($this->fields as $field) {
0 ignored issues
show
Duplication introduced by
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...
404
            if (!$validator = $field->getValidator($request->all())) {
405
                continue;
406
            }
407
408
            if (($validator instanceof Validator) && !$validator->passes()) {
409
                $failedValidators[] = $validator;
410
            }
411
        }
412
413
        $message = $this->mergeValidationMessages($failedValidators);
414
415
        if ($message->any()) {
416
            throw ValidationException::withMessages($message->toArray());
417
        }
418
    }
419
420
    /**
421
     * Merge validation messages from input validators.
422
     *
423
     * @param \Illuminate\Validation\Validator[] $validators
424
     *
425
     * @return MessageBag
426
     */
427
    protected function mergeValidationMessages($validators)
428
    {
429
        $messageBag = new MessageBag();
430
431
        foreach ($validators as $validator) {
432
            $messageBag = $messageBag->merge($validator->messages());
433
        }
434
435
        return $messageBag;
436
    }
437
438
    /**
439
     * @param string $class
440
     *
441
     * @return string
442
     */
443 View Code Duplication
    protected function resolveView($class)
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...
444
    {
445
        $path = explode('\\', $class);
446
447
        $name = strtolower(array_pop($path));
448
449
        return "admin::actions.form.{$name}";
450
    }
451
452
    /**
453
     * @return void
454
     */
455
    public function addModalHtml()
456
    {
457
        $data = [
458
            'fields'   => $this->fields,
459
            'title'    => $this->action->name(),
460
            'modal_id' => $this->getModalId(),
461
        ];
462
463
        $modal = view('admin::actions.form.modal', $data)->render();
0 ignored issues
show
Bug introduced by
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...
464
465
        Admin::html($modal);
466
    }
467
468
    /**
469
     * @return string
470
     */
471
    public function getModalId()
472
    {
473
        if (!$this->modalId) {
474
            if ($this->action instanceof RowAction) {
475
                $this->modalId = uniqid('row-action-modal-').mt_rand(1000, 9999);
476
            } else {
477
                $this->modalId = strtolower(str_replace('\\', '-', get_class($this->action)));
478
            }
479
        }
480
481
        return $this->modalId;
482
    }
483
484
    /**
485
     * @return void
486
     */
487
    public function addScript()
488
    {
489
        $this->action->attribute('modal', $this->getModalId());
490
491
        $parameters = json_encode($this->action->parameters());
492
493
        $script = <<<SCRIPT
494
495
(function ($) {
496
    $('{$this->action->selector($this->action->selectorPrefix)}').off('{$this->action->event}').on('{$this->action->event}', function() {
497
        var data = $(this).data();
498
        var target = $(this);
499
        var modalId = $(this).attr('modal');
500
        Object.assign(data, {$parameters});
501
        {$this->action->actionScript()}
502
        $('#'+modalId).modal('show');
503
        $('#'+modalId+' form').off('submit').on('submit', function (e) {
504
            e.preventDefault();
505
            var form = this;
506
            {$this->buildActionPromise()}
507
            {$this->action->handleActionPromise()}
508
        });
509
    });
510
})(jQuery);
511
512
SCRIPT;
513
514
        Admin::script($script);
515
    }
516
517
    /**
518
     * @return string
519
     */
520
    protected function buildConfirmActionPromise()
521
    {
522
        $trans = [
523
            'cancel' => trans('admin.cancel'),
524
            'submit' => trans('admin.submit'),
525
        ];
526
527
        $settings = [
528
            'type'                => 'question',
529
            'showCancelButton'    => true,
530
            'showLoaderOnConfirm' => true,
531
            'confirmButtonText'   => $trans['submit'],
532
            'cancelButtonText'    => $trans['cancel'],
533
            'title'               => $this->confirm,
534
            'text'                => '',
535
        ];
536
537
        $settings = trim(substr(json_encode($settings, JSON_PRETTY_PRINT), 1, -1));
538
539
        return <<<PROMISE
540
        var process = $.admin.swal({
541
            {$settings},
542
            preConfirm: function() {
543
                {$this->buildGeneralActionPromise()}
544
545
                return process;
546
            }
547
        }).then(function(result) {
548
549
            if (typeof result.dismiss !== 'undefined') {
550
                return Promise.reject();
551
            }
552
553
            var result = result.value[0];
554
555
            if (typeof result.status === "boolean") {
556
                var response = result;
557
            } else {
558
                var response = result.value;
559
            }
560
561
            return [response, target];
562
        });
563
PROMISE;
564
    }
565
566
    protected function buildGeneralActionPromise()
567
    {
568
        return <<<SCRIPT
569
        var process = new Promise(function (resolve,reject) {
570
            Object.assign(data, {
571
                _token: $.admin.token,
572
                _action: '{$this->action->getCalledClass()}',
573
            });
574
575
            var formData = new FormData(form);
576
            for (var key in data) {
577
                formData.append(key, data[key]);
578
            }
579
580
            $.ajax({
581
                method: '{$this->action->getMethod()}',
582
                url: '{$this->action->getHandleRoute()}',
583
                data: formData,
584
                cache: false,
585
                contentType: false,
586
                processData: false,
587
                success: function (data) {
588
                    resolve([data, target]);
589
                    if (data.status === true) {
590
                        $('#'+modalId).modal('hide');
591
                    }
592
                },
593
                error:function(request){
594
                    reject(request);
595
                }
596
            });
597
        });
598
SCRIPT;
599
    }
600
601
    /**
602
     * @throws \Exception
603
     *
604
     * @return string
605
     */
606
    protected function buildActionPromise()
607
    {
608 View Code Duplication
        if ($this->action instanceof RowAction) {
0 ignored issues
show
Duplication introduced by
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...
609
            call_user_func([$this->action, 'form'], $this->action->getRow());
610
        } else {
611
            call_user_func([$this->action, 'form']);
612
        }
613
614
        $this->addModalHtml();
615
616
        if (!empty($this->confirm)) {
617
            return $this->buildConfirmActionPromise();
618
        }
619
620
        return $this->buildGeneralActionPromise();
621
    }
622
}
623