Slide 1

Slide 1 text

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

Slide 6

Slide 6 text

Awesomeness Therein 1. Stackable Finders 2. Collections 3. Lazy Loading 6 CC BY-NC 4.0 Justin Yost

Slide 7

Slide 7 text

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

Slide 23

Slide 23 text

Solution $orders = $this->Orders->findForUser($userId) foreach ($orders as $order) { echo $order->sum('charges') echo $order->sum('credits') echo $order->sum('balance') $func = function(Item $item) {$item->price >= 1000.00;}; echo $order->sum($func) } 23 CC BY-NC 4.0 Justin Yost

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Lazy Loading $user = $this->Users->get($id); echo $user->orders->balance(); 25 CC BY-NC 4.0 Justin Yost

Slide 26

Slide 26 text

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

Slide 30

Slide 30 text

Thanks/Questions? • https:/ /joind.in/talk/3bd1c • twitter.com/justinyost • github.com/justinyost • justinyost.com • loadsys.com • lynda.com/justinyost 30 CC BY-NC 4.0 Justin Yost