From 78d4d0d5dd2811047833d5f3624fc1a3bf63c855 Mon Sep 17 00:00:00 2001 From: Christopher Joe Date: Fri, 21 Jul 2017 10:20:17 +1200 Subject: [PATCH 1/2] Enhancement add support for TreeMultiselectField in react --- src/Forms/TreeDropdownField.php | 14 ++-- src/Forms/TreeMultiselectField.php | 113 ++++++++++++++++++++++------- 2 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/Forms/TreeDropdownField.php b/src/Forms/TreeDropdownField.php index c5c3945c0..99a519dee 100644 --- a/src/Forms/TreeDropdownField.php +++ b/src/Forms/TreeDropdownField.php @@ -387,7 +387,6 @@ class TreeDropdownField extends FormField $isSubTree = false; $this->search = $request->requestVar('search'); - $flatlist = $request->requestVar('flatList'); $id = (is_numeric($request->latestParam('ID'))) ? (int)$request->latestParam('ID') : (int)$request->requestVar('ID'); @@ -450,10 +449,11 @@ class TreeDropdownField extends FormField // Set title formatter $customised = function (DataObject $child) use ($isSubTree) { + $title = ($child->hasMethod('getMenuTitle')) ? $child->geMenuTitle() : $child->getTitle(); return [ 'name' => $this->getName(), 'id' => $child->obj($this->keyField), - 'title' => $child->getTitle(), + 'title' => $title, 'treetitle' => $child->obj($this->labelField), 'disabled' => $this->nodeIsDisabled($child), 'isSubTree' => $isSubTree @@ -466,7 +466,7 @@ class TreeDropdownField extends FormField $json = $markingSet ->getChildrenAsArray($customised); - if ($flatlist) { + if ($request->requestVar('flatList')) { // format and filter $json here $json['children'] = $this->flattenChildrenArray($json['children']); } @@ -590,7 +590,7 @@ class TreeDropdownField extends FormField $contextString = implode('/', $parentTitles); $child['contextString'] = ($contextString !== '') ? $contextString .'/' : ''; - $child['children'] = []; + unset($child['children']); if (!$this->search || in_array($child['id'], $this->realSearchIds)) { $output[] = $child; @@ -719,16 +719,15 @@ class TreeDropdownField extends FormField public function getSchemaStateDefaults() { $data = parent::getSchemaStateDefaults(); - // Check label for field $record = $this->Value() ? $this->objectForKey($this->Value()) : null; - $selectedlabel = null; // Ensure cache is keyed by last modified date of the underlying list $data['data']['cacheKey'] = DataList::create($this->sourceObject)->max('LastEdited'); if ($record) { + $title = ($record->hasMethod('getMenuTitle')) ? $record->geMenuTitle() : $record->getTitle(); $data['data']['valueObject'] = [ 'id' => $record->getField($this->keyField), - 'title' => $record->getTitle(), + 'title' => $title, 'treetitle' => $record->obj($this->labelField)->getSchemaValue(), ]; } @@ -744,6 +743,7 @@ class TreeDropdownField extends FormField 'showSearch' => $this->showSearch, 'emptyString' => $this->getEmptyString(), 'hasEmptyDefault' => $this->getHasEmptyDefault(), + 'multiple' => false, ]); return $data; diff --git a/src/Forms/TreeMultiselectField.php b/src/Forms/TreeMultiselectField.php index 85380bae8..8e4bb1276 100644 --- a/src/Forms/TreeMultiselectField.php +++ b/src/Forms/TreeMultiselectField.php @@ -5,9 +5,11 @@ namespace SilverStripe\Forms; use SilverStripe\Core\Convert; use SilverStripe\Control\Controller; use SilverStripe\ORM\ArrayList; +use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\FieldType\DBHTMLText; +use SilverStripe\Security\Group; use SilverStripe\View\ViewableData; use stdClass; @@ -54,52 +56,105 @@ use stdClass; */ class TreeMultiselectField extends TreeDropdownField { - public function __construct($name, $title = null, $sourceObject = "SilverStripe\\Security\\Group", $keyField = "ID", $labelField = "Title") - { + public function __construct( + $name, + $title = null, + $sourceObject = Group::class, + $keyField = "ID", + $labelField = "Title" + ) { parent::__construct($name, $title, $sourceObject, $keyField, $labelField); $this->removeExtraClass('single'); $this->addExtraClass('multiple'); $this->value = 'unchanged'; } + + public function getSchemaDataDefaults() + { + $data = parent::getSchemaDataDefaults(); + + $data['data'] = array_merge($data['data'], [ + 'hasEmptyDefault' => false, + 'multiple' => true, + ]); + return $data; + } + + public function getSchemaStateDefaults() + { + $data = parent::getSchemaStateDefaults(); + unset($data['data']['valueObject']); + + $items = $this->getItems(); + $values = []; + foreach ($items as $item) { + if ($item instanceof DataObject) { + $values[] = [ + 'id' => $item->getField($this->keyField), + 'title' => $item->getTitle(), + 'parentid' => $item->ParentID, + 'treetitle' => $item->obj($this->labelField)->getSchemaValue(), + ]; + } else { + $values[] = $item; + } + } + $data['data']['valueObjects'] = $values; + + // cannot rely on $this->value as this could be a many-many relationship + $value = array_column($values, 'id'); + $data['value'] = ($value) ? $value : 'unchanged'; + + return $data; + } /** * Return this field's linked items + * @return ArrayList|DataList $items */ public function getItems() { + $items = new ArrayList(); + // If the value has been set, use that - if ($this->value != 'unchanged' && is_array($this->sourceObject)) { - $items = array(); - $values = is_array($this->value) ? $this->value : preg_split('/ *, */', trim($this->value)); - foreach ($values as $value) { - $item = new stdClass; - $item->ID = $value; - $item->Title = $this->sourceObject[$value]; - $items[] = $item; - } - return $items; - - // Otherwise, look data up from the linked relation - } if ($this->value != 'unchanged' && is_string($this->value)) { - $items = new ArrayList(); - $ids = explode(',', $this->value); - foreach ($ids as $id) { - if (!is_numeric($id)) { - continue; - } - $item = DataObject::get_by_id($this->sourceObject, $id); - if ($item) { + if ($this->value != 'unchanged') { + if (is_array($this->sourceObject)) { + $values = is_array($this->value) ? $this->value : preg_split('/ *, */', trim($this->value)); + + foreach ($values as $value) { + $item = new stdClass; + $item->ID = $value; + $item->Title = $this->sourceObject[$value]; $items->push($item); } + return $items; } - return $items; - } elseif ($this->form) { + + // Otherwise, look data up from the linked relation + if (is_string($this->value)) { + $ids = explode(',', $this->value); + foreach ($ids as $id) { + if (!is_numeric($id)) { + continue; + } + $item = DataObject::get_by_id($this->sourceObject, $id); + if ($item) { + $items->push($item); + } + } + return $items; + } + } + + if ($this->form) { $fieldName = $this->name; $record = $this->form->getRecord(); if (is_object($record) && $record->hasMethod($fieldName)) { return $record->$fieldName(); } } + + return $items; } /** @@ -161,7 +216,7 @@ class TreeMultiselectField extends TreeDropdownField { // Detect whether this field has actually been updated if ($this->value !== 'unchanged') { - $items = array(); + $items = []; $fieldName = $this->name; $saveDest = $record->$fieldName(); @@ -173,8 +228,10 @@ class TreeMultiselectField extends TreeDropdownField E_USER_ERROR ); } - - if ($this->value) { + + if (is_array($this->value)) { + $items = $this->value; + } elseif ($this->value) { $items = preg_split("/ *, */", trim($this->value)); } From 9bff74bd61c0053118f42f726c421c4a052fe248 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Wed, 26 Jul 2017 18:13:56 +1200 Subject: [PATCH 2/2] Clean up all fluent property accessors --- src/Forms/TreeDropdownField.php | 225 ++++++++++++++++------- src/Forms/TreeDropdownField_Readonly.php | 3 +- src/Forms/TreeMultiselectField.php | 52 +++--- 3 files changed, 189 insertions(+), 91 deletions(-) diff --git a/src/Forms/TreeDropdownField.php b/src/Forms/TreeDropdownField.php index 99a519dee..ef972965d 100644 --- a/src/Forms/TreeDropdownField.php +++ b/src/Forms/TreeDropdownField.php @@ -94,12 +94,19 @@ class TreeDropdownField extends FormField protected $keyField = null; /** - * Name of lavel field on underlying object + * Name of label field on underlying object * * @var string */ protected $labelField = null; + /** + * Similar to labelField but for non-html equivalent of field + * + * @var string + */ + protected $titleField = 'Title'; + /** * Callback for filtering records * @@ -159,7 +166,7 @@ class TreeDropdownField extends FormField * @var array */ protected $searchIds = []; - + /** * List of ids which matches the search result * This excludes parents of search result children @@ -210,15 +217,15 @@ class TreeDropdownField extends FormField if (!DataObject::has_extension($sourceObject, Hierarchy::class)) { throw new InvalidArgumentException("SourceObject must have Hierarchy extension"); } - $this->sourceObject = $sourceObject; - $this->keyField = $keyField; - $this->labelField = $labelField; - $this->showSearch = $showSearch; + $this->setSourceObject($sourceObject); + $this->setKeyField($keyField); + $this->setLabelField($labelField); + $this->setShowSearch($showSearch); // Extra settings for Folders if (strcasecmp($sourceObject, Folder::class) === 0) { - $this->childrenMethod = 'ChildFolders'; - $this->numChildrenMethod = 'numChildFolders'; + $this->setChildrenMethod('ChildFolders'); + $this->setNumChildrenMethod('numChildFolders'); } $this->addExtraClass('single'); @@ -226,6 +233,17 @@ class TreeDropdownField extends FormField parent::__construct($name, $title); } + /** + * Set the ID of the root node of the tree. This defaults to 0 - i.e. + * displays the whole tree. + * + * @return int + */ + public function getTreeBaseID() + { + return $this->baseID; + } + /** * Set the ID of the root node of the tree. This defaults to 0 - i.e. * displays the whole tree. @@ -239,11 +257,22 @@ class TreeDropdownField extends FormField return $this; } + /** + * Get a callback used to filter the values of the tree before + * displaying to the user. + * + * @return callable + */ + public function getFilterFunction() + { + return $this->filterCallback; + } + /** * Set a callback used to filter the values of the tree before * displaying to the user. * - * @param callback $callback + * @param callable $callback * @return $this */ public function setFilterFunction($callback) @@ -256,10 +285,20 @@ class TreeDropdownField extends FormField return $this; } + /** + * Get the callback used to disable checkboxes for some items in the tree + * + * @return callable + */ + public function getDisableFunction() + { + return $this->disableCallback; + } + /** * Set a callback used to disable checkboxes for some items in the tree * - * @param callback $callback + * @param callable $callback * @return $this */ public function setDisableFunction($callback) @@ -276,7 +315,18 @@ class TreeDropdownField extends FormField * Set a callback used to search the hierarchy globally, even before * applying the filter. * - * @param callback $callback + * @return callable + */ + public function getSearchFunction() + { + return $this->searchCallback; + } + + /** + * Set a callback used to search the hierarchy globally, even before + * applying the filter. + * + * @param callable $callback * @return $this */ public function setSearchFunction($callback) @@ -309,6 +359,16 @@ class TreeDropdownField extends FormField return $this; } + /** + * Get method to invoke on each node to get the child collection + * + * @return string + */ + public function getChildrenMethod() + { + return $this->childrenMethod; + } + /** * @param string $method The parameter to ChildrenMethod to use when calling Hierarchy->getChildrenAsUL in * {@link Hierarchy}. The method specified determines the structure of the returned list. Use "ChildFolders" @@ -324,6 +384,16 @@ class TreeDropdownField extends FormField return $this; } + /** + * Get method to invoke on nodes to count children + * + * @return string + */ + public function getNumChildrenMethod() + { + return $this->numChildrenMethod; + } + /** * @param string $method The parameter to numChildrenMethod to use when calling Hierarchy->getChildrenAsUL in * {@link Hierarchy}. Should be used in conjunction with setChildrenMethod(). @@ -344,9 +414,9 @@ class TreeDropdownField extends FormField { $record = $this->Value() ? $this->objectForKey($this->Value()) : null; if ($record instanceof ViewableData) { - $title = $record->obj($this->labelField)->forTemplate(); + $title = $record->obj($this->getLabelField())->forTemplate(); } elseif ($record) { - $title = Convert::raw2xml($record->{$this->labelField}); + $title = Convert::raw2xml($record->{$this->getLabelField()}); } else { $title = $this->getEmptyString(); } @@ -354,7 +424,7 @@ class TreeDropdownField extends FormField // TODO Implement for TreeMultiSelectField $metadata = array( 'id' => $record ? $record->ID : null, - 'ClassName' => $record ? $record->ClassName : $this->sourceObject + 'ClassName' => $record ? $record->ClassName : $this->getSourceObject() ); $properties = array_merge( @@ -371,7 +441,7 @@ class TreeDropdownField extends FormField public function extraClass() { - return implode(' ', array(parent::extraClass(), ($this->showSearch ? "searchable" : null))); + return implode(' ', array(parent::extraClass(), ($this->getShowSearch() ? "searchable" : null))); } /** @@ -390,7 +460,7 @@ class TreeDropdownField extends FormField $id = (is_numeric($request->latestParam('ID'))) ? (int)$request->latestParam('ID') : (int)$request->requestVar('ID'); - + // pre-process the tree - search needs to operate globally, not locally as marking filter does if ($this->search) { $this->populateIDs(); @@ -398,29 +468,30 @@ class TreeDropdownField extends FormField /** @var DataObject|Hierarchy $obj */ $obj = null; + $sourceObject = $this->getSourceObject(); if ($id && !$request->requestVar('forceFullTree')) { - $obj = DataObject::get_by_id($this->sourceObject, $id); + $obj = DataObject::get_by_id($sourceObject, $id); $isSubTree = true; if (!$obj) { throw new Exception( - "TreeDropdownField->tree(): the object #$id of type $this->sourceObject could not be found" + "TreeDropdownField->tree(): the object #$id of type $sourceObject could not be found" ); } } else { - if ($this->baseID) { - $obj = DataObject::get_by_id($this->sourceObject, $this->baseID); + if ($this->getTreeBaseID()) { + $obj = DataObject::get_by_id($sourceObject, $this->getTreeBaseID()); } - if (!$this->baseID || !$obj) { - $obj = DataObject::singleton($this->sourceObject); + if (!$this->getTreeBaseID() || !$obj) { + $obj = DataObject::singleton($sourceObject); } } // Create marking set - $markingSet = MarkedSet::create($obj, $this->childrenMethod, $this->numChildrenMethod, 30); + $markingSet = MarkedSet::create($obj, $this->getChildrenMethod(), $this->getNumChildrenMethod(), 30); // Set filter on searched nodes - if ($this->filterCallback || $this->search) { + if ($this->getFilterFunction() || $this->search) { // Rely on filtering to limit tree $markingSet->setMarkingFilterFunction(function ($node) { return $this->filterMarking($node); @@ -449,12 +520,11 @@ class TreeDropdownField extends FormField // Set title formatter $customised = function (DataObject $child) use ($isSubTree) { - $title = ($child->hasMethod('getMenuTitle')) ? $child->geMenuTitle() : $child->getTitle(); return [ 'name' => $this->getName(), - 'id' => $child->obj($this->keyField), - 'title' => $title, - 'treetitle' => $child->obj($this->labelField), + 'id' => $child->obj($this->getKeyField()), + 'title' => $child->obj($this->getTitleField()), + 'treetitle' => $child->obj($this->getLabelField()), 'disabled' => $this->nodeIsDisabled($child), 'isSubTree' => $isSubTree ]; @@ -465,7 +535,7 @@ class TreeDropdownField extends FormField // Format JSON output $json = $markingSet ->getChildrenAsArray($customised); - + if ($request->requestVar('flatList')) { // format and filter $json here $json['children'] = $this->flattenChildrenArray($json['children']); @@ -495,7 +565,8 @@ class TreeDropdownField extends FormField */ public function filterMarking($node) { - if ($this->filterCallback && !call_user_func($this->filterCallback, $node)) { + $callback = $this->getFilterFunction(); + if ($callback && !call_user_func($callback, $node)) { return false; } @@ -513,7 +584,8 @@ class TreeDropdownField extends FormField */ public function nodeIsDisabled($node) { - return ($this->disableCallback && call_user_func($this->disableCallback, $node)); + $callback = $this->getDisableFunction(); + return $callback && call_user_func($callback, $node); } /** @@ -527,13 +599,35 @@ class TreeDropdownField extends FormField } /** - * @return String + * @return string */ public function getLabelField() { return $this->labelField; } + /** + * Field to use for item titles + * + * @return string + */ + public function getTitleField() + { + return $this->titleField; + } + + /** + * Set field to use for item title + * + * @param string $field + * @return $this + */ + public function setTitleField($field) + { + $this->titleField = $field; + return $this; + } + /** * @param string $field * @return $this @@ -563,13 +657,15 @@ class TreeDropdownField extends FormField } /** - * @return String + * Get class of source object + * + * @return string */ public function getSourceObject() { return $this->sourceObject; } - + /** * Flattens a given list of children array items, so the data is no longer * structured in a hierarchy @@ -583,24 +679,24 @@ class TreeDropdownField extends FormField protected function flattenChildrenArray($children, $parentTitles = []) { $output = []; - + foreach ($children as $child) { $childTitles = array_merge($parentTitles, [$child['title']]); $grandChildren = $child['children']; $contextString = implode('/', $parentTitles); - + $child['contextString'] = ($contextString !== '') ? $contextString .'/' : ''; unset($child['children']); - + if (!$this->search || in_array($child['id'], $this->realSearchIds)) { $output[] = $child; } $output = array_merge($output, $this->flattenChildrenArray($grandChildren, $childTitles)); } - + return $output; } - + /** * Populate $this->searchIds with the IDs of the pages matching the searched parameter and their parents. * Reverse-constructs the tree starting from the leaves. Initially taken from CMSSiteTreeFilter, but modified @@ -610,11 +706,11 @@ class TreeDropdownField extends FormField { // get all the leaves to be displayed $res = $this->getSearchResults(); - + if (!$res) { return; } - + // iteratively fetch the parents in bulk, until all the leaves can be accessed using the tree control foreach ($res as $row) { if ($row->ParentID) { @@ -624,7 +720,7 @@ class TreeDropdownField extends FormField } $this->realSearchIds = $res->column(); - $sourceObject = $this->sourceObject; + $sourceObject = $this->getSourceObject(); while (!empty($parents)) { $items = DataObject::get($sourceObject) @@ -640,7 +736,7 @@ class TreeDropdownField extends FormField } } } - + /** * Get the DataObjects that matches the searched parameter. * @@ -648,14 +744,15 @@ class TreeDropdownField extends FormField */ protected function getSearchResults() { - if ($this->searchCallback) { - return call_user_func($this->searchCallback, $this->sourceObject, $this->labelField, $this->search); + $callback = $this->getSearchFunction(); + if ($callback) { + return call_user_func($callback, $this->getSourceObject(), $this->getLabelField(), $this->search); } - - $sourceObject = $this->sourceObject; + + $sourceObject = $this->getSourceObject(); $filters = array(); - if (singleton($sourceObject)->hasDatabaseField($this->labelField)) { - $filters["{$this->labelField}:PartialMatch"] = $this->search; + if (singleton($sourceObject)->hasDatabaseField($this->getLabelField())) { + $filters["{$this->getLabelField()}:PartialMatch"] = $this->search; } else { if (singleton($sourceObject)->hasDatabaseField('Title')) { $filters["Title:PartialMatch"] = $this->search; @@ -669,10 +766,10 @@ class TreeDropdownField extends FormField throw new InvalidArgumentException(sprintf( 'Cannot query by %s.%s, not a valid database column', $sourceObject, - $this->labelField + $this->getLabelField() )); } - return DataObject::get($this->sourceObject)->filterAny($filters); + return DataObject::get($this->getSourceObject())->filterAny($filters); } /** @@ -683,8 +780,8 @@ class TreeDropdownField extends FormField */ protected function objectForKey($key) { - return DataObject::get($this->sourceObject) - ->filter($this->keyField, $key) + return DataObject::get($this->getSourceObject()) + ->filter($this->getKeyField(), $key) ->first(); } @@ -695,9 +792,10 @@ class TreeDropdownField extends FormField { /** @var TreeDropdownField_Readonly $copy */ $copy = $this->castedCopy(TreeDropdownField_Readonly::class); - $copy->setKeyField($this->keyField); - $copy->setLabelField($this->labelField); - $copy->setSourceObject($this->sourceObject); + $copy->setKeyField($this->getKeyField()); + $copy->setLabelField($this->getLabelField()); + $this->setTitleField($this->getTitleField()); + $copy->setSourceObject($this->getSourceObject()); return $copy; } @@ -710,7 +808,7 @@ class TreeDropdownField extends FormField $field = $classOrCopy; if (!is_object($field)) { - $field = new $classOrCopy($this->name, $this->title, $this->sourceObject); + $field = new $classOrCopy($this->name, $this->title, $this->getSourceObject()); } return parent::castedCopy($field); @@ -722,13 +820,12 @@ class TreeDropdownField extends FormField $record = $this->Value() ? $this->objectForKey($this->Value()) : null; // Ensure cache is keyed by last modified date of the underlying list - $data['data']['cacheKey'] = DataList::create($this->sourceObject)->max('LastEdited'); + $data['data']['cacheKey'] = DataList::create($this->getSourceObject())->max('LastEdited'); if ($record) { - $title = ($record->hasMethod('getMenuTitle')) ? $record->geMenuTitle() : $record->getTitle(); $data['data']['valueObject'] = [ - 'id' => $record->getField($this->keyField), - 'title' => $title, - 'treetitle' => $record->obj($this->labelField)->getSchemaValue(), + 'id' => $record->obj($this->getKeyField())->getValue(), + 'title' => $record->obj($this->getTitleField())->getValue(), + 'treetitle' => $record->obj($this->getLabelField())->getSchemaValue(), ]; } @@ -740,7 +837,7 @@ class TreeDropdownField extends FormField $data = parent::getSchemaDataDefaults(); $data['data'] = array_merge($data['data'], [ 'urlTree' => $this->Link('tree'), - 'showSearch' => $this->showSearch, + 'showSearch' => $this->getShowSearch(), 'emptyString' => $this->getEmptyString(), 'hasEmptyDefault' => $this->getHasEmptyDefault(), 'multiple' => false, @@ -791,7 +888,7 @@ class TreeDropdownField extends FormField return $this->emptyString; } - $item = DataObject::singleton($this->sourceObject); + $item = DataObject::singleton($this->getSourceObject()); $emptyString = _t( 'SilverStripe\\Forms\\DropdownField.CHOOSE_MODEL', '(Choose {name})', diff --git a/src/Forms/TreeDropdownField_Readonly.php b/src/Forms/TreeDropdownField_Readonly.php index 59c5b1b2e..c86e270f6 100644 --- a/src/Forms/TreeDropdownField_Readonly.php +++ b/src/Forms/TreeDropdownField_Readonly.php @@ -8,7 +8,7 @@ class TreeDropdownField_Readonly extends TreeDropdownField public function Field($properties = array()) { - $fieldName = $this->labelField; + $fieldName = $this->getLabelField(); if ($this->value) { $keyObj = $this->objectForKey($this->value); $obj = $keyObj ? $keyObj->$fieldName : ''; @@ -23,7 +23,6 @@ class TreeDropdownField_Readonly extends TreeDropdownField $field = new LookupField($this->name, $this->title, $source); $field->setValue($this->value); $field->setForm($this->form); - $field->dontEscape = true; return $field->Field(); } } diff --git a/src/Forms/TreeMultiselectField.php b/src/Forms/TreeMultiselectField.php index 8e4bb1276..4f6c3cc5c 100644 --- a/src/Forms/TreeMultiselectField.php +++ b/src/Forms/TreeMultiselectField.php @@ -68,43 +68,43 @@ class TreeMultiselectField extends TreeDropdownField $this->addExtraClass('multiple'); $this->value = 'unchanged'; } - + public function getSchemaDataDefaults() { $data = parent::getSchemaDataDefaults(); - + $data['data'] = array_merge($data['data'], [ 'hasEmptyDefault' => false, 'multiple' => true, ]); return $data; } - + public function getSchemaStateDefaults() { $data = parent::getSchemaStateDefaults(); unset($data['data']['valueObject']); - + $items = $this->getItems(); $values = []; foreach ($items as $item) { if ($item instanceof DataObject) { $values[] = [ - 'id' => $item->getField($this->keyField), - 'title' => $item->getTitle(), + 'id' => $item->obj($this->getKeyField())->getValue(), + 'title' => $item->obj($this->getTitleField())->getValue(), 'parentid' => $item->ParentID, - 'treetitle' => $item->obj($this->labelField)->getSchemaValue(), + 'treetitle' => $item->obj($this->getLabelField())->getSchemaValue(), ]; } else { $values[] = $item; } } $data['data']['valueObjects'] = $values; - + // cannot rely on $this->value as this could be a many-many relationship $value = array_column($values, 'id'); $data['value'] = ($value) ? $value : 'unchanged'; - + return $data; } @@ -115,21 +115,22 @@ class TreeMultiselectField extends TreeDropdownField public function getItems() { $items = new ArrayList(); - + // If the value has been set, use that if ($this->value != 'unchanged') { - if (is_array($this->sourceObject)) { + $sourceObject = $this->getSourceObject(); + if (is_array($sourceObject)) { $values = is_array($this->value) ? $this->value : preg_split('/ *, */', trim($this->value)); - + foreach ($values as $value) { $item = new stdClass; $item->ID = $value; - $item->Title = $this->sourceObject[$value]; + $item->Title = $sourceObject[$value]; $items->push($item); } return $items; } - + // Otherwise, look data up from the linked relation if (is_string($this->value)) { $ids = explode(',', $this->value); @@ -137,7 +138,7 @@ class TreeMultiselectField extends TreeDropdownField if (!is_numeric($id)) { continue; } - $item = DataObject::get_by_id($this->sourceObject, $id); + $item = DataObject::get_by_id($sourceObject, $id); if ($item) { $items->push($item); } @@ -145,7 +146,7 @@ class TreeMultiselectField extends TreeDropdownField return $items; } } - + if ($this->form) { $fieldName = $this->name; $record = $this->form->getRecord(); @@ -153,7 +154,7 @@ class TreeMultiselectField extends TreeDropdownField return $record->$fieldName(); } } - + return $items; } @@ -176,8 +177,8 @@ class TreeMultiselectField extends TreeDropdownField foreach ($items as $item) { $idArray[] = $item->ID; $titleArray[] = ($item instanceof ViewableData) - ? $item->obj($this->labelField)->forTemplate() - : Convert::raw2xml($item->{$this->labelField}); + ? $item->obj($this->getLabelField())->forTemplate() + : Convert::raw2xml($item->{$this->getLabelField()}); } $title = implode(", ", $titleArray); @@ -228,7 +229,7 @@ class TreeMultiselectField extends TreeDropdownField E_USER_ERROR ); } - + if (is_array($this->value)) { $items = $this->value; } elseif ($this->value) { @@ -253,11 +254,12 @@ class TreeMultiselectField extends TreeDropdownField */ public function performReadonlyTransformation() { - $copy = $this->castedCopy('SilverStripe\\Forms\\TreeMultiselectField_Readonly'); - $copy->setKeyField($this->keyField); - $copy->setLabelField($this->labelField); - $copy->setSourceObject($this->sourceObject); - + /** @var TreeMultiselectField_Readonly $copy */ + $copy = $this->castedCopy(TreeMultiselectField_Readonly::class); + $copy->setKeyField($this->getKeyField()); + $copy->setLabelField($this->getLabelField()); + $copy->setSourceObject($this->getSourceObject()); + $copy->setTitleField($this->getTitleField()); return $copy; } }