The CakePHP 3 ORM:
Even More Awesome
Than You Realize
Justin Yost
Web Developer at Loadsys
1 CC BY-NC 4.0 Justin Yost
Slide 2
Slide 2 text
How the ORM Works
1. Table
2. Query
3. ResultSet
4. Entity
2 CC BY-NC 4.0 Justin Yost
Slide 3
Slide 3 text
How the ORM Works
$users = $this->Users->find()->where(['name' => 'Test']);
foreach($users as $user) {
echo $user->name;
echo $user->calcGpa();
}
3 CC BY-NC 4.0 Justin Yost
Slide 4
Slide 4 text
How the ORM Works
1. Table
2. Query
3. ResultSet
4. Entity
4 CC BY-NC 4.0 Justin Yost
Slide 5
Slide 5 text
How the ORM Works
1. Table
2. Query (Fluent Interface)
3. ResultSet (Collection)
4. Entity (Objects, magic methods, etc)
5 CC BY-NC 4.0 Justin Yost
Stackable Finders
• Query operates through a fluent interface
• $this->find('foo')->find('awesome')->find('bar')
• Each method call returns an instance of self/this allowing each
to be called in sequence and modify each.
7 CC BY-NC 4.0 Justin Yost
Slide 8
Slide 8 text
Real Life Examples
$students = $this->Students
->find()
->find('forGrade', ['gradeId' => $grade])
->find('hadHistoricalTeacher', ['teacherId' => $teacherId])
->contain(['Tests', 'Demographics'])
->find('registeredForCampYear', ['year' => 2016]);
8 CC BY-NC 4.0 Justin Yost
Slide 9
Slide 9 text
Real Life Examples
$this->Camp->find()->find('canRegister');
public function findCanRegister(\Cake\ORM\Query $query, array $options = []) {
return $query->where(['enrollment_date >= NOW()']);
}
9 CC BY-NC 4.0 Justin Yost
Slide 10
Slide 10 text
Real Life Examples
$this->Camp->find()->find('byTag', ['tag' => 'programming']);
public function findByTag(Query $query, array $options = []) {
if (!isset($options['tag'])) {
throw new BadMethodCallException('Requires an option param of `tag` to be set');
}
return $query
->matching('Tags', function ($q) use ($options) {
return $q->where([
'Tags.slug' => $options['tag'],
]);
});
}
10 CC BY-NC 4.0 Justin Yost
Slide 11
Slide 11 text
Real Life Examples
$this->Camp->find('canRegisterByTag', ['tag' => 'programming']);
public function findCanRegisterByTag(Query $query, array $options = []) {
return $query->find('canRegister')->find('byTag', $options);
}
11 CC BY-NC 4.0 Justin Yost
Slide 12
Slide 12 text
What this means?
• Building complex queries is easy
• Building useful reusable queries is easy
• Building queries that are testable is easy
• Building queries that follow best practices is easy
12 CC BY-NC 4.0 Justin Yost
Slide 13
Slide 13 text
ResultSet
• ResultSet is the object that stores the results from a Query.
• Queries lazily executed, into an Iterator stored inside of the
ResultSet
• ResultSet stores the matching Entity instances for each result,
as a Collection
• Is Query truly a Collection - err no.
13 CC BY-NC 4.0 Justin Yost
Slide 14
Slide 14 text
Lazy Execution?
• So, until you call ->all() or ->first() or iterate over the results,
Queries are not executed
• Write a gazillion queries for a page, only talk to the database for the
ones that actually are needed
14 CC BY-NC 4.0 Justin Yost
Slide 15
Slide 15 text
What is an Iterator?
• C Library for PHP that enables looping over and executing code in
Big O(n)
15 CC BY-NC 4.0 Justin Yost
Slide 16
Slide 16 text
Iterator Examples
• Iterators give you a way to do a pipeline of work on a Collection of
Objects
• Iterators stack, no matter if you call an iterator from multiple
classes, iterators loop through the Collection of stuff, once
• Lots of CakePHP 3 is Collections/Iterators
• Perhaps even more importantly, lots of PHP stuff is using Iterators
for lots of stuff
16 CC BY-NC 4.0 Justin Yost
Slide 17
Slide 17 text
Real Life Example
• Orders = Receipt
• Items = Primary Registration for a Camp or a Coupon tied to the
Order
• ChildItems = Related Services for a Registration (TShirt, Lunches,
etc)
17 CC BY-NC 4.0 Justin Yost
Slide 18
Slide 18 text
Real Life Example
• Orders = id, purchaser_id, created
• Items = id, order_id, foreign_table, foreign_id,
parent_item_id, order_id, price
18 CC BY-NC 4.0 Justin Yost
Slide 19
Slide 19 text
Questions to Answer
• Order(s): total, balance, totalPreCoupon, totalPostCoupon,
totalCharges, totalCredits, totalShirts, etc
• Same for Item(s)
• Same for ChildItem(s)
19 CC BY-NC 4.0 Justin Yost
Slide 20
Slide 20 text
Requirements
1. I want to know anything and everything about a single Item up to all
the Orders in the database
2. My questions will change over time
3. My answers need to be live and relatively fast
4. I want this both in the user interface and make decisions using the
answers
5. I don't want some MySQL dependent solution or writing custom
queries (#2)
20 CC BY-NC 4.0 Justin Yost
Slide 21
Slide 21 text
Solution for Item
public function sum($filterFunc) {
return $this->collect($filterFunc)->sumOf('price');
}
public function collect($filterFunc) {
$items = $this->child_items;
$items[] = $this;
$itemCollection = new Collection($items);
return $itemCollection->filter($filterFunc);
}
21 CC BY-NC 4.0 Justin Yost
Slide 22
Slide 22 text
Solution for Order
public function sum($itemDetectorMethod) {
$itemCollection = new Collection($this->items);
// Create the anonymous function to sum up each Item, using the underlying
// ItemEntity->{$itemSummerMethod}()
$summer = function ($accumulated = 0.0, Item $item) use ($itemDetectorMethod) {
return ($accumulated + $item->sum($itemDetectorMethod));
};
return $itemCollection->reduce($summer, 0.0);
}
22 CC BY-NC 4.0 Justin Yost
Solution Benefits
• Solution is pretty fast
• Solution is based on live data
• Solution lets me easily adjust as needed
• Solution works on any quantity of Orders/Items
24 CC BY-NC 4.0 Justin Yost
Lazy Loading
protected function _getOrders($orders) {
if (is_null($orders)) {
$this->_properties['orders'] = TableRegistry::get('Orders')
->find()
->where([
'purchaser_id' => $this->get('id'),
])
->all()
->toArray();
}
if (is_null($this->_properties['orders'])) {
$this->_properties['orders'] = [];
}
return $this->_properties['orders'];
}
26 CC BY-NC 4.0 Justin Yost
Slide 27
Slide 27 text
I'm Too Lazy to Write Lazy Loading
Code
https:/
/github.com/jeremyharris/cakephp-lazyload
27 CC BY-NC 4.0 Justin Yost
Slide 28
Slide 28 text
And more
• Magic _get/_set
• Better Validation/Rule API
• Better Eventing System
• And so much more
28 CC BY-NC 4.0 Justin Yost
Slide 29
Slide 29 text
Take Aways
• Stackable Finders are super useful, anything more than a
findWhereBlah or get, uses a custom finder.
• Collections are beyond useful, easily one of the most powerful
pieces of CakePHP 3 in general
• Iterators in general are the foundation of writing good pipelines of
work needing to be done
• https:/
/leanpub.com/iteratingphpiterators
• http:/
/adamwathan.me/refactoring-to-collections/
29 CC BY-NC 4.0 Justin Yost