Big Name Blog http://heybigname.com/blog CodeIgniter Conference 2012! We're so there! http://heybigname.com/2012/02/13/codeigniter-conference-2012-were-so-there/ Mon, 13 Feb 2012 15:33:04 +0000 Excitement is building as CIConf 2012 gets closer! This weekend --the 19th and 20th of February-- I am going to be in London, England meeting a bunch of fellow CodeIgniter developers and checking out some great speakers.

If you see me there make sure you say hi! I'm so very lonely.

http://ciconf.com

]]>
Interview with Laravel Creator, Taylor Otwell http://heybigname.com/2012/02/11/interview-with-laravel-creator-taylor-otwell/ Sat, 11 Feb 2012 14:49:00 +0000 I was given the opportunity to interview Taylor Otwell about his PHP web development framework Laravel.

We love Laravel (this site was built with it) and we think that you will love it, too.

You can learn more about Laravel at http://laravel.com. There you can find the Laravel forums which are great for checking out information about new releases and asking questions. You can also join the #Laravel IRC channel on irc.freenode.net is full of smart people who can help to answer any questions that you have.

]]>
Big Name Turns One http://heybigname.com/2012/01/24/big-name-turns-one/ Tue, 24 Jan 2012 05:24:53 +0000 It's been a fun and exciting year for us. We worked with a lot of great people, released our first web app (Picofin.com) and Shawn moved to the Netherlands where he established a Dutch branch for Big.

We've learned a lot about running our own business over these past 12 months and we're looking forward to another year.

Thank you all for supporting us and helping us grow!

Birthday Cake Costume Photo Credit: mediamolecule

]]>
Big Name Designer Hangout - Episode 1 http://heybigname.com/2011/12/27/big-name-designer-hangout-episode-1/ Tue, 27 Dec 2011 00:00:00 +0000 Here is Episode 1 of our Designer Hangout where we talked about Design Processes. We’ve noted discussion topic changes in the player if you want to skip around. If you weren’t able to join us for this one, don’t worry, we’ll be hosting more of them.

]]>
Big Name Designer Hangout Announced http://heybigname.com/2011/12/09/big-name-designer-hangout-announced/ Fri, 09 Dec 2011 00:00:00 +0000 The first Big Name Designer Hangout will be Dec 16th from 2PM to 3PM Central (8PM to 9PM GMT).

We'd like to know what you want to chat about. Head on over to our Google+ post and check out some ideas and suggestions for topics.

Hope you can make it!

]]>
Big Name Developer Hangout - Episode 1 http://heybigname.com/2011/12/08/big-name-developer-hangout-episode-1/ Thu, 08 Dec 2011 00:00:00 +0000 Here is Episode 1 of our Developer Hangout where we talked about Walking the Line Between Performance and Ease of Development. We've noted discussion topic changes in the player if you want to skip around. If you weren't able to join us for this one, don't worry, we'll be hosting more of them.

We'd like to give a special thanks to the talented designer and electronic music producer +Scott Weber (Line 47) for putting together some awesome intro/outro music for us.

]]>
Big Name Hangouts http://heybigname.com/2011/11/21/big-name-hangouts/ Mon, 21 Nov 2011 00:00:00 +0000 Google+ Starting in December, we'll be hosting some Designer and Developer Hangouts.

The first Big Name Developer Hangout is on December 2nd from 9pm to 10PM GMT and open to anyone that wants to stop by. So grab your webcam (or just a headset) and join us for a mostly on-topic discussion.

The topic for our initial Developer Hangout is: Walking the Line Between Performance and Ease of Development

Some potential areas of discussion include:

  • ORM Data Models vs ActiveRecord Query Builders
  • Convention Over Configuration Design Philosophy
  • Development Workflow

Add our Google+ page to your circles to get more information about future hangouts.

We'll be announcing the Designer Hangout shortly!

]]>
Sublime Text 2 Review http://heybigname.com/2011/11/03/sublime-text-2-review/ Thu, 03 Nov 2011 00:00:00 +0000 We're consistently asked about the software that we use to develop websites. Recently, both Justin and I have made a change that we both found exciting. We're using the new IDE Sublime Text 2. We've been using it for close to a month now and we thought that it'd be nice to tell people about it.

Read more about Sublime Text 2 on their development blog.

The current version of my syntax highlighting theme, "Soylent Theme."

To install: open ST2, choose 'preferences', 'browse packages', choose 'user', and drop the file there. You can then change to the theme by going to 'preferences' and choosing it under 'color scheme -> user'. In addition I highly recommend installing "Package Control" and use it to install the "Dark Soda UI Theme" as it compliments my syntax highlighting theme very well.

]]>
CodeIgniter 2 + Sparks + PHP ActiveRecord Part 6: Form Input and Model Validation http://heybigname.com/2011/09/27/codeigniter-2-sparks-php-activerecord-part-6-form-input-and-model-validation/ Tue, 27 Sep 2011 10:27:45 +0000 This screencast is the sixth in a series utilizing CodeIgniter 2 (Reactor) and PHP ActiveRecord. I'm fundamentally focusing on PHP ActiveRecord due to the dramatic improvement it provides over raw CI models. I'm using Sparks (getsparks.org) in order to install extensions that will be used throughout this series as I think that it's a system with merit that should be utilized at every opportunity.

In this episode we go discuss creating input forms, using flashdata, validating data at the model level, accessible attributes, and a number of other concepts.

Be sure to watch in HD.

Download the source for part 6.

]]>
Are You Developing with Your Clients' Best Interests in Mind? http://heybigname.com/2011/09/16/are-you-developing-with-your-clients-best-interests-in-mind/ Fri, 16 Sep 2011 11:52:08 +0000 Software and Database engineering / architecture are difficult jobs. It's absolutely necessary to constantly learn more design patterns and to keep up with new developments so that one can implement best-practices. Frequently, web developers who are under-qualified for database architecture jobs end up in that role due to team size constraints or due to management not realizing that there is a need for experienced professionals. We end up with developers obsessing over every small performance hit while remaining somewhat oblivious to the big picture.

