$30 off During Our Annual Pro Sale. View Details »

Active Record (2010)

Active Record (2010)

The fifth in a short series of presentations given at a PHP development shop.

Rob Howard

June 29, 2010
Tweet

More Decks by Rob Howard

Other Decks in Technology

Transcript

  1. ActiveRecord

    View Slide

  2. What?

    Model for database
    records.

    Grouping of data and
    behaviour.

    “Active Record Pattern”
    recorded by Martin
    Fowler, circa 1857 -->

    View Slide

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

    View Slide

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

    View Slide

  5. 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().

    View Slide

  6. I think I'm using this already.

    PHP itself has mysql_fetch_object(), which
    gives me back an object the data as
    properties.

    View Slide

  7. 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.

    View Slide

  8. 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.

    View Slide

  9. 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

    View Slide

  10. Example Time

    Let's have a look at how other systems use
    Active Record.

    Detailed example:

    Yii Framework's CActiveRecord

    View Slide

  11. The Basics

    Setup:

    class Order extends CActiveRecord {
    public function tableName() {
    return 'order';
    }
    }

    View Slide

  12. The Basics

    Creating:

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

    View Slide

  13. 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

    View Slide

  14. The Basics

    Updating:

    $order->payer_email = '[email protected]';
    if ($order->validate()) {
    $order->save();
    }

    Deleting:

    $order->findByPk(56)->delete();

    View Slide

  15. 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.
    );
    }

    View Slide

  16. 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.

    View Slide

  17. 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;
    }

    View Slide

  18. 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'),
    );
    }

    View Slide

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

    View Slide

  20. Filters

    Set up named rules for retrieving parts of your
    data, eg.

    class TicketType {
    // ...
    function scopes() {
    return array(
    'availableToPublic' => array(
    'condition' => 'closing >= CURDATE()',
    )
    );
    }

    View Slide

  21. 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),
    ));
    }

    View Slide

  22. 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'
    );
    }

    View Slide

  23. 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.

    View Slide

  24. 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',
    )
    );
    }

    View Slide

  25. 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.
    }

    View Slide

  26. 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);
    }

    View Slide

  27. 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.

    View Slide

  28. 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.

    View Slide

  29. 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

    View Slide

  30. 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.)

    View Slide

  31. I'm Interested.

    .NET:

    Uhh...

    ADO.NET Entity Framework (w/ .NET 3.5)

    www.castleproject.org/activerecord/

    View Slide

  32. View Slide