silverstripe-tagfield/code/TagField.php

388 lines
7.2 KiB
PHP
Raw Normal View History

2015-04-14 03:15:28 +02:00
<?php
/**
2015-05-15 12:29:17 +02:00
* Provides a tagging interface, storing links between tag DataObjects and a parent DataObject.
2015-04-14 03:15:28 +02:00
*
2015-05-15 12:29:17 +02:00
* @package forms
* @subpackage fields
2015-04-14 03:15:28 +02:00
*/
class TagField extends DropdownField {
/**
* @var array
*/
public static $allowed_actions = array(
'suggest',
);
/**
* @var bool
*/
2015-05-15 12:29:17 +02:00
protected $shouldLazyLoad = false;
2015-04-14 03:15:28 +02:00
/**
2015-05-15 12:29:17 +02:00
* @var int
*/
2015-05-15 12:29:17 +02:00
protected $lazyLoadItemLimit = 10;
/**
2015-05-15 12:29:17 +02:00
* @var bool
*/
2015-05-15 12:29:17 +02:00
protected $canCreate = true;
2015-04-14 03:15:28 +02:00
/**
2015-05-15 12:29:17 +02:00
* @var string
2015-04-14 03:15:28 +02:00
*/
2015-05-15 12:29:17 +02:00
protected $titleField = 'Title';
2015-04-14 03:15:28 +02:00
2015-10-20 22:40:54 +02:00
/**
* @var bool
2015-10-20 22:40:54 +02:00
*/
protected $isMultiple = true;
2015-10-20 22:40:54 +02:00
2015-04-14 03:15:28 +02:00
/**
* @param string $name
2015-05-15 12:29:17 +02:00
* @param string $title
* @param null|DataList $source
* @param null|DataList $value
2015-04-14 03:15:28 +02:00
*/
2015-05-15 12:29:17 +02:00
public function __construct($name, $title = '', $source = null, $value = null) {
2015-04-14 03:15:28 +02:00
parent::__construct($name, $title, $source, $value);
}
/**
* @return bool
*/
2015-05-15 12:29:17 +02:00
public function getShouldLazyLoad() {
return $this->shouldLazyLoad;
}
/**
2015-05-15 12:29:17 +02:00
* @param bool $shouldLazyLoad
*
* @return static
*/
2015-05-15 12:29:17 +02:00
public function setShouldLazyLoad($shouldLazyLoad) {
$this->shouldLazyLoad = $shouldLazyLoad;
return $this;
}
/**
2015-05-15 12:29:17 +02:00
* @return int
*/
2015-05-15 12:29:17 +02:00
public function getLazyLoadItemLimit() {
return $this->lazyLoadItemLimit;
}
/**
2015-05-15 12:29:17 +02:00
* @param int $lazyLoadItemLimit
*
* @return static
*/
2015-05-15 12:29:17 +02:00
public function setLazyLoadItemLimit($lazyLoadItemLimit) {
$this->lazyLoadItemLimit = $lazyLoadItemLimit;
return $this;
}
/**
* @return bool
*/
public function getIsMultiple() {
return $this->isMultiple;
}
/**
* @param bool $isMultiple
*
* @return static
*/
public function setIsMultiple($isMultiple) {
$this->isMultiple = $isMultiple;
return $this;
}
/**
2015-05-15 12:29:17 +02:00
* @return bool
*/
2015-05-15 12:29:17 +02:00
public function getCanCreate() {
return $this->canCreate;
}
/**
2015-05-15 12:29:17 +02:00
* @param bool $canCreate
*
* @return static
*/
2015-05-15 12:29:17 +02:00
public function setCanCreate($canCreate) {
$this->canCreate = $canCreate;
return $this;
}
/**
2015-05-15 12:29:17 +02:00
* @return string
*/
2015-05-15 12:29:17 +02:00
public function getTitleField() {
return $this->titleField;
}
/**
2015-05-15 12:29:17 +02:00
* @param string $titleField
*
2015-05-15 12:29:17 +02:00
* @return $this
*/
2015-05-15 12:29:17 +02:00
public function setTitleField($titleField) {
$this->titleField = $titleField;
return $this;
}
/**
* {@inheritdoc}
2015-04-14 03:15:28 +02:00
*/
public function Field($properties = array()) {
Requirements::css(TAG_FIELD_DIR . '/css/select2.min.css');
Requirements::css(TAG_FIELD_DIR . '/css/TagField.css');
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
2015-05-15 12:29:17 +02:00
Requirements::javascript(TAG_FIELD_DIR . '/js/select2.js');
Requirements::javascript(TAG_FIELD_DIR . '/js/TagField.js');
2015-04-14 03:15:28 +02:00
2015-05-15 12:29:17 +02:00
$this->addExtraClass('ss-tag-field');
2015-04-14 03:15:28 +02:00
if ($this->getIsMultiple()) {
$this->setAttribute('multiple', 'multiple');
}
2015-04-14 03:15:28 +02:00
2015-05-15 12:29:17 +02:00
if($this->shouldLazyLoad) {
$this->setAttribute('data-ss-tag-field-suggest-url', $this->getSuggestURL());
} else {
$properties = array_merge($properties, array(
'Options' => $this->getOptions()
));
}
return $this
->customise($properties)
->renderWith(array("templates/TagField"));
}
/**
* @return string
*/
protected function getSuggestURL() {
return Controller::join_links($this->Link(), 'suggest');
}
/**
* @return ArrayList
*/
protected function getOptions() {
2015-04-14 03:15:28 +02:00
$options = ArrayList::create();
$source = $this->getSource();
2015-05-15 12:29:17 +02:00
if(!$source) {
$source = new ArrayList();
}
2015-05-15 12:29:17 +02:00
$dataClass = $source->dataClass();
2015-04-14 03:15:28 +02:00
$values = $this->Value();
2015-05-15 12:29:17 +02:00
if(!$values) {
return $options;
}
if(is_array($values)) {
$values = DataList::create($dataClass)->filter('ID', $values);
}
$ids = $values->column('ID');
$titleField = $this->getTitleField();
foreach($source as $object) {
2015-04-14 03:15:28 +02:00
$options->push(
ArrayData::create(array(
2015-05-15 12:29:17 +02:00
'Title' => $object->$titleField,
'Value' => $object->ID,
'Selected' => in_array($object->ID, $ids),
2015-04-14 03:15:28 +02:00
))
);
}
return $options;
2015-04-14 03:15:28 +02:00
}
/**
* {@inheritdoc}
2015-04-14 03:15:28 +02:00
*/
2015-05-15 12:29:17 +02:00
public function setValue($value, $source = null) {
if($source instanceof DataObject) {
$name = $this->getName();
2015-05-15 12:29:17 +02:00
if($source->hasMethod($name)) {
$value = $source->$name()->getIDList();
2015-04-14 03:15:28 +02:00
}
2015-05-15 12:29:17 +02:00
} elseif($value instanceof SS_List) {
$value = $value->column('ID');
2015-04-14 03:15:28 +02:00
}
if(!is_array($value)) {
return parent::setValue($value);
}
2015-05-15 12:29:17 +02:00
return parent::setValue(array_filter($value));
2015-04-14 03:15:28 +02:00
}
/**
* {@inheritdoc}
2015-04-14 03:15:28 +02:00
*/
public function getAttributes() {
return array_merge(
parent::getAttributes(),
array('name' => $this->getName() . '[]')
);
}
/**
* {@inheritdoc}
2015-04-14 03:15:28 +02:00
*/
public function saveInto(DataObjectInterface $record) {
parent::saveInto($record);
$name = $this->getName();
2015-05-15 12:29:17 +02:00
$titleField = $this->getTitleField();
$source = $this->getSource();
2015-04-14 03:15:28 +02:00
$values = $this->Value();
2015-05-15 12:29:17 +02:00
if(!$values) {
$values = array();
2015-04-14 03:15:28 +02:00
}
2015-05-15 12:29:17 +02:00
if(empty($record) || empty($source) || empty($titleField)) {
return;
}
2015-04-14 03:15:28 +02:00
2015-05-15 12:29:17 +02:00
if(!$record->hasMethod($name)) {
throw new Exception(
sprintf("%s does not have a %s method", get_class($record), $name)
);
}
2015-04-14 03:15:28 +02:00
2015-05-15 12:29:17 +02:00
$relation = $record->$name();
2015-04-14 03:15:28 +02:00
2015-05-15 12:29:17 +02:00
foreach($values as $i => $value) {
if(!is_numeric($value)) {
if(!$this->getCanCreate()) {
unset($values[$i]);
continue;
2015-04-14 03:15:28 +02:00
}
2015-05-15 12:29:17 +02:00
// Get or create record
$record = $this->getOrCreateTag($value);
2015-05-15 12:29:17 +02:00
$values[$i] = $record->ID;
2015-04-14 03:15:28 +02:00
}
2015-05-15 12:29:17 +02:00
}
2015-04-14 03:15:28 +02:00
2015-05-15 12:29:17 +02:00
if($values instanceof SS_List) {
$values = iterator_to_array($values);
2015-04-14 03:15:28 +02:00
}
2015-05-15 12:29:17 +02:00
$relation->setByIDList(array_filter($values));
2015-04-14 03:15:28 +02:00
}
/**
* Get or create tag with the given value
*
* @param string $term
* @return DataObject
*/
protected function getOrCreateTag($term) {
// Check if existing record can be found
$source = $this->getSource();
$titleField = $this->getTitleField();
$record = $source
->filter($titleField, $term)
->first();
if($record) {
return $record;
}
// Create new instance if not yet saved
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
$record->{$titleField} = $term;
$record->write();
return $record;
}
/**
2015-05-15 12:29:17 +02:00
* Returns a JSON string of tags, for lazy loading.
*
* @param SS_HTTPRequest $request
*
* @return SS_HTTPResponse
*/
public function suggest(SS_HTTPRequest $request) {
2015-05-15 12:29:17 +02:00
$tags = $this->getTags($request->getVar('term'));
$response = new SS_HTTPResponse();
$response->addHeader('Content-Type', 'application/json');
2015-05-15 12:29:17 +02:00
$response->setBody(json_encode(array('items' => $tags)));
return $response;
}
/**
2015-05-15 12:29:17 +02:00
* Returns array of arrays representing tags.
*
* @param string $term
*
* @return array
*/
2015-05-15 12:29:17 +02:00
protected function getTags($term) {
/**
* @var DataList $source
*/
$source = $this->getSource();
$titleField = $this->getTitleField();
$query = $source
2015-05-15 12:29:17 +02:00
->filter($titleField . ':PartialMatch:nocase', $term)
->sort($titleField)
->limit($this->getLazyLoadItemLimit());
// Map into a distinct list
$items = array();
$titleField = $this->getTitleField();
2015-05-15 12:29:17 +02:00
foreach($query->map('ID', $titleField) as $id => $title) {
$items[$title] = array(
'id' => $id,
'text' => $title
);
}
return array_values($items);
}
/**
* DropdownField assumes value will be a scalar so we must
* override validate. This only applies to Silverstripe 3.2+
*
* @param Validator $validator
* @return bool
*/
public function validate($validator) {
return true;
}
2015-04-14 03:15:28 +02:00
}