Completed
Push — master ( 303741...88bbbb )
by Song
03:46 queued 01:20
created

CanCascadeFields::hitsCondition()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 12
nop 1
dl 0
loc 35
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Encore\Admin\Form\Field;
4
5
use Encore\Admin\Admin;
6
use Encore\Admin\Form;
7
use Illuminate\Support\Arr;
8
9
/**
10
 * @property Form $form
11
 */
12
trait CanCascadeFields
13
{
14
    /**
15
     * @var array
16
     */
17
    protected $conditions = [];
18
19
    /**
20
     * @param $operator
21
     * @param $value
22
     * @param $closure
23
     *
24
     * @return $this
25
     */
26
    public function when($operator, $value, $closure = null)
27
    {
28
        if (func_num_args() == 2) {
29
            $closure = $value;
30
            $value = $operator;
31
            $operator = '=';
32
        }
33
34
        $this->formatValues($operator, $value);
35
36
        $this->addDependents($operator, $value, $closure);
37
38
        return $this;
39
    }
40
41
    /**
42
     * @param string $operator
43
     * @param mixed  $value
44
     */
45
    protected function formatValues(string $operator, &$value)
46
    {
47
        if (in_array($operator, ['in', 'notIn'])) {
48
            $value = Arr::wrap($value);
49
        }
50
51
        if (is_array($value)) {
52
            $value = array_map('strval', $value);
53
        } else {
54
            $value = strval($value);
55
        }
56
    }
57
58
    /**
59
     * @param string   $operator
60
     * @param mixed    $value
61
     * @param \Closure $closure
62
     */
63
    protected function addDependents(string $operator, $value, \Closure $closure)
64
    {
65
        $this->conditions[] = compact('operator', 'value', 'closure');
66
67
        $this->form->cascadeGroup($closure, [
68
            'column' => $this->column(),
0 ignored issues
show
Bug introduced by
It seems like column() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
69
            'index'  => count($this->conditions) - 1,
70
            'class'  => $this->getCascadeClass($value),
71
        ]);
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function fill($data)
78
    {
79
        parent::fill($data);
80
81
        $this->applyCascadeConditions();
82
    }
83
84
    /**
85
     * @param mixed $value
86
     *
87
     * @return string
88
     */
89
    protected function getCascadeClass($value)
90
    {
91
        if (is_array($value)) {
92
            $value = implode('-', $value);
93
        }
94
95
        return sprintf('cascade-%s-%s', $this->getElementClassString(), $value);
0 ignored issues
show
Bug introduced by
It seems like getElementClassString() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
96
    }
97
98
    /**
99
     * Apply conditions to dependents fields.
100
     *
101
     * @return void
102
     */
103
    protected function applyCascadeConditions()
104
    {
105
        if( $this->form ) {
106
            $this->form->fields()
107
                ->filter(function (Form\Field $field) {
108
                    return $field instanceof CascadeGroup
109
                        && $field->dependsOn($this)
110
                        && $this->hitsCondition($field);
111
                })->each->visiable();
112
        }
113
    }
114
115
    /**
116
     * @param CascadeGroup $group
117
     *
118
     * @throws \Exception
119
     *
120
     * @return bool
121
     */
122
    protected function hitsCondition(CascadeGroup $group)
123
    {
124
        $condition = $this->conditions[$group->index()];
125
126
        extract($condition);
127
128
        $old = old($this->column(), $this->value());
0 ignored issues
show
Bug introduced by
It seems like column() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
Bug introduced by
It seems like value() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
129
130
        switch ($operator) {
131
            case '=':
132
                return $old == $value;
133
            case '>':
134
                return $old > $value;
135
            case '<':
136
                return $old < $value;
137
            case '>=':
138
                return $old >= $value;
139
            case '<=':
140
                return $old <= $value;
141
            case '!=':
142
                return $old != $value;
143
            case 'in':
144
                return in_array($old, $value);
145
            case 'notIn':
146
                return !in_array($old, $value);
147
            case 'has':
148
                return in_array($value, $old);
149
            case 'oneIn':
150
                return count(array_intersect($value, $old)) >= 1;
151
            case 'oneNotIn':
152
                return count(array_intersect($value, $old)) == 0;
153
            default:
154
                throw new \Exception("Operator [$operator] not support.");
155
        }
156
    }
157
158
    /**
159
     * Add cascade scripts to contents.
160
     *
161
     * @return void
162
     */
163
    protected function addCascadeScript()
164
    {
165
        if (empty($this->conditions)) {
166
            return;
167
        }
168
169
        $cascadeGroups = collect($this->conditions)->map(function ($condition) {
170
            return [
171
                'class'    => $this->getCascadeClass($condition['value']),
172
                'operator' => $condition['operator'],
173
                'value'    => $condition['value'],
174
            ];
175
        })->toJson();
176
177
        $script = <<<SCRIPT
178
;(function () {
179
    var operator_table = {
180
        '=': function(a, b) {
181
            if ($.isArray(a) && $.isArray(b)) {
182
                return $(a).not(b).length === 0 && $(b).not(a).length === 0;
183
            }
184
185
            return a == b;
186
        },
187
        '>': function(a, b) { return a > b; },
188
        '<': function(a, b) { return a < b; },
189
        '>=': function(a, b) { return a >= b; },
190
        '<=': function(a, b) { return a <= b; },
191
        '!=': function(a, b) {
192
             if ($.isArray(a) && $.isArray(b)) {
193
                return !($(a).not(b).length === 0 && $(b).not(a).length === 0);
194
             }
195
196
             return a != b;
197
        },
198
        'in': function(a, b) { return $.inArray(a, b) != -1; },
199
        'notIn': function(a, b) { return $.inArray(a, b) == -1; },
200
        'has': function(a, b) { return $.inArray(b, a) != -1; },
201
        'oneIn': function(a, b) { return a.filter(v => b.includes(v)).length >= 1; },
202
        'oneNotIn': function(a, b) { return a.filter(v => b.includes(v)).length == 0; },
203
    };
204
    var cascade_groups = {$cascadeGroups};
205
    $('{$this->getElementClassSelector()}').on('{$this->cascadeEvent}', function (e) {
0 ignored issues
show
Bug introduced by
The property cascadeEvent 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...
Bug introduced by
It seems like getElementClassSelector() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
206
207
        {$this->getFormFrontValue()}
208
209
        cascade_groups.forEach(function (event) {
210
            var group = $('div.cascade-group.'+event.class);
211
            if( operator_table[event.operator](checked, event.value) ) {
212
                group.removeClass('hide');
213
            } else {
214
                group.addClass('hide');
215
            }
216
        });
217
    })
218
})();
219
SCRIPT;
220
221
        Admin::script($script);
222
    }
223
224
    /**
225
     * @return string
226
     */
227
    protected function getFormFrontValue()
228
    {
229
        switch (get_class($this)) {
230
            case Radio::class:
231
            case RadioButton::class:
232
            case RadioCard::class:
233
            case Select::class:
234
            case BelongsTo::class:
235
            case BelongsToMany::class:
236
            case MultipleSelect::class:
237
                return 'var checked = $(this).val();';
238
            case Checkbox::class:
239
            case CheckboxButton::class:
240
            case CheckboxCard::class:
241
                return <<<SCRIPT
242
var checked = $('{$this->getElementClassSelector()}:checked').map(function(){
0 ignored issues
show
Bug introduced by
It seems like getElementClassSelector() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
243
  return $(this).val();
244
}).get();
245
SCRIPT;
246
            default:
247
                throw new \InvalidArgumentException('Invalid form field type');
248
        }
249
    }
250
}
251