The MacBook Air and Web Development

I migrated to a MacBook Air as my primary development machine a few months ago and I think it is the ideal computer for a web developer. I think it’s the future of personal computing and that everyone should do themselves a favor and pick one up the first chance they get.

There are already some great articles reviewing the specs of the computer and why it is well-suited for web development, but I just wanted to point out a few, hopefully less-obvious, benefits of the machine for others who are considering making the switch.

Pros

There are a lot of things I like about the machine, but here are the big ones.

Focus

I actually enjoy the restrictions of the smaller screen. I no longer need to worry about where I’m going to position windows. They are either full screen or half screen, no more lost time organizing my windows.

As a result of the more minimalistic setup I’ve become accustomed to, I’ve found that I am less tolerant of distractions. Now, I turn off anything that can potentially distract me from the task at hand and I’ve been much more productive as a result.

Efficiency

At first I didn’t like them, but I use spaces all the time now. I can switch back and forth between personal and work space very efficiently.

Speed

I’ve never had a computer with an SSD before, and I completely underestimated how big of a difference this makes. It’s amazing. I’ve found my Air to be much more responsive than the much more powerful computers I was using previously. Like I said, I don’t want to get too technical here, so if you want more details on this stuff, I would check out Apple’s Performance page for more details.

Portability

I really didn’t think I would care too much about the portability of the machine, but now that I have been carrying it with me everywhere for the past few months I don’t think I could ever go back to a MacBook Pro. Even my wife’s 13” MBP feels clunky (and slow) relative to my Air.

One thing a lot of people don’t seem to realize is that the resolution of the Air is actually the same as a 15” MBP. This makes it feel a lot bigger than it is, and it has a Thunderbolt port if you want to hook it up to a larger display.

Cons

None… just kidding, but they are fairly obvious. You’re clearly getting a machine with less screen real estate and a slower processor, but you will not notice the difference unless you are doing a lot of graphics/video editing or 3D modeling.

Conclusion

Computers are typically reviewed on specs, and if you compare the MacBook Air to a more powerful machine, the MacBookAir might look limited, but if you compare it based on the user experience provided by the machine, nothing comes close.

Note: I am a complete Apple fanboy, but I was a Windows user for the majority of my life. I’ve even had a Windows 7 laptop that I worked on for about a year so I am able to make a somewhat fair comparison.

Other Relevant Articles

The CodeIgniter Decorator Library

I’ve been thinking a lot lately about how I can clean up my CodeIgniter code, specifically my controllers and views. CodeIgniter is dead simple to work with, but on larger, more complex projects I find that my controllers get a little long and my views get messy. Here are my main issues and how I’ve decided to address them.

Controllers

The main thing I don’t like doing in my controllers is all of the data retrieval and preparation to pass data to the view. Things like this:

$product = $this->product_model->find($product_id);
$company = $this->company_model->find($product->company_id);

And this:

$view_data = array(
    'product' => $product,
    'company' => $company
);
$this->load->view('products/view', '$view_data');

They just don’t feel right to have in the controller because they get in the way of the core function of a controller.

The controller receives user input and initiates a response by making calls on model objects. A controller accepts input from the user and instructs the model and a view port to perform actions based on that input.

Wikipedia

Views

I’ve always disliked putting any sort of logic in my views. In fact, anything beyond looping through an object or array and echoing its values makes me feel dirty. Things like setting a default value if an attribute is empty:

<?php echo empty($product->price) ? $product->price : 'N/A'; ?>

Or updating the layout of a page based on the type of user that is viewing it:

<?php if ($user->company_id == $product->company_id): ?>
    <div class="product-admin-links">
        <?php echo anchor('products/edit/' . $product->id, 'Edit Product Info'); ?>
        <?php echo anchor('products/inventory/' . $product->id, 'Edit Product Inventory'); ?>
    </div>
<?php else: ?>
    <?php echo anchor('cart/add/' . $product->id, 'Add to Cart'); ?>
<?php endif; ?>

Not only does this clutter up my views, but it also has to be repeated anywhere that I want to display information for a resource, in this case a product. I would much prefer to have this data prepared outside of the view and the controller, but it doesn’t really belong in the model either.

