Passed
Pull Request — master (#117)
by Damian
03:49
created
src/TagField.php 1 patch
Indentation   +486 added lines, -486 removed lines patch added patch discarded remove patch
@@ -26,491 +26,491 @@
 block discarded – undo
26 26
  */
27 27
 class TagField extends DropdownField
28 28
 {
29
-    /**
30
-     * @var array
31
-     */
32
-    private static $allowed_actions = array(
33
-        'suggest'
34
-    );
35
-
36
-    /**
37
-     * @var bool
38
-     */
39
-    protected $shouldLazyLoad = false;
40
-
41
-    /**
42
-     * @var int
43
-     */
44
-    protected $lazyLoadItemLimit = 10;
45
-
46
-    /**
47
-     * @var bool
48
-     */
49
-    protected $canCreate = true;
50
-
51
-    /**
52
-     * @var string
53
-     */
54
-    protected $titleField = 'Title';
55
-
56
-    /**
57
-     * @var DataList
58
-     */
59
-    protected $sourceList;
60
-
61
-    /**
62
-     * @var bool
63
-     */
64
-    protected $isMultiple = true;
65
-
66
-    /**
67
-     * @param string $name
68
-     * @param string $title
69
-     * @param null|DataList $source
70
-     * @param null|DataList $value
71
-     * @param string $titleField
72
-     */
73
-    public function __construct($name, $title = '', $source = null, $value = null, $titleField = 'Title')
74
-    {
75
-        $this->setTitleField($titleField);
76
-        parent::__construct($name, $title, $source, $value);
77
-    }
78
-
79
-    /**
80
-     * @return bool
81
-     */
82
-    public function getShouldLazyLoad()
83
-    {
84
-        return $this->shouldLazyLoad;
85
-    }
86
-
87
-    /**
88
-     * @param bool $shouldLazyLoad
89
-     *
90
-     * @return static
91
-     */
92
-    public function setShouldLazyLoad($shouldLazyLoad)
93
-    {
94
-        $this->shouldLazyLoad = $shouldLazyLoad;
95
-
96
-        return $this;
97
-    }
98
-
99
-    /**
100
-     * @return int
101
-     */
102
-    public function getLazyLoadItemLimit()
103
-    {
104
-        return $this->lazyLoadItemLimit;
105
-    }
106
-
107
-    /**
108
-     * @param int $lazyLoadItemLimit
109
-     *
110
-     * @return static
111
-     */
112
-    public function setLazyLoadItemLimit($lazyLoadItemLimit)
113
-    {
114
-        $this->lazyLoadItemLimit = $lazyLoadItemLimit;
115
-
116
-        return $this;
117
-    }
118
-
119
-    /**
120
-     * @return bool
121
-     */
122
-    public function getIsMultiple()
123
-    {
124
-        return $this->isMultiple;
125
-    }
126
-
127
-    /**
128
-     * @param bool $isMultiple
129
-     *
130
-     * @return static
131
-     */
132
-    public function setIsMultiple($isMultiple)
133
-    {
134
-        $this->isMultiple = $isMultiple;
135
-
136
-        return $this;
137
-    }
138
-
139
-    /**
140
-     * @return bool
141
-     */
142
-    public function getCanCreate()
143
-    {
144
-        return $this->canCreate;
145
-    }
146
-
147
-    /**
148
-     * @param bool $canCreate
149
-     *
150
-     * @return static
151
-     */
152
-    public function setCanCreate($canCreate)
153
-    {
154
-        $this->canCreate = $canCreate;
155
-
156
-        return $this;
157
-    }
158
-
159
-    /**
160
-     * @return string
161
-     */
162
-    public function getTitleField()
163
-    {
164
-        return $this->titleField;
165
-    }
166
-
167
-    /**
168
-     * @param string $titleField
169
-     *
170
-     * @return $this
171
-     */
172
-    public function setTitleField($titleField)
173
-    {
174
-        $this->titleField = $titleField;
175
-
176
-        return $this;
177
-    }
178
-
179
-    /**
180
-     * Get the DataList source. The 4.x upgrade for SelectField::setSource starts to convert this to an array.
181
-     * If empty use getSource() for array version
182
-     *
183
-     * @return DataList
184
-     */
185
-    public function getSourceList()
186
-    {
187
-        return $this->sourceList;
188
-    }
189
-
190
-    /**
191
-     * Set the model class name for tags
192
-     *
193
-     * @param DataList $sourceList
194
-     * @return self
195
-     */
196
-    public function setSourceList($sourceList)
197
-    {
198
-        $this->sourceList = $sourceList;
199
-        return $this;
200
-    }
201
-
202
-    /**
203
-     * {@inheritdoc}
204
-     */
205
-    public function Field($properties = array())
206
-    {
207
-        Requirements::css('silverstripe/tagfield:css/select2.min.css');
208
-        Requirements::css('silverstripe/tagfield:css/TagField.css');
209
-
210
-        Requirements::javascript('silverstripe/tagfield:js/select2.js');
211
-        Requirements::javascript('silverstripe/tagfield:js/TagField.js');
212
-
213
-        $this->addExtraClass('ss-tag-field');
214
-
215
-        if ($this->getIsMultiple()) {
216
-            $this->setAttribute('multiple', 'multiple');
217
-        }
218
-
219
-        if ($this->shouldLazyLoad) {
220
-            $this->setAttribute('data-ss-tag-field-suggest-url', $this->getSuggestURL());
221
-        } else {
222
-            $properties = array_merge($properties, array(
223
-                'Options' => $this->getOptions()
224
-            ));
225
-        }
226
-
227
-        $this->setAttribute('data-can-create', (int) $this->getCanCreate());
228
-
229
-        return $this
230
-            ->customise($properties)
231
-            ->renderWith(self::class);
232
-    }
233
-
234
-    /**
235
-     * @return string
236
-     */
237
-    protected function getSuggestURL()
238
-    {
239
-        return Controller::join_links($this->Link(), 'suggest');
240
-    }
241
-
242
-    /**
243
-     * @return ArrayList
244
-     */
245
-    protected function getOptions()
246
-    {
247
-        $options = ArrayList::create();
248
-
249
-        $source = $this->getSourceList();
250
-
251
-        if (!$source) {
252
-            $source = ArrayList::create();
253
-        }
254
-
255
-        $dataClass = $source->dataClass();
256
-
257
-        $values = $this->Value();
258
-
259
-        if (!$values) {
260
-            return $options;
261
-        }
262
-
263
-        if (is_array($values)) {
264
-            $values = DataList::create($dataClass)->filter($this->getTitleField(), $values);
265
-        }
266
-
267
-        $ids = $values->column($this->getTitleField());
268
-
269
-        $titleField = $this->getTitleField();
29
+	/**
30
+	 * @var array
31
+	 */
32
+	private static $allowed_actions = array(
33
+		'suggest'
34
+	);
35
+
36
+	/**
37
+	 * @var bool
38
+	 */
39
+	protected $shouldLazyLoad = false;
40
+
41
+	/**
42
+	 * @var int
43
+	 */
44
+	protected $lazyLoadItemLimit = 10;
45
+
46
+	/**
47
+	 * @var bool
48
+	 */
49
+	protected $canCreate = true;
50
+
51
+	/**
52
+	 * @var string
53
+	 */
54
+	protected $titleField = 'Title';
55
+
56
+	/**
57
+	 * @var DataList
58
+	 */
59
+	protected $sourceList;
60
+
61
+	/**
62
+	 * @var bool
63
+	 */
64
+	protected $isMultiple = true;
65
+
66
+	/**
67
+	 * @param string $name
68
+	 * @param string $title
69
+	 * @param null|DataList $source
70
+	 * @param null|DataList $value
71
+	 * @param string $titleField
72
+	 */
73
+	public function __construct($name, $title = '', $source = null, $value = null, $titleField = 'Title')
74
+	{
75
+		$this->setTitleField($titleField);
76
+		parent::__construct($name, $title, $source, $value);
77
+	}
78
+
79
+	/**
80
+	 * @return bool
81
+	 */
82
+	public function getShouldLazyLoad()
83
+	{
84
+		return $this->shouldLazyLoad;
85
+	}
86
+
87
+	/**
88
+	 * @param bool $shouldLazyLoad
89
+	 *
90
+	 * @return static
91
+	 */
92
+	public function setShouldLazyLoad($shouldLazyLoad)
93
+	{
94
+		$this->shouldLazyLoad = $shouldLazyLoad;
95
+
96
+		return $this;
97
+	}
98
+
99
+	/**
100
+	 * @return int
101
+	 */
102
+	public function getLazyLoadItemLimit()
103
+	{
104
+		return $this->lazyLoadItemLimit;
105
+	}
106
+
107
+	/**
108
+	 * @param int $lazyLoadItemLimit
109
+	 *
110
+	 * @return static
111
+	 */
112
+	public function setLazyLoadItemLimit($lazyLoadItemLimit)
113
+	{
114
+		$this->lazyLoadItemLimit = $lazyLoadItemLimit;
115
+
116
+		return $this;
117
+	}
118
+
119
+	/**
120
+	 * @return bool
121
+	 */
122
+	public function getIsMultiple()
123
+	{
124
+		return $this->isMultiple;
125
+	}
126
+
127
+	/**
128
+	 * @param bool $isMultiple
129
+	 *
130
+	 * @return static
131
+	 */
132
+	public function setIsMultiple($isMultiple)
133
+	{
134
+		$this->isMultiple = $isMultiple;
135
+
136
+		return $this;
137
+	}
138
+
139
+	/**
140
+	 * @return bool
141
+	 */
142
+	public function getCanCreate()
143
+	{
144
+		return $this->canCreate;
145
+	}
146
+
147
+	/**
148
+	 * @param bool $canCreate
149
+	 *
150
+	 * @return static
151
+	 */
152
+	public function setCanCreate($canCreate)
153
+	{
154
+		$this->canCreate = $canCreate;
155
+
156
+		return $this;
157
+	}
158
+
159
+	/**
160
+	 * @return string
161
+	 */
162
+	public function getTitleField()
163
+	{
164
+		return $this->titleField;
165
+	}
166
+
167
+	/**
168
+	 * @param string $titleField
169
+	 *
170
+	 * @return $this
171
+	 */
172
+	public function setTitleField($titleField)
173
+	{
174
+		$this->titleField = $titleField;
175
+
176
+		return $this;
177
+	}
178
+
179
+	/**
180
+	 * Get the DataList source. The 4.x upgrade for SelectField::setSource starts to convert this to an array.
181
+	 * If empty use getSource() for array version
182
+	 *
183
+	 * @return DataList
184
+	 */
185
+	public function getSourceList()
186
+	{
187
+		return $this->sourceList;
188
+	}
189
+
190
+	/**
191
+	 * Set the model class name for tags
192
+	 *
193
+	 * @param DataList $sourceList
194
+	 * @return self
195
+	 */
196
+	public function setSourceList($sourceList)
197
+	{
198
+		$this->sourceList = $sourceList;
199
+		return $this;
200
+	}
201
+
202
+	/**
203
+	 * {@inheritdoc}
204
+	 */
205
+	public function Field($properties = array())
206
+	{
207
+		Requirements::css('silverstripe/tagfield:css/select2.min.css');
208
+		Requirements::css('silverstripe/tagfield:css/TagField.css');
209
+
210
+		Requirements::javascript('silverstripe/tagfield:js/select2.js');
211
+		Requirements::javascript('silverstripe/tagfield:js/TagField.js');
212
+
213
+		$this->addExtraClass('ss-tag-field');
214
+
215
+		if ($this->getIsMultiple()) {
216
+			$this->setAttribute('multiple', 'multiple');
217
+		}
218
+
219
+		if ($this->shouldLazyLoad) {
220
+			$this->setAttribute('data-ss-tag-field-suggest-url', $this->getSuggestURL());
221
+		} else {
222
+			$properties = array_merge($properties, array(
223
+				'Options' => $this->getOptions()
224
+			));
225
+		}
226
+
227
+		$this->setAttribute('data-can-create', (int) $this->getCanCreate());
228
+
229
+		return $this
230
+			->customise($properties)
231
+			->renderWith(self::class);
232
+	}
233
+
234
+	/**
235
+	 * @return string
236
+	 */
237
+	protected function getSuggestURL()
238
+	{
239
+		return Controller::join_links($this->Link(), 'suggest');
240
+	}
241
+
242
+	/**
243
+	 * @return ArrayList
244
+	 */
245
+	protected function getOptions()
246
+	{
247
+		$options = ArrayList::create();
248
+
249
+		$source = $this->getSourceList();
250
+
251
+		if (!$source) {
252
+			$source = ArrayList::create();
253
+		}
254
+
255
+		$dataClass = $source->dataClass();
256
+
257
+		$values = $this->Value();
258
+
259
+		if (!$values) {
260
+			return $options;
261
+		}
262
+
263
+		if (is_array($values)) {
264
+			$values = DataList::create($dataClass)->filter($this->getTitleField(), $values);
265
+		}
266
+
267
+		$ids = $values->column($this->getTitleField());
268
+
269
+		$titleField = $this->getTitleField();
270 270
         
271
-        if ($this->shouldLazyLoad) {
272
-            // only render options that are selected as everything else should be lazy loaded, and or loaded by the form
273
-            foreach ($values as $value) {
274
-                $options->push(
275
-                    ArrayData::create(array(
276
-                        'Title' => $value->$titleField,
277
-                        'Value' => $value->Title,
278
-                        'Selected' => true, // only values are iterated.
279
-                    ))
280
-                );
281
-            }
282
-            return $options;
283
-        }
284
-
285
-        foreach ($source as $object) {
286
-            $options->push(
287
-                ArrayData::create(array(
288
-                'Title' => $object->$titleField,
289
-                'Value' => $object->Title,
290
-                'Selected' => in_array($object->Title, $ids),
291
-                ))
292
-            );
293
-        }
294
-
295
-        return $options;
296
-    }
297
-
298
-    /**
299
-     * {@inheritdoc}
300
-     */
301
-    public function setValue($value, $source = null)
302
-    {
303
-        if ($source instanceof DataObject) {
304
-            $name = $this->getName();
305
-
306
-            if ($source->hasMethod($name)) {
307
-                $value = $source->$name()->column($this->getTitleField());
308
-            }
309
-        } elseif ($value instanceof SS_List) {
310
-            $value = $value->column($this->getTitleField());
311
-        }
312
-
313
-        if (!is_array($value)) {
314
-            return parent::setValue($value);
315
-        }
316
-
317
-        return parent::setValue(array_filter($value));
318
-    }
319
-
320
-    /**
321
-     * Gets the source array if required
322
-     *
323
-     * Note: this is expensive for a SS_List
324
-     *
325
-     * @return array
326
-     */
327
-    public function getSource()
328
-    {
329
-        if (is_null($this->source)) {
330
-            $this->source = $this->getListMap($this->getSourceList());
331
-        }
332
-        return $this->source;
333
-    }
334
-
335
-    /**
336
-     * Intercept DataList source
337
-     *
338
-     * @param mixed $source
339
-     * @return $this
340
-     */
341
-    public function setSource($source)
342
-    {
343
-        // When setting a datalist force internal list to null
344
-        if ($source instanceof DataList) {
345
-            $this->source = null;
346
-            $this->setSourceList($source);
347
-        } else {
348
-            parent::setSource($source);
349
-        }
350
-        return $this;
351
-    }
352
-
353
-    /**
354
-     * {@inheritdoc}
355
-     */
356
-    public function getAttributes()
357
-    {
358
-        return array_merge(
359
-            parent::getAttributes(),
360
-            [
361
-                'name' => $this->getName() . '[]',
362
-                'style'=> 'width: 100%'
363
-            ]
364
-        );
365
-    }
366
-
367
-    /**
368
-     * @param DataObject|DataObjectInterface $record DataObject to save data into
369
-     * @throws Exception
370
-     */
371
-    public function saveInto(DataObjectInterface $record)
372
-    {
373
-        parent::saveInto($record);
374
-
375
-        $name = $this->getName();
376
-        $titleField = $this->getTitleField();
377
-        $values = $this->Value();
378
-
379
-        /** @var Relation $relation */
380
-        $relation = $record->$name();
381
-        $ids = array();
382
-
383
-        if (!$values) {
384
-            $values = array();
385
-        }
386
-        if (empty($record) || empty($titleField)) {
387
-            return;
388
-        }
389
-
390
-        if (!$record->hasMethod($name)) {
391
-            throw new Exception(
392
-                sprintf("%s does not have a %s method", get_class($record), $name)
393
-            );
394
-        }
395
-
396
-        foreach ($values as $key => $value) {
397
-            // Get or create record
398
-            $record = $this->getOrCreateTag($value);
399
-            if ($record) {
400
-                $ids[] = $record->ID;
401
-                $values[$key] = $record->Title;
402
-            }
403
-        }
404
-
405
-        $relation->setByIDList(array_filter($ids));
406
-    }
407
-
408
-    /**
409
-     * Get or create tag with the given value
410
-     *
411
-     * @param  string $term
412
-     * @return DataObject|false
413
-     */
414
-    protected function getOrCreateTag($term)
415
-    {
416
-        // Check if existing record can be found
417
-        $source = $this->getSourceList();
418
-        if (!$source) {
419
-            return false;
420
-        }
421
-
422
-        $titleField = $this->getTitleField();
423
-        $record = $source
424
-            ->filter($titleField, $term)
425
-            ->first();
426
-        if ($record) {
427
-            return $record;
428
-        }
429
-
430
-        // Create new instance if not yet saved
431
-        if ($this->getCanCreate()) {
432
-            $dataClass = $source->dataClass();
433
-            $record = Injector::inst()->create($dataClass);
434
-            $record->{$titleField} = $term;
435
-            $record->write();
436
-            return $record;
437
-        } else {
438
-            return false;
439
-        }
440
-    }
441
-
442
-    /**
443
-     * Returns a JSON string of tags, for lazy loading.
444
-     *
445
-     * @param  HTTPRequest $request
446
-     * @return HTTPResponse
447
-     */
448
-    public function suggest(HTTPRequest $request)
449
-    {
450
-        $tags = $this->getTags($request->getVar('term'));
451
-
452
-        $response = new HTTPResponse();
453
-        $response->addHeader('Content-Type', 'application/json');
454
-        $response->setBody(json_encode(array('items' => $tags)));
455
-
456
-        return $response;
457
-    }
458
-
459
-    /**
460
-     * Returns array of arrays representing tags.
461
-     *
462
-     * @param  string $term
463
-     * @return array
464
-     */
465
-    protected function getTags($term)
466
-    {
467
-        $source = $this->getSourceList();
468
-        if (!$source) {
469
-            return [];
470
-        }
471
-
472
-        $titleField = $this->getTitleField();
473
-
474
-        $query = $source
475
-            ->filter($titleField . ':PartialMatch:nocase', $term)
476
-            ->sort($titleField)
477
-            ->limit($this->getLazyLoadItemLimit());
478
-
479
-        // Map into a distinct list
480
-        $items = array();
481
-        $titleField = $this->getTitleField();
482
-        foreach ($query->map('ID', $titleField) as $id => $title) {
483
-            $items[$title] = array(
484
-                'id' => $title,
485
-                'text' => $title
486
-            );
487
-        }
488
-
489
-        return array_values($items);
490
-    }
491
-
492
-    /**
493
-     * DropdownField assumes value will be a scalar so we must
494
-     * override validate. This only applies to Silverstripe 3.2+
495
-     *
496
-     * @param Validator $validator
497
-     * @return bool
498
-     */
499
-    public function validate($validator)
500
-    {
501
-        return true;
502
-    }
503
-
504
-    /**
505
-     * Converts the field to a readonly variant.
506
-     *
507
-     * @return ReadonlyTagField
508
-     */
509
-    public function performReadonlyTransformation()
510
-    {
511
-        /** @var ReadonlyTagField $copy */
512
-        $copy = $this->castedCopy(ReadonlyTagField::class);
513
-        $copy->setSourceList($this->getSourceList());
514
-        return $copy;
515
-    }
271
+		if ($this->shouldLazyLoad) {
272
+			// only render options that are selected as everything else should be lazy loaded, and or loaded by the form
273
+			foreach ($values as $value) {
274
+				$options->push(
275
+					ArrayData::create(array(
276
+						'Title' => $value->$titleField,
277
+						'Value' => $value->Title,
278
+						'Selected' => true, // only values are iterated.
279
+					))
280
+				);
281
+			}
282
+			return $options;
283
+		}
284
+
285
+		foreach ($source as $object) {
286
+			$options->push(
287
+				ArrayData::create(array(
288
+				'Title' => $object->$titleField,
289
+				'Value' => $object->Title,
290
+				'Selected' => in_array($object->Title, $ids),
291
+				))
292
+			);
293
+		}
294
+
295
+		return $options;
296
+	}
297
+
298
+	/**
299
+	 * {@inheritdoc}
300
+	 */
301
+	public function setValue($value, $source = null)
302
+	{
303
+		if ($source instanceof DataObject) {
304
+			$name = $this->getName();
305
+
306
+			if ($source->hasMethod($name)) {
307
+				$value = $source->$name()->column($this->getTitleField());
308
+			}
309
+		} elseif ($value instanceof SS_List) {
310
+			$value = $value->column($this->getTitleField());
311
+		}
312
+
313
+		if (!is_array($value)) {
314
+			return parent::setValue($value);
315
+		}
316
+
317
+		return parent::setValue(array_filter($value));
318
+	}
319
+
320
+	/**
321
+	 * Gets the source array if required
322
+	 *
323
+	 * Note: this is expensive for a SS_List
324
+	 *
325
+	 * @return array
326
+	 */
327
+	public function getSource()
328
+	{
329
+		if (is_null($this->source)) {
330
+			$this->source = $this->getListMap($this->getSourceList());
331
+		}
332
+		return $this->source;
333
+	}
334
+
335
+	/**
336
+	 * Intercept DataList source
337
+	 *
338
+	 * @param mixed $source
339
+	 * @return $this
340
+	 */
341
+	public function setSource($source)
342
+	{
343
+		// When setting a datalist force internal list to null
344
+		if ($source instanceof DataList) {
345
+			$this->source = null;
346
+			$this->setSourceList($source);
347
+		} else {
348
+			parent::setSource($source);
349
+		}
350
+		return $this;
351
+	}
352
+
353
+	/**
354
+	 * {@inheritdoc}
355
+	 */
356
+	public function getAttributes()
357
+	{
358
+		return array_merge(
359
+			parent::getAttributes(),
360
+			[
361
+				'name' => $this->getName() . '[]',
362
+				'style'=> 'width: 100%'
363
+			]
364
+		);
365
+	}
366
+
367
+	/**
368
+	 * @param DataObject|DataObjectInterface $record DataObject to save data into
369
+	 * @throws Exception
370
+	 */
371
+	public function saveInto(DataObjectInterface $record)
372
+	{
373
+		parent::saveInto($record);
374
+
375
+		$name = $this->getName();
376
+		$titleField = $this->getTitleField();
377
+		$values = $this->Value();
378
+
379
+		/** @var Relation $relation */
380
+		$relation = $record->$name();
381
+		$ids = array();
382
+
383
+		if (!$values) {
384
+			$values = array();
385
+		}
386
+		if (empty($record) || empty($titleField)) {
387
+			return;
388
+		}
389
+
390
+		if (!$record->hasMethod($name)) {
391
+			throw new Exception(
392
+				sprintf("%s does not have a %s method", get_class($record), $name)
393
+			);
394
+		}
395
+
396
+		foreach ($values as $key => $value) {
397
+			// Get or create record
398
+			$record = $this->getOrCreateTag($value);
399
+			if ($record) {
400
+				$ids[] = $record->ID;
401
+				$values[$key] = $record->Title;
402
+			}
403
+		}
404
+
405
+		$relation->setByIDList(array_filter($ids));
406
+	}
407
+
408
+	/**
409
+	 * Get or create tag with the given value
410
+	 *
411
+	 * @param  string $term
412
+	 * @return DataObject|false
413
+	 */
414
+	protected function getOrCreateTag($term)
415
+	{
416
+		// Check if existing record can be found
417
+		$source = $this->getSourceList();
418
+		if (!$source) {
419
+			return false;
420
+		}
421
+
422
+		$titleField = $this->getTitleField();
423
+		$record = $source
424
+			->filter($titleField, $term)
425
+			->first();
426
+		if ($record) {
427
+			return $record;
428
+		}
429
+
430
+		// Create new instance if not yet saved
431
+		if ($this->getCanCreate()) {
432
+			$dataClass = $source->dataClass();
433
+			$record = Injector::inst()->create($dataClass);
434
+			$record->{$titleField} = $term;
435
+			$record->write();
436
+			return $record;
437
+		} else {
438
+			return false;
439
+		}
440
+	}
441
+
442
+	/**
443
+	 * Returns a JSON string of tags, for lazy loading.
444
+	 *
445
+	 * @param  HTTPRequest $request
446
+	 * @return HTTPResponse
447
+	 */
448
+	public function suggest(HTTPRequest $request)
449
+	{
450
+		$tags = $this->getTags($request->getVar('term'));
451
+
452
+		$response = new HTTPResponse();
453
+		$response->addHeader('Content-Type', 'application/json');
454
+		$response->setBody(json_encode(array('items' => $tags)));
455
+
456
+		return $response;
457
+	}
458
+
459
+	/**
460
+	 * Returns array of arrays representing tags.
461
+	 *
462
+	 * @param  string $term
463
+	 * @return array
464
+	 */
465
+	protected function getTags($term)
466
+	{
467
+		$source = $this->getSourceList();
468
+		if (!$source) {
469
+			return [];
470
+		}
471
+
472
+		$titleField = $this->getTitleField();
473
+
474
+		$query = $source
475
+			->filter($titleField . ':PartialMatch:nocase', $term)
476
+			->sort($titleField)
477
+			->limit($this->getLazyLoadItemLimit());
478
+
479
+		// Map into a distinct list
480
+		$items = array();
481
+		$titleField = $this->getTitleField();
482
+		foreach ($query->map('ID', $titleField) as $id => $title) {
483
+			$items[$title] = array(
484
+				'id' => $title,
485
+				'text' => $title
486
+			);
487
+		}
488
+
489
+		return array_values($items);
490
+	}
491
+
492
+	/**
493
+	 * DropdownField assumes value will be a scalar so we must
494
+	 * override validate. This only applies to Silverstripe 3.2+
495
+	 *
496
+	 * @param Validator $validator
497
+	 * @return bool
498
+	 */
499
+	public function validate($validator)
500
+	{
501
+		return true;
502
+	}
503
+
504
+	/**
505
+	 * Converts the field to a readonly variant.
506
+	 *
507
+	 * @return ReadonlyTagField
508
+	 */
509
+	public function performReadonlyTransformation()
510
+	{
511
+		/** @var ReadonlyTagField $copy */
512
+		$copy = $this->castedCopy(ReadonlyTagField::class);
513
+		$copy->setSourceList($this->getSourceList());
514
+		return $copy;
515
+	}
516 516
 }
Please login to merge, or discard this patch.