Completed
Push — master ( dbdbdd...f0b48d )
by Daniel
24:40 queued 13:42
created

code/TagField.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Provides a tagging interface, storing links between tag DataObjects and a parent DataObject.
5
 *
6
 * @package forms
7
 * @subpackage fields
8
 */
9
class TagField extends DropdownField
10
{
11
    /**
12
     * @var array
13
     */
14
    public static $allowed_actions = array(
15
        'suggest',
16
    );
17
18
    /**
19
     * @var bool
20
     */
21
    protected $shouldLazyLoad = false;
22
23
    /**
24
     * @var int
25
     */
26
    protected $lazyLoadItemLimit = 10;
27
28
    /**
29
     * @var bool
30
     */
31
    protected $canCreate = true;
32
33
    /**
34
     * @var string
35
     */
36
    protected $titleField = 'Title';
37
38
    /**
39
     * @var bool
40
     */
41
    protected $isMultiple = true;
42
43
    /**
44
     * @param string $name
45
     * @param string $title
46
     * @param null|DataList $source
47
     * @param null|DataList $value
48
     */
49
    public function __construct($name, $title = '', $source = null, $value = null)
50
    {
51
        parent::__construct($name, $title, $source, $value);
52
    }
53
54
    /**
55
     * @return bool
56
     */
57
    public function getShouldLazyLoad()
58
    {
59
        return $this->shouldLazyLoad;
60
    }
61
62
    /**
63
     * @param bool $shouldLazyLoad
64
     *
65
     * @return static
66
     */
67
    public function setShouldLazyLoad($shouldLazyLoad)
68
    {
69
        $this->shouldLazyLoad = $shouldLazyLoad;
70
71
        return $this;
72
    }
73
74
    /**
75
     * @return int
76
     */
77
    public function getLazyLoadItemLimit()
78
    {
79
        return $this->lazyLoadItemLimit;
80
    }
81
82
    /**
83
     * @param int $lazyLoadItemLimit
84
     *
85
     * @return static
86
     */
87
    public function setLazyLoadItemLimit($lazyLoadItemLimit)
88
    {
89
        $this->lazyLoadItemLimit = $lazyLoadItemLimit;
90
91
        return $this;
92
    }
93
94
    /**
95
     * @return bool
96
     */
97
    public function getIsMultiple()
98
    {
99
        return $this->isMultiple;
100
    }
101
102
    /**
103
     * @param bool $isMultiple
104
     *
105
     * @return static
106
     */
107
    public function setIsMultiple($isMultiple)
108
    {
109
        $this->isMultiple = $isMultiple;
110
111
        return $this;
112
    }
113
114
    /**
115
     * @return bool
116
     */
117
    public function getCanCreate()
118
    {
119
        return $this->canCreate;
120
    }
121
122
    /**
123
     * @param bool $canCreate
124
     *
125
     * @return static
126
     */
127
    public function setCanCreate($canCreate)
128
    {
129
        $this->canCreate = $canCreate;
130
131
        return $this;
132
    }
133
134
    /**
135
     * @return string
136
     */
137
    public function getTitleField()
138
    {
139
        return $this->titleField;
140
    }
141
142
    /**
143
     * @param string $titleField
144
     *
145
     * @return $this
146
     */
147
    public function setTitleField($titleField)
148
    {
149
        $this->titleField = $titleField;
150
151
        return $this;
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157 View Code Duplication
    public function Field($properties = array())
158
    {
159
        Requirements::css(TAG_FIELD_DIR . '/css/select2.min.css');
160
        Requirements::css(TAG_FIELD_DIR . '/css/TagField.css');
161
162
        Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
163
        Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
164
        Requirements::javascript(TAG_FIELD_DIR . '/js/select2.js');
165
        Requirements::javascript(TAG_FIELD_DIR . '/js/TagField.js');
166
167
        $this->addExtraClass('ss-tag-field');
168
169
        if ($this->getIsMultiple()) {
170
            $this->setAttribute('multiple', 'multiple');
171
        }
172
173
        if ($this->shouldLazyLoad) {
174
            $this->setAttribute('data-ss-tag-field-suggest-url', $this->getSuggestURL());
175
        } else {
176
            $properties = array_merge($properties, array(
177
                'Options' => $this->getOptions()
178
            ));
179
        }
180
181
        return $this
182
            ->customise($properties)
183
            ->renderWith(array("templates/TagField"));
184
    }
185
186
    /**
187
     * @return string
188
     */
189
    protected function getSuggestURL()
190
    {
191
        return Controller::join_links($this->Link(), 'suggest');
192
    }
193
194
    /**
195
     * @return ArrayList
196
     */
197
    protected function getOptions()
198
    {
199
        $options = ArrayList::create();
200
201
        $source = $this->getSource();
202
203
        if (!$source) {
204
            $source = new ArrayList();
205
        }
206
207
        $dataClass = $source->dataClass();
208
209
        $values = $this->Value();
0 ignored issues
show
$values is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
210
211
        // Mark selected tags while still returning a full list of possible options
212
        $ids = array(); // empty fallback array for comparing
213
        $values = $this->Value();
214
        if($values){
215
            // @TODO conversion from array to DataList to array...(?)
216
            if(is_array($values)) {
217
                $values = DataList::create($dataClass)->filter('ID', $values);
218
            }
219
            $ids = $values->column('ID');
220
        }
221
222
        $titleField = $this->getTitleField();
223
224
        foreach ($source as $object) {
225
            $options->push(
226
                ArrayData::create(array(
227
                    'Title' => $object->$titleField,
228
                    'Value' => $object->ID,
229
                    'Selected' => in_array($object->ID, $ids),
230
                ))
231
            );
232
        }
233
234
        return $options;
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function setValue($value, $source = null)
241
    {
242
        if ($source instanceof DataObject) {
243
            $name = $this->getName();
244
245
            if ($source->hasMethod($name)) {
246
                $value = $source->$name()->getIDList();
247
            }
248
        } elseif ($value instanceof SS_List) {
249
            $value = $value->column('ID');
250
        }
251
252
        if (!is_array($value)) {
253
            return parent::setValue($value);
254
        }
255
256
        return parent::setValue(array_filter($value));
257
    }
258
259
    /**
260
     * {@inheritdoc}
261
     */
262
    public function getAttributes()
263
    {
264
        return array_merge(
265
            parent::getAttributes(),
266
            array('name' => $this->getName() . '[]')
267
        );
268
    }
269
270
    /**
271
     * {@inheritdoc}
272
     */
273
    public function saveInto(DataObjectInterface $record)
274
    {
275
        parent::saveInto($record);
276
277
        $name = $this->getName();
278
        $titleField = $this->getTitleField();
279
280
        $source = $this->getSource();
281
282
        $values = $this->Value();
283
284
        if (!$values) {
285
            $values = array();
286
        }
287
288
        if (empty($record) || empty($source) || empty($titleField)) {
289
            return;
290
        }
291
292
        if (!$record->hasMethod($name)) {
293
            throw new Exception(
294
                sprintf("%s does not have a %s method", get_class($record), $name)
295
            );
296
        }
297
298
        $relation = $record->$name();
299
300
        foreach ($values as $i => $value) {
301
            if (!is_numeric($value)) {
302
                if (!$this->getCanCreate()) {
303
                    unset($values[$i]);
304
                    continue;
305
                }
306
307
                // Get or create record
308
                $record = $this->getOrCreateTag($value);
309
                $values[$i] = $record->ID;
310
            }
311
        }
312
313
        if ($values instanceof SS_List) {
314
            $values = iterator_to_array($values);
315
        }
316
317
        $relation->setByIDList(array_filter($values));
318
    }
319
320
    /**
321
     * Get or create tag with the given value
322
     *
323
     * @param string $term
324
     * @return DataObject
325
     */
326
    protected function getOrCreateTag($term)
327
    {
328
        // Check if existing record can be found
329
        $source = $this->getSource();
330
        $titleField = $this->getTitleField();
331
        $record = $source
332
            ->filter($titleField, $term)
333
            ->first();
334
        if ($record) {
335
            return $record;
336
        }
337
338
        // Create new instance if not yet saved
339
        $dataClass = $source->dataClass();
340
        $record = Injector::inst()->create($dataClass);
341
        $record->{$titleField} = $term;
342
        $record->write();
343
        return $record;
344
    }
345
346
    /**
347
     * Returns a JSON string of tags, for lazy loading.
348
     *
349
     * @param SS_HTTPRequest $request
350
     *
351
     * @return SS_HTTPResponse
352
     */
353
    public function suggest(SS_HTTPRequest $request)
354
    {
355
        $tags = $this->getTags($request->getVar('term'));
356
357
        $response = new SS_HTTPResponse();
358
        $response->addHeader('Content-Type', 'application/json');
359
        $response->setBody(json_encode(array('items' => $tags)));
360
361
        return $response;
362
    }
363
364
    /**
365
     * Returns array of arrays representing tags.
366
     *
367
     * @param string $term
368
     *
369
     * @return array
370
     */
371
    protected function getTags($term)
372
    {
373
        /**
374
         * @var DataList $source
375
         */
376
        $source = $this->getSource();
377
378
        $titleField = $this->getTitleField();
379
380
        $query = $source
381
            ->filter($titleField . ':PartialMatch:nocase', $term)
382
            ->sort($titleField)
383
            ->limit($this->getLazyLoadItemLimit());
384
385
        // Map into a distinct list
386
        $items = array();
387
        $titleField = $this->getTitleField();
388
        foreach ($query->map('ID', $titleField) as $id => $title) {
389
            $items[$title] = array(
390
                'id' => $id,
391
                'text' => $title
392
            );
393
        }
394
395
        return array_values($items);
396
    }
397
398
    /**
399
     * DropdownField assumes value will be a scalar so we must
400
     * override validate. This only applies to Silverstripe 3.2+
401
     *
402
     * @param Validator $validator
403
     * @return bool
404
     */
405
    public function validate($validator)
406
    {
407
        return true;
408
    }
409
}
410