Passed
Push — master ( 9f0115...a4cd98 )
by Robbie
02:45
created

EditableCustomRule::buildExpression()   C

Complexity

Conditions 16
Paths 1

Size

Total Lines 80
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 55
nc 1
nop 0
dl 0
loc 80
rs 5.5666
c 0
b 0
f 0

How to fix   Long Method    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 SilverStripe\UserForms\Model;
4
5
use LogicException;
6
use SilverStripe\CMS\Controllers\CMSMain;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\Security\Member;
11
use SilverStripe\Versioned\Versioned;
12
13
/**
14
 * A custom rule for showing / hiding an EditableFormField
15
 * based the value of another EditableFormField.
16
 *
17
 * @method EditableFormField Parent()
18
 * @method EditableFormField ConditionField()
19
 *
20
 * @property string Display
21
 * @property string ConditionOption
22
 * @property string FieldValue
23
 */
24
class EditableCustomRule extends DataObject
25
{
26
    private static $condition_options = [
0 ignored issues
show
introduced by
The private property $condition_options is not used, and could be removed.
Loading history...
27
        'IsBlank' => 'Is blank',
28
        'IsNotBlank' => 'Is not blank',
29
        'HasValue' => 'Equals',
30
        'ValueNot' => 'Doesn\'t equal',
31
        'ValueLessThan' => 'Less than',
32
        'ValueLessThanEqual' => 'Less than or equal',
33
        'ValueGreaterThan' => 'Greater than',
34
        'ValueGreaterThanEqual' => 'Greater than or equal'
35
    ];
36
37
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
38
        'Display' => 'Enum("Show,Hide")',
39
        'ConditionOption' => 'Enum("IsBlank,IsNotBlank,HasValue,ValueNot,ValueLessThan,ValueLessThanEqual,ValueGreaterThan,ValueGreaterThanEqual")',
40
        'FieldValue' => 'Varchar(255)'
41
    ];
42
43
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
44
        'Parent' => EditableFormField::class,
45
        'ConditionField' => EditableFormField::class
46
    ];
47
48
    /**
49
     * Built in extensions required
50
     *
51
     * @config
52
     * @var array
53
     */
54 1
    private static $extensions = [
0 ignored issues
show
introduced by
The private property $extensions is not used, and could be removed.
Loading history...
55
        Versioned::class . "('Stage', 'Live')"
56 1
    ];
57 1
58
    private static $table_name = 'EditableCustomRule';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
59
60
    /**
61
     * @param Member $member
62
     * @return bool
63
     */
64
    public function canDelete($member = null)
65
    {
66
        return $this->canEdit($member);
67
    }
68
69
    /**
70
     * @param Member $member
71
     * @return bool
72
     */
73
    public function canEdit($member = null)
74
    {
75
        return $this->Parent()->canEdit($member);
76
    }
77
78
    /**
79
     * @param Member $member
80
     * @return bool
81
     */
82
    public function canView($member = null)
83
    {
84
        return $this->Parent()->canView($member);
85
    }
86
87
    /**
88
     * Return whether a user can create an object of this type
89
     *
90
     * @param Member $member
91
     * @param array $context Virtual parameter to allow context to be passed in to check
92
     * @return bool
93
     */
94
    public function canCreate($member = null, $context = [])
95
    {
96
        // Check parent page
97
        $parent = $this->getCanCreateContext(func_get_args());
98
        if ($parent) {
0 ignored issues
show
introduced by
$parent is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
99
            return $parent->canEdit($member);
100
        }
101
102
        // Fall back to secure admin permissions
103
        return parent::canCreate($member);
104
    }
105
106
    /**
107
     * Helper method to check the parent for this object
108
     *
109
     * @param array $args List of arguments passed to canCreate
110
     * @return DataObject Some parent dataobject to inherit permissions from
111
     */
112
    protected function getCanCreateContext($args)
113
    {
114
        // Inspect second parameter to canCreate for a 'Parent' context
115
        if (isset($args[1]['Parent'])) {
116
            return $args[1]['Parent'];
117
        }
118
        // Hack in currently edited page if context is missing
119
        if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
120
            return Controller::curr()->currentPage();
121
        }
122
123
        // No page being edited
124
        return null;
125
    }
126
127
    /**
128
     * @param Member $member
129
     * @return bool
130
     */
131
    public function canPublish($member = null)
132
    {
133
        return $this->canEdit($member);
134
    }
135
136
    /**
137
     * @param Member $member
138
     * @return bool
139
     */
140
    public function canUnpublish($member = null)
141
    {
142
        return $this->canDelete($member);
143
    }
144
145
    /**
146
     * Substitutes configured rule logic with it's JS equivalents and returns them as array elements
147
     *
148
     * @return array
149
     * @throws LogicException If the provided condition option was not able to be handled
150
     */
