Completed
Pull Request — master (#647)
by Robbie
20:35 queued 18:30
created

EditableCustomRule::buildExpression()   D

Complexity

Conditions 16
Paths 37

Size

Total Lines 81
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 81
rs 4.9906
c 0
b 0
f 0
cc 16
eloc 56
nc 37
nop 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\UserForms\Model\EditableFormField;
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
 * @package userforms
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
Unused Code introduced by
The property $condition_options is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can 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
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can 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
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can 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
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $extensions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
55
        Versioned::class . "('Stage', 'Live')"
56 1
    ];
57 1
58
    private static $table_name = 'EditableCustomRule';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $table_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can 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 View Code Duplication
    public function canCreate($member = null, $context = [])
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...
95
    {
96
        // Check parent page
97
        $parent = $this->getCanCreateContext(func_get_args());
98
        if ($parent) {
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 View Code Duplication
    protected function getCanCreateContext($args)
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...
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();
0 ignored issues
show
Documentation Bug introduced by
The method ConditionField does not exist on object<SilverStripe\User...del\EditableCustomRule>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
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() == ''";
176
                if ($this->ConditionOption == 'IsNotBlank') {
177 2
                    //Negate
178
                    $expression = "!({$expression})";
179 2
                }
180 2
                break;
181 2
            case 'HasValue':
182
            case 'ValueNot':
183
                if ($checkboxField) {
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) {
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 ($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;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
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 $text
238
     * @return string
239
     */
240
    public function toggleDisplayText($text)
241
    {
242
        return (strtolower($text) === 'hide') ? 'removeClass("hide")' : 'addClass("hide")';
243
    }
244
}
245