Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — pass-column-subfields-through-... ( 7988ae )
by Pedro
13:16 queued 44s
created

CrudField::on()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel;
4
5
use Backpack\CRUD\app\Library\CrudPanel\Support\HasSubfields;
6
use Backpack\CRUD\app\Library\CrudPanel\Traits\Support\MacroableWithAttributes;
7
use Illuminate\Support\Traits\Conditionable;
8
9
/**
10
 * Adds fluent syntax to Backpack CRUD Fields.
11
 *
12
 * In addition to the existing:
13
 * - CRUD::addField(['name' => 'price', 'type' => 'number']);
14
 *
15
 * Developers can also do:
16
 * - CRUD::field('price')->type('number');
17
 *
18
 * And if the developer uses CrudField as Field in their CrudController:
19
 * - Field::name('price')->type('number');
20
 *
21
 * @method self type(string $value)
22
 * @method self label(string $value)
23
 * @method self tab(string $value)
24
 * @method self prefix(string $value)
25
 * @method self suffix(string $value)
26
 * @method self default(mixed $value)
27
 * @method self hint(string $value)
28
 * @method self attributes(array $value)
29
 * @method self wrapper(array $value)
30
 * @method self fake(bool $value)
31
 * @method self store_in(string $value)
32
 * @method self validationRules(string $value)
33
 * @method self validationMessages(array $value)
34
 * @method self entity(string $value)
35
 * @method self addMorphOption(string $key, string $label, array $options)
36
 * @method self morphTypeField(array $value)
37
 * @method self morphIdField(array $value)
38
 * @method self upload(bool $value)
39
 */
