API Combine Sortable, Filterable and Limitable into SS_List

This commit is contained in:
Steve Boyd 2024-10-22 10:49:17 +13:00
parent ba97de9458
commit 41cbfe4e49
18 changed files with 194 additions and 283 deletions

View File

@ -22,9 +22,6 @@ use SilverStripe\Model\List\ArrayList;
use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\Model\List\Filterable;
use SilverStripe\Model\List\Limitable;
use SilverStripe\Model\List\Sortable;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\View\HTML; use SilverStripe\View\HTML;
use SilverStripe\Model\ModelData; use SilverStripe\Model\ModelData;
@ -86,7 +83,7 @@ class GridField extends FormField
/** /**
* Data source. * Data source.
* *
* @var SS_List&Filterable&Sortable&Limitable * @var SS_List
*/ */
protected $list = null; protected $list = null;
@ -397,7 +394,7 @@ class GridField extends FormField
/** /**
* Set the data source. * Set the data source.
* *
* @param SS_List&Filterable&Sortable&Limitable $list * @param SS_List $list
* *
* @return $this * @return $this
*/ */
@ -411,7 +408,7 @@ class GridField extends FormField
/** /**
* Get the data source. * Get the data source.
* *
* @return SS_List&Filterable&Sortable&Limitable * @return SS_List
*/ */
public function getList() public function getList()
{ {
@ -421,7 +418,7 @@ class GridField extends FormField
/** /**
* Get the data source after applying every {@link GridField_DataManipulator} to it. * Get the data source after applying every {@link GridField_DataManipulator} to it.
* *
* @return SS_List&Filterable&Sortable&Limitable * @return SS_List
*/ */
public function getManipulatedList() public function getManipulatedList()
{ {

View File

@ -11,7 +11,6 @@ use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form; use SilverStripe\Forms\Form;
use SilverStripe\Forms\Schema\FormSchema; use SilverStripe\Forms\Schema\FormSchema;
use SilverStripe\Model\List\Filterable;
use SilverStripe\ORM\Search\SearchContext; use SilverStripe\ORM\Search\SearchContext;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\Model\ArrayData; use SilverStripe\Model\ArrayData;
@ -109,13 +108,13 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
*/ */
protected function checkDataType($dataList) protected function checkDataType($dataList)
{ {
if ($dataList instanceof Filterable) { if ($dataList instanceof SS_List) {
return true; return true;
} else { } else {
// This will be changed to always throw an exception in a future major release. // This will be changed to always throw an exception in a future major release.
if ($this->throwExceptionOnBadDataType) { if ($this->throwExceptionOnBadDataType) {
throw new LogicException( throw new LogicException(
static::class . " expects an SS_Filterable list to be passed to the GridField." static::class . " expects an SS_List list to be passed to the GridField."
); );
} }
return false; return false;
@ -209,7 +208,7 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
public function canFilterAnyColumns($gridField) public function canFilterAnyColumns($gridField)
{ {
$list = $gridField->getList(); $list = $gridField->getList();
if (!($list instanceof Filterable) || !$this->checkDataType($list)) { if (!($list instanceof SS_List) || !$this->checkDataType($list)) {
return false; return false;
} }
$modelClass = $gridField->getModelClass(); $modelClass = $gridField->getModelClass();

View File

@ -5,7 +5,6 @@ namespace SilverStripe\Forms\GridField;
use SilverStripe\Forms\FormField; use SilverStripe\Forms\FormField;
use SilverStripe\Forms\TabSet; use SilverStripe\Forms\TabSet;
use SilverStripe\Model\List\ArrayList; use SilverStripe\Model\List\ArrayList;
use SilverStripe\Model\List\Filterable;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
/** /**
@ -28,7 +27,7 @@ class GridFieldLazyLoader extends AbstractGridFieldComponent implements GridFiel
{ {
// If we are lazy loading an empty the list // If we are lazy loading an empty the list
if ($this->isLazy($gridField)) { if ($this->isLazy($gridField)) {
if ($dataList instanceof Filterable) { if ($dataList instanceof SS_List) {
// If our original list can be filtered, filter out all results. // If our original list can be filtered, filter out all results.
$dataList = $dataList->byIDs([-1]); $dataList = $dataList->byIDs([-1]);
} else { } else {

View File

@ -3,7 +3,6 @@
namespace SilverStripe\Forms\GridField; namespace SilverStripe\Forms\GridField;
use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Config\Configurable;
use SilverStripe\Model\List\Limitable;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\UnsavedRelationList; use SilverStripe\ORM\UnsavedRelationList;
use SilverStripe\Model\ArrayData; use SilverStripe\Model\ArrayData;
@ -89,13 +88,13 @@ class GridFieldPaginator extends AbstractGridFieldComponent implements GridField
*/ */
protected function checkDataType($dataList) protected function checkDataType($dataList)
{ {
if ($dataList instanceof Limitable) { if ($dataList instanceof SS_List) {
return true; return true;
} else { } else {
// This will be changed to always throw an exception in a future major release. // This will be changed to always throw an exception in a future major release.
if ($this->throwExceptionOnBadDataType) { if ($this->throwExceptionOnBadDataType) {
throw new LogicException( throw new LogicException(
static::class . " expects an SS_Limitable list to be passed to the GridField." static::class . " expects an SS_List list to be passed to the GridField."
); );
} }
return false; return false;
@ -183,7 +182,7 @@ class GridFieldPaginator extends AbstractGridFieldComponent implements GridField
$startRow = 0; $startRow = 0;
} }
if (!($dataList instanceof Limitable) || ($dataList instanceof UnsavedRelationList)) { if (!($dataList instanceof SS_List) || ($dataList instanceof UnsavedRelationList)) {
return $dataList; return $dataList;
} }

View File

@ -4,7 +4,6 @@ namespace SilverStripe\Forms\GridField;
use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\LiteralField;
use SilverStripe\ORM\DataObjectSchema; use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\Model\List\Sortable;
use SilverStripe\Model\List\ArrayList; use SilverStripe\Model\List\ArrayList;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
@ -78,13 +77,13 @@ class GridFieldSortableHeader extends AbstractGridFieldComponent implements Grid
*/ */
protected function checkDataType($dataList) protected function checkDataType($dataList)
{ {
if ($dataList instanceof Sortable) { if ($dataList instanceof SS_List) {
return true; return true;
} else { } else {
// This will be changed to always throw an exception in a future major release. // This will be changed to always throw an exception in a future major release.
if ($this->throwExceptionOnBadDataType) { if ($this->throwExceptionOnBadDataType) {
throw new LogicException( throw new LogicException(
static::class . " expects an SS_Sortable list to be passed to the GridField." static::class . " expects an SS_List list to be passed to the GridField."
); );
} }
return false; return false;
@ -246,7 +245,7 @@ class GridFieldSortableHeader extends AbstractGridFieldComponent implements Grid
* {@link DataQuery} first. * {@link DataQuery} first.
* *
* @param GridField $gridField * @param GridField $gridField
* @param SS_List&Sortable $dataList * @param SS_List $dataList
* @return SS_List * @return SS_List
*/ */
public function getManipulatedData(GridField $gridField, SS_List $dataList) public function getManipulatedData(GridField $gridField, SS_List $dataList)

View File

@ -15,8 +15,8 @@ use Traversable;
/** /**
* A list object that wraps around an array of objects or arrays. * A list object that wraps around an array of objects or arrays.
* *
* Note that (like DataLists), the implementations of the methods from SS_Filterable, SS_Sortable and * Note that (like DataLists), the implementations of the methods from SS_List return a new instance of ArrayList,
* SS_Limitable return a new instance of ArrayList, rather than modifying the existing instance. * rather than modifying the existing instance.
* *
* For easy reference, methods that operate in this way are: * For easy reference, methods that operate in this way are:
* *
@ -28,11 +28,8 @@ use Traversable;
* *
* @template T * @template T
* @implements SS_List<T> * @implements SS_List<T>
* @implements Filterable<T>
* @implements Sortable<T>
* @implements Limitable<T>
*/ */
class ArrayList extends ModelData implements SS_List, Filterable, Sortable, Limitable class ArrayList extends ModelData implements SS_List
{ {
use SearchFilterable; use SearchFilterable;
@ -597,7 +594,7 @@ class ArrayList extends ModelData implements SS_List, Filterable, Sortable, Limi
/** /**
* Filter the list to include items with these characteristics * Filter the list to include items with these characteristics
* *
* @see Filterable::filter() * @see SS_List::filter()
* @example $list->filter('Name', 'bob'); // only bob in the list * @example $list->filter('Name', 'bob'); // only bob in the list
* @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list * @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the Age 21 in list * @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the Age 21 in list
@ -854,7 +851,7 @@ class ArrayList extends ModelData implements SS_List, Filterable, Sortable, Limi
} }
/** /**
* @see Filterable::filterByCallback() * @see SS_List::filterByCallback()
* *
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; }) * @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback * @param callable $callback
@ -864,7 +861,7 @@ class ArrayList extends ModelData implements SS_List, Filterable, Sortable, Limi
{ {
if (!is_callable($callback)) { if (!is_callable($callback)) {
throw new LogicException(sprintf( throw new LogicException(sprintf(
"SS_Filterable::filterByCallback() passed callback must be callable, '%s' given", "SS_List::filterByCallback() passed callback must be callable, '%s' given",
gettype($callback) gettype($callback)
)); ));
} }

View File

@ -1,107 +0,0 @@
<?php
namespace SilverStripe\Model\List;
/**
* Additional interface for {@link SS_List} classes that are filterable.
*
* All methods in this interface are immutable - they should return new instances with the filter
* applied, rather than applying the filter in place
*
* @see SS_List
* @see Sortable
* @see Limitable
*
* @template T
* @extends SS_List<T>
*/
interface Filterable extends SS_List
{
/**
* Returns TRUE if the list can be filtered by a given field expression.
*
* @param string $by
* @return bool
*/
public function canFilterBy($by);
/**
* Return a new instance of this list that only includes items with these characteristics
*
* @example $list = $list->filter('Name', 'bob'); // only bob in the list
* @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*
* @return static<T>
*/
public function filter();
/**
* Return a copy of this list which contains items matching any of these characteristics.
*
* @example // only bob in the list
* $list = $list->filterAny('Name', 'bob');
* // SQL: WHERE "Name" = 'bob'
* @example // azis or bob in the list
* $list = $list->filterAny('Name', array('aziz', 'bob');
* // SQL: WHERE ("Name" IN ('aziz','bob'))
* @example // bob or anyone aged 21 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
* // SQL: WHERE ("Name" = 'bob' OR "Age" = '21')
* @example // bob or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
* // SQL: WHERE ("Name" = 'bob' OR ("Age" IN ('21', '43'))
* @example // all bobs, phils or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @param string|array See {@link filter()}
* @return static<T>
*/
public function filterAny();
/**
* Return a new instance of this list that excludes any items with these characteristics
*
* @example $list = $list->exclude('Name', 'bob'); // exclude bob from list
* @example $list = $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*
* @return static<T>
*/
public function exclude();
/**
* Return a new instance of this list that excludes any items with these characteristics
* Filter this List by a callback function. The function will be passed each record of the List in turn,
* and must return true for the record to be included. Returns the filtered list.
*
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback
* @return SS_List<T>
*/
public function filterByCallback($callback);
/**
* Return the first item with the given ID
*
* @param int $id
* @return T|null
*/
public function byID($id);
/**
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers
* @return static<T>
*/
public function byIDs($ids);
}

View File

@ -1,32 +0,0 @@
<?php
namespace SilverStripe\Model\List;
/**
* Additional interface for {@link SS_List} classes that are limitable - able to have a subset of the list extracted.
*
* All methods in this interface are immutable - they should return new instances with the limit
* applied, rather than applying the limit in place
*
* @see SS_List
* @see Sortable
* @see Filterable
*
* @template T
* @implements SS_List<T>
*/
interface Limitable extends SS_List
{
/**
* Returns a new instance of this list where no more than $limit records are included.
* If $offset is specified, then that many records at the beginning of the list will be skipped.
* This matches the behaviour of the SQL LIMIT clause.
*
* If `$length` is null, then no limit is applied. If `$length` is 0, then an empty list is returned.
*
* @throws InvalidArgumentException if $length or offset are negative
* @return static<T>
*/
public function limit(?int $length, int $offset = 0): Limitable;
}

View File

@ -11,33 +11,29 @@ use Traversable;
* functionality. It passes through list methods to the underlying list * functionality. It passes through list methods to the underlying list
* implementation. * implementation.
* *
* @template TList of SS_List&Sortable&Filterable&Limitable
* @template T * @template T
* @implements SS_List<T> * @implements SS_List<T>
* @implements Sortable<T>
* @implements Filterable<T>
* @implements Limitable<T>
*/ */
abstract class ListDecorator extends ModelData implements SS_List, Sortable, Filterable, Limitable abstract class ListDecorator extends ModelData implements SS_List
{ {
/** /**
* @var TList<T> * @var SS_List<T>
*/ */
protected SS_List&Sortable&Filterable&Limitable $list; protected SS_List $list;
/** /**
* @param TList<T> $list * @param SS_List<T> $list
*/ */
public function __construct(SS_List&Sortable&Filterable&Limitable $list) public function __construct(SS_List $list)
{ {
$this->setList($list); $this->setList($list);
parent::__construct(); parent::__construct();
} }
/** /**
* @return TList<T> * @return SS_List<T>
*/ */
public function getList(): SS_List&Sortable&Filterable&Limitable public function getList(): SS_List
{ {
return $this->list; return $this->list;
} }
@ -48,12 +44,12 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
* Useful for keeping a decorator/paginated list configuration intact while modifying * Useful for keeping a decorator/paginated list configuration intact while modifying
* the underlying list. * the underlying list.
* *
* @template TListA * @template SS_ListA
* @template TA * @template TA
* @param TListA<TA> $list * @param SS_ListA<TA> $list
* @return static<TListA, TA> * @return static<SS_ListA, TA>
*/ */
public function setList(SS_List&Sortable&Filterable&Limitable $list): ListDecorator public function setList(SS_List $list): ListDecorator
{ {
$this->list = $list; $this->list = $list;
$this->failover = $this->list; $this->failover = $this->list;
@ -165,7 +161,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
} }
/** /**
* @return TList<T> * @return SS_List<T>
*/ */
public function each($callback) public function each($callback)
{ {
@ -191,7 +187,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
* @example $list->sort('Name', 'ASC'); * @example $list->sort('Name', 'ASC');
* @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC')); * @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
* *
* @return TList<T> * @return SS_List<T>
*/ */
public function sort() public function sort()
{ {
@ -211,7 +207,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
* @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob or someone with Age 21 * @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob or someone with Age 21
* @example $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob or anyone with Age 21 or 43 * @example $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob or anyone with Age 21 or 43
* *
* @return TList<T> * @return SS_List<T>
*/ */
public function filter() public function filter()
{ {
@ -239,7 +235,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
* *
* @param string|array See {@link filter()} * @param string|array See {@link filter()}
* *
* @return TList<T> * @return SS_List<T>
*/ */
public function filterAny() public function filterAny()
{ {
@ -249,7 +245,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
/** /**
* Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a * Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a
* future implementation. * future implementation.
* @see Filterable::filterByCallback() * @see SS_List::filterByCallback()
* *
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; }) * @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback * @param callable $callback
@ -259,7 +255,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
{ {
if (!is_callable($callback)) { if (!is_callable($callback)) {
throw new LogicException(sprintf( throw new LogicException(sprintf(
"SS_Filterable::filterByCallback() passed callback must be callable, '%s' given", "SS_List::filterByCallback() passed callback must be callable, '%s' given",
gettype($callback) gettype($callback)
)); ));
} }
@ -273,9 +269,9 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
} }
/** /**
* @return TList<T> * @return SS_List<T>
*/ */
public function limit(?int $length, int $offset = 0): SS_List&Sortable&Filterable&Limitable public function limit(?int $length, int $offset = 0): SS_List
{ {
return $this->list->limit($length, $offset); return $this->list->limit($length, $offset);
} }
@ -290,7 +286,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
* *
* @param array $ids Array of integers * @param array $ids Array of integers
* *
* @return TList<T> * @return SS_List<T>
*/ */
public function byIDs($ids) public function byIDs($ids)
{ {
@ -305,7 +301,7 @@ abstract class ListDecorator extends ModelData implements SS_List, Sortable, Fil
* @example $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob or someone with Age 21 * @example $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob or someone with Age 21
* @example $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob or anyone with Age 21 or 43 * @example $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob or anyone with Age 21 or 43
* *
* @return TList<T> * @return SS_List<T>
*/ */
public function exclude() public function exclude()
{ {

View File

@ -15,6 +15,10 @@ use IteratorAggregate;
*/ */
interface SS_List extends ArrayAccess, Countable, IteratorAggregate interface SS_List extends ArrayAccess, Countable, IteratorAggregate
{ {
/**
* Representation of the list for use in a template
*/
public function forTemplate(): string;
/** /**
* Returns all the items in the list in an array. * Returns all the items in the list in an array.
@ -94,4 +98,135 @@ interface SS_List extends ArrayAccess, Countable, IteratorAggregate
* @return static<T> * @return static<T>
*/ */
public function each($callback); public function each($callback);
/**
* Returns TRUE if the list can be filtered by a given field expression.
*
* @param string $by
* @return bool
*/
public function canFilterBy($by);
/**
* Return a new instance of this list that only includes items with these characteristics
*
* @example $list = $list->filter('Name', 'bob'); // only bob in the list
* @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*
* @return static<T>
*/
public function filter();
/**
* Return a copy of this list which contains items matching any of these characteristics.
*
* @example // only bob in the list
* $list = $list->filterAny('Name', 'bob');
* // SQL: WHERE "Name" = 'bob'
* @example // azis or bob in the list
* $list = $list->filterAny('Name', array('aziz', 'bob');
* // SQL: WHERE ("Name" IN ('aziz','bob'))
* @example // bob or anyone aged 21 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
* // SQL: WHERE ("Name" = 'bob' OR "Age" = '21')
* @example // bob or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
* // SQL: WHERE ("Name" = 'bob' OR ("Age" IN ('21', '43'))
* @example // all bobs, phils or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @param string|array See {@link filter()}
* @return static<T>
*/
public function filterAny();
/**
* Return a new instance of this list that excludes any items with these characteristics
*
* @example $list = $list->exclude('Name', 'bob'); // exclude bob from list
* @example $list = $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*
* @return static<T>
*/
public function exclude();
/**
* Return a new instance of this list that excludes any items with these characteristics
* Filter this List by a callback function. The function will be passed each record of the List in turn,
* and must return true for the record to be included. Returns the filtered list.
*
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback
* @return SS_List<T>
*/
public function filterByCallback($callback);
/**
* Return the first item with the given ID
*
* @param int $id
* @return T|null
*/
public function byID($id);
/**
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers
* @return static<T>
*/
public function byIDs($ids);
/**
* Returns TRUE if the list can be sorted by a field.
*
* @param string $by
* @return bool
*/
public function canSortBy($by);
/**
* Return a new instance of this list that is sorted by one or more fields. You can either pass in a single
* field name and direction, or a map of field names to sort directions.
*
* @example $list = $list->sort('Name'); // default ASC sorting
* @example $list = $list->sort('Name DESC'); // DESC sorting
* @example $list = $list->sort('Name', 'ASC');
* @example $list = $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
*
* @return static<T>
*/
public function sort();
/**
* Return a new instance of this list based on reversing the current sort.
*
* @example $list = $list->reverse();
*
* @return static<T>
*/
public function reverse();
/**
* Returns a new instance of this list where no more than $limit records are included.
* If $offset is specified, then that many records at the beginning of the list will be skipped.
* This matches the behaviour of the SQL LIMIT clause.
*
* If `$length` is null, then no limit is applied. If `$length` is 0, then an empty list is returned.
*
* @throws InvalidArgumentException if $length or offset are negative
* @return static<T>
*/
public function limit(?int $length, int $offset = 0): SS_List;
} }

View File

@ -1,51 +0,0 @@
<?php
namespace SilverStripe\Model\List;
/**
* Additional interface for {@link SS_List} classes that are sortable.
*
* All methods in this interface are immutable - they should return new instances with the sort
* applied, rather than applying the sort in place
*
* @see SS_List
* @see Filterable
* @see Limitable
*
* @template T
* @implements SS_List<T>
*/
interface Sortable extends SS_List
{
/**
* Returns TRUE if the list can be sorted by a field.
*
* @param string $by
* @return bool
*/
public function canSortBy($by);
/**
* Return a new instance of this list that is sorted by one or more fields. You can either pass in a single
* field name and direction, or a map of field names to sort directions.
*
* @example $list = $list->sort('Name'); // default ASC sorting
* @example $list = $list->sort('Name DESC'); // DESC sorting
* @example $list = $list->sort('Name', 'ASC');
* @example $list = $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
*
* @return static<T>
*/
public function sort();
/**
* Return a new instance of this list based on reversing the current sort.
*
* @example $list = $list->reverse();
*
* @return static<T>
*/
public function reverse();
}

View File

@ -14,10 +14,7 @@ use SilverStripe\ORM\Connect\Query;
use Traversable; use Traversable;
use SilverStripe\ORM\DataQuery; use SilverStripe\ORM\DataQuery;
use SilverStripe\Model\List\ArrayList; use SilverStripe\Model\List\ArrayList;
use SilverStripe\Model\List\Filterable;
use SilverStripe\Model\List\Limitable;
use SilverStripe\Model\List\Map; use SilverStripe\Model\List\Map;
use SilverStripe\Model\List\Sortable;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\Filters\SearchFilterable; use SilverStripe\ORM\Filters\SearchFilterable;
@ -43,11 +40,8 @@ use SilverStripe\ORM\Filters\SearchFilterable;
* *
* @template T of DataObject * @template T of DataObject
* @implements SS_List<T> * @implements SS_List<T>
* @implements Filterable<T>
* @implements Sortable<T>
* @implements Limitable<T>
*/ */
class DataList extends ModelData implements SS_List, Filterable, Sortable, Limitable class DataList extends ModelData implements SS_List
{ {
use SearchFilterable; use SearchFilterable;
@ -474,7 +468,7 @@ class DataList extends ModelData implements SS_List, Filterable, Sortable, Limit
* *
* Raw SQL is not accepted, only actual field names can be passed * Raw SQL is not accepted, only actual field names can be passed
* *
* @see Filterable::filter() * @see SS_List::filter()
* *
* @example $list = $list->filter('Name', 'bob'); // only bob in the list * @example $list = $list->filter('Name', 'bob'); // only bob in the list
* @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list * @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
@ -591,7 +585,7 @@ class DataList extends ModelData implements SS_List, Filterable, Sortable, Limit
/** /**
* Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a * Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a
* future implementation. * future implementation.
* @see Filterable::filterByCallback() * @see SS_List::filterByCallback()
* *
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; }) * @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback * @param callable $callback
@ -601,7 +595,7 @@ class DataList extends ModelData implements SS_List, Filterable, Sortable, Limit
{ {
if (!is_callable($callback)) { if (!is_callable($callback)) {
throw new LogicException(sprintf( throw new LogicException(sprintf(
"SS_Filterable::filterByCallback() passed callback must be callable, '%s' given", "SS_List::filterByCallback() passed callback must be callable, '%s' given",
gettype($callback) gettype($callback)
)); ));
} }

View File

@ -11,10 +11,7 @@ use InvalidArgumentException;
use LogicException; use LogicException;
use SilverStripe\Core\ArrayLib; use SilverStripe\Core\ArrayLib;
use SilverStripe\Model\List\ArrayList; use SilverStripe\Model\List\ArrayList;
use SilverStripe\Model\List\Filterable;
use SilverStripe\Model\List\Limitable;
use SilverStripe\Model\List\Map; use SilverStripe\Model\List\Map;
use SilverStripe\Model\List\Sortable;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\Filters\SearchFilterable; use SilverStripe\ORM\Filters\SearchFilterable;
use Traversable; use Traversable;
@ -32,11 +29,8 @@ use Traversable;
* @template T of DataObject * @template T of DataObject
* @implements Relation<T> * @implements Relation<T>
* @implements SS_List<T> * @implements SS_List<T>
* @implements Filterable<T>
* @implements Sortable<T>
* @implements Limitable<T>
*/ */
class EagerLoadedList extends ModelData implements Relation, SS_List, Filterable, Sortable, Limitable class EagerLoadedList extends ModelData implements Relation, SS_List
{ {
use SearchFilterable; use SearchFilterable;

View File

@ -2,9 +2,6 @@
namespace SilverStripe\ORM; namespace SilverStripe\ORM;
use SilverStripe\Model\List\Filterable;
use SilverStripe\Model\List\Limitable;
use SilverStripe\Model\List\Sortable;
use SilverStripe\Model\List\SS_List; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBField;
@ -19,11 +16,8 @@ use SilverStripe\ORM\FieldType\DBField;
* *
* @template T * @template T
* @extends SS_List<T> * @extends SS_List<T>
* @extends Filterable<T>
* @extends Sortable<T>
* @extends Limitable<T>
*/ */
interface Relation extends SS_List, Filterable, Sortable, Limitable interface Relation extends SS_List
{ {
/** /**

View File

@ -8,11 +8,9 @@ use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\Deprecation;
use SilverStripe\Model\List\Filterable;
use SilverStripe\ORM\Filters\PartialMatchFilter; use SilverStripe\ORM\Filters\PartialMatchFilter;
use SilverStripe\ORM\Filters\SearchFilter; use SilverStripe\ORM\Filters\SearchFilter;
use SilverStripe\Model\List\Limitable; use SilverStripe\Model\List\SS_List;
use SilverStripe\Model\List\Sortable;
/** /**
* A SearchContext that can be used with non-ORM data. * A SearchContext that can be used with non-ORM data.
@ -37,12 +35,12 @@ class BasicSearchContext extends SearchContext
* for example "Comments__Name" instead of the filter name "Comments.Name". * for example "Comments__Name" instead of the filter name "Comments.Name".
* @param array|bool|string $sort Field to sort on. * @param array|bool|string $sort Field to sort on.
* @param array|null|string $limit * @param array|null|string $limit
* @param Filterable&Sortable&Limitable $existingQuery * @param SS_List $existingQuery
*/ */
public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null): Filterable&Sortable&Limitable public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null): SS_List
{ {
if (!$existingQuery || !($existingQuery instanceof Filterable) || !($existingQuery instanceof Sortable) || !($existingQuery instanceof Limitable)) { if (!$existingQuery || !is_a($existingQuery, SS_List::class)) {
throw new InvalidArgumentException('getQuery requires a pre-existing filterable/sortable/limitable list to be passed as $existingQuery.'); throw new InvalidArgumentException('getQuery requires a pre-existing SS_List list to be passed as $existingQuery.');
} }
if ((count(func_get_args()) >= 3) && (!in_array(gettype($limit), ['array', 'NULL', 'string']))) { if ((count(func_get_args()) >= 3) && (!in_array(gettype($limit), ['array', 'NULL', 'string']))) {
@ -98,7 +96,7 @@ class BasicSearchContext extends SearchContext
return $applied; return $applied;
} }
private function applyGeneralSearchField(array &$searchParams, Filterable $existingQuery): Filterable private function applyGeneralSearchField(array &$searchParams, SS_List $existingQuery): SS_List
{ {
$generalFieldName = static::config()->get('general_search_field_name'); $generalFieldName = static::config()->get('general_search_field_name');
if (array_key_exists($generalFieldName, $searchParams)) { if (array_key_exists($generalFieldName, $searchParams)) {

View File

@ -5,7 +5,7 @@ namespace SilverStripe\Model\Tests\List;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\Model\List\ArrayList; use SilverStripe\Model\List\ArrayList;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\Model\List\Filterable; use SilverStripe\Model\List\SS_List;
use SilverStripe\Model\ArrayData; use SilverStripe\Model\ArrayData;
use SilverStripe\Model\List\Map; use SilverStripe\Model\List\Map;
use stdClass; use stdClass;
@ -1455,7 +1455,7 @@ class ArrayListTest extends SapphireTest
$this->assertEquals(2, $list->count()); $this->assertEquals(2, $list->count());
$this->assertEquals($steve, $list[0]->toMap(), 'List should only contain Steve and Clair'); $this->assertEquals($steve, $list[0]->toMap(), 'List should only contain Steve and Clair');
$this->assertEquals($clair, $list[1]->toMap(), 'List should only contain Steve and Clair'); $this->assertEquals($clair, $list[1]->toMap(), 'List should only contain Steve and Clair');
$this->assertTrue($list instanceof Filterable, 'The List should be of type SS_Filterable'); $this->assertTrue($list instanceof SS_List, 'The List should be of type SS_List');
} }
/** /**

View File

@ -12,7 +12,7 @@ use SilverStripe\ORM\Connect\MySQLiConnector;
use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataQuery; use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\DB; use SilverStripe\ORM\DB;
use SilverStripe\Model\List\Filterable; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\Filters\ExactMatchFilter; use SilverStripe\ORM\Filters\ExactMatchFilter;
use SilverStripe\ORM\Tests\DataObjectTest\DataListQueryCounter; use SilverStripe\ORM\Tests\DataObjectTest\DataListQueryCounter;
use SilverStripe\ORM\Tests\DataObjectTest\Fixture; use SilverStripe\ORM\Tests\DataObjectTest\Fixture;
@ -1591,7 +1591,7 @@ class DataListTest extends SapphireTest
$this->assertEquals(2, $list->count()); $this->assertEquals(2, $list->count());
$this->assertEquals($expected, $result, 'List should only contain comments from Team 1 (Joe and Bob)'); $this->assertEquals($expected, $result, 'List should only contain comments from Team 1 (Joe and Bob)');
$this->assertTrue($list instanceof Filterable, 'The List should be of type SS_Filterable'); $this->assertTrue($list instanceof SS_List, 'The List should be of type SS_List');
} }
/** /**

View File

@ -10,7 +10,7 @@ use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\Connect\MySQLiConnector; use SilverStripe\ORM\Connect\MySQLiConnector;
use SilverStripe\ORM\EagerLoadedList; use SilverStripe\ORM\EagerLoadedList;
use SilverStripe\ORM\DB; use SilverStripe\ORM\DB;
use SilverStripe\Model\List\Filterable; use SilverStripe\Model\List\SS_List;
use SilverStripe\ORM\Tests\DataObjectTest\EquipmentCompany; use SilverStripe\ORM\Tests\DataObjectTest\EquipmentCompany;
use SilverStripe\ORM\Tests\DataObjectTest\Fan; use SilverStripe\ORM\Tests\DataObjectTest\Fan;
use SilverStripe\ORM\Tests\DataObjectTest\Player; use SilverStripe\ORM\Tests\DataObjectTest\Player;
@ -1690,7 +1690,7 @@ class EagerLoadedListTest extends SapphireTest
$this->assertEquals(2, $list->count()); $this->assertEquals(2, $list->count());
$this->assertEquals($expected, $result, 'List should only contain comments from Team 1 (Joe and Bob)'); $this->assertEquals($expected, $result, 'List should only contain comments from Team 1 (Joe and Bob)');
$this->assertTrue($list instanceof Filterable, 'The List should be of type SS_Filterable'); $this->assertTrue($list instanceof SS_List, 'The List should be of type SS_List');
} }
public function testSimpleExclude() public function testSimpleExclude()