Completed
Pull Request — master (#1350)
by
unknown
03:06
created

Form::render()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Widgets;
4
5
use Encore\Admin\Form\Field;
6
use Illuminate\Contracts\Support\Arrayable;
7
use Illuminate\Contracts\Support\Renderable;
8
use Illuminate\Contracts\Validation\Validator;
9
use Illuminate\Support\Facades\Input;
10
use Illuminate\Support\MessageBag;
11
use phpDocumentor\Reflection\Types\This;
12
13
/**
14
 * Class Form.
15
 *
16
 * @method Field\Text           text($name, $label = '')
17
 * @method Field\Password       password($name, $label = '')
18
 * @method Field\Checkbox       checkbox($name, $label = '')
19
 * @method Field\Radio          radio($name, $label = '')
20
 * @method Field\Select         select($name, $label = '')
21
 * @method Field\MultipleSelect multipleSelect($name, $label = '')
22
 * @method Field\Textarea       textarea($name, $label = '')
23
 * @method Field\Hidden         hidden($name, $label = '')
24
 * @method Field\Id             id($name, $label = '')
25
 * @method Field\Ip             ip($name, $label = '')
26
 * @method Field\Url            url($name, $label = '')
27
 * @method Field\Color          color($name, $label = '')
28
 * @method Field\Email          email($name, $label = '')
29
 * @method Field\Mobile         mobile($name, $label = '')
30
 * @method Field\Slider         slider($name, $label = '')
31
 * @method Field\Map            map($latitude, $longitude, $label = '')
32
 * @method Field\Editor         editor($name, $label = '')
33
 * @method Field\File           file($name, $label = '')
34
 * @method Field\Image          image($name, $label = '')
35
 * @method Field\Date           date($name, $label = '')
36
 * @method Field\Datetime       datetime($name, $label = '')
37
 * @method Field\Time           time($name, $label = '')
38
 * @method Field\DateRange      dateRange($start, $end, $label = '')
39
 * @method Field\DateTimeRange  dateTimeRange($start, $end, $label = '')
40
 * @method Field\TimeRange      timeRange($start, $end, $label = '')
41
 * @method Field\Number         number($name, $label = '')
42
 * @method Field\Currency       currency($name, $label = '')
43
 * @method Field\Json           json($name, $label = '')
44
 * @method Field\SwitchField    switch ($name, $label = '')
45
 * @method Field\Display        display($name, $label = '')
46
 * @method Field\Rate           rate($name, $label = '')
47
 * @method Field\Divide         divide()
48
 * @method Field\Decimal        decimal($column, $label = '')
49
 * @method Field\Html           html($html)
50
 * @method Field\Tags           tags($column, $label = '')
51
 * @method Field\Icon           icon($column, $label = '')
52
 * @method \App\Admin\Extensions\Form\Script script($script, $arguments)
53
 */
54
class Form implements Renderable
55
{
56
57
    public static $VALID = "valid";
58
59
    /**
60
     * @var Field[]
61
     */
62
    protected $fields = [];
63
64
    /**
65
     * @var array
66
     */
67
    protected $data = [];
68
69
    /**
70
     * @var array
71
     */
72
    protected $attributes = [];
73
74
    /**
75
     * Form constructor.
76
     *
77
     * @param array $data
78
     */
79
    public function __construct($data = [])
80
    {
81
        if ($data instanceof Arrayable) {
82
            $data = $data->toArray();
83
        }
84
85
        if (!empty($data)) {
86
            $this->data = $data;
87
        }
88
89
        $this->initFormAttributes();
90
    }
91
92
    /**
93
     * Initialize the form attributes.
94
     */
95
    protected function initFormAttributes()
96
    {
97
        $this->attributes = [
98
            'method'         => 'POST',
99
            'action'         => '',
100
            'class'          => 'form-horizontal',
101
            'accept-charset' => 'UTF-8',
102
            'pjax-container' => true,
103
            'fieldWidth'     => 8,
104
            'labelWidth'     => 2,
105
        ];
106
    }
107
108
    /**
109
     * Action uri of the form.
110
     *
111
     * @param string $action
112
     *
113
     * @return $this
114
     */
115
    public function action($action)
116
    {
117
        return $this->attribute('action', $action);
118
    }
119
120
    /**
121
     * Method of the form.
122
     *
123
     * @param string $method
124
     *
125
     * @return $this
126
     */
127
    public function method($method = 'POST')
128
    {
129
        return $this->attribute('method', strtoupper($method));
130
    }
131
132
    /**
133
     * Add form attributes.
134
     *
135
     * @param string|array $attr
136
     * @param string       $value
137
     *
138
     * @return $this
139
     */
140
    public function attribute($attr, $value = '')
141
    {
142
        if (is_array($attr)) {
143
            foreach ($attr as $key => $value) {
144
                $this->attribute($key, $value);
145
            }
146
        } else {
147
            $this->attributes[$attr] = $value;
148
        }
149
150
        return $this;
151
    }
152
153
    /**
154
     * Disable Pjax.
155
     *
156
     * @return $this
157
     */
158
    public function disablePjax()
159
    {
160
        array_forget($this->attributes, 'pjax-container');
161
162
        return $this;
163
    }
164
165
    /**
166
     * Set field and label width in current form.
167
     *
168
     * @param int $fieldWidth
169
     * @param int $labelWidth
170
     *
171
     * @return $this
172
     */
173
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
174
    {
175
        collect($this->fields)->each(function ($field) use ($fieldWidth, $labelWidth) {
176
            /* @var Field $field */
177
            $field->setWidth($fieldWidth, $labelWidth);
178
        });
179
        $this->attributes['fieldWidth'] = $fieldWidth;
180
        $this->attributes['labelWidth'] = $labelWidth;
181
182
        return $this;
183
    }
184
185
    /**
186
     * Find field class with given name.
187
     *
188
     * @param string $method
189
     *
190
     * @return bool|string
191
     */
192 View Code Duplication
    public static function findFieldClass($method)
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...
193
    {
194
        $class = array_get(\Encore\Admin\Form::$availableFields, $method);
195
196
        if (class_exists($class)) {
197
            return $class;
198
        }
199
200
        return false;
201
    }
202
203
    /**
204
     * Add a form field to form.
205
     *
206
     * @param Field $field
207
     *
208
     * @return $this
209
     */
210
    protected function pushField(Field &$field)
211
    {
212
        array_push($this->fields, $field);
213
214
        return $this;
215
    }
216
217
    /**
218
     * Get variables for render form.
219
     *
220
     * @return array
221
     */
222
    protected function getVariables()
223
    {
224
        foreach ($this->fields as $field) {
225
            $field->fill($this->data);
226
        }
227
228
        return [
229
            'fields'     => $this->fields,
230
            'attributes' => $this->formatAttribute(),
231
            'method'     => $this->attributes['method'],
232
            'fieldWidth' => $this->attributes['fieldWidth'],
233
            'labelWidth' => $this->attributes['labelWidth'],
234
            'rules' => $this->getRules(),
235
            'rules_message' => $this->getRuleMessages()
236
        ];
237
    }
238
239
    /**
240
     * Format form attributes form array to html.
241
     *
242
     * @param array $attributes
243
     *
244
     * @return string
245
     */
246
    public function formatAttribute($attributes = [])
247
    {
248
        $attributes = $attributes ?: $this->attributes;
249
250
        if ($this->hasFile()) {
251
            $attributes['enctype'] = 'multipart/form-data';
252
        }
253
254
        $html = [];
255
        foreach ($attributes as $key => $val) {
256
            $html[] = "$key=\"$val\"";
257
        }
258
259
        return implode(' ', $html) ?: '';
260
    }
261
262
    /**
263
     * Determine if form fields has files.
264
     *
265
     * @return bool
266
     */
267
    public function hasFile()
268
    {
269
        foreach ($this->fields as $field) {
270
            if ($field instanceof Field\File) {
271
                return true;
272
            }
273
        }
274
275
        return false;
276
    }
277
278
    /**
279
     * Get validation messages.
280
     *
281
     * @param array $input
282
     *
283
     * @return MessageBag|bool
284
     */
285 View Code Duplication
    protected function validationMessages($input)
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...
286
    {
287
        $failedValidators = [];
288
289
        foreach ($this->fields as $field) {
290
            if (!$validator = $field->getValidator($input)) {
291
                continue;
292
            }
293
294
            if (($validator instanceof Validator) && !$validator->passes()) {
295
                $failedValidators[] = $validator;
296
            }
297
        }
298
299
        $message = $this->mergeValidationMessages($failedValidators);
300
301
        return $message->any() ? $message : false;
302
    }
303
304
    /**
305
     * Merge validation messages from input validators.
306
     *
307
     * @param \Illuminate\Validation\Validator[] $validators
308
     *
309
     * @return MessageBag
310
     */
311
    protected function mergeValidationMessages($validators)
312
    {
313
        $messageBag = new MessageBag();
314
315
        foreach ($validators as $validator) {
316
            $messageBag = $messageBag->merge($validator->messages());
317
        }
318
319
        return $messageBag;
320
    }
321
322
    /**
323
     * @return bool|Form|\Illuminate\Http\RedirectResponse
324
     */
325
    public function validate()
326
    {
327
        $data = Input::all();
328
329
        // Handle validation errors.
330
        if ($validationMessages = $this->validationMessages($data)) {
331
            return back()->withInput()->withErrors($validationMessages);
332
        }
333
        return self::$VALID;
334
    }
335
    /**
336
     * Collect rules of all fields.
337
     *
338
     * @return array
339
     */
340 View Code Duplication
    public function getRules()
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...
341
    {
342
        $rules = [];
343
        foreach ($this->fields as $item) {
344
            if(!empty($item->getRules())){
345
                $rules[$item->id] = $item->getRules();
346
            }
347
        }
348
        $this->Rules = $rules;
0 ignored issues
show
Bug introduced by
The property Rules does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
349
        return $rules;
350
    }
351
352
    /**
353
     * Collect validationMessages of all fields.
354
     *
355
     * @return array
356
     */
357 View Code Duplication
    public function getRuleMessages()
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...
358
    {
359
        $rules = [];
360
        foreach ($this->fields as $item ) {
361
            foreach ($item->validationMessages as $key => $value) {
362
                $rules[$key] = $value;
363
            }
364
        }
365
        $this->RuleMessages = $rules;
0 ignored issues
show
Bug introduced by
The property RuleMessages does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
366
        return $rules;
367
    }
368
369
    /**
370
     * Generate a Field object and add to form builder if Field exists.
371
     *
372
     * @param string $method
373
     * @param array  $arguments
374
     *
375
     * @return Field|null
376
     */
377
    public function __call($method, $arguments)
378
    {
379
        if ($className = static::findFieldClass($method)) {
380
            $name = array_get($arguments, 0, '');
381
382
            $element = new $className($name, array_slice($arguments, 1));
383
384
            $this->pushField($element);
385
386
            return $element;
387
        }
388
    }
389
390
    /**
391
     * Render the form.
392
     *
393
     * @return string
394
     */
395
    public function render()
396
    {
397
        return view('admin::widgets.form', $this->getVariables())->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...
398
    }
399
400
    /**
401
     * Output as string.
402
     *
403
     * @return string
404
     */
405
    public function __toString()
406
    {
407
        return $this->render();
408
    }
409
}
410