My Solution: Decorators

After seeing how a couple other frameworks handle this (FuelPHP and Rails) and thinking through what my ideal solution would be, I’ve started writing a decorator library and created a spark out of it.

The library allows you to put all of the data retrieval and preparation into a decorator, which is stored in application/decorators. A decorator must extend the CI_Deocrator class which gives it access to the CodeIgniter super global ($this) so you can load models and everything else like you are working in a controller or model. Going back to our earlier example, if you removed the data retrieval from the controller and preparation logic form your views, you would end up with something likes this:

File: application/controllers/products.php

public function view($product_id)
{
    $this->load->view('products/view', $this->decorator->decorate('product', 'view', $product_id));
}

As opposed to this:

public function view($product_id)
{
    $this->load->model('product_model', 'company_model');

    $product = $this->product_model->find($product_id);
    $company = $this->company_model->find($product->id);

    $view_data = array(
        'product' => $product,
        'company' => $company
    );
    $this->load->view('products/view', $view_data);
}

Tip: By default, the decorate() method will look for a class/method that matches the controller class/method, otherwise you can specify them as parameters (i.e. decorate('products', 'view')). It will also accept a third parameter as an array of parameters to pass to the decorator.

Going back at our views, we can now do this:

File: application/views/products/view.php

<?php echo $product->price; ?>
<?php echo $product_links; ?>

As opposed to this:

<?php echo $product->price ? $product->price : 'N/A'; ?>
<?php if ($user->company_id == $product->company_id): ?>
    <div class="product-admin-links">
        <?php echo anchor('products/edit/' . $product->id, 'Edit Product Info'); ?>
        <?php echo anchor('products/inventory/' . $product->id, 'Edit Product Inventory'); ?>
    </div>
<?php else: ?>
    <?php echo anchor('cart/add/' . $product->id, 'Add to Cart'); ?>
<?php endif; ?>

And our decorator looks like this:

File: application/decorators/products_decorator.php

class Product_decorator extends CI_Decorator {

    public function view($product_id)
    {
        $this->load->model('product_model', 'company_model');

        $this->view_data->company = $this->company_model->find($product->id);
        $product = $this->product_model->find($product_id);

        if (empty($product->price)) $product->price = 'N/A'

        $this->view_data->product = $product;

        if ($this->session->userdata('company_id') == $product->company_id)
        {
            $product_links = '<div class="product-admin-links">';
            $product_links .= anchor('products/edit/' . $product->id, 'Edit Product Info');
            $product_links .= anchor('products/inventory/' . $product->id, 'Edit Product Inventory');
            $product_links .= '</div>';
        }
        else
        {
            $product_links = anchor('cart/add/' . $product->id, 'Add to Cart');
        }

        $this->view_data->product_links = $product_links;
    }
}

Tip: The class variable, $view_data, that I’m referencing here is a variable that is automatically returned by the decorate() method. Using this, you don’t have to worry about explicitly returning the data, but if you do return something, decorate() will use that instead of the $view_data class variable.

Help Me Out

It’s definitely not perfect, and I certainly make no claims of it being bug-free, but it’s a start and I hope to keep improving it based on the feedback I receive (Hint, hint!). I’ve been using this on a couple of my larger projects the last month or so and it has really helped me clean up my controllers and views. Hopefully others find it useful too.

Cleaning Up Your CodeIgniter Controllers

One of the issues I’ve always had with CodeIgniter is that it’s kind of difficult to keep my controllers skinny and my models fat. Certain things that are typically handled in the model with other MVC frameworks are often handled in the controller in CI.

For example, it’s fairly common to see code like this in a controller:

application/controllers/products.php

class Products extends CI_Controller {

