Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP Collection Classes | PHPBelfast

PHP Collection Classes | PHPBelfast

Refactor Class properties of type Array to use collection classes instead.
-----------

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.

A plain array is just the array members, anything else e.g. aggregate functions such as SUM, AVG MIN MAX etc, boolean states, labels, tags, diffs etc are all outside our array and require an extra class and extra layer of containment.

Our collection class can have additional properties or methods



e.g.
$troopers = new ArmyCollection()->isGoodGuys = false

$rebels = new ArmyCollection()->isGoodGuys = true

A Mutable object can be changed
you could add elements, edit existing elements or remove elements

Immutable elements cannot be changed.
you can create an object, 
you can delete it and recreate a slightly different new object.

Immutable collection classes don’t need to have accessor methods to add, get, change or remove individual elements.

You don’t need to worry about indexes either.
There are no direct access methods for specific collection members.

We add items when we construct the object
with no addItem or setItems() methods, the object can only ever be read or destroyed - it cannot be altered.

What’s the point in that?
Lightweight class that offers Strict Type checked members, no mutability means members can’t transform into something unexpected.

to work with the members remove items etc, transform to an Array with a __toArray() method
work away and then create a new Collection.


Avatar for Tim Swann

Tim Swann

May 04, 2017
Tweet

More Decks by Tim Swann

Other Decks in Programming

Transcript

  1. • 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 by Value… • Array[‘Notation’]… WHATS WRONG WITH ARRAYS?
  2. • Easy to use… • Easy to add, access, remove

    items… • Easy to traverse/loop… • How many array_ functions are there?… WHAT WAS RIGHT WITH ARRAYS?
  3. • 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?
  4. • private $items = array(); // property… • traversable… •

    countable… • Other Interfaces?
 e.g. JsonSerializable() or toJSON() ATTRIBUTES OF A COLLECTION CLASS
  5. • Arrays are mutable… • Collection Classes are often mutable…

    • mutability is a choice to be made early on… • The Immutable class is lightweight… MUTABILITY
  6. • with function addItem(FixedType $item){}… • we now have type

    checking • adding individual elements MUTABILITY & TYPE CHECKING
  7. • 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
  8. // 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';

  9. 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';
  10. 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;
 }
 
 }
  11. // 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);
  12. 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);
 }
 
 }
  13. • Our app will contain more than one collection… •

    Remember DRY principles… • Don’t copy/paste the interface implementations… ABSTRACT FOR PORTABLITY
  14. 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);
 }
 
 }

  15. // 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;
 }
 
 }
  16. class Artist {
 
 // @var AlbumCollection
 protected $albums;
 


    // @var SongCollection
 protected $songs;
 
 // @var TourCollection
 protected $concerts;
 
 // MetadataCollection
 protected $metadata;
 
 // GenreCollection;
 protected $genres;
 
 // Tags Collection
 protected $tags;
 
 }
  17. • 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+ HAS THE …TOKEN
 THE VARIABLE LENGTH TOKEN bit.ly/VarL3nT0k
  18. 
 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);

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

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

    IteratorAggregate, ArrayAccess, Serializable, Countable • Only implement the functionality that you 
 will use/need USE THE SPL…
  22. • Add commonly used functionlaity to your base class •

    e.g. findBy($property, $value)
 BUILD ON YOUR 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); } } } 
 }
  23. BUILD ON YOUR 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;
 }
 }
 
 }
  24. • Many Frameworks, especially ORM components have their own Collection

    implementations • If you use a Framework investigate the options
 Doctrine ArrayCollection, Illuminate\Support\Collection • If you aren’t using a framework…
 …Learn from the Frameworks A FINAL WORD ON FRAMEWORKS