About Jamie on Software

Jamie on Software is Jamie Rumbelow's online journal. An opinionated, charismatic teenager's views on web development, business, programming, education, technology and journalism.

Sparkplugs

We build ExpressionEngine and MojoMotor add-ons to help web professionals build better sites.

http://getsparkplugs.com/

Tags
Tweets
Feeds
We Love
Powered by Squarespace

Entries in php (9)

Thursday
Feb232012

The CodeIgniter Handbook - Volume 1 - Available!

I am utterly thrilled to announce that my new book, The CodeIgniter Handbook Volume 1, is now available for order!

Volume 1, Who Needs Ruby? is a pragmatic, succinct guide to improving your efficiency and the cleanness of your code. Attractive to amateurs and professionals alike, The CodeIgniter Handbook isn't your usual programming book. It's short, useful and easy to read, and covers how to improve the quality of your code and the speed of your development time.

It's available to buy as a print book or an eBook. It's also much cheaper than most programming books, at only £12 (roughly $18) for the print book and £6 ($10) for the eBook.

I've had some great feedback already -- everyone appears to be enjoying it! Thanks for your support.

Purchase the CodeIgniter Handbook today!

Monday
Dec262011

CodeIgniter View Presenters

Whenever building applications with CodeIgniter, I always try my best to follow the Model-View-Controller design pattern as much as possible. MVC tells us that views are the presentational filter of the model's data. Over the course of an application's development, it can be very common for views to grow and accumulate cruft. I find that presenters can be a really sleek way of hiding presentational logic away in a class.

Let's look at a simple view that displays some information about a user's bank account:

<div id="account">
    <h1><?= $this->bank->get($account->bank_id)->name ?> - <?= $account->title ?></h1>

    <p class="information">
        <strong>Name:</strong> <?php if ($account->name): ?><?= $account->name ?><?php else: ?>N/A<?php endif; ?><br />
        <strong>Number:</strong> <?php if ($account->number): ?><?= $account->number ?><?php else: ?>N/A<?php endif; ?><br />
        <strong>Sort Code:</strong> <?php if ($account->sort_code): ?><?= substr($account->sort_code, 0, 2) . "-" . substr($account->sort_code, 2, 2) . "-" . substr($account->sort_code, 4, 2) ?><?php else: ?>N/A<?php endif; ?>
    </p>

    <p class="balances">
        <strong>Total Balance:</strong> <?php if ($account->total_balance): ?><?= "&pound;" . number_format($account->total_balance) ?><?php else: ?>N/A<?php endif; ?>
        <strong>Available Balance:</strong> <?php if ($account->available_balance): ?><?= "&pound;" . number_format($account->available_balance) ?><?php else: ?>N/A<?php endif; ?>
    </p>

    <p class="statements">
        <?php if ($this->statements->get_by('account_id', $account->id)): ?>
            <?= anchor('/statements/' . $account->id, 'View Statements') ?>
        <?php else: ?>
            Statements Not Currently Available
        <?php endif; ?>
    </p>
</div>

This is a rather typical view; it's displaying bits of content from an object, checking for a value's existence and pulling in bits from other database tables. It's fine, but it's all a bit messy, and we're duplicating a fair bit of code. Ideally, we want our view to look something like this:

<div id="account">
    <h1><?= $account->title() ?></h1>

    <p class="information">
        <strong>Name:</strong> <?= $account->name() ?><br />
        <strong>Number:</strong> <?= $account->number() ?><br />
        <strong>Sort Code:</strong> <?= $account->sort_code() ?>
    </p>

    <p class="balances">
        <strong>Total Balance:</strong> <?= $account->total_balance() ?>
        <strong>Available Balance:</strong> <?= $account->available_balance() ?>
    </p>

    <p class="statements"><?= $account->statements_link() ?></p>
</div>

This clears up our view considerably and removes a bunch of the duplication. It also strips out as much logic as possible from the views, and can make for some very succinct code.

We're going to create an application/presenters directory, and inside there an account_presenter.php file. This file will contain the presenter class for our account object. Let's start by extracting the title:

<?php

class Account_Presenter {

    public function __construct($account) {
        $this->account = $account;
        $this->ci =& get_instance();
    }

    public function title() {
        return $this->ci->bank->get($this->account->bank_id)->name . "-" . $account->title;
    }
}

We make sure that we can access the CodeIgniter superobject in $this->ci and the account object that we're presenting in $this->account. We can then port the logic that was previously in the view into a title() method.

We can now go ahead and tidy up the information section:

public function name() {
    return $this->account->name ?: "N/A";
}

public function number() {
    return $this->account->number ?: "N/A";
}

public function sort_code() {
    if ($sc = $this->account->sort_code) {
        return substr($sc, 0, 2) . "-" . substr($sc, 2, 2) . "-" . substr($sc, 4, 2);
    } else {
        return "N/A";
    }
}

Similarly, we can tidy up the balances section:

public function total_balance() {
    return ($this->account->total_balance) ? "&pound;" . number_format($this->account->total_balance) : "N/A";
}

public function available_balance() {
    return ($this->account->available_balance) ? "&pound;" . number_format($this->account->available_balance) : "N/A";
}

And finally, the statements link:

public function statements_link() {
    if ($this->ci->statements->get_by('account_id', $this->account->id)) {
        return anchor('/statements/' . $this->account->id, 'View Statements');
    } else {
        return "Statements Not Currently Available";
    }
}

Fantastic. Finally, in our controller, we need to pass through an instance of the presenter rather than the account object itself.

public function show($account_id) {
    $account = $this->account->get($account_id);
    $this->data['account'] = new Account_Presenter($account);

    $this->load->view('account/show', $this->data);
}