It's important to choose the right tools for the job. By observing basic practices such as reducing the number of queries per page, reducing the number of hits to the server per page, and intelligently caching our data we can keep performance quite high under a heavy load. Should the site become a massive success and performance starts to become an issue then that is a GREAT problem to have. We're building successful web applications and we're making some money. You can then start load balancing your web servers and using data storage patterns such as the "data warehouse" in order to kick your system's performance back up to the top.

The biggest issue that our clients come against tends to NOT be performance. Instead, it tends to be their ROI (return on investment). The less money that our clients spend developing (or paying us to develop) and paying for maintenance (adding features and generally just making changes to keep up with their business) the more income becomes profit. I submit to you that our primary goal should be to reduce the lifetime cost of the products that we're either building in house for a company or are turning over to clients.

We can reduce the lifetime cost of our products by:

  1. Using industry standards across all aspects of our development (from database architecture to variable naming conventions).
  2. Keeping our code small and easy to read (code should read like a sentence, variable names should be easy to understand).
  3. Using technologies that are established and have strong community support.
  4. Properly refactoring code when the need presents itself.

Your Choices Directly Influence Your Clients' Costs

It's important to remember that code you write is going to eventually enter into the hands of another developer. You may hand a product off to a client who then hires an internal development team. The experience level of that development team is an unknown. It's quite possible that they hired inexperienced developers in order to reduce their costs. The more difficult it is for these developers to understand and maintain your code the more likely the modifications that they make will be of the "hacked up" persuasion. This can violate the integrity of the project's conventions, reduce performance, and reduce readability and maintainability. This turns your product into a long-term challenge for the company involved. It's easy to blame your client for not having hired experienced development, but our profession is one that is easy for amateurs to enter into and is difficult for many business people to understand. Sometimes the budget for an experience developer simply does not exist. It is not our job to predict or judge the failings of our clients but to improve their experience with the product that you created for them.

By developing your code by standards and intentionally making sure that your code is self-documenting (easy enough to read that you don't need to generate as much documentation) you're reducing the lifetime cost of the software for your client. By using established technologies you're giving yourself and others access to free community support.

This is the philosophy that leads Big Name's web development decisions. Do you agree with us or does your organization subscribe to a different philosophy?

]]>
CodeIgniter 2 + Sparks + PHP ActiveRecord Part 5: ORM Associations and N+1 http://heybigname.com/2011/09/15/codeigniter-2-sparks-php-activerecord-part-5-orm-associations-and-n1/ Thu, 15 Sep 2011 14:35:04 +0000 In this episode we'll start creating associations between models by creating a belongs_to / has_many relationship between users and blog posts and we'll talk about accessing the related data in multiple ways. We'll also discuss the N+1 query problem and how to resolve it using PHP-ActiveRecord.

Be sure to watch in HD.

Download the source for part 5.

]]>
The N+1 Problem and ORM Data Modeling Performance. http://heybigname.com/2011/09/14/the-n1-problem-and-orm-data-modeling-performance/ Wed, 14 Sep 2011 04:38:53 +0000 A Brief Explanation of N+1

N+1 is an expression that represents a popular pitfall in data modeling for web applications. I'm going to briefly explain N+1 using an example involving the following database tables:

mysql> describe songs;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| album_id | int(11)      | NO   | MUL | NULL    |                |
| name     | varchar(128) | NO   |     | NULL    |                |
| filename | varchar(128) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

mysql> describe albums;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(128) | NO   |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

In this example you need to loop through the albums, creating a container, then loop through the songs providing links to each of them.

How do you approach this from a data modeling perspective?

You could query to pull back all of the albums, then, loop through them. For each album you could query to pull back the songs for it. Consequently, you'd have the initial album query (+1) and then add another query to the pile for each album in the database (N). This means that the number of queries that you need scales with the amount of content in the database. To pull back songs from 10 albums you'd do 11 queries. To pull back songs from 100 albums you'd do 101 queries. This is not good.

select id, name from albums
select id, name, filename from songs where album_id = 1
select id, name, filename from songs where album_id = 2
select id, name, filename from songs where album_id = 3
select id, name, filename from songs where album_id = 4
select id, name, filename from songs where album_id = 5

An alternative would be to select all of the albums, then aggregate the returned album ID's. Then, you can query for all of the songs like so:

select id, title, filename from songs where album_id in (1, 2, 3, 4, 5)

With 2 queries we have pulled back all of the data that could have taken us anywhere between 2 queries and (total database album count + 1) queries.

The problem here being that you must now arrange the data in a format useful for you development. You might do something like this:

$album_songs = array();

foreach($songs as $song)
{
    // create array container
    if(!isset($album_songs[$song->album_id]))
        $album_songs[$song->album_id] = array();

    // push to array
    $album_songs[$song->album_id][] = $song;
}

This will allow you to do something that more or less does this in your view:

foreach($albums as $album)
{
    echo "$album->name";

    // loop songs if they exist
    if(isset($album_songs[$album->id])
        foreach($album_songs[$album->id] as $song)
        {
            echo anchor('download/' . $song->filename, $song->name);
        }
}

There's a bit of extra processing as you're combining everything into an array that is indexed by album id.

Compare and contrast this with using an ORM ActiveRecord model.

You'd have a model called Albums and a model called Songs. You could do something like this:

$albums = Album::find('all');

foreach($albums as $album)
{
    echo "$album->name";

    // loop songs if they exist
    if($album->songs)
        foreach($album->songs as $song)
        {
            echo anchor('download/' . $song->filename, $song->name);
        }
}

We pull back a list of all albums as one query. However, then we take the amount of records returned and instantiate that many album model objects. In this case $album is an array of Album model objects. Each of those objects contains the member variables and methods defined in the model as well as those in the model's parent class (ActiveRecord\Model).

As you look through them you call if($album->songs). If there are no songs tied to an album then using the foreach() loop without this if() statement will result in an error. The error is that foreach expects an ARRAY and we're sending a boolean (false). So, we simply check to see if it's set at ALL, and if it is we know it's populated as an array of Song objects, so we can pass it into the foreach() loop.

Well, at this point the ActiveRecord model has not yet run any queries relating to songs. This is due to a pattern called 'lazy loading'. There is no reason to populate every relationship in an ORM model by default (and indeed, you'd sometimes create infinite loops by doing so). Only when you make a call against $album->songs does the model then query against songs.

It goes something like this:

  1. Query for all albums
  2. Start to loop through all albums
  3. Query for all songs belonging to an album
  4. Start to loop through all songs

As you can see, we're dealing with N+1 here again. However, It's actually much easier to resolve with PHP-ActiveRecord.

$albums = Album::find('all', array('include' => 'songs'));

foreach($albums as $album)
{
    echo "$album->name";

    // loop songs if they exist
    if($album->songs)
        foreach($album->songs as $song)
        {
            echo anchor('download/' . $song->filename, $song->name);
        }
}

By passing the additional parameter to include the songs models we're telling the ORM that we want "eager loading" for the albums to songs association. This means that instead of waiting until we make a call against $album->songs to pull back the data, we'll just go ahead and do it right now. Since the model has far more information about how you're going to want to use the data it can take steps to make the entire process more efficient. In this case, it'll query for albums, then query for all songs that have the returned album_ids. 2 queries, much like our solution above. We are avoiding N+1 and we can easily and cleanly loop through the results.

An Argument Against ORM

Here is where opponents of ORM argue that you're introducing unnecessary overhead into your application. It's true that we're adding additional memory requirements and some amount of additional processing requirements. Each returned record will require an instantiated model object, after all. The data still loops in the background to assign the values (as we did manually earlier in the article), so that's pretty much the same. So, in the end we're dealing with the overhead of the ORM model's infrastructure (sanity checks, etc) and then we're dealing with the instantiation of returned result objects. It is true that the ORM data model pattern has these extra costs.

In my opinion ORM data models should be used by MOST projects on the web as it reduces the complexity of development, makes avoiding unpleasant challenges (N+1) easier, and improves readability and maintainability.

What is your opinion on this topic?

]]>
CodeIgniter 2 + Sparks + PHP ActiveRecord Part 4: Views and Layouts http://heybigname.com/2011/08/26/codeigniter-2-sparks-php-activerecord-part-4-views-and-layouts/ Fri, 26 Aug 2011 11:00:32 +0000 In this episode we'll take a few minutes to improve the infrastructure of the project. We'll add functionality that will allow our controller to automatically determine which content view should be loaded. We'll also add the ability to have multiple application layouts (aka templates) and to be able to easily switch between them.

Be sure to watch in HD.

Download the source for part 4.

]]>
Big Name Releases Design Sharing App Picofin http://heybigname.com/2011/08/22/big-name-releases-design-sharing-app-picofin/ Mon, 22 Aug 2011 12:53:47 +0000 Sign up now for a *FREE* lifetime subscription.

We're excited to announce the release of Picofin. Picofin is a fast, simple tool for online proofing and sharing. We needed a little more and a little less from a design sharing app. We made our own and invite you to use it as well.

]]>
CodeIgniter 2 + Sparks + PHP ActiveRecord Part 3: Login http://heybigname.com/2011/08/17/codeigniter-2-sparks-php-activerecord-part-3-login/ Wed, 17 Aug 2011 12:53:57 +0000 In this episode we'll use the user model that we created to enable our users to login to our site. We'll refactor the user model a bit, extend the CI_Controller class, and create the forms necessary to have a completely working login system.

Be sure to watch in HD.

Download the source for part 3.

]]>
CodeIgniter 2 + Sparks + PHP ActiveRecord Part 2: The User Model http://heybigname.com/2011/08/03/codeigniter-2-sparks-php-activerecord-part-2-the-user-model/ Wed, 03 Aug 2011 08:52:04 +0000 In this episode we create a user model and focus on securely storing passwords in a database table by using sha256 encryption. We go over the reasons why using MD5 is no longer acceptable and we create a password validation method.

Be sure to watch in HD.

Download the source for part 2.

]]>
Quickly Add SwiftMailer to Your CodeIgniter 2 Application http://heybigname.com/2011/07/29/quickly-add-swiftmailer-to-your-codeigniter-2-application/ Fri, 29 Jul 2011 09:43:50 +0000 SwiftMailer is a powerful library for sending mail written in PHP5. SwiftMailer supports SMTP, Sendmail, Postfix, or your custom transport implementations. There are many advantages to using SwiftMailer in your application as opposed to simply using PHP's mail() function.

Sparks are small packages that can be quickly installed into your CodeIgniter 2 application.

We have created a swift-mailer spark that enables developers to install swift-mailer with a single command.

php tools/spark install swift-mailer

If you don't have Spark installed into your project you can find instructions here.

You can find more information about our swift-mailer Spark on the official Spark page or on the Github page.

]]>
CodeIgniter 2 + Sparks + PHP ActiveRecord Part 1: Installation http://heybigname.com/2011/07/28/codeigniter-2-sparks-php-activerecord-part-1-installation/ Thu, 28 Jul 2011 06:30:50 +0000 This screencast covers the installation of CodeIgniter 2 and the PHP ActiveRecord model library.  We install and use CodeIgniter Sparks to simplify the installation of PHP ActiveRecord and any compatible functionality that we wish to add in the future.

Be sure to watch in HD.

Download the source for part 1.

]]>
HTML5 Boilerplate Adds Normalize.css http://heybigname.com/2011/06/23/html5-boilerplate-adds-normalize-css/ Thu, 23 Jun 2011 22:48:19 +0000 We're big fans of HTML5 Boilerplate. It's a great starter and the project is constantly being refined. The latest version now incorporates Normalize.css in the main style sheet. This refreshed file no longer includes the classic CSS reset or a myriad of default styles. What's left is a very lean and sexy style sheet.

html5 boilerplate

What is Normalize.css? From the authors at the Normalize.css project:

Normalize.css is a customisable CSS file that makes browsers render all elements more consistently and in line with modern standards. We researched the differences between default browser styles in order to precisely target only the styles that need normalizing.