    public function edit($product_id)
    {
        $this->form_validation
            ->set_rules('name', 'Name', 'required')
            ->set_rules('price', 'Price', 'required|decimal')
            ->set_rules('short_description', 'Short Description', 'required')
            ->set_rules('long_description', 'Long Description', 'required');

        if ($this->form_validation->run())
        {
            $attributes = array(
                'id' => $product_id,
                'name' => $this->input->post('name'),
                'price' => $this->input->post('price'),
                'short_description' => $this->input->post('short_description'),
                'long_description' => $this->input->post('long_description')
            );

            if ($this->company_model->update($attributes))
            {
                $this->messages->success('Product updated successfully!');
                redirect('products');
            }
            else
            {
                $this->messages->error("There was an error updating this product's attributes...");
            }
        }

        $product = $this->product_model->find($product_id);
        $company = $this->company_model->find($company_id);

        $view_data = array(
            'company' => $company,
            'product' => $company_settings
        );
        $this->load->view('products/edit', $view_data);
    }
}

and a model like this:

application/models/product_model.php

class Product_model extends CI_Model {

    public function find($product_id)
    {
        return $this->db
            ->where('id', $product_id)
            ->limit(1)
            ->get('products')
            ->row();
    }

    public function update($product_id, $attributes)
    {
        return $this->db
            ->where('id', $product_id)
            ->update('products', $attributes);
    }

    public function get_product_price_breaks($product_id)
    {
        return $this->db
            ->where('product_id', $product_id)
            ->get('product_price_breaks');
    }
}

Room for Improvement

At first glance this controller may not look too bad, and if you’re used to working with CodeIgniter you’ve probably seen something close to this pattern a lot. You may think that, “we are separating our code well and we aren’t running queries in our controller or anything so we’re good to go!”, but I think this is a common misconception in the CI community and that there are a few things we can do here to clean up our controllers and fatten up our models.

Move Validation Rules

In MVC frameworks that use an ORM you can setup validations for your data in the model, which helps keep controllers skinny. Since CodeIgniter doesn’t come with an ORM and the form validation class is all we have, it’s a good idea to make use of the ability to save validation rules to a config file.

application/config/form_validation.php

$config = array(
    array(
        'field' => 'name',
        'label' => 'Name',
        'rules' => 'required'
    ),
    array(
        'field' => 'price',
        'label' => 'Price',
        'rules' => 'required|decimal'
    ),
    array(
        'field' => 'short_description',
        'label' => 'Short Description',
        'rules' => 'required'
    ),
    array(
        'field' => 'long_description',
        'label' => 'Long Description',
        'rules' => 'required'
    )
);

Although your config file can get a little long when you are working on a large application (not so much a problem if you are using Modular Extensions and can have module-level config files), it’s still better than fattening your controllers with validation rules all over.

Prep Form Data in the Model

Once we’ve validated our form in the controller and are ready to submit the data to the database, we may as well setup the associative array we need for the update inside the model.

public function update($product_id, $input)
{
    $attributes = array(
        'name' => $input['name'],
        'price' => $input['price'],
        'short_description' => $input['short_description'],
        'long_description' => $input['long_description']
    );

    return $this->db
        ->where('id', $product_id)
        ->update('products', $attributes);
}

Assign Secondary Queries to Object Attributes

One last optimization we can do is perform all of our queries for the view data and return them as a single object. This way we cut down on clutter in the controller and provide a more logical placeholder for the view data. In this case, we’ll make a product’s price breaks available via the price_breaks attribute ($product->price_breaks). We’ll also remove a couple lines of assigning the results from the model to the $view_data variable.

application/models/product_model.php

public function get_all_product_info($product_id)
{
    $product = $this->find($product_id);
    $product->price_breaks = $this->get_product_price_breaks($product_id);

    return $product;
}

application/controllers/products.php

$view_data['product'] = $this->product_model->get_all_product_info($product_id);

Summary

With just a few simple changes we’ve drastically simplified the controller and made it responsible for only what it needs to be. Take a look at our refactored code compared to our original code and look how much cleaner it is.

Refactored

application/controllers/products.php

class Products extends CI_Controller {

    public function edit($product_id)
    {
        if ($this->form_validation->run())
        {
            if ($this->product_model->update($product_id, $this->input->post()))
            {
                $this->messages->success('Product updated successfully!');
                redirect('products');
            }
            else
            {
                $this->messages->error('There was an error updating this product's attributes...');
            }
        }

        $view_data['product'] = $this->product_model->find($product_id);

        $this->load->view('products/edit', $view_data);
    }
}

application/models/product_model.php

class Product_model extends CI_Model {