...and don't forget to load the presenter at the top of your controller:

require_once APPPATH . 'presenters/account_presenter.php';

...and we are done!

As I'm sure you can see, using presenters allows you to tidy up your views in a really object-oriented way. It'd be great to hear your thoughts about presenters and whether or not you plan on using them.

Monday
Jul252011

Syntax Sugar #3 - Easily Parsing HTML

The PHP SimpleXML extension makes parsing and using XML documents in your code a piece of cake. Unfortunately, HTML rarely complies as a well-formed XML document.

Using SimpleXML combined with DOMDocument, we can parse a reasonably badly formatted HTML document in very few lines of code. The trick here is using the DOMDocument::$strictErrorChecking variable to ensure that the source is parsed as dodgey HTML.

<?php
// Some HTML string...
$html = file_get_contents("http://codeigniter.com");

// Create a new DOMDocument and set strictErrorChecking to FALSE
$dom = new DOMDocument();
$dom->strictErrorChecking = FALSE;

// Load the HTML into the DOMDocument
$dom->loadHTML($html);

// Load the DOMDocument into SimpleXML... and win!
$obj = simplexml_import_dom($dom);
Friday
Apr012011

A radical new shift in data storage; Rumblestorage

Last night I had an epiphany. I was working with one of those old, cruddy databases, and it got me thinking. Why isn't there a better solution? Why am I doomed to use legacy systems such as MySQL and MongoDB for the rest of eternity? So, I began researching alternatives. And when I couldn't find any, I chose to write my own.

I am proud to announce Rumblestorage. From now on, every project of mine will be using Rumblestorage as its data storage medium. I hope that you too can see the light and understand that, frankly, databases just don't cut it any more.

Rumblestorage utilises the native capabilities of your language and stores everything in a flat text file; a simple and elegant solution. It brings your data into memory at class instantiation and saves it on destruction. It has a beautifully usable API. It's clear that in this 21st century world, databases are just far too restrictive. Your storage engine should be built specifically for your programming language; it should be fast, easy to use and above all flexible.

Best of all, Rumblestorage is free and open source, available on GitHub. I'm already in talks with the CodeIgniter Reactor team as to integrate Rumblestorage as a replacement for the old and tired database class.

Access the source code at GitHub, and feel free to fork and make your contribution to the future of data storage!

Wednesday
Jan262011

Syntax Sugar #2 - Model Scoping

I've written about model scoping before, but I thought I'd revisit it with some lessons I've learnt since last time. The essence of model scoping is that you can chain named methods together that add certain parameters onto your query. In a similar way that CodeIgniter's ActiveRecord allows you to chain methods together to build queries, you can use model scoping to incrementally add onto your model query.

The trick to writing a scope is to return $this. This lets PHP chain the methods onto one-another, resulting in a nice syntax. Here's an example:

class User_model extends CI_Model {
    public function __construct() {
        parent::__construct();
    }

    public function confirmed() {
        $this->db->where('confirmed', 1);
        return $this;
    }

    public function sorted($column = 'date') {
        $this->db->order_by($column);
        return $this;
    }

    public function get() {
        return $this->db->get('users')->result();
    }
}

With these methods defined, we can produce some nice, readable code to pull the results from the database.

$this->user_model->confirmed()
                 ->sorted()
                 ->get();

Comparing this to what we'd write if we didn't define those lookups as scopes:

$this->db->where('confirmed', 1)
         ->order_by('date')
         ->get('users')
         ->result();

It's obvious that the former is much cleaner and more readable. We can add or remove as many as we like, and they can be as comprehensive or as simple as we like. It doesn't even have to be limited to database lookups. We're sharing an instance of the class, so we can add things to an instance variable which we can then print, or we can perform other data-related processes all scoped out by our method scopes.

While this is generally all rosy, you may come across something like this:

public function favourited() {
    // Get the favourites
    $fav = $this->db->select('id')->get('favourites')->result();
    $ids = array();

    foreach ($fav as $row) {
        $ids[] = $row->id;
    }

    // Restrict the current query
    $this->db->where_in('id', $ids);
    return $this;
}

This will only work if it's called first. You can easily get conflicts in SQL queries when you're chaining and making separate queries inside the scopes. You can start to look for columns that don't exist, or affect the query in some other bad way.

The solution to this problem is to isolate the query. Take the current values from the class you're accessing (in our case the CodeIgniter ActiveRecord class), cache them, clear them, make the query, and then reset the variables to their original state. This makes sure the query starts with a blank slate.

public function favourited() {
    // Cache the CodeIgniter ActiveRecord values
    $ar_values = array(
        'ar_select', 'ar_distinct', 'ar_from', 'ar_join', 'ar_where', 'ar_like', 
        'ar_groupby', 'ar_having', 'ar_limit', 'ar_offset', 'ar_order', 'ar_orderby', 
        'ar_set', 'ar_wherein', 'ar_aliased_tables', 'ar_store_array'
    );
    foreach ($ar_values as $val) { $$val = $this->db->$val; $this->db->$val = null; }

    // Get the favourites
    $fav = $this->db->select('id')->get('favourites')->result();
    $ids = array();

    foreach ($fav as $row) {
        $ids[] = $row->id;
    }

    // Restore the AR values
    foreach ($ar_values as $val) { $this->db->$val = $$val; }

    // Restrict the current query
    $this->db->where_in('id', $ids);
    return $this;
}

Now we can use the favourited() scope without fear!

$this->user_model->confirmed()
                 ->sorted()
                 ->favourited()
                 ->get();

As you can see, scopes are a powerful and flexible way of creating really, really nice semantic and clean code. I encourage you to start using them today.