40
class CrudField
41
{
42
    use MacroableWithAttributes;
0 ignored issues
show
Bug introduced by
The trait Backpack\CRUD\app\Librar...MacroableWithAttributes requires the property $name which is not provided by Backpack\CRUD\app\Library\CrudPanel\CrudField.
Loading history...
43
    use Conditionable;
44
    use HasSubfields;
45
46
    protected $attributes;
47
48
    public function __construct($nameOrDefinitionArray)
49
    {
50
        if (empty($nameOrDefinitionArray)) {
51
            abort(500, 'Field name can\'t be empty.', ['developer-error-exception']);
52
        }
53
54
        if (is_array($nameOrDefinitionArray)) {
55
            $this->crud()->addField($nameOrDefinitionArray);
56
            $name = $nameOrDefinitionArray['name'];
57
        } else {
58
            $name = $nameOrDefinitionArray;
59
        }
60
61
        if (is_array($name)) {
62
            abort(500, 'Field name can\'t be an array. It should be a string. Error in field: '.json_encode($name), ['developer-error-exception']);
63
        }
64
65
        $field = $this->crud()->firstFieldWhere('name', $name);
66
67
        // if field exists
68
        if ((bool) $field) {
69
            // use all existing attributes
70
            $this->setAllAttributeValues($field);
0 ignored issues
show
Bug introduced by
$field of type boolean is incompatible with the type array expected by parameter $array of Backpack\CRUD\app\Librar...setAllAttributeValues(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
            $this->setAllAttributeValues(/** @scrutinizer ignore-type */ $field);
Loading history...
71
        } else {
72
            // it means we're creating the field now,
73
            // so at the very least set the name attribute
74
            $this->setAttributeValue('name', $name);
75
        }
76
77
        $this->save();
78
    }
79
80
    public function crud()
81
    {
82
        return app()->make('crud');
83
    }
84
85
    /**
86
     * Create a CrudField object with the parameter as its name.
87
     *
88
     * @param  string  $name  Name of the column in the db, or model attribute.
89
     * @return CrudField
90
     */
91
    public static function name($name)
92
    {
93
        return new static($name);
94
    }
95
96
    /**
97
     * When defining the entity, make sure Backpack guesses the relationship attributes if needed.
98
     *
99
     * @param  string|bool  $entity
100
     * @return self
101
     */
102
    public function entity($entity)
103
    {
104
        $this->attributes['entity'] = $entity;
105
106
        if ($entity !== false) {
107
            $this->attributes = $this->crud()->makeSureFieldHasRelationshipAttributes($this->attributes);
108
        }
109
110
        return $this->save();
111
    }
112
113
    /**
114
     * Remove the current field from the current operation.
115
     *
116
     * @return void
117
     */
118
    public function remove()
119
    {
120
        $this->crud()->removeField($this->attributes['name']);
121
    }
122
123
    /**
124
     * Remove an attribute from the current field definition array.
125
     *
126
     * @param  string  $attribute  Name of the attribute being removed.
127
     * @return CrudField
128
     */
129
    public function forget($attribute)
130
    {
131
        $this->crud()->removeFieldAttribute($this->attributes['name'], $attribute);
132
133
        return $this;
134
    }
135
136
    /**
137
     * Move the current field after another field.
138
     *
139
     * @param  string  $destinationField  Name of the destination field.
140
     * @return CrudField
141
     */
142
    public function after($destinationField)
143
    {
144
        $this->crud()->removeField($this->attributes['name']);
145
        $this->crud()->addField($this->attributes)->afterField($destinationField);
146
147
        return $this;
148
    }
149
150
    /**
151
     * Move the current field before another field.
152
     *
153
     * @param  string  $destinationField  Name of the destination field.
154
     * @return CrudField
155
     */
156
    public function before($destinationField)
157
    {
158
        $this->crud()->removeField($this->attributes['name']);
159
        $this->crud()->addField($this->attributes)->beforeField($destinationField);
160
161
        return $this;
162
    }
163
164
    /**
165
     * Make the current field the first one in the fields list.
166
     *
167
     * @return CrudField
168
     */
169
    public function makeFirst()
170
    {
171
        $this->crud()->removeField($this->attributes['name']);
172
        $this->crud()->addField($this->attributes)->makeFirstField();
173
174
        return $this;
175
    }
176
177
    /**
178
     * Make the current field the last one in the fields list.
179
     *
180
     * @return CrudField
181
     */
182
    public function makeLast()
183
    {
184
        $this->crud()->removeField($this->attributes['name']);
185
        $this->crud()->addField($this->attributes);
186
187
        return $this;
188
    }
189
190
    // -------------------
191
    // CONVENIENCE METHODS
192
    // -------------------
193
    // These methods don't do exactly what advertised by their name.
194
    // They exist because the original syntax was too long.
195
196
    /**
197
     * Set the wrapper width at this many number of columns.
198
     * For example, to set a field wrapper to span across 6 columns, you can do both:
199
     * ->wrapper(['class' => 'form-group col-md-6'])
200
     * ->size(6).
201
     *
202
     * @param  int  $numberOfColumns  How many columns should this field span across (1-12)?
203
     * @return CrudField
204
     */
205
    public function size($numberOfColumns)
206
    {
207
        $this->attributes['wrapper']['class'] = 'form-group col-md-'.$numberOfColumns.' mb-3';
208
209
        return $this->save();
210
    }
211
212
    /**
213
     * Set an event to a certain closure. Will overwrite if existing.
214
     *
215
     * @param  string  $event  Name of Eloquent Model event
216
     * @param  \Closure  $closure  The function aka callback aka closure to run.
217
     * @return CrudField
218
     */
219
    public function on(string $event, \Closure $closure)
220
    {
221
        $this->attributes['events'][$event] = $closure;
222
223
        return $this->save();
224
    }
225
226
    /**
227
     * Mark the field has having upload functionality, so that the form would become multipart.
228
     *
229
     * @param  bool  $upload
230
     * @return self
231
     */
232
    public function upload($upload = true)
233
    {
234
        $this->attributes['upload'] = $upload;
235
236
        return $this->save();
237
    }
238
239
    /**
240
     * Save the validation rules on the CrudPanel per field basis.
241
     *
242
     * @param  string  $rules  the field rules: required|min:1|max:5
243
     * @return self
244
     */
245
    public function validationRules(string $rules)
246
    {
247
        $this->attributes['validationRules'] = $rules;
248
        $this->crud()->setValidationFromArray([$this->attributes['name'] => $rules]);
249
250
        return $this;
251
    }
252
253
    /**
254
     * Save the validation messages on the CrudPanel per field basis.
255
     *
256
     * @param  array  $messages  the messages for field rules: [required => please input something, min => the minimum allowed is 1]
257
     * @return self
258
     */
259
    public function validationMessages(array $messages)
260
    {
261
        $this->attributes['validationMessages'] = $messages;
262
263
        // append the field name to the rule name of validationMessages array.
264
        // eg: ['required => 'This field is required']
265
        // will be transformed into: ['field_name.required' => 'This field is required]
266
        $this->crud()->setValidationFromArray([], array_merge(...array_map(function ($rule, $message) {
267
            return [$this->attributes['name'].'.'.$rule => $message];
268
        }, array_keys($messages), $messages)));
269
270
        return $this;
271
    }
272
273
    /**
274
     * This function is responsible for setting up the morph fields structure.
275
     * Developer can define the morph structure as follows:
276
     *  'morphOptions => [
277
     *       ['nameOnAMorphMap', 'label', [options]],
278
     *       ['App\Models\Model'], // display the name of the model
279
     *       ['App\Models\Model', 'label', ['data_source' => backpack_url('smt')]
280
     *  ]
281
     * OR
282
     * ->addMorphOption('App\Models\Model', 'label', ['data_source' => backpack_url('smt')]).
283
     *
284
     * @param  string  $key  - the morph option key, usually a \Model\Class or a string for the morphMap
285
     * @param  string|null  $label  - the displayed text for this option
286
     * @param  array  $options  - options for the corresponding morphable_id field (usually ajax options)
287
     * @return self
288
     *
289
     * @throws \Exception
290
     */
291
    public function addMorphOption(string $key, $label = null, array $options = [])
292
    {
293
        $this->crud()->addMorphOption($this->attributes['name'], $key, $label, $options);
294
295
        return $this;
296
    }
297
298
    /**
299
     * Allow developer to configure the morph type field.
300
     *
301
     * @param  array  $configs
302
     * @return self
303
     *
304
     * @throws \Exception
305
     */
306
    public function morphTypeField(array $configs)
307
    {
308
        $morphField = $this->crud()->fields()[$this->attributes['name']];
309
310
        if (empty($morphField) || ($morphField['relation_type'] ?? '') !== 'MorphTo') {
311
            abort(500, 'Trying to configure the morphType on a non-morphTo field. Check if field and relation name matches.', ['developer-error-exception']);
312
        }
313
        [$morphTypeField, $morphIdField] = $morphField['subfields'];
314
315
        $morphTypeField = array_merge($morphTypeField, $configs);
316
317
        $morphField['subfields'] = [$morphTypeField, $morphIdField];
318
319
        $this->crud()->modifyField($this->attributes['name'], $morphField);
320
321
        return $this;
322
    }
323
324
    /**
325
     * Allow developer to configure the morph type id selector.
326
     *
327
     * @param  array  $configs
328
     * @return self
329
     *
330
     * @throws \Exception
331
     */
332
    public function morphIdField(array $configs)
333
    {
334
        $morphField = $this->crud()->fields()[$this->attributes['name']];
335
336
        if (empty($morphField) || ($morphField['relation_type'] ?? '') !== 'MorphTo') {
337
            abort(500, 'Trying to configure the morphType on a non-morphTo field. Check if field and relation name matches.', ['developer-error-exception']);
338
        }
339
340
        [$morphTypeField, $morphIdField] = $morphField['subfields'];
341
342
        $morphIdField = array_merge($morphIdField, $configs);
343
344
        $morphField['subfields'] = [$morphTypeField, $morphIdField];
345
346
        $this->crud()->modifyField($this->attributes['name'], $morphField);
347
348
        return $this;
349
    }
350
351
    public function getAttributes()
352
    {
353
        return $this->attributes;
354
    }
355
356
    // ---------------
357
    // PRIVATE METHODS
358
    // ---------------
359
360
    /**
361
     * Set the value for a certain attribute on the CrudField object.
362
     *
363
     * @param  string  $attribute  Name of the attribute.
364
     * @param  mixed  $value  Value of that attribute.
365
     */
366
    private function setAttributeValue($attribute, $value)
367
    {
368
        $this->attributes[$attribute] = $value;
369
    }
370
371
    /**
372
     * Replace all field attributes on the CrudField object
373
     * with the given array of attribute-value pairs.
374
     *
375
     * @param  array  $array  Array of attributes and their values.
376
     */
377
    private function setAllAttributeValues($array)
378
    {
379
        $this->attributes = $array;
380
    }
381
382
    /**
383
     * Update the global CrudPanel object with the current field attributes.
384
     *
385
     * @return CrudField
386
     */
387
    private function save()
388
    {
389
        $key = $this->attributes['name'];
390
391
        if ($this->crud()->hasFieldWhere('name', $key)) {
392
            $this->crud()->modifyField($key, $this->attributes);
393
        } else {
394
            $this->crud()->addField($this->attributes);
395
            $this->attributes = $this->getFreshAttributes();
396
        }
397
398
        return $this;
399
    }
400
401
    /**
402
     * Get the fresh attributes for the current field.
403
     *
404
     * @return array
405
     */
406
    private function getFreshAttributes()
407
    {
408
        $key = isset($this->attributes['key']) ? 'key' : 'name';
409
        $search = $this->attributes['key'] ?? $this->attributes['name'];
410
411
        return $this->crud()->firstFieldWhere($key, $search);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->crud()->fi...eldWhere($key, $search) returns the type boolean which is incompatible with the documented return type array.
Loading history...
412
    }
413
414
    // -----------------
415
    // DEBUGGING METHODS
416
    // -----------------
417
418
    /**
419
     * Dump the current object to the screen,
420
     * so that the developer can see its contents.
421
     *
422
     * @codeCoverageIgnore
423
     *
424
     * @return CrudField
425
     */
426
    public function dump()
427
    {
428
        dump($this);
429
430
        return $this;
431
    }
432
433
    /**
434
     * Dump and die. Dumps the current object to the screen,
435
     * so that the developer can see its contents, then stops
436
     * the execution.
437
     *
438
     * @codeCoverageIgnore
439
     *
440
     * @return CrudField
441
     */
442
    public function dd()
443
    {
444
        dd($this);
445
446
        return $this;
447
    }
448
449
    // -------------
450
    // MAGIC METHODS
451
    // -------------
452
453
    /**
454
     * If a developer calls a method that doesn't exist, assume they want:
455
     * - the CrudField object to have an attribute with that value;
456
     * - that field be updated inside the global CrudPanel object;.
457
     *
458
     * Eg: type('number') will set the "type" attribute to "number"
459
     *
460
     * @param  string  $method  The method being called that doesn't exist.
461
     * @param  array  $parameters  The arguments when that method was called.
462
     * @return CrudField
463
     */
464
    public function __call($method, $parameters)
465
    {
466
        if (static::hasMacro($method)) {
467
            return $this->macroCall($method, $parameters);
0 ignored issues
show
Bug introduced by
The method macroCall() does not exist on Backpack\CRUD\app\Library\CrudPanel\CrudField. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

467
            return $this->/** @scrutinizer ignore-call */ macroCall($method, $parameters);
Loading history...
468
        }
469
470
        $this->setAttributeValue($method, $parameters[0]);
471
472
        return $this->save();
473
    }
474
}
475