Completed
Push — master ( d80d0a...d3563c )
by Damian
06:07 queued 03:57
created

StringTagField   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 333
Duplicated Lines 8.41 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 8
Bugs 1 Features 2
Metric Value
wmc 35
c 8
b 1
f 2
lcom 1
cbo 13
dl 28
loc 333
rs 9

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getShouldLazyLoad() 0 4 1
A setShouldLazyLoad() 0 6 1
A getLazyLoadItemLimit() 0 4 1
A setLazyLoadItemLimit() 0 6 1
A getIsMultiple() 0 4 1
A setIsMultiple() 0 6 1
A getRecord() 0 12 3
A setRecord() 0 6 1
B Field() 28 28 3
A getSuggestURL() 0 4 1
B getOptions() 0 24 3
B setValue() 0 21 5
A getAttributes() 0 7 1
A saveInto() 0 9 1
B suggest() 0 26 3
B getTags() 0 34 6
A validate() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * Provides a tagging interface, storing comma-delimited tags in a DataObject string field.
5
 *
6
 * This is intended bridge the gap between 1.x and 2.x, and when possible TagField should be used
7
 * instead.
8
 *
9
 * @package forms
10
 * @subpackage fields
11
 */
12
class StringTagField extends DropdownField
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
13
{
14
    /**
15
     * @var array
16
     */
17
    public static $allowed_actions = array(
18
        'suggest',
19
    );
20
21
    /**
22
     * @var bool
23
     */
24
    protected $shouldLazyLoad = false;
25
26
    /**
27
     * @var int
28
     */
29
    protected $lazyLoadItemLimit = 10;
30
31
    /**
32
     * @var null|DataObject
33
     */
34
    protected $record;
35
36
    /**
37
     * @var bool
38
     */
39
    protected $isMultiple = true;
40
41
    /**
42
     * @param string $name
43
     * @param string $title
44
     * @param array|SS_List $source
45
     * @param array|SS_List $value
46
     */
47
    public function __construct($name, $title = '', $source = array(), $value = array())
48
    {
49
        parent::__construct($name, $title, $source, $value);
0 ignored issues
show
Documentation introduced by
$value is of type array|object<SS_List>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
50
    }
51
52
    /**
53
     * @return bool
54
     */
55
    public function getShouldLazyLoad()
56
    {
57
        return $this->shouldLazyLoad;
58
    }
59
60
    /**
61
     * @param bool $shouldLazyLoad
62
     *
63
     * @return static
64
     */
65
    public function setShouldLazyLoad($shouldLazyLoad)
66
    {
67
        $this->shouldLazyLoad = $shouldLazyLoad;
68
69
        return $this;
70
    }
71
72
    /**
73
     * @return int
74
     */
75
    public function getLazyLoadItemLimit()
76
    {
77
        return $this->lazyLoadItemLimit;
78
    }
79
80
    /**
81
     * @param int $lazyLoadItemLimit
82
     *
83
     * @return static
84
     */
85
    public function setLazyLoadItemLimit($lazyLoadItemLimit)
86
    {
87
        $this->lazyLoadItemLimit = $lazyLoadItemLimit;
88
89
        return $this;
90
    }
91
92
    /**
93
     * @return bool
94
     */
95
    public function getIsMultiple()
96
    {
97
        return $this->isMultiple;
98
    }
99
100
    /**
101
     * @param bool $isMultiple
102
     *
103
     * @return static
104
     */
105
    public function setIsMultiple($isMultiple)
106
    {
107
        $this->isMultiple = $isMultiple;
108
109
        return $this;
110
    }
111
112
    /**
113
     * @return null|DataObject
114
     */
115
    public function getRecord()
116
    {
117
        if ($this->record) {
118
            return $this->record;
119
        }
120
121
        if ($form = $this->getForm()) {
122
            return $form->getRecord();
123
        }
124
125
        return null;
126
    }
127
128
    /**
129
     * @param DataObject $record
130
     *
131
     * @return $this
132
     */
133
    public function setRecord(DataObject $record)
134
    {
135
        $this->record = $record;
136
137
        return $this;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143 View Code Duplication
    public function Field($properties = array())
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...
144
    {
145
        Requirements::css(TAG_FIELD_DIR . '/css/select2.min.css');
146
        Requirements::css(TAG_FIELD_DIR . '/css/TagField.css');
147
148
        Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
149
        Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
150
        Requirements::javascript(TAG_FIELD_DIR . '/js/select2.js');
151
        Requirements::javascript(TAG_FIELD_DIR . '/js/TagField.js');
152
153
        $this->addExtraClass('ss-tag-field');
154
155
        if ($this->getIsMultiple()) {
156
            $this->setAttribute('multiple', 'multiple');
157
        }
158
159
        if ($this->getShouldLazyLoad()) {
160
            $this->setAttribute('data-ss-tag-field-suggest-url', $this->getSuggestURL());
161
        } else {
162
            $properties = array_merge($properties, array(
163
                'Options' => $this->getOptions()
164
            ));
165
        }
166
167
        return $this
168
            ->customise($properties)
169
            ->renderWith(array("templates/TagField"));
170
    }
171
172
    /**
173
     * @return string
174
     */
175
    protected function getSuggestURL()
176
    {
177
        return Controller::join_links($this->Link(), 'suggest');
178
    }
179
180
    /**
181
     * @return ArrayList
182
     */
183
    protected function getOptions()
184
    {
185
        $options = ArrayList::create();
186
187
        $source = $this->getSource();
188
189
        if ($source instanceof Iterator) {
190
            $source = iterator_to_array($source);
191
        }
192
193
        $values = $this->Value();
194
195
        foreach ($source as $value) {
196
            $options->push(
197
                ArrayData::create(array(
198
                    'Title' => $value,
199
                    'Value' => $value,
200
                    'Selected' => in_array($value, $values),
201
                ))
202
            );
203
        }
204
205
        return $options;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function setValue($value, $source = null)
212
    {
213
        if (is_string($value)) {
214
            $value = explode(',', $value);
215
        }
216
217
        if ($source instanceof DataObject) {
218
            $name = $this->getName();
219
            $value = explode(',', $source->$name);
220
        }
221
222
        if ($source instanceof SS_List) {
223
            $value = $source->column('ID');
224
        }
225
226
        if (is_null($value)) {
227
            $value = array();
228
        }
229
230
        return parent::setValue(array_filter($value));
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     */
236
    public function getAttributes()
237
    {
238
        return array_merge(
239
            parent::getAttributes(),
240
            array('name' => $this->getName() . '[]')
241
        );
242
    }
243
244
    /**
245
     * {@inheritdoc}
246
     */
247
    public function saveInto(DataObjectInterface $record)
248
    {
249
        parent::saveInto($record);
250
251
        $name = $this->getName();
252
253
        $record->$name = join(',', $this->Value());
254
        $record->write();
255
    }
256
257
    /**
258
     * Returns a JSON string of tags, for lazy loading.
259
     *
260
     * @param SS_HTTPRequest $request
261
     *
262
     * @return SS_HTTPResponse
263
     */
264
    public function suggest(SS_HTTPRequest $request)
265
    {
266
        $responseBody = Convert::raw2json(
267
            array('items' => array())
268
        );
269
270
        $response = new SS_HTTPResponse();
271
        $response->addHeader('Content-Type', 'application/json');
272
273
        if ($record = $this->getRecord()) {
274
            $tags = array();
275
            $term = $request->getVar('term');
276
277
            if ($record->hasField($this->getName())) {
278
                $tags = $this->getTags($term);
279
            }
280
281
            $responseBody = Convert::raw2json(
282
                array('items' => $tags)
283
            );
284
        }
285
286
        $response->setBody($responseBody);
287
288
        return $response;
289
    }
290
291
    /**
292
     * Returns array of arrays representing tags.
293
     *
294
     * @param string $term
295
     *
296
     * @return array
297
     */
298
    protected function getTags($term)
299
    {
300
        $record = $this->getRecord();
301
302
        if (!$record) {
303
            return array();
304
        }
305
306
        $fieldName = $this->getName();
307
        $className = $record->getClassName();
308
309
        $term = Convert::raw2sql($term);
310
311
        $query = $className::get()
312
            ->filter($fieldName . ':PartialMatch:nocase', $term)
313
            ->limit($this->getLazyLoadItemLimit());
314
315
        $items = array();
316
317
        foreach ($query->column($fieldName) as $tags) {
318
            $tags = explode(',', $tags);
319
320
            foreach ($tags as $i => $tag) {
321
                if (stripos($tag, $term) !== false && !in_array($tag, $items)) {
322
                    $items[] = array(
323
                        'id' => $tag,
324
                        'text' => $tag
325
                    );
326
                }
327
            }
328
        }
329
330
        return $items;
331
    }
332
333
    /**
334
     * DropdownField assumes value will be a scalar so we must
335
     * override validate. This only applies to Silverstripe 3.2+
336
     *
337
     * @param Validator $validator
338
     * @return bool
339
     */
340
    public function validate($validator)
341
    {
342
        return true;
343
    }
344
}
345