151
    public function buildExpression()
152
    {
153
        /** @var EditableFormField $formFieldWatch */
154
        $formFieldWatch = $this->ConditionField();
155
        //Encapsulated the action to the object
156
        $action = $formFieldWatch->getJsEventHandler();
157
158
        // is this field a special option field
159 2
        $checkboxField = $formFieldWatch->isCheckBoxField();
160
        $radioField = $formFieldWatch->isRadioField();
161
        $target = sprintf('$("%s")', $formFieldWatch->getSelectorFieldOnly());
162 2
        $fieldValue = Convert::raw2js($this->FieldValue);
163
164 2
        $conditionOptions = [
165
            'ValueLessThan'         => '<',
166
            'ValueLessThanEqual'    => '<=',
167 2
            'ValueGreaterThan'      => '>',
168 2
            'ValueGreaterThanEqual' => '>='
169 2
        ];
170 2
171
        // and what should we evaluate
172
        switch ($this->ConditionOption) {
173 2
            case 'IsNotBlank':
174 2
            case 'IsBlank':
175 2
                $expression = ($checkboxField || $radioField) ? "!{$target}.is(\":checked\")" : "{$target}.val() == ''";
0 ignored issues
show
introduced by
The condition $radioField is always false.
Loading history...
176
                if ((string) $this->ConditionOption === 'IsNotBlank') {
177 2
                    //Negate
178
                    $expression = "!({$expression})";
179 2
                }
180 2
                break;
181 2
            case 'HasValue':
182
            case 'ValueNot':
183
                if ($checkboxField) {
0 ignored issues
show
introduced by
The condition $checkboxField is always false.
Loading history...
184
                    if ($formFieldWatch->isCheckBoxGroupField()) {
185
                        $expression = sprintf(
186
                            "$.inArray('%s', %s.filter(':checked').map(function(){ return $(this).val();}).get()) > -1",
187
                            $fieldValue,
188 2
                            $target
189 2
                        );
190 2
                    } else {
191
                        $expression = "{$target}.prop('checked')";
192
                    }
193
                } elseif ($radioField) {
0 ignored issues
show
introduced by
The condition $radioField is always false.
Loading history...
194
                    // We cannot simply get the value of the radio group, we need to find the checked option first.
195
                    $expression = sprintf(
196
                        '%s.closest(".field, .control-group").find("input:checked").val() == "%s"',
197
                        $target,
198
                        $fieldValue
199
                    );
200 2
                } else {
201
                    $expression = sprintf('%s.val() == "%s"', $target, $fieldValue);
202
                }
203
204
                if ((string) $this->ConditionOption === 'ValueNot') {
205
                    //Negate
206
                    $expression = "!({$expression})";
207
                }
208 2
                break;
209
            case 'ValueLessThan':
210
            case 'ValueLessThanEqual':
211 2
            case 'ValueGreaterThan':
212
            case 'ValueGreaterThanEqual':
213
                $expression = sprintf(
214
                    '%s.val() %s parseFloat("%s")',
215 2
                    $target,
216 1
                    $conditionOptions[$this->ConditionOption],
217 1
                    $fieldValue
218 1
                );
219 1
                break;
220 1
            default:
221 1
                throw new LogicException("Unhandled rule {$this->ConditionOption}");
222 1
                break;
223 1
        }
224
225 1
        $result = [
226 1
            'operation' => $expression,
227
            'event'     => $action,
228
        ];
229
230 2
        return $result;
231
    }
232
233 2
    /**
234 2
     * Returns the opposite visibility function for the value of the initial visibility field, e.g. show/hide. This
235 2
     * will toggle the "hide" class either way, which is handled by CSS.
236
     *
237 2
     * @param string $initialState
238
     * @param boolean $invert
239
     * @return string
240
     */
241
    public function toggleDisplayText($initialState, $invert = false)
242
    {
243
        $action = strtolower($initialState) === 'hide' ? 'removeClass' : 'addClass';
244
        if ($invert) {
245
            $action = $action === 'removeClass' ? 'addClass' : 'removeClass';
246
        }
247 2
        return sprintf('%s("hide")', $action);
248
    }
249 2
250
    /**
251
     * Returns an event name to be dispatched when the field is changed. Matches up with the visibility classes
252 1
     * added or removed in `toggleDisplayText()`.
253
     *
254
     * @param string $initialState
255
     * @param bool $invert
256
     * @return string
257
     */
258
    public function toggleDisplayEvent($initialState, $invert = false)
259
    {
260
        $action = strtolower($initialState) === 'hide' ? 'show' : 'hide';
261
        if ($invert) {
262
            $action = $action === 'hide' ? 'show' : 'hide';
263
        }
264
        return sprintf('userform.field.%s', $action);
265
    }
266
}
267