Completed
Pull Request — master (#106)
by
unknown
03:26
created

StringTagField::suggest()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
c 0
b 0
f 0
rs 8.8571
cc 3
eloc 13
nc 3
nop 1
1
<?php
2
3
namespace SilverStripe\TagField;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Control\HTTPRequest;
7
use SilverStripe\Control\HTTPResponse;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\Forms\DropdownField;
10
use SilverStripe\ORM\ArrayList;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\DataObjectInterface;
13
use SilverStripe\ORM\SS_List;
14
use SilverStripe\View\ArrayData;
15
use SilverStripe\View\Requirements;
16
17
/**
18
 * Provides a tagging interface, storing comma-delimited tags in a DataObject string field.
19
 *
20
 * This is intended bridge the gap between 1.x and 2.x, and when possible TagField should be used
21
 * instead.
22
 *
23
 * @package    tagfield
24
 * @subpackage fields
25
 */
26
class StringTagField extends DropdownField
27
{
28
    /**
29
     * @var array
30
     */
31
    private static $allowed_actions = [
32
        'suggest'
33
    ];
34
35
    /**
36
     * @var bool
37
     */
38
    protected $shouldLazyLoad = false;
39
40
    /**
41
     * @var int
42
     */
43
    protected $lazyLoadItemLimit = 10;
44
45
    /**
46
     * @var bool
47
     */
48
    protected $canCreate = true;
49
50
    /**
51
     * @var null|DataObject
52
     */
53
    protected $record;
54
55
    /**
56
     * @var bool
57
     */
58
    protected $isMultiple = true;
59
60
    /**
61
     * @return bool
62
     */
63
    public function getShouldLazyLoad()
64
    {
65
        return $this->shouldLazyLoad;
66
    }
67
68
    /**
69
     * @param bool $shouldLazyLoad
70
     *
71
     * @return static
72
     */
73
    public function setShouldLazyLoad($shouldLazyLoad)
74
    {
75
        $this->shouldLazyLoad = $shouldLazyLoad;
76
77
        return $this;
78
    }
79
80
    /**
81
     * @return int
82
     */
83
    public function getLazyLoadItemLimit()
84
    {
85
        return $this->lazyLoadItemLimit;
86
    }
87
88
    /**
89
     * @param int $lazyLoadItemLimit
90
     *
91
     * @return static
92
     */
93
    public function setLazyLoadItemLimit($lazyLoadItemLimit)
94
    {
95
        $this->lazyLoadItemLimit = $lazyLoadItemLimit;
96
97
        return $this;
98
    }
99
100
    /**
101
     * @return bool
102
     */
103
    public function getIsMultiple()
104
    {
105
        return $this->isMultiple;
106
    }
107
108
    /**
109
     * @param bool $isMultiple
110
     *
111
     * @return static
112
     */
113
    public function setIsMultiple($isMultiple)
114
    {
115
        $this->isMultiple = $isMultiple;
116
117
        return $this;
118
    }
119
120
    /**
121
     * @return null|DataObject
122
     */
123
    public function getRecord()
124
    {
125
        if ($this->record) {
126
            return $this->record;
127
        }
128
129
        if ($form = $this->getForm()) {
130
            return $form->getRecord();
131
        }
132
133
        return null;
134
    }
135
136
    /**
137
     * @param DataObject $record
138
     *
139
     * @return $this
140
     */
141
    public function setRecord(DataObject $record)
142
    {
143
        $this->record = $record;
144
145
        return $this;
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function Field($properties = array())
152
    {
153
        Requirements::css('silverstripe/tagfield:css/select2.min.css');
154
        Requirements::css('silverstripe/tagfield:css/TagField.css');
155
156
        Requirements::javascript('silverstripe/tagfield:js/select2.js');
157
        Requirements::javascript('silverstripe/tagfield:js/TagField.js');
158
159
        $this->addExtraClass('ss-tag-field');
160
161
        if ($this->getIsMultiple()) {
162
            $this->setAttribute('multiple', 'multiple');
163
        }
164
165
        if ($this->getShouldLazyLoad()) {
166
            $this->setAttribute('data-ss-tag-field-suggest-url', $this->getSuggestURL());
167
        } else {
168
            $properties = array_merge($properties, array(
169
                'Options' => $this->getOptions()
170
            ));
171
        }
172
173
        $this->setAttribute('data-can-create', (int) $this->getCanCreate());
174
175
        return $this
176
            ->customise($properties)
177
            ->renderWith(TagField::class);
178
    }
179
180
    /**
181
     * @return string
182
     */
183
    protected function getSuggestURL()
184
    {
185
        return Controller::join_links($this->Link(), 'suggest');
186
    }
187
188
    /**
189
     * @return ArrayList
190
     */
191
    protected function getOptions()
192
    {
193
        $options = ArrayList::create();
194
195
        $source = $this->getSource();
196
197
        if ($source instanceof Iterator) {
0 ignored issues
show
Bug introduced by
The type SilverStripe\TagField\Iterator was not found. Did you mean Iterator? If so, make sure to prefix the type with \.
Loading history...
198
            $source = iterator_to_array($source);
199
        }
200
201
        $values = $this->Value();
202
203
        foreach ($source as $value) {
204
            $options->push(
205
                ArrayData::create(array(
206
                    'Title' => $value,
207
                    'Value' => $value,
208
                    'Selected' => in_array($value, $values),
209
                ))
210
            );
211
        }
212
213
        return $options;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function setValue($value, $source = null)
220
    {
221
        if (is_string($value)) {
222
            $value = explode(',', $value);
223
        }
224
225
        if ($source instanceof DataObject) {
226
            $name = $this->getName();
227
            $value = explode(',', $source->$name);
228
        }
229
230
        if ($source instanceof SS_List) {
231
            $value = $source->column('ID');
232
        }
233
234
        if (is_null($value)) {
235
            $value = array();
236
        }
237
238
        return parent::setValue(array_filter($value));
239
    }
240
241
    /**
242
     * {@inheritdoc}
243
     */
244
    public function getAttributes()
245
    {
246
        return array_merge(
247
            parent::getAttributes(),
248
            array('name' => $this->getName() . '[]')
249
        );
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255
    public function saveInto(DataObjectInterface $record)
256
    {
257
        parent::saveInto($record);
258
259
        $name = $this->getName();
260
261
        $record->$name = join(',', $this->Value());
262
        $record->write();
263
    }
264
265
    /**
266
     * Returns a JSON string of tags, for lazy loading.
267
     *
268
     * @param  HTTPRequest $request
269
     * @return HTTPResponse
270
     */
271
    public function suggest(HTTPRequest $request)
272
    {
273
        $responseBody = Convert::raw2json(
274
            array('items' => array())
275
        );
276
277
        $response = new HTTPResponse;
278
        $response->addHeader('Content-Type', 'application/json');
279
280
        if ($record = $this->getRecord()) {
281
            $tags = array();
282
            $term = $request->getVar('term');
283
284
            if ($record->hasField($this->getName())) {
285
                $tags = $this->getTags($term);
286
            }
287
288
            $responseBody = Convert::raw2json(
289
                array('items' => $tags)
290
            );
291
        }
292
293
        $response->setBody($responseBody);
294
295
        return $response;
296
    }
297
298
    /**
299
     * Returns array of arrays representing tags.
300
     *
301
     * @param string $term
302
     *
303
     * @return array
304
     */
305
    protected function getTags($term)
306
    {
307
        $record = $this->getRecord();
308
309
        if (!$record) {
310
            return array();
311
        }
312
313
        $fieldName = $this->getName();
314
        $className = $record->getClassName();
315
316
        $term = Convert::raw2sql($term);
317
318
        $query = $className::get()
319
            ->filter($fieldName . ':PartialMatch:nocase', $term)
320
            ->limit($this->getLazyLoadItemLimit());
321
322
        $items = array();
323
324
        foreach ($query->column($fieldName) as $tags) {
325
            $tags = explode(',', $tags);
326
327
            foreach ($tags as $i => $tag) {
328
                if (stripos($tag, $term) !== false && !in_array($tag, $items)) {
329
                    $items[] = array(
330
                        'id' => $tag,
331
                        'text' => $tag
332
                    );
333
                }
334
            }
335
        }
336
337
        return $items;
338
    }
339
340
    /**
341
     * DropdownField assumes value will be a scalar so we must
342
     * override validate. This only applies to Silverstripe 3.2+
343
     *
344
     * @param Validator $validator
0 ignored issues
show
Bug introduced by
The type SilverStripe\TagField\Validator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
345
     * @return bool
346
     */
347
    public function validate($validator)
348
    {
349
        return true;
350
    }
351
352
    /**
353
     * @return bool
354
     */
355
    public function getCanCreate()
356
    {
357
        return $this->canCreate;
358
    }
359
360
    /**
361
     * @param bool $canCreate
362
     *
363
     * @return static
364
     */
365
    public function setCanCreate($canCreate)
366
    {
367
        $this->canCreate = $canCreate;
368
369
        return $this;
370
    }
371
    
372
}
373