mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGE: Deprecated DataObjectSet in favour of DataList or ArrayList.
MINOR: Moved DataObjectSet tests to ArrayListTest.
This commit is contained in:
parent
16a016b9dc
commit
2294ef5820
@ -1,889 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* This class represents a set of {@link ViewableData} subclasses (mostly {@link DataObject} or {@link ArrayData}).
|
||||
* It is used by the ORM-layer of Silverstripe to return query-results from {@link SQLQuery}.
|
||||
* @package sapphire
|
||||
* @deprecated Please use {@link DataList} or {@link ArrayList} instead.
|
||||
* @package sapphire
|
||||
* @subpackage model
|
||||
*/
|
||||
class DataObjectSet extends ViewableData implements SS_List {
|
||||
/**
|
||||
* The DataObjects in this set.
|
||||
* @var array
|
||||
*/
|
||||
protected $items = array();
|
||||
|
||||
protected $odd = 0;
|
||||
|
||||
/**
|
||||
* True if the current DataObject is the first in this set.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $first = true;
|
||||
|
||||
/**
|
||||
* True if the current DataObject is the last in this set.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $last = false;
|
||||
|
||||
/**
|
||||
* The current DataObject in this set.
|
||||
* @var DataObject
|
||||
*/
|
||||
protected $current = null;
|
||||
class DataObjectSet extends ArrayList {
|
||||
|
||||
/**
|
||||
* Create a new DataObjectSet. If you pass one or more arguments, it will try to convert them into {@link ArrayData} objects.
|
||||
* @todo Does NOT automatically convert objects with complex datatypes (e.g. converting arrays within an objects to its own DataObjectSet)
|
||||
*
|
||||
* @param ViewableData|array|mixed $items Parameters to use in this set, either as an associative array, object with simple properties, or as multiple parameters.
|
||||
*/
|
||||
public function __construct($items = null) {
|
||||
if($items) {
|
||||
// if the first parameter is not an array, or we have more than one parameter, collate all parameters to an array
|
||||
// otherwise use the passed array
|
||||
$itemsArr = (!is_array($items) || count(func_get_args()) > 1) ? func_get_args() : $items;
|
||||
|
||||
// We now have support for using the key of a data object set
|
||||
foreach($itemsArr as $i => $item) {
|
||||
if(is_subclass_of($item, 'ViewableData')) {
|
||||
$this->items[$i] = $item;
|
||||
} elseif(is_object($item) || ArrayLib::is_associative($item)) {
|
||||
$this->items[$i] = new ArrayData($item);
|
||||
public function __construct($items = array()) {
|
||||
user_error(
|
||||
'DataObjectSet is deprecated, please use DataList or ArrayList instead.',
|
||||
E_USER_NOTICE
|
||||
);
|
||||
|
||||
if ($items) {
|
||||
if (!is_array($items) || func_num_args() > 1) {
|
||||
$items = func_get_args();
|
||||
}
|
||||
|
||||
foreach ($items as $i => $item) {
|
||||
if ($item instanceof ViewableData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_object($item) || ArrayLib::is_associative($item)) {
|
||||
$items[$i] = new ArrayData($item);
|
||||
} else {
|
||||
user_error(
|
||||
"DataObjectSet::__construct: Passed item #{$i} is not an object or associative array,
|
||||
can't be properly iterated on in templates",
|
||||
E_USER_WARNING
|
||||
);
|
||||
$this->items[$i] = $item;
|
||||
"DataObjectSet::__construct: Passed item #{$i} is not an"
|
||||
. ' and object or associative array, can\'t be properly'
|
||||
. ' iterated on in templates', E_USER_WARNING
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for interface ArrayAccess. Returns whether an item with $key exists
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($key) {
|
||||
return isset($this->items[$key]);
|
||||
|
||||
parent::__construct($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for interface ArrayAccess. Returns item stored in array with index $key
|
||||
* @param mixed $key
|
||||
* @return DataObject
|
||||
*/
|
||||
public function offsetGet($key) {
|
||||
return $this->items[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for interface ArrayAccess. Set an item with the key in $key
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($key, $value) {
|
||||
$this->items[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for interface ArrayAccess. Unset an item with the key in $key
|
||||
* @param mixed $key
|
||||
*/
|
||||
public function offsetUnset($key) {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destory all of the DataObjects in this set.
|
||||
*/
|
||||
public function destroy() {
|
||||
foreach($this as $item) {
|
||||
$item->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the items in this set.
|
||||
*/
|
||||
public function emptyItems() {
|
||||
$this->items = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this DataObjectSet to an array of DataObjects.
|
||||
* @param string $index Index the array by this field.
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($index = null) {
|
||||
$map = array();
|
||||
foreach($this as $item) {
|
||||
if($index) $map[$item->$index] = $item;
|
||||
else $map[] = $item;
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this DataObjectSet to an array of maps.
|
||||
* @param string $index Index the array by this field.
|
||||
* @return array
|
||||
*/
|
||||
public function toNestedArray($index = null){
|
||||
if(!$index) {
|
||||
$index = "ID";
|
||||
}
|
||||
|
||||
$map = array();
|
||||
|
||||
foreach( $this as $item ) {
|
||||
$map[$item->$index] = $item->getAllFields();
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of ID => Title for the items in this set.
|
||||
*
|
||||
* This is an alias of {@link DataObjectSet->map()}
|
||||
*
|
||||
* @deprecated 2.5 Please use map() instead
|
||||
*
|
||||
* @param string $index The field to use as a key for the array
|
||||
* @param string $titleField The field (or method) to get values for the map
|
||||
* @param string $emptyString Empty option text e.g "(Select one)"
|
||||
* @param bool $sort Sort the map alphabetically based on the $titleField value
|
||||
* @return array
|
||||
*/
|
||||
public function toDropDownMap($index = 'ID', $titleField = 'Title', $emptyString = null, $sort = false) {
|
||||
return $this->map($index, $titleField, $emptyString, $sort);
|
||||
}
|
||||
|
||||
public function add($item) {
|
||||
$this->push($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the DataObject Set.
|
||||
* @param DataObject $item Item to add.
|
||||
* @param string $key Key to index this DataObject by.
|
||||
*/
|
||||
public function push($item, $key = null) {
|
||||
if($key != null) {
|
||||
unset($this->items[$key]);
|
||||
$this->items[$key] = $item;
|
||||
} else {
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the beginning of the DataObjectSet
|
||||
* @param DataObject $item Item to add
|
||||
* @param string $key Key to index this DataObject by.
|
||||
*/
|
||||
public function insertFirst($item, $key = null) {
|
||||
if($key == null) {
|
||||
array_unshift($this->items, $item);
|
||||
} else {
|
||||
$this->items = array_merge(array($key=>$item), $this->items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a DataObject at the beginning of this set.
|
||||
* @param DataObject $item Item to insert.
|
||||
*/
|
||||
public function unshift($item) {
|
||||
$this->insertFirst($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a DataObject from the beginning of this set and return it.
|
||||
* This is the equivalent of pop() but acts on the head of the set.
|
||||
* Opposite of unshift().
|
||||
*
|
||||
* @return DataObject (or null if there are no items in the set)
|
||||
*/
|
||||
public function shift() {
|
||||
return array_shift($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a DataObject from the end of this set and return it.
|
||||
* This is the equivalent of shift() but acts on the tail of the set.
|
||||
* Opposite of push().
|
||||
*
|
||||
* @return DataObject (or null if there are no items in the set)
|
||||
*/
|
||||
public function pop() {
|
||||
return array_pop($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a DataObject from this set.
|
||||
* @param DataObject $itemObject Item to remove.
|
||||
*/
|
||||
public function remove($itemObject) {
|
||||
foreach($this->items as $key=>$item){
|
||||
if($item === $itemObject){
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces $itemOld with $itemNew
|
||||
*
|
||||
* @param DataObject $itemOld
|
||||
* @param DataObject $itemNew
|
||||
*/
|
||||
public function replace($itemOld, $itemNew) {
|
||||
foreach($this->items as $key => $item) {
|
||||
if($item === $itemOld) {
|
||||
$this->items[$key] = $itemNew;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge another set onto the end of this set.
|
||||
* To merge without causing duplicates, consider calling
|
||||
* {@link removeDuplicates()} after this method on the new set.
|
||||
*
|
||||
* @param DataObjectSet $anotherSet Set to mege onto this set.
|
||||
*/
|
||||
public function merge($anotherSet){
|
||||
if($anotherSet) {
|
||||
foreach($anotherSet as $item){
|
||||
$this->push($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a specific slice of an existing set.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return DataObjectSet
|
||||
*/
|
||||
public function getRange($offset, $length) {
|
||||
$set = array_slice($this->items, (int)$offset, (int)$length);
|
||||
return new DataObjectSet($set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for this DataObjectSet.
|
||||
* This function allows you to use DataObjectSets in foreach loops
|
||||
* @return DataObjectSet_Iterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
return new DataObjectSet_Iterator($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the set is empty.
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists() {
|
||||
return $this->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first item in the set.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function First() {
|
||||
if(count($this->items) < 1)
|
||||
return null;
|
||||
|
||||
$keys = array_keys($this->items);
|
||||
return $this->items[$keys[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last item in the set.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function Last() {
|
||||
if(count($this->items) < 1)
|
||||
return null;
|
||||
|
||||
$keys = array_keys($this->items);
|
||||
return $this->items[$keys[sizeof($keys)-1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.0 Use {@link DataObjectSet::Count()}.
|
||||
*/
|
||||
public function TotalItems() {
|
||||
return $this->Count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual number of items in this dataset.
|
||||
* @return int
|
||||
*/
|
||||
public function Count() {
|
||||
return sizeof($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this set as a XHTML unordered list.
|
||||
* @return string
|
||||
*/
|
||||
public function UL() {
|
||||
if($this->exists()) {
|
||||
$result = "<ul id=\"Menu1\">\n";
|
||||
foreach($this as $item) {
|
||||
$result .= "<li onclick=\"location.href = this.getElementsByTagName('a')[0].href\"><a href=\"$item->Link\">$item->Title</a></li>\n";
|
||||
}
|
||||
$result .= "</ul>\n";
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this set as a XHTML unordered list.
|
||||
* @return string
|
||||
*/
|
||||
public function forTemplate() {
|
||||
return $this->UL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of ID => Title for the items in this set.
|
||||
*
|
||||
* @param string $index The field to use as a key for the array
|
||||
* @param string $titleField The field (or method) to get values for the map
|
||||
* @param string $emptyString Empty option text e.g "(Select one)"
|
||||
* @param bool $sort Sort the map alphabetically based on the $titleField value
|
||||
* @return array
|
||||
*/
|
||||
public function map($index = 'ID', $titleField = 'Title', $emptyString = null, $sort = false) {
|
||||
$map = array();
|
||||
foreach($this as $item) {
|
||||
$map[$item->$index] = ($item->hasMethod($titleField))
|
||||
? $item->$titleField() : $item->$titleField;
|
||||
}
|
||||
if($emptyString) $map = array('' => $emptyString) + $map;
|
||||
|
||||
if($sort) asort($map);
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an item in this list where the field $key is equal to $value
|
||||
* Eg: $doSet->find('ID', 4);
|
||||
* @return ViewableData The first matching item.
|
||||
*/
|
||||
public function find($key, $value) {
|
||||
foreach($this as $item) {
|
||||
if($item->$key == $value) return $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a column of the given field
|
||||
* @param string $value The field name
|
||||
* @return array
|
||||
*/
|
||||
public function column($value = "ID") {
|
||||
$list = array();
|
||||
foreach($this as $item ){
|
||||
$list[] = ($item->hasMethod($value)) ? $item->$value() : $item->$value;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of DataObjectSets. The array is keyed by index.
|
||||
*
|
||||
* @param string $index The field name to index the array by.
|
||||
* @return array
|
||||
*/
|
||||
public function groupBy($index){
|
||||
foreach($this as $item ){
|
||||
$key = ($item->hasMethod($index)) ? $item->$index() : $item->$index;
|
||||
if(!isset($result[$key])) {
|
||||
$result[$key] = new DataObjectSet();
|
||||
}
|
||||
$result[$key]->push($item);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the items by a given field.
|
||||
* Returns a DataObjectSet suitable for use in a nested template.
|
||||
* @param string $index The field to group by
|
||||
* @param string $childControl The name of the nested page control
|
||||
* @return DataObjectSet
|
||||
*/
|
||||
public function GroupedBy($index, $childControl = "Children") {
|
||||
$grouped = $this->groupBy($index);
|
||||
$groupedAsSet = new DataObjectSet();
|
||||
foreach($grouped as $group) {
|
||||
$groupedAsSet->push($group->First()->customise(array(
|
||||
$childControl => $group
|
||||
)));
|
||||
}
|
||||
return $groupedAsSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nested unordered list out of a "chain" of DataObject-relations,
|
||||
* using the automagic ComponentSet-relation-methods to find subsequent DataObjectSets.
|
||||
* The formatting of the list can be different for each level, and is evaluated as an SS-template
|
||||
* with access to the current DataObjects attributes and methods.
|
||||
*
|
||||
* Example: Groups (Level 0, the "calling" DataObjectSet, needs to be queried externally)
|
||||
* and their Members (Level 1, determined by the Group->Members()-relation).
|
||||
*
|
||||
* @param array $nestingLevels
|
||||
* Defines relation-methods on DataObjects as a string, plus custom
|
||||
* SS-template-code for the list-output. Use "Root" for the current DataObjectSet (is will not evaluate into
|
||||
* a function).
|
||||
* Caution: Don't close the list-elements (determined programatically).
|
||||
* You need to escape dollar-signs that need to be evaluated as SS-template-code.
|
||||
* Use $EvenOdd to get appropriate classes for CSS-styling.
|
||||
* Format:
|
||||
* array(
|
||||
* array(
|
||||
* "dataclass" => "Root",
|
||||
* "template" => "<li class=\"\$EvenOdd\"><a href=\"admin/crm/show/\$ID\">\$AccountName</a>"
|
||||
* ),
|
||||
* array(
|
||||
* "dataclass" => "GrantObjects",
|
||||
* "template" => "<li class=\"\$EvenOdd\"><a href=\"admin/crm/showgrant/\$ID\">#\$GrantNumber: \$TotalAmount.Nice, \$ApplicationDate.ShortMonth \$ApplicationDate.Year</a>"
|
||||
* )
|
||||
* );
|
||||
* @param string $ulExtraAttributes Extra attributes
|
||||
*
|
||||
* @return string Unordered List (HTML)
|
||||
*/
|
||||
public function buildNestedUL($nestingLevels, $ulExtraAttributes = "") {
|
||||
return $this->getChildrenAsUL($nestingLevels, 0, "", $ulExtraAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called recursively on the child-objects of the chain.
|
||||
*
|
||||
* @param array $nestingLevels see {@buildNestedUL}
|
||||
* @param int $level Current nesting level
|
||||
* @param string $template Template for list item
|
||||
* @param string $ulExtraAttributes Extra attributes
|
||||
* @return string
|
||||
*/
|
||||
public function getChildrenAsUL($nestingLevels, $level = 0, $template = "<li id=\"record-\$ID\" class=\"\$EvenOdd\">\$Title", $ulExtraAttributes = null, &$itemCount = 0) {
|
||||
$output = "";
|
||||
$hasNextLevel = false;
|
||||
$ulExtraAttributes = " $ulExtraAttributes";
|
||||
$output = "<ul" . eval($ulExtraAttributes) . ">\n";
|
||||
|
||||
$currentNestingLevel = $nestingLevels[$level];
|
||||
|
||||
// either current or default template
|
||||
$currentTemplate = (!empty($currentNestingLevel)) ? $currentNestingLevel['template'] : $template;
|
||||
$myViewer = SSViewer::fromString($currentTemplate);
|
||||
|
||||
if(isset($nestingLevels[$level+1]['dataclass'])){
|
||||
$childrenMethod = $nestingLevels[$level+1]['dataclass'];
|
||||
}
|
||||
// sql-parts
|
||||
|
||||
$filter = (isset($nestingLevels[$level+1]['filter'])) ? $nestingLevels[$level+1]['filter'] : null;
|
||||
$sort = (isset($nestingLevels[$level+1]['sort'])) ? $nestingLevels[$level+1]['sort'] : null;
|
||||
$join = (isset($nestingLevels[$level+1]['join'])) ? $nestingLevels[$level+1]['join'] : null;
|
||||
$limit = (isset($nestingLevels[$level+1]['limit'])) ? $nestingLevels[$level+1]['limit'] : null;
|
||||
$having = (isset($nestingLevels[$level+1]['having'])) ? $nestingLevels[$level+1]['having'] : null;
|
||||
|
||||
foreach($this as $parent) {
|
||||
$evenOdd = ($itemCount % 2 == 0) ? "even" : "odd";
|
||||
$parent->setField('EvenOdd', $evenOdd);
|
||||
$template = $myViewer->process($parent);
|
||||
|
||||
// if no output is selected, fall back to the id to keep the item "clickable"
|
||||
$output .= $template . "\n";
|
||||
|
||||
if(isset($childrenMethod)) {
|
||||
// workaround for missing groupby/having-parameters in instance_get
|
||||
// get the dataobjects for the next level
|
||||
$children = $parent->$childrenMethod($filter, $sort, $join, $limit, $having);
|
||||
if($children) {
|
||||
$output .= $children->getChildrenAsUL($nestingLevels, $level+1, $currentTemplate, $ulExtraAttributes);
|
||||
}
|
||||
}
|
||||
$output .= "</li>\n";
|
||||
$itemCount++;
|
||||
}
|
||||
|
||||
$output .= "</ul>\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function canSortBy($by) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the current DataObjectSet instance.
|
||||
* @param string $fieldname The name of the field on the DataObject that you wish to sort the set by.
|
||||
* @param string $direction Direction to sort by, either "ASC" or "DESC".
|
||||
*/
|
||||
public function sort($fieldname, $direction = "ASC") {
|
||||
if($this->items) {
|
||||
if (is_string($fieldname) && preg_match('/(.+?)(\s+?)(A|DE)SC$/', $fieldname, $matches)) {
|
||||
$fieldname = $matches[1];
|
||||
$direction = $matches[3].'SC';
|
||||
}
|
||||
column_sort($this->items, $fieldname, $direction, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove duplicates from this set based on the dataobjects field.
|
||||
* Assumes all items contained in the set all have that field.
|
||||
* Useful after merging to sets via {@link merge()}.
|
||||
*
|
||||
* @param string $field the field to check for duplicates
|
||||
*/
|
||||
public function removeDuplicates($field = 'ID') {
|
||||
$exists = array();
|
||||
foreach($this->items as $key => $item) {
|
||||
if(isset($exists[$fullkey = ClassInfo::baseDataClass($item) . ":" . $item->$field])) {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
$exists[$fullkey] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about this set in HTML format for debugging.
|
||||
* @return string
|
||||
*/
|
||||
public function debug() {
|
||||
$val = "<h2>" . $this->class . "</h2><ul>";
|
||||
foreach($this as $item) {
|
||||
$val .= "<li style=\"list-style-type: disc; margin-left: 20px\">" . Debug::text($item) . "</li>";
|
||||
}
|
||||
$val .= "</ul>";
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the set by $groupField and returns the parent of each group whose class
|
||||
* is $groupClassName. If $collapse is true, the group will be collapsed up until an ancestor with the
|
||||
* given class is found.
|
||||
* @param string $groupField The field to group by.
|
||||
* @param string $groupClassName Classname.
|
||||
* @param string $sortParents SORT clause to insert into the parents SQL.
|
||||
* @param string $parentField Parent field.
|
||||
* @param boolean $collapse Collapse up until an ancestor with the given class is found.
|
||||
* @param string $requiredParents Required parents
|
||||
* @return DataObjectSet
|
||||
*/
|
||||
public function groupWithParents($groupField, $groupClassName, $sortParents = null, $parentField = 'ID', $collapse = false, $requiredParents = null) {
|
||||
$groupTable = ClassInfo::baseDataClass($groupClassName);
|
||||
|
||||
// Each item in this DataObjectSet is grouped into a multidimensional array
|
||||
// indexed by it's parent. The parent IDs are later used to find the parents
|
||||
// that make up the returned set.
|
||||
$groupedSet = array();
|
||||
|
||||
// Array to store the subgroups matching the requirements
|
||||
$resultsArray = array();
|
||||
|
||||
// Put this item into the array indexed by $groupField.
|
||||
// the keys are later used to retrieve the top-level records
|
||||
foreach( $this as $item ) {
|
||||
$groupedSet[$item->$groupField][] = $item;
|
||||
}
|
||||
|
||||
$parentSet = null;
|
||||
|
||||
// retrieve parents for this set
|
||||
|
||||
// TODO How will we collapse the hierarchy to bridge the gap?
|
||||
|
||||
// if collapse is specified, then find the most direct ancestor of type
|
||||
// $groupClassName
|
||||
if($collapse) {
|
||||
// The most direct ancestors with the type $groupClassName
|
||||
$parentSet = array();
|
||||
|
||||
// get direct parents
|
||||
$parents = DataObject::get($groupClassName, "\"$groupTable\".\"$parentField\" IN( " . implode( ",", array_keys( $groupedSet ) ) . ")", $sortParents );
|
||||
|
||||
// for each of these parents...
|
||||
foreach($parents as $parent) {
|
||||
// store the old parent ID. This is required to change the grouped items
|
||||
// in the $groupSet array
|
||||
$oldParentID = $parent->ID;
|
||||
|
||||
// get the parental stack
|
||||
$parentObjects= $parent->parentStack();
|
||||
$parentStack = array();
|
||||
|
||||
foreach( $parentObjects as $parentObj )
|
||||
$parentStack[] = $parentObj->ID;
|
||||
|
||||
// is some particular IDs are required, then get the intersection
|
||||
if($requiredParents && count($requiredParents)) {
|
||||
$parentStack = array_intersect($requiredParents, $parentStack);
|
||||
}
|
||||
|
||||
$newParent = null;
|
||||
|
||||
// If there are no parents, the group can be omitted
|
||||
if(empty($parentStack)) {
|
||||
$newParent = new DataObjectSet();
|
||||
} else {
|
||||
$newParent = DataObject::get_one( $groupClassName, "\"$groupTable\".\"$parentField\" IN( " . implode( ",", $parentStack ) . ")" );
|
||||
}
|
||||
|
||||
// change each of the descendant's association from the old parent to
|
||||
// the new parent. This effectively collapses the hierarchy
|
||||
foreach( $groupedSet[$oldParentID] as $descendant ) {
|
||||
$groupedSet[$newParent->ID][] = $descendant;
|
||||
}
|
||||
|
||||
// Add the most direct ancestor of type $groupClassName
|
||||
$parentSet[] = $newParent;
|
||||
}
|
||||
// otherwise get the parents of these items
|
||||
} else {
|
||||
|
||||
$requiredIDs = array_keys( $groupedSet );
|
||||
|
||||
if( $requiredParents && cont($requiredParents)) {
|
||||
$requiredIDs = array_intersect($requiredParents, $requiredIDs);
|
||||
}
|
||||
|
||||
if(empty($requiredIDs)) {
|
||||
$parentSet = new DataObjectSet();
|
||||
} else {
|
||||
$parentSet = DataObject::get( $groupClassName, "\"$groupTable\".\"$parentField\" IN( " . implode( ",", $requiredIDs ) . ")", $sortParents );
|
||||
}
|
||||
|
||||
$parentSet = $parentSet->toArray();
|
||||
}
|
||||
|
||||
foreach($parentSet as $parent) {
|
||||
$resultsArray[] = $parent->customise(array(
|
||||
"GroupItems" => new DataObjectSet($groupedSet[$parent->$parentField])
|
||||
));
|
||||
}
|
||||
|
||||
return new DataObjectSet($resultsArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to this set without writing it to the database
|
||||
* @param DataObject $field Field to add
|
||||
*/
|
||||
function addWithoutWrite($field) {
|
||||
$this->items[] = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the DataObjectSet contains all of the IDs givem
|
||||
* @param $idList An array of object IDs
|
||||
*/
|
||||
function containsIDs($idList) {
|
||||
foreach($idList as $item) $wants[$item] = true;
|
||||
foreach($this as $item) if($item) unset($wants[$item->ID]);
|
||||
return !$wants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the DataObjectSet contains all of and *only* the IDs given.
|
||||
* Note that it won't like duplicates very much.
|
||||
* @param $idList An array of object IDs
|
||||
*/
|
||||
function onlyContainsIDs($idList) {
|
||||
return $this->containsIDs($idList) && sizeof($idList) == $this->count();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a 2D array by particular column.
|
||||
* @param array $data The array to sort.
|
||||
* @param mixed $column The name of the column you wish to sort by, or an array of column=>directions to sort by.
|
||||
* @param string $direction Direction to sort by, either "ASC" or "DESC".
|
||||
* @param boolean $preserveIndexes Preserve indexes
|
||||
*/
|
||||
function column_sort(&$data, $column, $direction = "ASC", $preserveIndexes = true) {
|
||||
global $column_sort_field;
|
||||
|
||||
// if we were only given a string for column, move it into an array
|
||||
if (is_string($column)) $column = array($column => $direction);
|
||||
|
||||
// convert directions to integers
|
||||
foreach ($column as $k => $v) {
|
||||
if ($v == 'ASC') {
|
||||
$column[$k] = 1;
|
||||
}
|
||||
elseif ($v == 'DESC') {
|
||||
$column[$k] = -1;
|
||||
}
|
||||
elseif (!is_numeric($v)) {
|
||||
$column[$k] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$column_sort_field = $column;
|
||||
|
||||
if($preserveIndexes) {
|
||||
uasort($data, "column_sort_callback_basic");
|
||||
} else {
|
||||
usort($data, "column_sort_callback_basic");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used by column_sort
|
||||
*/
|
||||
function column_sort_callback_basic($a, $b) {
|
||||
global $column_sort_field;
|
||||
$result = 0;
|
||||
// loop through each sort field
|
||||
foreach ($column_sort_field as $field => $multiplier) {
|
||||
// if A < B then no further examination is necessary
|
||||
if ($a->$field < $b->$field) {
|
||||
$result = -1 * $multiplier;
|
||||
break;
|
||||
}
|
||||
// if A > B then no further examination is necessary
|
||||
elseif ($a->$field > $b->$field) {
|
||||
$result = $multiplier;
|
||||
break;
|
||||
}
|
||||
// A == B means we need to compare the two using the next field
|
||||
// if this was the last field, then function returns that objects
|
||||
// are equivalent
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Iterator for a DataObjectSet
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage model
|
||||
*/
|
||||
class DataObjectSet_Iterator implements Iterator {
|
||||
function __construct($items) {
|
||||
$this->items = $items;
|
||||
|
||||
$this->current = $this->prepareItem(current($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an item taken from the internal array for
|
||||
* output by this iterator. Ensures that it is an object.
|
||||
* @param DataObject $item Item to prepare
|
||||
* @return DataObject
|
||||
*/
|
||||
protected function prepareItem($item) {
|
||||
if(is_object($item)) {
|
||||
$item->iteratorProperties(key($this->items), sizeof($this->items));
|
||||
}
|
||||
// This gives some reliablity but it patches over the root cause of the bug...
|
||||
// else if(key($this->items) !== null) $item = new ViewableData();
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current object of the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function current() {
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current object of the iterator.
|
||||
* @return mixed
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next item in this set.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function next() {
|
||||
$this->current = $this->prepareItem(next($this->items));
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the iterator to the beginning of the set.
|
||||
* @return DataObject The first item in the set.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->current = $this->prepareItem(reset($this->items));
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the iterator is pointing to a valid item in the set.
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->current !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next item in this set without progressing the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function peekNext() {
|
||||
return $this->getOffset(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prvious item in this set, without affecting the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function peekPrev() {
|
||||
return $this->getOffset(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object in this set offset by $offset from the iterator pointer.
|
||||
* @param int $offset The offset.
|
||||
* @return DataObject|boolean DataObject of offset item, or boolean FALSE if not found
|
||||
*/
|
||||
public function getOffset($offset) {
|
||||
$keys = array_keys($this->items);
|
||||
foreach($keys as $i => $key) {
|
||||
if($key == key($this->items)) break;
|
||||
}
|
||||
|
||||
if(isset($keys[$i + $offset])) {
|
||||
$requiredKey = $keys[$i + $offset];
|
||||
return $this->items[$requiredKey];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
}
|
@ -5,6 +5,34 @@
|
||||
*/
|
||||
class ArrayListTest extends SapphireTest {
|
||||
|
||||
public function testArrayAccessExists() {
|
||||
$list = new ArrayList(array(
|
||||
$one = new DataObject(array('Title' => 'one')),
|
||||
$two = new DataObject(array('Title' => 'two')),
|
||||
$three = new DataObject(array('Title' => 'three'))
|
||||
));
|
||||
$this->assertEquals(count($list), 3);
|
||||
$this->assertTrue(isset($list[0]), 'First item in the set is set');
|
||||
$this->assertEquals($one, $list[0], 'First item in the set is accessible by array notation');
|
||||
}
|
||||
|
||||
public function testArrayAccessUnset() {
|
||||
$list = new ArrayList(array(
|
||||
$one = new DataObject(array('Title' => 'one')),
|
||||
$two = new DataObject(array('Title' => 'two')),
|
||||
$three = new DataObject(array('Title' => 'three'))
|
||||
));
|
||||
unset($list[0]);
|
||||
$this->assertEquals(count($list), 2);
|
||||
}
|
||||
|
||||
public function testArrayAccessSet() {
|
||||
$list = new ArrayList();
|
||||
$this->assertEquals(0, count($list));
|
||||
$list['testing!'] = $test = new DataObject(array('Title' => 'I\'m testing!'));
|
||||
$this->assertEquals($test, $list['testing!'], 'Set item is accessible by the key we set it as');
|
||||
}
|
||||
|
||||
public function testCount() {
|
||||
$list = new ArrayList();
|
||||
$this->assertEquals(0, $list->count());
|
||||
|
@ -1,384 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Test the {@link DataObjectSet} class.
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage tests
|
||||
*/
|
||||
class DataObjectSetTest extends SapphireTest {
|
||||
|
||||
static $fixture_file = 'DataObjectSetTest.yml';
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'DataObjectTest_Team',
|
||||
'DataObjectTest_SubTeam',
|
||||
'DataObjectTest_Player',
|
||||
'DataObjectSetTest_TeamComment',
|
||||
'DataObjectSetTest_Base',
|
||||
'DataObjectSetTest_ChildClass',
|
||||
);
|
||||
|
||||
function testArrayAccessExists() {
|
||||
$set = new DataObjectSet(array(
|
||||
$one = new DataObject(array('Title' => 'one')),
|
||||
$two = new DataObject(array('Title' => 'two')),
|
||||
$three = new DataObject(array('Title' => 'three'))
|
||||
));
|
||||
$this->assertEquals(count($set), 3);
|
||||
$this->assertTrue(isset($set[0]), 'First item in the set is set');
|
||||
$this->assertEquals($one, $set[0], 'First item in the set is accessible by array notation');
|
||||
}
|
||||
|
||||
function testArrayAccessUnset() {
|
||||
$set = new DataObjectSet(array(
|
||||
$one = new DataObject(array('Title' => 'one')),
|
||||
$two = new DataObject(array('Title' => 'two')),
|
||||
$three = new DataObject(array('Title' => 'three'))
|
||||
));
|
||||
unset($set[0]);
|
||||
$this->assertEquals(count($set), 2);
|
||||
}
|
||||
|
||||
function testArrayAccessSet() {
|
||||
$set = new DataObjectSet();
|
||||
$this->assertEquals(0, count($set));
|
||||
$set['testing!'] = $test = new DataObject(array('Title' => 'I\'m testing!'));
|
||||
$this->assertEquals($test, $set['testing!'], 'Set item is accessible by the key we set it as');
|
||||
}
|
||||
|
||||
function testIterator() {
|
||||
$set = new DataObjectSet(array(
|
||||
$one = new DataObject(array('Title'=>'one')),
|
||||
$two = new DataObject(array('Title'=>'two')),
|
||||
$three = new DataObject(array('Title'=>'three')),
|
||||
$four = new DataObject(array('Title'=>'four'))
|
||||
));
|
||||
|
||||
// test Pos() with foreach()
|
||||
$i = 0;
|
||||
foreach($set as $item) {
|
||||
$i++;
|
||||
$this->assertEquals($i, $item->Pos(), "Iterator position is set correctly on ViewableData when iterated with foreach()");
|
||||
}
|
||||
|
||||
// test Pos() manually
|
||||
$this->assertEquals(1, $one->Pos());
|
||||
$this->assertEquals(2, $two->Pos());
|
||||
$this->assertEquals(3, $three->Pos());
|
||||
$this->assertEquals(4, $four->Pos());
|
||||
|
||||
// test DataObjectSet->Count()
|
||||
$this->assertEquals(4, $set->Count());
|
||||
|
||||
// test DataObjectSet->First()
|
||||
$this->assertSame($one, $set->First());
|
||||
|
||||
// test DataObjectSet->Last()
|
||||
$this->assertSame($four, $set->Last());
|
||||
|
||||
// test ViewableData->First()
|
||||
$this->assertTrue($one->First());
|
||||
$this->assertFalse($two->First());
|
||||
$this->assertFalse($three->First());
|
||||
$this->assertFalse($four->First());
|
||||
|
||||
// test ViewableData->Last()
|
||||
$this->assertFalse($one->Last());
|
||||
$this->assertFalse($two->Last());
|
||||
$this->assertFalse($three->Last());
|
||||
$this->assertTrue($four->Last());
|
||||
|
||||
// test ViewableData->Middle()
|
||||
$this->assertFalse($one->Middle());
|
||||
$this->assertTrue($two->Middle());
|
||||
$this->assertTrue($three->Middle());
|
||||
$this->assertFalse($four->Middle());
|
||||
|
||||
// test ViewableData->Even()
|
||||
$this->assertFalse($one->Even());
|
||||
$this->assertTrue($two->Even());
|
||||
$this->assertFalse($three->Even());
|
||||
$this->assertTrue($four->Even());
|
||||
|
||||
// test ViewableData->Odd()
|
||||
$this->assertTrue($one->Odd());
|
||||
$this->assertFalse($two->Odd());
|
||||
$this->assertTrue($three->Odd());
|
||||
$this->assertFalse($four->Odd());
|
||||
}
|
||||
|
||||
public function testMultipleOf() {
|
||||
$comments = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
$commArr = $comments->toArray();
|
||||
$multiplesOf3 = 0;
|
||||
|
||||
foreach($commArr as $comment) {
|
||||
if($comment->MultipleOf(3)) {
|
||||
$comment->IsMultipleOf3 = true;
|
||||
$multiplesOf3++;
|
||||
} else {
|
||||
$comment->IsMultipleOf3 = false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertEquals(1, $multiplesOf3);
|
||||
|
||||
$this->assertFalse($commArr[0]->IsMultipleOf3);
|
||||
$this->assertFalse($commArr[1]->IsMultipleOf3);
|
||||
$this->assertTrue($commArr[2]->IsMultipleOf3);
|
||||
|
||||
foreach($comments as $comment) {
|
||||
if($comment->MultipleOf(3, 1)) {
|
||||
$comment->IsMultipleOf3 = true;
|
||||
} else {
|
||||
$comment->IsMultipleOf3 = false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertFalse($commArr[0]->IsMultipleOf3);
|
||||
$this->assertFalse($commArr[1]->IsMultipleOf3);
|
||||
$this->assertTrue($commArr[2]->IsMultipleOf3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->Count()}
|
||||
*/
|
||||
function testCount() {
|
||||
$comments = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
|
||||
/* There are a total of 8 items in the set */
|
||||
$this->assertEquals($comments->Count(), 3, 'There are a total of 3 items in the set');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->First()}
|
||||
*/
|
||||
function testFirst() {
|
||||
$comments = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
|
||||
/* The first object is Joe's comment */
|
||||
$this->assertEquals($comments->First()->Name, 'Joe', 'The first object has a Name field value of "Joe"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->Last()}
|
||||
*/
|
||||
function testLast() {
|
||||
$comments = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
|
||||
/* The last object is Dean's comment */
|
||||
$this->assertEquals($comments->Last()->Name, 'Phil', 'The last object has a Name field value of "Phil"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->map()}
|
||||
*/
|
||||
function testMap() {
|
||||
$comments = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
|
||||
/* Now we get a map of all the PageComment records */
|
||||
$map = $comments->map('ID', 'Title');
|
||||
|
||||
$expectedMap = array(
|
||||
1 => 'Joe',
|
||||
2 => 'Bob',
|
||||
3 => 'Phil'
|
||||
);
|
||||
|
||||
/* There are 9 items in the map. 3 are records. 1 is the empty value */
|
||||
$this->assertEquals(count($map), 3, 'There are 3 items in the map.');
|
||||
}
|
||||
|
||||
function testRemoveDuplicates() {
|
||||
// Note that PageComment and DataObjectSetTest_TeamComment are both descendants of DataObject, and don't
|
||||
// share an inheritance relationship below that.
|
||||
$pageComments = DataObject::get('DataObjectSetTest_TeamComment');
|
||||
$teamComments = DataObject::get('DataObjectSetTest_TeamComment');
|
||||
|
||||
/* Test default functionality (remove by ID). We'd expect to loose all our
|
||||
* team comments as they have the same IDs as the first three page comments */
|
||||
|
||||
$allComments = new DataObjectSet();
|
||||
$allComments->merge($pageComments);
|
||||
$allComments->merge($teamComments);
|
||||
|
||||
$this->assertEquals($allComments->Count(), 6);
|
||||
|
||||
$allComments->removeDuplicates();
|
||||
|
||||
$this->assertEquals($allComments->Count(), 3, 'Standard functionality is to remove duplicate base class/IDs');
|
||||
|
||||
/* Now test removing duplicates based on a common field. In this case we shall
|
||||
* use 'Name', so we can get all the unique commentators */
|
||||
|
||||
|
||||
$comment = new DataObjectSetTest_TeamComment();
|
||||
$comment->Name = "Bob";
|
||||
|
||||
$allComments->push($comment);
|
||||
|
||||
$this->assertEquals($allComments->Count(), 4);
|
||||
|
||||
$allComments->removeDuplicates('Name');
|
||||
|
||||
$this->assertEquals($allComments->Count(), 3, 'There are 3 uniquely named commentators');
|
||||
|
||||
// Ensure that duplicates are removed where the base data class is the same.
|
||||
$mixedSet = new DataObjectSet();
|
||||
$mixedSet->push(new DataObjectSetTest_Base(array('ID' => 1)));
|
||||
$mixedSet->push(new DataObjectSetTest_ChildClass(array('ID' => 1))); // dup: same base class and ID
|
||||
$mixedSet->push(new DataObjectSetTest_ChildClass(array('ID' => 1))); // dup: more than one dup of the same object
|
||||
$mixedSet->push(new DataObjectSetTest_ChildClass(array('ID' => 2))); // not dup: same type again, but different
|
||||
$mixedSet->push(new DataObjectSetTest_Base(array('ID' => 1))); // dup: another dup, not consequetive.
|
||||
|
||||
$mixedSet->removeDuplicates('ID');
|
||||
|
||||
$this->assertEquals($mixedSet->Count(), 2, 'There are 3 unique data objects in a very mixed set');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->insertFirst()}
|
||||
*/
|
||||
function testInsertFirst() {
|
||||
// inserFirst doesn't work with DataLists any more, because of new ORM.
|
||||
/*
|
||||
// Get one comment
|
||||
$comment = DataObject::get_one('DataObjectSetTest_TeamComment', "\"Name\" = 'Joe'");
|
||||
|
||||
// Get all other comments
|
||||
$set = DataObject::get('DataObjectSetTest_TeamComment', '"Name" != \'Joe\'');
|
||||
|
||||
// Duplicate so we can use it later without another lookup
|
||||
$otherSet = clone $set;
|
||||
// insert without a key
|
||||
$otherSet->insertFirst($comment);
|
||||
$this->assertEquals($comment, $otherSet->First(), 'Comment should be first');
|
||||
|
||||
// Give us another copy
|
||||
$otherSet = clone $set;
|
||||
// insert with a numeric key
|
||||
$otherSet->insertFirst($comment, 2);
|
||||
$this->assertEquals($comment, $otherSet->First(), 'Comment should be first');
|
||||
|
||||
// insert with a non-numeric key
|
||||
$set->insertFirst($comment, 'SomeRandomKey');
|
||||
$this->assertEquals($comment, $set->First(), 'Comment should be first');
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->getRange()}
|
||||
*/
|
||||
function testGetRange() {
|
||||
$comments = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
|
||||
// Make sure we got all 8 comments
|
||||
$this->assertEquals($comments->Count(), 3, 'Three comments in the database.');
|
||||
|
||||
// Grab a range
|
||||
$range = $comments->getRange(1, 2);
|
||||
$this->assertEquals($range->Count(), 2, 'Two comment in the range.');
|
||||
|
||||
// And now grab a range that shouldn't be full. Remember counting starts at 0.
|
||||
$range = $comments->getRange(2, 1);
|
||||
$this->assertEquals($range->Count(), 1, 'One comment in the range.');
|
||||
|
||||
// Make sure it's the last one
|
||||
$this->assertEquals($range->First(), $comments->Last(), 'The only item in the range should be the last one.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->exists()}
|
||||
*/
|
||||
function testExists() {
|
||||
// Test an empty set
|
||||
$set = new DataObjectSet();
|
||||
$this->assertFalse($set->exists(), 'Empty set doesn\'t exist.');
|
||||
|
||||
// Test a non-empty set
|
||||
$set = DataObject::get('DataObjectSetTest_TeamComment', '', "\"ID\" ASC");
|
||||
$this->assertTrue($set->exists(), 'Non-empty set does exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->shift()}
|
||||
*/
|
||||
function testShift() {
|
||||
$set = new DataObjectSet();
|
||||
$set->push(new ArrayData(array('Name' => 'Joe')));
|
||||
$set->push(new ArrayData(array('Name' => 'Bob')));
|
||||
$set->push(new ArrayData(array('Name' => 'Ted')));
|
||||
$this->assertEquals('Joe', $set->shift()->Name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->unshift()}
|
||||
*/
|
||||
function testUnshift() {
|
||||
$set = new DataObjectSet();
|
||||
$set->push(new ArrayData(array('Name' => 'Joe')));
|
||||
$set->push(new ArrayData(array('Name' => 'Bob')));
|
||||
$set->push(new ArrayData(array('Name' => 'Ted')));
|
||||
$set->unshift(new ArrayData(array('Name' => 'Steve')));
|
||||
$this->assertEquals('Steve', $set->First()->Name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->pop()}
|
||||
*/
|
||||
function testPop() {
|
||||
$set = new DataObjectSet();
|
||||
$set->push(new ArrayData(array('Name' => 'Joe')));
|
||||
$set->push(new ArrayData(array('Name' => 'Bob')));
|
||||
$set->push(new ArrayData(array('Name' => 'Ted')));
|
||||
$this->assertEquals('Ted', $set->pop()->Name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link DataObjectSet->sort()}
|
||||
*/
|
||||
function testSort() {
|
||||
$set = new DataObjectSet(array(
|
||||
array('Name'=>'Object1', 'F1'=>1, 'F2'=>2, 'F3'=>3),
|
||||
array('Name'=>'Object2', 'F1'=>2, 'F2'=>1, 'F3'=>4),
|
||||
array('Name'=>'Object3', 'F1'=>5, 'F2'=>2, 'F3'=>2),
|
||||
));
|
||||
// test a single sort ASC
|
||||
$set->sort('F3', 'ASC');
|
||||
$this->assertEquals($set->First()->Name, 'Object3', 'Object3 should be first in the set');
|
||||
// test a single sort DESC
|
||||
$set->sort('F3', 'DESC');
|
||||
$this->assertEquals($set->First()->Name, 'Object2', 'Object2 should be first in the set');
|
||||
// test a multi sort
|
||||
$set->sort(array('F2'=>'ASC', 'F1'=>'ASC'));
|
||||
$this->assertEquals($set->Last()->Name, 'Object3', 'Object3 should be last in the set');
|
||||
// test a multi sort
|
||||
$set->sort(array('F2'=>'ASC', 'F1'=>'DESC'));
|
||||
$this->assertEquals($set->Last()->Name, 'Object1', 'Object1 should be last in the set');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage tests
|
||||
*/
|
||||
class DataObjectSetTest_TeamComment extends DataObject implements TestOnly {
|
||||
|
||||
static $db = array(
|
||||
'Name' => 'Varchar',
|
||||
'Comment' => 'Text',
|
||||
);
|
||||
|
||||
static $has_one = array(
|
||||
'Team' => 'DataObjectTest_Team',
|
||||
);
|
||||
}
|
||||
|
||||
class DataObjectSetTest_Base extends DataObject implements TestOnly {
|
||||
static $db = array(
|
||||
'Name' => 'Varchar'
|
||||
);
|
||||
}
|
||||
|
||||
class DataObjectSetTest_ChildClass extends DataObjectSetTest_Base implements TestOnly {
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
DataObjectTest_Team:
|
||||
team1:
|
||||
Title: Team 1
|
||||
team2:
|
||||
Title: Team 2
|
||||
|
||||
DataObjectTest_Player:
|
||||
captain1:
|
||||
FirstName: Captain 1
|
||||
FavouriteTeam: =>DataObjectTest_Team.team1
|
||||
Teams: =>DataObjectTest_Team.team1
|
||||
captain2:
|
||||
FirstName: Captain 2
|
||||
Teams: =>DataObjectTest_Team.team2
|
||||
player1:
|
||||
FirstName: Player 1
|
||||
player2:
|
||||
FirstName: Player 2
|
||||
Teams: =>DataObjectTest_Team.team1,=>DataObjectTest_Team.team2
|
||||
|
||||
DataObjectTest_SubTeam:
|
||||
subteam1:
|
||||
Title: Subteam 1
|
||||
SubclassDatabaseField: Subclassed 1
|
||||
ExtendedDatabaseField: Extended 1
|
||||
subteam2_with_player_relation:
|
||||
Title: Subteam 2
|
||||
SubclassDatabaseField: Subclassed 2
|
||||
ExtendeHasOneRelationship: =>DataObjectTest_Player.player1
|
||||
subteam3_with_empty_fields:
|
||||
Title: Subteam 3
|
||||
|
||||
DataObjectSetTest_TeamComment:
|
||||
comment1:
|
||||
Name: Joe
|
||||
Comment: This is a team comment by Joe
|
||||
Team: =>DataObjectTest_Team.team1
|
||||
comment2:
|
||||
Name: Bob
|
||||
Comment: This is a team comment by Bob
|
||||
Team: =>DataObjectTest_Team.team1
|
||||
comment3:
|
||||
Name: Phil
|
||||
Comment: Phil is a unique guy, and comments on team2
|
||||
Team: =>DataObjectTest_Team.team2
|
Loading…
Reference in New Issue
Block a user