    public function find($product_id)
    {
        return $this->db
            ->where('id', $product_id)
            ->limit(1)
            ->get('products')
            ->row();
    }

    public function get_all_product_info($product_id)
    {
        $product = $this->find($product_id);
        $product->price_breaks = $this->get_product_price_breaks($product_id);

        return $product;
    }

    public function update($product_id, $input)
    {
        $attributes = array(
            'name' => $input['name'],
            'price' => $input['price'],
            'short_description' => $input['short_description'],
            'long_description' => $input['long_description']
        );

        return $this->db
            ->where('id', $product_id)
            ->update('products', $attributes);
    }

    public function get_product_price_breaks($product_id)
    {
        return $this->db
            ->where('product_id', $product_id)
            ->get('product_price_breaks');
    }
}

application/config/form_validation.php

$config = array(
    array(
        'field' => 'name',
        'label' => 'Name',
        'rules' => 'required'
    ),
    array(
        'field' => 'price',
        'label' => 'Price',
        'rules' => 'required|decimal'
    ),
    array(
        'field' => 'short_description',
        'label' => 'Short Description',
        'rules' => 'required'
    ),
    array(
        'field' => 'long_description',
        'label' => 'Long Description',
        'rules' => 'required'
    )
);

Note: Another very significant benefit to putting more code in the models is that your code will be much easier to test. Models are easy to test because they almost always return something while controllers are difficult because they usually just display a view. Breaking your code up this way will make it a lot easier if you decide to provide test coverage for your application.

Initial Code

application/controllers/products.php

class Products extends CI_Controller {

    public function edit($product_id)
    {
        $this->form_validation
            ->set_rules('name', 'Name', 'required')
            ->set_rules('price', 'Price', 'required|decimal')
            ->set_rules('short_description', 'Short Description', 'required')
            ->set_rules('long_description', 'Long Description', 'required');

        if ($this->form_validation->run())
        {
            $attributes = array(
                'id' => $product_id,
                'name' => $this->input->post('name'),
                'price' => $this->input->post('price'),
                'short_description' => $this->input->post('short_description'),
                'long_description' => $this->input->post('long_description')
            );

            if ($this->company_model->update($attributes))
            {
                $this->messages->success('Product updated successfully!');
                redirect('products');
            }
            else
            {
                $this->messages->error('There was an error updating this product's attributes...');
            }
        }

        $product = $this->product_model->find($product_id);
        $company = $this->company_model->find($company_id);

        $view_data = array(
            'company' => $company,
            'product' => $company_settings
        );
        $this->load->view('products/edit', $view_data);
    }
}

application/models/product_model.php

class Product_model extends CI_Model {

    public function find($product_id)
    {
        return $this->db
            ->where('id', $product_id)
            ->limit(1)
            ->get('products')
            ->row();
    }

    public function update($product_id, $attributes)
    {
        return $this->db
            ->where('id', $product_id)
            ->update('products', $attributes);
    }

    public function get_product_price_breaks($product_id)
    {
        return $this->db
            ->where('product_id', $product_id)
            ->get('product_price_breaks');
    }
}

Changing Default Error Delimiters in CodeIgniter

One thing that I end up doing in almost every CodeIgniter project is changing the default error delimiters from a plain ol’ paragraph

<p></p>

to a fancy shmancy paragraph with an error class on it

<p class="error"></p>

Not a big deal, but I always want a CSS hook for styling my form errors. There are two ways I used to handle this:

  1. Set error delimiters on an as-needed basis in my controller, but that resulted in a lot of code duplication and calling set_error_delimiters() all over the place.
  2. Set error delimiters in the construct of a base controller. It always annoyed me that there wasn’t a config option or something for this and that I still had to put this in a controller and call it on every request.

My Current Solution

One day I was looking through the form validation library and I realized that the error prefix and suffix are set as private variables for the class so I could override them if I extend the form validation library. I just created a MY_Form_validation.php file and dropped it into application/libraries with the following code overriding the default delimiters.

class MY_Form_validation extends CI_Form_validation {