The new HTML5 Boilerplate style.css file is now much lighter and is documented very well. Currently, this updated version is only available via the GitHub repository and the developers are looking for feedback on the latest commit at their Google Group.

We're excited to try out the updated boilerplate. Anyone worked with the new files yet?

]]>
Use jQuery in Real-time with Firebug http://heybigname.com/2011/06/05/use-jquery-in-real-time-with-firebug/ Sun, 05 Jun 2011 03:50:54 +0000 I'm sure that you already know that http://getfirebug.com/ is an invaluable Firefox extension for developers.  It's very useful in identifying errors with JavaScript and CSS.  It also can be useful in modifying HTML / CSS in real-time to see how changes would look when rendered by the browser.   Firebug also supports a JavaScript console with command-line.  By using this you're able to run JavaScript against the DOM.  This has many ramifications, let's take a look.

Make sure that you install Firebug and then drag the "jQuerify" bookmarklet link to your bookmark toolbar. The bookmarklet will enable you to inject jQuery into a page should it not already have it.

Bookmarklet (from John Resig) >> Jquerify

Once you have all of that set up simply go to a page, and open the Firebug console.  Open the command-line (icon pictured below).

Firebug Command Line Icon

You'll then see the command-line at the bottom (pictured below).

This is the command-line.

On the command-line you can now execute any JavaScript that you want, including jQuery.  If the site doesn't already include jQuery just click your "jQuerify" bookmarklet and jQuery will be injected into the page.  Voila!  This is a really great tool.  Put some serious thought into adding this to your toolbox and you won't be sorry.

Firebug console example usage.

]]>
Shawn In Dutch http://heybigname.com/2011/05/30/shawn-in-dutch/ Mon, 30 May 2011 11:02:40 +0000 We have some big news. Shawn and his family are moving to the Netherlands! More specifically, Utrecht. They traveled all weekend long and have made it to their destination safely. Their next step, among a lot of others, is to find a place to live. So, if you have any friends across the pond that can recommend real estate in Utrecht, they'd be much obliged. You can keep up with Shawn and his experiences at Shawn in Dutch.

alt text

Just so we're clear, Shawn isn't leaving the company. In fact, he'll be opening the Dutch branch of Big Name! We're very excited about this opportunity and we're looking forward to the challenges ahead.

Photo by Marcel030NL

]]>
Deconstructing Design http://heybigname.com/2011/03/31/deconstructing-design/ Thu, 31 Mar 2011 21:50:26 +0000 Deconstructing a design is a fantastic way to discover new ways of overcoming design challenges. When I encounter a unique approach to web design, the first thing I do is open up Firebug and break down what went into creating the effect.

Sometimes I find it was a clever combination of CSS and JavaScript and other times it's a graphic that has done a lot of the work. When it's the latter, I can only make assumptions as to how it was accomplished. I hate that feeling.

That's why I was interested to see Campaign Monitor's release of their Designer Email Templates a while back. Not only did they offer downloads of the HTML template, but they also included the original layered Photoshop docs. These documents give you a view of the techniques that were used to create those little touches that make a design really work.

Download and learn.

]]>
Setting Type on the Web - Tips http://heybigname.com/2011/02/08/setting-type-on-the-web-tips/ Tue, 08 Feb 2011 20:52:51 +0000 Copy on the web needs to be legible, accessible and consistent. No one wants to have to labor over copy. Our goal as designers is to get users through the content as quickly and efficiently as possible.

At Big Name, I'm always looking for ways to improve the typography for our sites. Here are some of the things I do to keep the content easy on the eyes.

Set font-size on the body to 100%

Setting the body font-size to a percentage ensures that your type will scale properly even if the user has altered the browser's default text size. This can't be accomplished with pixels or points as they are fixed-sized units.

I set the body font-size to 100% or greater. Some say this is too large, but I believe it creates highly readable text – especially when paired with a proper line-height.

When set to 100%, the browser will use its default setting for displaying text, normally 16-pixels. This is the equivalent to the standard print size of 12-points. 16px = 1em = 100% = 12pt

The alternative to setting a percentage is using EMs. Yes, the scalability is there, but IE 6/7 will disproportionately interpret the EM. This causes the size to be way larger or smaller when using the "Largest" or "Smallest" text-size settings, for example.

IE Font Size Comparison

Why not just rely on the browser's "zoom" feature? Zoom increases the size of everything on the page, not just the text. This can make other elements on the page look nasty. Not to mention, high resolution devices like the iPhone and some Android phones now sport super-high resolutions that make 12-pixel fonts tough to read.

Use EMs for setting type throughout the rest of the site

Yes, I know we just set the entire document to a percentage. You can obviously continue using percentages, but I prefer the EM because it's a true typographic unit and because it's the W3C recommended measure.

Regardless of your choice, remember that relative values like percentages and EMs are inherited and will cascade in a document. For example, setting an ordered list size to 1.5em and a list item to 1.5em will cause the text to be displayed at 1.5 x 1.5.

Be consistent with your font sizes by composing on a scale and utilizing an EM calculator. If you're coming from the pixel world, try starting with http://pxtoem.com/". This will help you come up with a set of default styles based on a default body font size.

Pixels     EMs         Percent Points
----------------------------------
6px     0.333em     33.3%     5pt
7px     0.389em     38.9%     5pt
8px     0.444em     44.4%     6pt
9px     0.5em         50%     7pt
10px     0.556em     55.6%     8pt
11px     0.611em     61.1%     8pt
12px     0.667em     66.7%     9pt
13px     0.722em     72.2%     10pt
14px     0.778em     77.8%     11pt
15px     0.833em     83.3%     11pt
16px     0.889em     88.9%     12pt
17px     0.944em     94.4%     13pt
--------------------------------
18px     1em         100%     14pt
--------------------------------
19px     1.056em     105.6%     14pt
20px     1.111em     111.1%     15pt
21px     1.167em     116.7%     16pt
22px     1.222em     122.2%     17pt
23px     1.278em     127.8%     17pt
24px     1.333em     133.3%     18pt

