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

The CakePHP 3 ORM: Even More Awesome Than You Realize

The CakePHP 3 ORM: Even More Awesome Than You Realize

CakeFest 2016 talk covering some of the more interesting and intriguing aspects of the CakePHP 3 ORM.

Justin Yost

May 28, 2016
Tweet

More Decks by Justin Yost

Other Decks in Programming

Transcript

  1. The CakePHP 3 ORM: Even More Awesome Than You Realize

    Justin Yost Web Developer at Loadsys 1 CC BY-NC 4.0 Justin Yost
  2. How the ORM Works 1. Table 2. Query 3. ResultSet

    4. Entity 2 CC BY-NC 4.0 Justin Yost
  3. 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
  4. How the ORM Works 1. Table 2. Query 3. ResultSet

    4. Entity 4 CC BY-NC 4.0 Justin Yost
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. And more • Magic _get/_set • Better Validation/Rule API •

    Better Eventing System • And so much more 28 CC BY-NC 4.0 Justin Yost
  26. 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