Passed
Push — master ( bf8b66...f610dd )
by Andreas
10:08
created

grid   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 311
Duplicated Lines 0 %

Test Coverage

Coverage 92.54%

Importance

Changes 0
Metric Value
eloc 128
dl 0
loc 311
ccs 124
cts 134
cp 0.9254
rs 9.2
c 0
b 0
f 0
wmc 40

16 Methods

Rating   Name   Duplication   Size   Complexity  
A add_head_elements() 0 26 3
A get_identifier() 0 3 1
A __construct() 0 6 1
A get_provider() 0 3 1
A set_provider() 0 3 1
A prepend_js() 0 3 1
B __toString() 0 41 7
A get_option() 0 6 2
A remove_column() 0 10 4
A set_column() 0 11 2
A set_footer_data() 0 9 2
A render() 0 11 3
A set_option() 0 8 3
A set_select_column() 0 17 2
A _render_colmodel() 0 27 6
A add_pager() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like grid often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use grid, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package midcom.grid
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
namespace midcom\grid;
10
11
use midcom;
12
use midcom_error;
13
14
/**
15
 * Helper class for jqgrid widgets
16
 *
17
 * @package midcom.grid
18
 */
19
class grid
20
{
21
    /**
22
     * The grid's ID
23
     */
24
    private string $_identifier;
25
26
    /**
27
     * The grid's options (converted for use in JS constructor)
28
     */
29
    private array $_options = [];
30
31
    /**
32
     * The grid's options as passed in PHP
33
     */
34
    private array $_raw_options = [];
35
36
    /**
37
     * The grid's columns
38
     *
39
     * They have the following structure
40
     *
41
     * 'key' => [
42
     *     'label' => "Some label",
43
     *     'options' => 'javascript option string'
44
     * ]
45
     */
46
    private array $_columns = [];
47
48
    /**
49
     * Flag that tracks if JS/CSS files have already been added
50
     */
51
    private static bool $_head_elements_added = false;
52
53
    /**
54
     * Data for the table footer
55
     */
56
    private array $_footer_data = [];
57
58
    /**
59
     * Should formatters be applied to footer row
60
     */
61
    private bool $format_footer = true;
62
63
    /**
64
     * The data provider, if any
65
     */
66
    private ?provider $_provider = null;
67
68
    /**
69
     * Javascript code that should be prepended to the widget constructor
70
     */
71
    private string $_prepend_js = '';
72
73
    /**
74
     * Adds the necessary javascript & css files for jqgrid
75
     */
76 38
    public static function add_head_elements()
77
    {
78 38
        if (self::$_head_elements_added) {
79 37
            return;
80
        }
81 1
        $version = '4.15.4';
82 1
        $jqgrid_path = '/midcom.grid/jqGrid-' . $version . '/';
83
84 1
        $head = midcom::get()->head;
85 1
        $head->enable_jquery_ui(['button', 'mouse', 'resizable']);
86
87
        //needed js/css-files for jqgrid
88 1
        $lang = "en";
89 1
        $language = midcom::get()->i18n->get_current_language();
90 1
        if (file_exists(MIDCOM_STATIC_ROOT . $jqgrid_path . 'i18n/grid.locale-' . $language . '.js')) {
91 1
            $lang = $language;
92
        }
93 1
        $head->add_jsfile(MIDCOM_STATIC_URL . $jqgrid_path . 'i18n/grid.locale-'. $lang . '.js');
94 1
        $head->add_jsfile(MIDCOM_STATIC_URL . $jqgrid_path . 'jquery.jqgrid.min.js');
95
96 1
        $head->add_jsfile(MIDCOM_STATIC_URL . '/midcom.grid/jqGrid.custom.js');
97
98 1
        $head->add_stylesheet(MIDCOM_STATIC_URL . $jqgrid_path . 'ui.jqgrid.min.css');
99 1
        $head->add_stylesheet(MIDCOM_STATIC_URL . "/stock-icons/font-awesome-4.7.0/css/font-awesome.min.css");
100 1
        $head->add_stylesheet(MIDCOM_STATIC_URL . '/midcom.grid/jqGrid.custom.css');
101 1
        self::$_head_elements_added = true;
102
    }
103
104
    /**
105
     * Constructor. Add head elements when necessary and the ID column
106
     */
107 37
    public function __construct(string $identifier, string $datatype)
108
    {
109 37
        $this->_identifier = $identifier;
110 37
        $this->set_column('id', 'id', 'hidden:true, key:true');
111 37
        $this->set_option('datatype', $datatype);
112 37
        self::add_head_elements();
113
    }
114
115 32
    public function set_provider(provider $provider)
116
    {
117 32
        $this->_provider = $provider;
118
    }
119
120 3
    public function get_provider()
121
    {
122 3
        return $this->_provider;
123
    }
124
125
    /**
126
     * Returns the grid's ID
127
     */
128 31
    public function get_identifier() : string
129
    {
130 31
        return $this->_identifier;
131
    }
132
133
    /**
134
     * Set an option
135
     */
136 37
    public function set_option(string $key, $value, bool $autoquote_string = true) : self
137
    {
138 37
        $this->_raw_options[$key] = $value;
139 37
        if ($autoquote_string || !is_string($value)) {
140 37
            $value = json_encode($value);
141
        }
142 37
        $this->_options[$key] = $value;
143 37
        return $this;
144
    }
145
146 26
    public function get_option(string $key)
147
    {
148 26
        if (empty($this->_raw_options[$key])) {
149 25
            return null;
150
        }
151 11
        return $this->_raw_options[$key];
152
    }
153
154
    /**
155
     * Set a column
156
     *
157
     * @param array $selectdata Should the column have a separate index, if so, which sort type
158
     */
159 10
    public function set_select_column(string $name, string $label, string $options, array $selectdata) : self
160
    {
161 10
        $selectstring = implode(';', array_map(
162 10
            function ($key, $value) {
163 10
                return $key . ':' . $value;
164 10
            },
165 10
            array_keys($selectdata),
166 10
            $selectdata
167 10
        ));
168
169 10
        if ($options !== '') {
170 8
            $options .= ', ';
171
        }
172 10
        $options .= 'stype: "select", searchoptions: {sopt: ["eq"], value: ":;' . $selectstring . '"}';
173 10
        $options .= ', edittype:"select", formatter:"select", editoptions:{value:"' . $selectstring . '"}';
174
175 10
        return $this->set_column($name, $label, $options);
176
    }
177
178
    /**
179
     * Set a column
180
     *
181
     * @param string $separate_index Should the column have a separate index, if so, which sort type
182
     */
183 37
    public function set_column(string $name, string $label, string $options = '', $separate_index = false) : self
184
    {
185 37
        if (empty($name)) {
186
            throw new midcom_error('Invalid column name ' . $name);
187
        }
188 37
        $this->_columns[$name] = [
189 37
            'label' => $label,
190 37
            'options' => $options,
191 37
            'separate_index' => $separate_index
192 37
        ];
193 37
        return $this;
194
    }
195
196 2
    public function add_pager(int $rows_per_page = 30) : self
197
    {
198 2
        $this->set_option('pager', '#p_' . $this->_identifier);
199 2
        $this->set_option('rowNum', $rows_per_page);
200 2
        return $this;
201
    }
202
203
    /**
204
     * Removes a column
205
     */
206
    public function remove_column(string $name)
207
    {
208
        if (   empty($name)
209
            || !array_key_exists($name, $this->_columns)) {
210
            throw new midcom_error('Invalid column name ' . $name);
211
        }
212
        if ($this->_columns[$name]['separate_index']) {
213
            unset($this->_columns[$name . '_index']);
214
        }
215
        unset($this->_columns[$name]);
216
    }
217
218
    /**
219
     * Set the grid's footer data
220
     *
221
     * @param mixed $data The data to set as array or the column name
222
     * @param mixed $value The value, if setting individual columns
223
     * @param boolean $formatted Should formatters be applied to footer data
224
     */
225 17
    public function set_footer_data($data, $value = null, bool $formatted = true)
226
    {
227 17
        if (null == $value) {
228 17
            $this->_footer_data = $data;
229
        } else {
230
            $this->_footer_data[$data] = $value;
231
        }
232 17
        $this->format_footer = $formatted;
233 17
        $this->set_option('footerrow', true);
234
    }
235
236
    /**
237
     * Add Javascript code that should be run before the widget constructor
238
     */
239 25
    public function prepend_js(string $string)
240
    {
241 25
        $this->_prepend_js .= $string . "\n";
242
    }
243
244
    /**
245
     * Renders the grid as HTML
246
     */
247 33
    public function render(?array $entries = null)
248
    {
249 33
        if (is_array($entries)) {
250 10
            if (null !== $this->_provider) {
251
                $this->_provider->set_rows($entries);
252
            } else {
253 10
                $this->_provider = new provider($entries, $this->get_option('datatype'));
0 ignored issues
show
Bug introduced by
It seems like $this->get_option('datatype') can also be of type null; however, parameter $datatype of midcom\grid\provider::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

253
                $this->_provider = new provider($entries, /** @scrutinizer ignore-type */ $this->get_option('datatype'));
Loading history...
254 10
                $this->_provider->set_grid($this);
255
            }
256
        }
257 33
        echo (string) $this;
258
    }
259
260 34
    public function __toString()
261
    {
262 34
        if ($this->_provider) {
263 31
            $this->_provider->setup_grid();
264
        }
265
266 34
        $string = '<table id="' . $this->_identifier . '"></table>';
267 34
        $string .= '<div id="p_' . $this->_identifier . '"></div>';
268 34
        $string .= '<script type="text/javascript">//<![CDATA[' . "\n";
269 34
        $string .= $this->_prepend_js;
270 34
        $string .= 'midcom_grid_helper.setup_grid("' . $this->_identifier . '", {';
271
272 34
        $colnames = [];
273 34
        foreach ($this->_columns as $name => $column) {
274 34
            if ($column['separate_index']) {
275 32
                $colnames[] = 'index_' . $name;
276
            }
277 34
            $colnames[] = $column['label'];
278
        }
279 34
        $string .= "\ncolNames: " . json_encode($colnames) . ",\n";
280
281 34
        $string .= $this->_render_colmodel();
282
283 34
        $total_options = count($this->_options);
284 34
        $i = 0;
285 34
        foreach ($this->_options as $name => $value) {
286 34
            $string .= $name . ': ' . $value;
287 34
            if (++$i < $total_options) {
288 33
                $string .= ',';
289
            }
290 34
            $string .= "\n";
291
        }
292 34
        $string .= "});\n";
293
294 34
        if ($this->_footer_data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_footer_data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
295 17
            $format = json_encode($this->format_footer);
296 17
            $string .= 'jQuery("#' . $this->_identifier . '").jqGrid("footerData", "set", ' . json_encode($this->_footer_data) . ", " . $format . ");\n";
297
        }
298
299 34
        $string .= '//]]></script>';
300 34
        return $string;
301
    }
302
303 34
    private function _render_colmodel() : string
304
    {
305 34
        $string = "colModel: [\n";
306 34
        $total_columns = count($this->_columns);
307 34
        $i = 0;
308 34
        foreach ($this->_columns as $name => $column) {
309 34
            if ($column['separate_index']) {
310 32
                $string .= '{name: "index_' . $name . '", index: "index_' . $name . '", ';
311 32
                $string .= 'sorttype: "' . $column['separate_index'] . '", hidden: true}' . ",\n";
312
            }
313
314 34
            $string .= '{name: "' . $name . '", ';
315 34
            if ($column['separate_index']) {
316 32
                $string .= 'index: "index_' . $name . '"';
317
            } else {
318 34
                $string .= 'index: "' . $name . '"';
319
            }
320 34
            if (!empty($column['options'])) {
321 34
                $string .= ', ' . $column['options'];
322
            }
323 34
            $string .= '}';
324 34
            if (++$i < $total_columns) {
325 33
                $string .= ",\n";
326
            }
327
        }
328 34
        $string .= "\n],\n";
329 34
        return $string;
330
    }
331
}
332