Note: As mentioned above, IE will mishandle the EM if a user sets their text-size "larger" or "smaller". However, once the body font-size is set as a percentage, IE won't continue adding the values of an EM cascade.

Keep measure in mind

Measure refers to the width of a body of type – or more precisely, the number of characters in a single line of a column of text. According to The Elements of Typographic Style:

Anything from 45 to 75 characters is widely regarded as a satisfactory length of line for a single-column page set in a serifed text face in a text size. The 66-character line (counting both letters and spaces) is widely regarded as ideal. For multiple column work, a better average is 40 to 50 characters.

We're able to read with ease when our eyes encounter line measures in this range. But trying to keep an ideal character-per-line count of 66 characters can be challenging on the web.

Liquid and elastic layouts can help address issues of text and browser resizing, but your design needs are going to influence your overall approach.

Here, I've used a pixel width for my columns (at the time of this writing) in order to retain control over the line length. It will throw off the measure when text is resized, but this layout presents an ideal line width to the vast majority of my audience.

Additional resources for setting type on the web

]]>
Hey Big Name! http://heybigname.com/2011/01/20/hey-big-name/ Thu, 20 Jan 2011 17:35:03 +0000 Hey everyone!  The talented designer Justin Seiter and I have joined forces to create the web design company Big Name.  In addition to our regular web design work we're excited to announce that we'll be creating articles and screencasts on numerous design and development topics.

You can learn more about us on the about page and always feel free to drop us a line.

As we get up to speed you can follow us here and on our twitter @heybigname.

]]>
Developing a Website with CodeIgniter Part 4: Admin Site CRUD http://heybigname.com/2010/01/30/developing-a-website-with-codeigniter-part-4-admin-site-crud/ Sat, 30 Jan 2010 01:25:43 +0000 This screencast continues a series with the goal of documenting the development of a functionally complete site using PHP with the CodeIgniter framework and jQuery for JavaScript UI Improvements including AJAX interactions.

This video covers the development of an admin user management system (CRUD) and includes updates to the authentication system including a method that is used allow only admins into specific sections of the site.

Download the Source Here

]]>
Managing Assets with CodeIgniter http://heybigname.com/2009/11/23/managing-assets-with-codeigniter/ Mon, 23 Nov 2009 17:16:32 +0000 This article discusses the reason behind the base_url() method and the creation of a helper file that will assist in managing images, css files, and more.

PHP code is far from the only content necessary for most web applications. Most frequently we need to include images, css files, flash files, and many other types of files. The CodeIgniter framework doesn't contain a built-in methodology for asset management, leaving the developer to their own devices when it comes to deciding where to put this content. Every developer is going to have their own personal preference, and for the most part they're equally valid.

The base_url() method

Imagine a scenario where you have developed a site that contains user profiles. Each user profile can have an image. The code in your view that would display the image may look something like this:


This works fine until your cool new site is bought up and now your application rests at http://www.bigcompany.com/mycoolsite. Now, you have to go through all of your code and search/replace "/images with "/mycoolsite/images while hoping that you've found everything and that something small won't be missed until 500 users have seen it.

Instead, we use the base_url() method for creating absolute links. base_url() simply outputs the value of the $config['base_url'] variable that is set in your application/config/config.php file. Your new code would look like this:

~~~

]]>
Developing a Website with CodeIgniter Part 3: The Login Process http://heybigname.com/2009/11/09/developing-a-website-with-code-igniter-part-3-the-login-process/ Mon, 09 Nov 2009 23:48:51 +0000 This screencast continues a series with the goal of documenting the development of a functionally complete site using PHP with the Code Igniter framework and jQuery for Javascript UI improvements including AJAX interactions.

In this video we utilize the user model that we've created to create a login process for our website. We'll use the code igniter form helper, database library, and session library.

I tried something different and recorded the video in HD.  The encoding left the dark background text a bit hard to read, so if I continue to upload HD videos I'll switch to using a white background.  Please let me know if you prefer the old resolution or the HD resolution.

Download the Source Here

]]>
Why Should I Use CodeIgniter? http://heybigname.com/2009/10/28/why-should-i-use-code-igniter/ Wed, 28 Oct 2009 12:33:18 +0000 CodeIgniter is a PHP development framework. You can think of it as a PHP web site without content. Instead it provides a structure and many methods that will be frequently used for most web sites.

So, why should I use CodeIgniter instead of just writing my own site from scratch in PHP?

1. Standardization and Popularity - Using a popular development framework reduces the development time and consequently cost of initial development and maintenance.

Hiring good developers is hard and adding developers mid-project is even harder. Dozens, maybe even hundreds, of hours are spent acclimating new developers to the code base. By hiring developers who are already familiar with CodeIgniter you're able to integrate them into the project much more quickly. CodeIgniter is very compartmentalized and finding / modifying various bits of code should be very easy for anyone familiar with the framework. Initial project development time can be reduced by partially bypassing the need for planning of a custom framework.

CodeIgniter is a framework that is widely used and it's easy to find developers who are familiar with it. There are many well made libraries and helpers created by users that will allow developers to integrate functionality into their sites without having to write it all from scratch. The CodeIgniter forums provide a centralized location for developers to get support from other developers.

2. Loose Coupling and Code Re-usability - The longer you develop your code base the quicker projects can be completed.

By keeping your database interactions in libraries and models, your html and display logic in views, and additional methods in helpers and plugins your resulting code becomes modular and is easily reusable on additional projects. Why rewrite your user authentication system from project to project when you can simply copy your database structure and user model and be done with it?

CodeIgniter is written with object oriented PHP. Object oriented code is significantly less expensive to develop, debug, and maintain. Programmers can no longer get away with using global variables to pass data around the application and the project quality benefits significantly.

3. Built in Functionality - You know those hundred things that you rewrite every time you make a site? Yea, they're already done.

