I propose adding new methods to AbstractCollection
to allow easy manipulation of the contents. We could then use these methods in place of using for/foreach loops and temporary variables for building new collections of data.
Current Problem
For example, given a class Foo and a typed Collection FooCollection:
Class Foo
{
public $id;
public $name;
public $createdAt;
public function __construct(int $id, string $name, DateTime $createdAt=null)
{
$this->id = $id;
$this->name = $name;
$this->createdAt = $createdAt;
}
}
Class FooCollection extends \Ramsey\Collection\AbstractCollection
{
public function getType()
{
return Foo::class;
}
}
And you have created a collection of those Foos:
$foos = [
new Foo(1, 'bar', new DateTime('now')),
new Foo(2, 'baz', new DateTime('1 week ago')),
new Foo(3, 'fizz', new DateTime('3 months ago')),
new Foo(4, 'buzz', new DateTime('2 days ago')),
];
$fooCollection = new FooCollection($foos);
And you have a method which was passed as an argument a FooCollection
, and you want to sort your collection by the createdAt time, you'd have to do something like this:
Class Bar{
public function doSomething(FooCollection $fooCollection){
$values = $fooCollection->toArray();
usort($values, function($a, $b){
return ($a->createdAt > $b->createdAt);
});
$fooCollection = new FooCollection($values);
//Do something else with the Foos.
}
}
Proposed Solution
I propose a sort method would be much simpler for users:
Class Bar{
public function doSomething(FooCollection $fooCollection){
$fooCollection->sort('createdAt');
//Do something else with the Foos.
}
}
Suggested Methods
Alters Contents / Fluent Interface
sort
/**
* Orders the items by a property or return value of the method in the class
* @param string property
* @param string order 'desc'/'asc'
* @return self
*/
sort($property, $order='asc')
Examples:
$fooCollection->sort('createdAt', 'desc'); //sorts by property
$fooCollection->sort('getName'); //will call the method
filter
/**
* Filters out items using a callback method
* @param function $callback
* @return self
*/
filter($callback)
Examples:
$foos = [
new Foo(1, 'bar'),
new Foo(2, 'baz'),
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
];
$fooCollection = new FooCollection($foos);
//$fooCollection will now contain only the 'bar' and 'baz' foos.
$fooCollection->filter(function($foo){
return strlen($foo->name) <= 3;
});
where
/**
* Filters to items where the property or return value of a method equals the given value
* @param string property
* @param string value
* @return self
*/
where($property, $value)
Examples:
$fooCollection->where('name', 'foo'); //accesses a property
$fooCollection->where('getName', 'foo'); //will call the method
each
/**
* Applies a callback method to each item
* @param function $callback
* @return self
*/
each($callback)
Examples:
//Every Foo's name in the collection will be updated to end in '.beta'
$fooCollection->each(function($foo){
$foo->name .= '.beta';
});
diff
/**
* Calculates the difference of two collections by comparing the objects - must be the same type
* @param AbstractCollection
*/
diff($collection)
Examples:
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(2, 'baz'),
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $diffCollection should contain the 'bar', 'fizz' and 'buzz' Foos - the difference between the two.
$diffCollection = $someFoos->diff($moreFoos);
intersect
/**
* Calculates the intersection of two collections by comparing the objects - must be the same type
* @param AbstractCollection
*/
intersect($collection)
Examples:
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(2, 'baz'),
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $intersectCollection should contain the 'baz' Foo - the intersection between the two.
$intersectCollection = $someFoos->intersect($moreFoos);
unique
/**
* Filters out non-unique values either by comparing the objects, or an optional property/method accessor
* @param string property
*/
unique($property=null)
Examples:
$foos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz') //Original "baz"
new Foo(3, 'baz'), //a duplicate "baz" with different id
new Foo(4, 'fizz'),
new Foo(5, 'buzz'), //Original "buzz"
new Foo(5, 'buzz'), //Exact copy of "buzz"
]);
// $uniqueFoos will contain 5 foos, the extra "buzz" having been removed.
$uniqueFoos = $uniqueFoos->unique();
// $uniqueFoos will contain 4 foos, the extra "buzz" and "baz" (#3) having been removed
$uniqueFoos = $uniqueFoos->unique('name');
merge
/**
* Merges the two collections - must be the same type
* @param AbstractCollection
*/
merge($collection)
Examples:
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $allFoos should contain all 4 foos
$allFoos = $someFoos->intersect($moreFoos);
merge
/**
* Merges the two collections - must be the same type
* @param AbstractCollection
*/
merge($collection)
Examples:
$someFoos = new FooCollection([
new Foo(1, 'bar'),
new Foo(2, 'baz')
]);
$moreFoos = new FooCollection([
new Foo(3, 'fizz'),
new Foo(4, 'buzz'),
]);
// $allFoos should contain all 4 foos
$allFoos = $someFoos->intersect($moreFoos);
Analyzes/Return Data, does not alter contents.
column
/**
* Wrapper for array_column: returns the values from a single property/method
* @param string property
* @return array
*/
column($property)
Examples:
$names = $fooCollection->column('name'); //accesses a property
$names = $fooCollection->column('getName'); //will call the method
first
/**
* Returns the first item in the collection
* @return stdClass
*/
first()
Examples:
$firstFoo = $fooCollection->first();