    public function __construct()
    {
        parent::__construct();

        $this->_error_prefix = '<p class="error">';
        $this->_error_suffix = '</p>';
    }
}

User Group-specific Controllers in CodeIgniter

I’ve been building out an application that has 2 types of users, corporate and operations. Each user type has access to a lot of the same methods, but operations has access to a little more than corporate. I’ve been struggling with how to break up the code for these two types of users since a lot of it is shared. At first, I thought I had a couple options:

1. Separate the logic into different directories

This would mean that I have completely separate controllers and I would be duplicating methods that are shared for each type of user. I started building this out as an HMVC application with a module for operations that provided the extra methods available to that type of user. This gave me regular URLs like products/edit/48 and operations users would get operations/products/edit/48.

Issues:

I found myself duplicating a lot of code for methods that were shared for each user type and I didn’t really want 2 URLs to access the same method.

2. Create user-specific methods in the same controller

I thought about prefixing methods with “operations_” or “corporate_” so I would have methods like “operations_edit”. Doing things this way I would have had to:

  1. See what type of user is trying to access the method
  2. See if a method with a prefix of their user type exists
  3. Route them accordingly

Issues:

I didn’t like this approach because my controllers would be huge and I’d have to manage 2 of the same method for each type of user, even though I’d try to share private methods with each user type to limit code duplication.

My Current Solution

I thought about it a little more and I decided the way I would like it to work is if I could have a single controller that both user types share, and then be able to provide a controller with user-specific methods if there is anything that I want to override or add only for one user type. I ended up with a file structure like this:

  • modules/products/controllers/products.php – The default
  • modules/products/controllers/operations.php – Operations only
  • modules/products/controllers/corporate.php – Corporate only

Then I use CodeIgniter’s _remap() method to route the user accordingly. This method will:

  1. Check if there is a controller that has methods specific to that user type (corporate.php or operations.php)
  2. Check to see if the requested method exists in that controller
  3. If the method exists, execute it, otherwise try to execute it from the default controller
public function _remap($method, $arguments)
{
    $controller = strtolower($this->router->class);

    if ($controller === $this->uri->segment(1))
    {
        if ($this->acl->is_admin())
        {
            $user_type = 'operations';
        }
        else
        {
            $user_type = 'corporate';
        }

        if ($module = $this->load->module($controller .'/'. $user_type))
        {
            if ( ! method_exists($module, $method))
            {
                $module = $this->load->module($controller);
            }
        }
    }
    else
    {
        $module = $this->load->module($this->uri->segment(1) .'/'. $controller);
    }

    call_user_func_array(array($module, $method), $arguments);
}

Break It Down Now Ya’ll

We start out with CodeIgniter’s _remap() method and setup the $controller variable to be the requested class name lowercased.

public function _remap($method, $arguments)
{
    $controller = strtolower($this->router->class);
}

Then we add a check to see if the requested controller is the default controller for the module. I do this because I don’t want to always override the requested controller, I still want to be able to have controllers for things like product images and categories in the same module. If it’s not the default controller for the module, I just load the requested controller.

if ($controller === $this->uri->segment(1))
{

}
else
{
    $module = $this->load->module($this->uri->segment(1) .'/'. $controller);
}

If they are requesting the default controller, I check to see what type of user they are and assign to to a variable. This could, and probably should, be setup dynamically in most cases.

if ($this->acl->is_admin())
{
    $user_type = 'operations';
}
else
{
    $user_type = 'corporate';
}

Next, I attempt to load the module and their respective controller. If the controller exists, I check to see if the requested method exists. If the method doesn’t exist, that means that I haven’t created a group-specific method for that type of user so I load the default controller.

if ($module = $this->load->module($controller .'/'. $user_type))
{
    if ( ! method_exists($module, $method))
    {
        $module = $this->load->module($controller);
    }
}

And finally, we call the actual method.

call_user_func_array(array($module, $method), $arguments);