Slide 1

Slide 1 text

ActiveRecord

Slide 2

Slide 2 text

What? ● Model for database records. ● Grouping of data and behaviour. ● “Active Record Pattern” recorded by Martin Fowler, circa 1857 -->

Slide 3

Slide 3 text

What? 1) Object that represents a single row. Model Instance id customer_full_name customer_gender create_time setPaid()

Slide 4

Slide 4 text

What? 2) Object that interacts with multiple rows. Model Find Model Instances Delete

Slide 5

Slide 5 text

I think I'm using this already. ● Wordpress' WPDB can interact with tables via insert(), update() and delete(). ● WPDB can retrieve records as objects with get_row().

Slide 6

Slide 6 text

I think I'm using this already. ● PHP itself has mysql_fetch_object(), which gives me back an object the data as properties.

Slide 7

Slide 7 text

I think I'm using this already. ● Our own CMS has a type of Active Record. ● Can create a new object, give it data, and save it. ● Can retrieve records and set them up as objects. ● Doesn't do direct record munging, eg. update() or delete(); that's usually left to manual sql_query() calls.

Slide 8

Slide 8 text

Yeah? And? ● Our class is a generic model. ● We don't sub-class it and use it as a base for anything else. ● It's always used as a data store, but hardly ever as a way to bundle behaviour. ● Behaviour is currently almost always put in the “controller” or “module” that uses it. ● Behaviour for specific components is currently spread throughout the system.

Slide 9

Slide 9 text

Yeah? And? ● The real power lies in being able to take a generic construct (eg. a base Active Record class), and augment it for a specific purpose. Base Active Record Class Extended “Ticket” Class

Slide 10

Slide 10 text

Example Time ● Let's have a look at how other systems use Active Record. ● Detailed example: ● Yii Framework's CActiveRecord

Slide 11

Slide 11 text

The Basics ● Setup: ● class Order extends CActiveRecord { public function tableName() { return 'order'; } }

Slide 12

Slide 12 text

The Basics ● Creating: ● $order = new Order; $order->payer_full_name = 'Rob Howard'; $order->payer_email = '[email protected]'; $order->save();

Slide 13

Slide 13 text

The Basics ● Reading: ● $an_order = Order::model()->findByPk(27); ● $deep_orders = Order::model()->findAll( 'payer_email LIKE :email', array(':email'=>'%@example.com.au') ); ● $orders = Order::model()->findAll(); // everything

Slide 14

Slide 14 text

The Basics ● Updating: ● $order->payer_email = '[email protected]'; if ($order->validate()) { $order->save(); } ● Deleting: ● $order->findByPk(56)->delete();

Slide 15

Slide 15 text

Validation ● Function inside the Order class: ● public function rules() { return array( array('payer_full_name', 'required'), array('payer_email', 'email'), array('create_time', 'safe'), // id (and any other fields) are, by omission, // deemed “unsafe” to set values to. // You don't change a record's primary // key value. ); }

Slide 16

Slide 16 text

Validation ● The validation is useful to have here tied with the object. ● Our class already has field rules tied together with its GUI configuration, but unfortunately doesn't take advantage of that to do validation.

Slide 17

Slide 17 text

Events and Hooks ● CActiveRecord throws events, and runs hook functions as it finds, saves, deletes (etc) records. ● We can pick up on these, and act as we want, eg. ● // Set up “soft deletion” for this model public function beforeDelete() { $this->deleted = 1; $this->save(); return false; }

Slide 18

Slide 18 text

Relations ● Define a relationship in the model; anything using that model has quick access to parents, children, many-to-many-related items, etc. ● class Order { // … public function relations() { return array( 'tickets' => array(self::HAS_MANY, 'Ticket', 'ticket_type_id'), ); }

Slide 19

Slide 19 text

Relations ● Now that Order HAS_MANY Ticket models: ● $order = Order::model()->findByPk(123); $tickets = $order->tickets; ● $tickets is now just an array of the tickets belonging to $order. ● It's lazy-loaded, so there isn't a performance hit until it's called for.

Slide 20

Slide 20 text

Filters ● Set up named rules for retrieving parts of your data, eg. ● class TicketType { // ... function scopes() { return array( 'availableToPublic' => array( 'condition' => 'closing >= CURDATE()', ) ); }

Slide 21

Slide 21 text

Filters ● Set up parametised filters for when you want to accept values in a filter call: ● function createdBetween($start, $end) { $this->getDbCriteria()->mergeWith(array( 'condition' => 'create_time BETWEEN :start AND :end', 'params' => array(':start'=>$start, ':end'=>$end), )); }

Slide 22

Slide 22 text

Filters ● Set up default filters for things like soft deletion, where you don't want Model->findAll() to retrieve records. ● function defaultScope() { return array( 'condition' => 'deleted = 0' ); }

Slide 23

Slide 23 text

Filters ● Why all this? ● $orders = Order::model() ->createdBetween('2010/06/01','Today') ->findAll(); ● $ticket_options = TicketTypes::model() ->availableToPublic() ->findAll(); ● ie. Neatly-encapsulated domain-specific logic.

Slide 24

Slide 24 text

Behaviours ● Modular bits of code you can plug into a model which react on model events. ● This one automatically handles timestamping of creation and saving events: ● public function behaviors() { return array( 'CTimestampBehavior' => array( 'class' => 'behaviors.CTimestampBehavior', 'updateAttribute' => 'update_time', ) ); }

Slide 25

Slide 25 text

Regular, Plain Functions ● Functions that belong to the model. ● You put domain-specific actions inside, rather than treating the record like a dumb storage box. ● You call them on model instances. ● Wow. eg. ● public function addPayment($amount) { // Creates payment records. // Secret Order model business, keep out. }

Slide 26

Slide 26 text

All Together Now ● Inside a controller somewhere: ● // will not load soft-deleted items (defaultScope) $orders = Order::model ->purchasedAtSupanova(2010) ->findAll(); foreach ($orders as $order) { foreach ($order->tickets as $ticket) { $order->addPayment($ticket->type->price); } $order->generateTickets($order->tickets); }

Slide 27

Slide 27 text

So What? ● The point of all this: ● Brief, neat code, with domain-specific logic tidied away somewhere: – Close to the data it needs to interact with. – Reusable. – … But not trying too hard to be reusable by getting in everyone's way.

Slide 28

Slide 28 text

So What? ● The point of all this: ● Although it'd be possible to create more generic-ification layers on top of our class (eg. GUI-configurable events and filtering), unless it's done cleverly it's very easy to obstruct the developer further. ● You want to make it easier for the developer to set up and get access to relations, events, behaviours, rather than having the developer puzzle their way through a generic object/class interface.

Slide 29

Slide 29 text

I'm Interested. ● PHP implementations to check out: ● www.phpactiverecord.org ● www.propelorm.org ● www.doctrine-project.org ● www.yiiframework.com/doc/guide/database.ar

Slide 30

Slide 30 text

I'm Interested. ● PHP implementations to ignore: ● CodeIgniter's ActiveRecord – (It doesn't even vaguely resemble either Fowler's or Ruby on Rails' ActiveRecord. Sorry guys.)

Slide 31

Slide 31 text

I'm Interested. ● .NET: ● Uhh... ● ADO.NET Entity Framework (w/ .NET 3.5) ● www.castleproject.org/activerecord/

Slide 32

Slide 32 text

No content