Objects often contain groups of related objects
Categories, tags, comments, reviews, anywhere there are data joins etc. Take a relatively straightforward real world item such as a music Album.. Artist Creates Albums, Albums Have Songs, Could be tagged, have reviews & ratings. Normally we use arrays for this but we can do better
An array is just big empty bucket… it doesn’t provide context, it doesn’t have any methods or properties - just members.
We need to add extra validation to ensure our arrays store the same types of information
No rules on how items are added, since Arrays can be key=>value or numeric index, can even be a mix of keyed and non keyed. can be nested An Array is just a big bucket we can throw stuff in
Arrays are very easy to use, we all know
- how to dump something in an array
- how to access a value by key
- how to loop through an array, split and array, pop values off
That long legacy of array functions to help work with arrays.
- maybe that’s a “what’s wrong with arrays” too many array_ functions.
So what is a collection class?
Yes.. it’s still a big old bucket to dump stuff in…
So It’s still an array at it’s core.. *but it’s more than just an array *
The array members are intrinsically linked to our objects, there is only one TYPE of object stored in a collection.
You can apply rules that reject objects of the correct type but which are missing specific values or properties.
This Separation of objects and encapsulation with rules makes testing much easier.
Controlling indexes.. whereas arrays could be numeric, text keys our keys could be more stringently controlled, however more common to not have any keys (so numeric then!)
*non member properties* This is arguably the biggest benefit.
2. • Object Relationships…
• Perfect for One-To-Many Joins…
• Traditionally just arrays which were good…
WHY?
3. • No rules about what is stored in the array…
• No rules about how to store/retrieve…
• Often public…
• No separation of concerns…
• Arrays are by default passed byValue…
• Array[‘Notation’]…
WHATS WRONG WITH ARRAYS?
4. • Easy to use…
• Easy to add, access, remove items…
• Easy to traverse/loop…
• How many array_ functions are there?…
WHAT WAS RIGHT WITH ARRAYS?
5. • It’s a “bucket” of objects
• So, essentially, it’s a wrapper for an array…
• But encapsulated with rules…
• We can control the indexes…
• We know more about the content types…
• It can have non-member properties
WHAT IS A COLLECTION CLASS?
6. • private $items = array(); // property…
• traversable…
• countable…
• Other Interfaces?
e.g. JsonSerializable() or toJSON()
ATTRIBUTES OF A COLLECTION CLASS
7. • Arrays are mutable…
• Collection Classes are often mutable…
• mutability is a choice to be made early on…
• The Immutable class is lightweight…
MUTABILITY
8. • with function addItem(FixedType $item){}…
• we now have type checking
• adding individual elements
MUTABILITY &TYPE CHECKING
9. • Only one way an object can be immutable…
• __construct($items){
$this->items = $items
}
• It is fixed after instantiation, no setItems / addItems
methods…
IMMUTABILITY &TYPE CHECKING
11. // the old way
Class Artist {
public $albums = array();
private $name;
public function __construct($name) {
$this->name = $name;
}
}
$artist = new Artist('Prince');
$artist->albums[] = '1999';
$artist->albums[] = 'Purple Rain';
12. Class Artist {
private $albums = array();
private $name;
public function __construct($name) {
$this->name = $name;
}
public function addItem($item) {
$this->albums[] = $item;
}
}
$artist = new Artist('Prince');
$artist->addItem[] = 'Purple Rain';
13. Class Artist {
/**
* @var AlbumCollection
*/
private $albums;
public function setAlbums($albums)
{
$this->albums = $albums;
}
//...
}
Class AlbumCollection {
private $items = array();
public function addItem(Album $album) {
$this->items[] = $album;
}
}
14. // Using the Collection Class
$artist = new Artist('Prince');
// Build The Album Collection
$albums = new AlbumCollection();
// Note we add Album types
$albums->addItem( new Album('1999') );
$albums->addItem( new Album(‘Purple Rain') );
$albums->addItem( new Album(‘Sign O The Times') );
// Set the Collection as an Artist property
$artist->setAlbums($albums);
16. Class AlbumCollection implements IteratorAggregate ,
Countable {
private $items = array();
public function addItem(Album $album) {
$this->items[] = $album;
}
// Implement Traversable (Iterator Aggregate)
// Interface
public function getIterator() {
return new ArrayIterator($this->items);
}
// Implement Countable Interface
public function count() {
return count($this->items);
}
}
17. • Our app will contain more than one collection…
• Remember DRY principles…
• Don’t copy/paste the interface implementations…
ABSTRACT FOR PORTABLITY
18. abstract Class BaseCollection implements
Countable, IteratorAggregate
{
private $items = array();
// Implement Traversable via
// IteratorAggregate Interface
public function getIterator() {
return new ArrayIterator($this->items);
}
// Implement Countable Interface
public function count() {
return count($this->items);
}
}
19. // An Artist has many Albums
Class AlbumCollection extends BaseCollection {
private $items = array();
public function addItem(Album $album) {
$this->items[] = $album;
}
}
// Albums have many Songs
Class SongCollection extends BaseCollection {
private $items = array();
public function addItem(Song $song) {
$this->items[] = $song;
}
}
23. • Pass an indeterminate number of parameters…
• Can pack a list of arguments…
• Can unpack an array to a list of arguments…
• Can be used in the function call or definition…
• So…
PHP5.6+ HASTHE …TOKEN
THEVARIABLE LENGTHTOKEN
bit.ly/VarL3nT0k
25.
Class AlbumCollection extends BaseCollection {
private $items = array();
// Strongly typed, unknown length
// All items passed must be of type Album
public function __construct(Album ...$albums)
{
$this->items = $albums;
}
}
$batman = new Album('Batman');
$purple = new Album('Purple Rain’);
// this works, but it’s messy
$albums = new AlbumCollection($batman, $purple);
26.
Class AlbumCollection extends BaseCollection {
//…
}
// we can create an array of Albums
// or foreach through a DB powered list
$Albums[] = new Album('Batman');
$Albums[] = new Album('Purple Rain’);
$Albums[] = new Album('Dirty Mind');
$Albums[] = new Album('Diamonds and Pearls');
// this wont work
$collection = new AlbumCollection($Albums);
// this WILL work, using ‘…’ to unpack the Array
$collection = new AlbumCollection(…$Albums);
27. • We are now portable thanks to our abstract
collection
• We are strongly typed
• We are decoupled, each type and collection can
be independently tested
PORTABLE &TESTABLE
28. • IteratorAggregate Interface as a minimum
• extend ArrayObject
implements IteratorAggregate,ArrayAccess, Serializable, Countable
• Only implement the functionality that you
will use/need
USETHE SPL…
29. • Add commonly used functionlaity to your base
class
• e.g. findBy($property, $value)
BUILD ONYOUR BASE
public function findBy($property, $value)
{
// look for an accessor method e.g. getTitle()
$method = “get" . ucwords($property);
foreach ($this->items as $key => $obj) {
if (method_exists($obj, $method) {
if ($obj->{$method}() == $property) {
return($obj);
}
}
}
}
30. BUILD ONYOUR CONCRETE CLASS
Class SongCollection extends BaseCollection {
protected $items;
// each song has a duration,
// collection has a total duration
private $totalDuration;
public function getTotalDuration()
{
$totalDuration= 0;
foreach($this->items as $song) {
$totalDuration += $song->duration;
}
}
}
31. • Many Frameworks, especially ORM components
have their own Collection implementations
• If you use a Framework investigate the options
Doctrine ArrayCollection, IlluminateSupportCollection
• If you aren’t using a framework…
…Learn from the Frameworks
A FINAL WORD ON FRAMEWORKS