May the 4th
PHP CollectionsImage by statefarm
• Object Relationships…
• Perfect for One-To-Many Joins…
• Traditionally just arrays which were good…
WHY?
• 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?
• Easy to use…
• Easy to add, access, remove items…
• Easy to traverse/loop…
• How many array_ functions are there?…
WHAT WAS RIGHT WITH ARRAYS?
• 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?
• private $items = array(); // property…
• traversable…
• countable…
• Other Interfaces?

e.g. JsonSerializable() or toJSON()
ATTRIBUTES OF A COLLECTION CLASS
• Arrays are mutable…
• Collection Classes are often mutable…
• mutability is a choice to be made early on…
• The Immutable class is lightweight…
MUTABILITY
• with function addItem(FixedType $item){}…
• we now have type checking
• adding individual elements
MUTABILITY &TYPE CHECKING
• 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
SOME CODE
// 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';

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';
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;

}



}
// 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);
WHAT ABOUT

COUNTABLE 

TRAVERSABLE

…
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);

}



}
• Our app will contain more than one collection…
• Remember DRY principles…
• Don’t copy/paste the interface implementations…
ABSTRACT FOR PORTABLITY
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);

}



}

// 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;

}



}
class Artist {



// @var AlbumCollection

protected $albums;



// @var SongCollection

protected $songs;



// @var TourCollection

protected $concerts;



// MetadataCollection

protected $metadata;



// GenreCollection;

protected $genres;



// Tags Collection

protected $tags;



}
COLLECTING THE DOTS…



WHAT ABOUTTHE DOTS..?
• 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
WITH IT WE CAN CREATE

STRONGLY TYPED ARRAYS


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);



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);

• 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
• IteratorAggregate Interface as a minimum
• extend ArrayObject

implements IteratorAggregate,ArrayAccess, Serializable, Countable
• Only implement the functionality that you 

will use/need
USETHE SPL…
• 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);
}
}
}


}
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;

}

}



}
• 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
T H A N K Y O U

PHP Belfast | Collection Classes

  • 1.
    May the 4th PHPCollectionsImage by statefarm
  • 2.
    • Object Relationships… •Perfect for One-To-Many Joins… • Traditionally just arrays which were good… WHY?
  • 3.
    • No rulesabout 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 touse… • 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 aremutable… • Collection Classes are often mutable… • mutability is a choice to be made early on… • The Immutable class is lightweight… MUTABILITY
  • 8.
    • with functionaddItem(FixedType $item){}… • we now have type checking • adding individual elements MUTABILITY &TYPE CHECKING
  • 9.
    • Only oneway an object can be immutable…
 • __construct($items){
 $this->items = $items
 } • It is fixed after instantiation, no setItems / addItems methods… IMMUTABILITY &TYPE CHECKING
  • 10.
  • 11.
    // the oldway
 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 theCollection 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);
  • 15.
  • 16.
    Class AlbumCollection implementsIteratorAggregate , 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 appwill contain more than one collection… • Remember DRY principles… • Don’t copy/paste the interface implementations… ABSTRACT FOR PORTABLITY
  • 18.
    abstract Class BaseCollectionimplements 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 Artisthas 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;
 }
 
 }
  • 20.
    class Artist {
 
 //@var AlbumCollection
 protected $albums;
 
 // @var SongCollection
 protected $songs;
 
 // @var TourCollection
 protected $concerts;
 
 // MetadataCollection
 protected $metadata;
 
 // GenreCollection;
 protected $genres;
 
 // Tags Collection
 protected $tags;
 
 }
  • 21.
  • 23.
    • Pass anindeterminate 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
  • 24.
    WITH IT WECAN CREATE
 STRONGLY TYPED ARRAYS
  • 25.
    
 Class AlbumCollection extendsBaseCollection {
 
 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 extendsBaseCollection {
 
 //…
 
 }
 // 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 arenow 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 Interfaceas a minimum • extend ArrayObject
 implements IteratorAggregate,ArrayAccess, Serializable, Countable • Only implement the functionality that you 
 will use/need USETHE SPL…
  • 29.
    • Add commonlyused 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 CONCRETECLASS 
 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
  • 32.
    T H AN K Y O U