CodeIgniter comes out of the metaphorical box with libraries and helpers that reduce the amount of code a developer will have to write. Some examples of the functionality that comes bundled in are: benchmarking, html calendar generation, shopping cart management, email sending, file uploading, form validation, ftp transfers, html generation, form generation, internationalization, pagination, session management, trackback management, string / typography manipulation, and more than I can list here.

]]>
Using CodeIgniter's Active Record Class to Create Subqueries http://heybigname.com/2009/09/18/using-code-igniters-active-record-class-to-create-subqueries/ Fri, 18 Sep 2009 10:50:34 +0000 A good friend of mine recently asked me how he would create a subquery using Code Igniter's Active Record class.  This does present a challenge as Code Igniter's Active Record class does not natively support subqueries.

However, if you look into the code for the Database class you'll see that CI uses some handy utility methods to compile the SQL for processing.  These methods exist in all Code Igniter drivers (providing the cross platform abstraction that makes Active Record so valuable in the first place).

This algorithm is definitely far from perfect, but it DOES provide some level of abstraction that manually typing your subquery would not.  If you have any questions, concerns, or suggestions feel free to leave them as comments.

// Generate the subquery
$this->db->select('count(*)');
$this->db->from('users');

// Render the subquery to a string
$subQuery = $this->db->_compile_select();

// Reset active record
$this->db->_reset_select();

// Generate the primary query and include the subquery
$this->db->select('users.id as userId, users.fullname as userName');
$this->db->select("($subQuery) as userCount");
$this->db->where('users.status', 'active');

To break this down into smaller parts:

// Generate the subquery
$this->db->select('count(*)');
$this->db->from('users');

This code generates the query: select count(*) from users

// Render the subquery to a string
$subQuery = $this->db->_compile_select();

This code uses Code Igniter's Active Record class to generate an abstracted select statement. Whether we're using Oracle, MySQL, or PostgreSQL the statement will be rendered appropriately by Active Record.

// Reset active record
$this->db->_reset_select();

This method is usually called automatically by the Active Record class after a select statement is completed. This will clear the Active Record cache so that our "select"(count(*)) and "from"(users) commands will not appear in the NEXT active record statement that we create.

// Generate the primary query and include the subquery
$this->db->select('users.id as userId, users.fullname as userName');
$this->db->select("($subQuery) as userCount");
$this->db->where('users.status', 'active');

This code generates a select statement calling back the userID and the userName as well as aliasing the subquery variable string as 'userCount'. Using the MySQL driver the SQL that is output is as follows:

SELECT `users`.`id` as userId, `users`.`fullname` as userName, (SELECT count(*) FROM (`users`)) as userCount FROM (`users`) WHERE `users`.`status` = 'active'

Keep in mind that the developers of Code Igniter did not intend for you to use the _compile_select method. Therefore, it's possible that they may change the functionality in future versions and not bother letting anyone know.

]]>
Developing a Website with CodeIgniter Part 2: Users Database Table and the User Model http://heybigname.com/2009/09/06/developing-a-website-with-code-igniter-part-2-users-database-table-and-the-user-model/ Sun, 06 Sep 2009 18:46:01 +0000 This screencast continues a series with the goal of documenting the development of a functionally complete site using PHP with the CodeIgniter framework and jQuery for Javascript UI improvements including AJAX interactions.

This video covers the creation of a database table for storing users and the CodeIgniter model class for interacting with it.

Errata:

I've noticed a few errors in the screencast. When errors creep up (and they will) I'll audit them as they're discovered and keep this post up to date.  Errors will also be updated during the following screencast.

  1. The UpdateUser method "set password" line (line 80) contains the variable $options['userEmail'] instead of $options['userPassword'].
  2. The UpdateUser method is missing the line $this->db->where('userId', $options['userId']); This should be added before the line that contains $query = $this->db->update('users'); (line 84)
  3. The AddUser method's default value is set incorrectly (line 48). It currently shows array('userStatus', 'active') where it should read array('userStatus' => 'active').
]]>
Developing a Website with CodeIgniter Part 1: Development Environment and Framework Configuration http://heybigname.com/2009/09/04/developing-a-website-with-code-igniter-part-1-configuration/ Fri, 04 Sep 2009 21:29:28 +0000 This screencast begins a series with the goal of documenting the development of a functionally complete site using PHP with the Code Igniter framework and jQuery for Javascript UI improvements including AJAX interactions.

This video briefly covers the establishment of a local development environment and the configuration of CodeIgniter.

]]>
Understand the Structure of CodeIgniter http://heybigname.com/2009/08/29/understand-the-structure-of-code-igniter/ Sat, 29 Aug 2009 19:41:19 +0000 In this screencast I attempt to explain the structure and terminology of the Code Igniter MVC PHP development framework.

]]>
How to Write a Better Model in CodeIgniter http://heybigname.com/2009/08/28/how-to-write-a-better-model-in-code-igniter/ Fri, 28 Aug 2009 14:29:30 +0000 Developing using an MVC framework can go a long way towards increasing the reusability and robustness of your code. In this article I focus on writing model methods for the Code Igniter PHP MVC framework. The concepts themselves are not specific to Code Igniter, but some of the code will be.

CRUD methods

CRUD is an acronym that stands for Create, Retrieve, Update, and Delete. These are the most basic types of interactions that your code will have with a data source.

A few examples: Create - adding a user account to your website Retrieve - getting a list of available products Update - changing a user's password or the name of a product Delete - removing a user account or product

When writing models you'll generally start by creating CRUD methods for each of your logical types of data. If you're writing user authentication (login, logout, forgotten password, account activation) you'll want a method to add, update, and delete users along with the ability to pull back the user account information for a single user or for a group of users.

For example:

function AddUser()
function UpdateUser()
function DeleteUser()
function GetUsers()

Most of the interactions that we have with user accounts can be handled by these four methods. Instead of creating a model method "GetActiveUsers" we can just create a parameter for GetUsers that determines the user status of the returned set, more on this below.

The Options Array

Typically parameters for methods are sent in the following manner:

function AddUser($insertData)
function UpdateUser($userId, $updateData)
function DeleteUser($userId)
function GetUsers($limit, $offset)

Naturally, (especially for GetUsers) we'll find more and more parameters to add to our methods. This not only gets very difficult to read, but can create issues where we have to go back and alter the code that we've already written if we have to tack a new parameter onto the method.

For Example:

function GetUsers($limit, $offset, $status)

We would also need a singular GetUser($userId) method in addition to the GetUsers method. This is a problem because we're creating multiple versions of the same method. If we change the way we want information to be returned then we'd need to change code in two places, the GetUser AND GetUsers methods.

To overcome these issues I propose the usage of an options array, as such:

function AddUser($options = array())
function UpdateUser($options = array())
function DeleteUser($options = array())
function GetUsers($options = array())

If, in the past, we'd use GetUsers(5, 5, 'active') to return a list of users, we would now use GetUsers(array('limit' => 5, 'offset' => 5, 'status' => 'active');

Parameters passed using the options array can be sent in any order and can even be completely omitted. As you need to add functional parameters you simply change the model method and will not have to update code anywhere else.

Utility Methods

In order to create properly robust methods we're going to need to implement a few common bits of functionality. Namely, the ability to assign required fields and field defaults. For example, a required field when adding a user might be userEmail. For this we create the 'required' method.

/**
* _required method returns false if the $data array does not contain all of the keys assigned by the $required array.
*
* @param array $required
* @param array $data
* @return bool
*/
function _required($required, $data)
{
    foreach($required as $field) if(!isset($data[$field])) return false;
    return true;
}

In the 'AddUser' example our code might look like this:

/**
* AddUser method creates a record in the users table.
*
* Option: Values
* --------------
* userEmail            (required)
* userPassword
* userName
* userStatus        active(default), inactive, deleted
*
* @param array $options
*/
function AddUser($options = array())
{
    // required values
    if(!$this->_required(array('userEmail'), $options)) return false;

    // At this point we know that the key 'userEmail' exists in the $options array.
}

Now our AddUser method will bail out and return false if the 'userEmail' key was not sent in the options array. Robust!

Notice that in the PHPdoc block we mention that the default status for newly created users is 'active'. Naturally, if we wanted to create an inactive user we would be able to pass the 'userStatus' parameter with the value inactive. However, we are lazy and prefer to not have to explicitly declare 'active'.

Introducing the 'default' method:

/**
* _default method combines the options array with a set of defaults giving the values in the options array priority.
*
* @param array $defaults
* @param array $options
* @return array
*/
function _default($defaults, $options)
{
    return array_merge($defaults, $options);
}

That's it, this method consists of a single command. I decided to create a method out of this because I may want to add extra functionality to the defaults method, and don't want to have to change every single one of my model methods.

Let's go ahead and implement the 'default' method:

/**
* AddUser method creates a record in the users table.
*
* Option: Values
* --------------
* userEmail            (required)
* userPassword
* userName
* userStatus        active(default), inactive, deleted
*
* @param array $options
*/
function AddUser($options = array())
{
    // required values
    if(!$this->_required(array('userEmail'), $options)) return false;

    // default values
    $options = $this->_default(array('userStatus' => 'active'), $options);
}

// At this point we know that the 'userEmail' key exists in the $options array and if no 'userStatus' key existed before, it does now and with the value 'active'.

These methods go a long way towards making your code more robust while only adding a few lines to each method.

Active Record

Many database constructs (MySQL, Oracle, Microsoft SQL, PostgreSQL, etc) use variations of SQL syntax, but they all work a little bit differently. An active record class allows us to create a database query using abstraction. In other words we are able to create queries that will work on any database construct that our class supports. They also give the added benefit of allowing us to send bits of the query to the class one by one before executing the query.

A Code Igniter active record query might look something like this:

$this->db->where('userStatus', 'active');
$this->db->get('users');

Those two commands will create and execute the query, "select * from users where userStatus = 'active'"

Putting It All Together

Using the concepts that we've explored let's look at a simple example set CRUD methods.

/**
* AddUser method creates a record in the users table.
*
* Option: Values
* --------------
* userEmail            (required)
* userPassword
* userName
* userStatus        active(default), inactive, deleted
*
* @param array $options
*/
function AddUser($options = array())
{
    // required values
    if(!$this->_required(array('userEmail'), $options)) return false;

    // default values
    $options = $this->_default(array('userStatus' => 'active'), $options);

    // qualification (make sure that we're not allowing the site to insert data that it shouldn't)
    $qualificationArray = array('userEmail', 'userName', 'userStatus');
    foreach($qualificationArray as $qualifier)
    {
        if(isset($options[$qualifier])) $this->db->set($qualifier, $options[$qualifier]);
    }

    // MD5 the password if it is set
    if(isset($options['userPassword'])) $this->db->set('userPassword', md5($options['userPassword']));

    // Execute the query
    $this->db->insert('users');

    // Return the ID of the inserted row, or false if the row could not be inserted
    return $this->db->insert_id();
}

/**
* UpdateUser method alters a record in the users table.
*
* Option: Values
* --------------
* userId            the ID of the user record that will be updated
* userEmail
* userPassword
* userName
* userStatus        active(default), inactive, deleted
*
* @param array $options
* @return int affected_rows()
*/
function UpdateUser($options = array())
{
    // required values
    if(!$this->_required(array('userId'), $options)) return false;

    // qualification (make sure that we're not allowing the site to update data that it shouldn't)
    $qualificationArray = array('userEmail', 'userName', 'userStatus');
    foreach($qualificationArray as $qualifier)
    {
        if(isset($options[$qualifier])) $this->db->set($qualifier, $options[$qualifier]);
    }

    $this->db->where('userId', $options['userId']);

    // MD5 the password if it is set
    if(isset($options['userPassword'])) $this->db->set('userPassword', md5($options['userPassword']));

    // Execute the query
    $this->db->update('users');

    // Return the number of rows updated, or false if the row could not be inserted
    return $this->db->affected_rows();
}

/**
* GetUsers method returns an array of qualified user record objects
*
* Option: Values
* --------------
* userId
* userEmail
* userStatus
* limit                limits the number of returned records
* offset                how many records to bypass before returning a record (limit required)
* sortBy                determines which column the sort takes place
* sortDirection        (asc, desc) sort ascending or descending (sortBy required)
*
* Returns (array of objects)
* --------------------------
* userId
* userEmail
* userName
* userStatus
*
* @param array $options
* @return array result()
*/
function GetUsers($options = array())
{
    // default values
    $options = $this->_default(array('sortDirection' => 'asc'), $options);

    // Add where clauses to query
    $qualificationArray = array('userId', 'userEmail', 'userStatus');
    foreach($qualificationArray as $qualifier)
    {
        if(isset($options[$qualifier])) $this->db->where($qualifier, $options[$qualifier]);
    }

    // If limit / offset are declared (usually for pagination) then we need to take them into account
    if(isset($options['limit']) && isset($options['offset'])) $this->db->limit($options['limit'], $options['offset']);
    else if(isset($options['limit'])) $this->db->limit($options['limit']);

    // sort
    if(isset($options['sortBy'])) $this->db->order_by($options['sortBy'], $options['sortDirection']);

    $query = $this->db->get('users');
    if($query->num_rows() == 0) return false;

    if(isset($options['userId']) && isset($options['userEmail']))
    {
        // If we know that we're returning a singular record, then let's just return the object
        return $query->row(0);
    }
    else
    {
        // If we could be returning any number of records then we'll need to do so as an array of objects
        return $query->result();
    }
}

/**
* DeleteUser method removes a record from the users table
*
* @param array $options
*/
function DeleteUser($options = array())
{
    // required values
    if(!$this->_required(array('userId'), $options)) return false;

    $this->db->where('userId', $options['userId']);
    $this->db->delete('users');
}

Here are some examples of how we can use these methods to interact with your database.

Adding a User

$userId = $this->user_model->AddUser($_POST);

if($userId)
    echo "The user you have created has been added successfully with ID #" . $userId;
else
    echo "There was an error adding your user.";

Updating a User

if($this->user_model->UpdateUser(array('userId' => 3, 'userName' => 'Shawn', 'userEmail' => 'not telling')))
    // The user has been successfully updated
else
    // The user was not updated

User Authentication (Retrieving a Single User)

$user = $this->user_model->GetUsers(array('userEmail' => $userEmail, 'userPassword' => md5($userPassword), 'userStatus' => 'active'));
if($user)
    // Log the user in
else
    // Sorry, your user / password combination isn't correct.

Retrieving a Set of Users

$users = $this->user_model->GetUsers(array('userStatus' => 'active'));

if($users)
{
    echo "Active Users";
    foreach($users as $user)
    {
        echo $user->userName . "";
    }
}
else
{
    echo "There are no active users.";
}

Deleting a User

$this->user_model->DeleteUser(array('userId' => $userId));

I hope that this will continue a dialog on how to better write such commonly reused bits of functionality. If you have any suggestions or comments please let me know. I'd love to hear about the algorithms that you've been designing to handle similar problems.

William Rufino has translated this article to Portuguese. This translation can be found here: http://www.williamrufino.com.br/blog/como-escrever-um-model-melhor-no-codeigniter

]]>
AJAX with jQuery: A Simple Login Example http://heybigname.com/2009/04/15/ajax-with-jquery-a-simple-login-example/ Wed, 15 Apr 2009 17:04:23 +0000 In this screencast we explore the jQuery $.post() command in order to create a simple AJAX login script that also gracefully degrades and works for users who do not have JavaScript enabled.

You may want to check out my post "What Exactly is JSON?" if you're not familiar with the JavaScript Object Notation before watching.

]]>
What exactly is JSON? http://heybigname.com/2009/04/15/what-exactly-is-json/ Wed, 15 Apr 2009 16:04:19 +0000 JSON is short for JavaScript Object Notation.  It's a notation used to create JavaScript objects.

JSON looks like this:

    {
    name: "Shawn McCool",
    language: "English"
    }

When evaluated by JavaScript or decoded with another language you'd end up with an object like the one below.

person.name is equal to "Shawn McCool" person.language is equal to "English".

Since using JSON allows you to easily serialize arrays and objects, it's often used during AJAX calls and web services. Below are some examples of the JavaScript Object Notation.

    var myArray = ['cat', 'dog'];
    var myObject = {animal: 'cow'};
    alert(myArray[0]);  // outputs 'cat'
    alert(myObject.animal); // outputs 'cow';

A standard set of interactions for an AJAX login script might be:

  1. JavaScript client (browser) posts username and password to PHP.

    $.post('/ajax.php', {
    username: $('input[name=username]').val(), 
    password: $('input[name=password]').val()
    }, function(data) {}, 'json');
    
  2. PHP authenticates the information against the database then generates a response which is json_encoded and sent to the browser.

    $user = $this->auth_model->Login($_POST['username'], $_POST['password']);
    
    if($user)
        $data['success'] = true;
    else
        $data['success'] = false;
    
    echo json_encode($data);
    
  3. JavaScript client ‘eval’uates the JSON which creates an object. JavaScript client then determines behavior based on the data in the object.

    $.post('/ajax.php', {username: $('input[name=username]').val(), password: $('input[name=password]').val()}, function(data) {
        if(data.success)
        {
            // login succeeded
        } else {
            // login failed
        }
    }, 'json');
    
]]>
AJAX with jQuery: The Beginning http://heybigname.com/2009/03/26/ajax-with-jquery-the-beginning/ Thu, 26 Mar 2009 19:40:53 +0000 This is the first in a series of screencasts that I'm creating to highlight the AJAX functionality present in the open source Javascript framework jQuery. In this screencast I give a brief overview of AJAX as well as delve into our first jQuery command.

AJAX stands for Asynchronous JavaScript and XML.

]]>
CodeIgniter at the Nashville PHP meetup March 20th. http://heybigname.com/2009/03/10/code-igniter-at-the-nashville-php-meetup-march-20th/ Tue, 10 Mar 2009 07:14:37 +0000 I'll be making a presentation on the popular PHP development framework 'Code Igniter' on March 20th at the Nashville PHP meetup.  This will be the first presentation of many as they systematically explore each of the most used and most functional PHP development frameworks.  Come visit, ask questions, and save time by learning the basics from experienced developers.

]]>