Your friends at Viget present Extend, a Code & Technology Blog

Why We Love Craft CMS

Because it’s awesome.

Mic Drop

Megan wrote a post last week discussing Craft and WordPress that sparked an internal discussion about the difficulty we’ve encountered selling less popular content management systems—basically, anything other than WordPress or Drupal—because so many clients have only heard of the big boys. Viget has dealt with this issue for quite a while since we have long been a proponent of ExpressionEngine. Having had extensive CMS experience, including experience building my own, Craft is the best CMS that I have ever used. Hopefully, this post will help explain to both developers and clients why I’ve come to this conclusion.


Exploring the `:inverse_of` Option on Rails Model Associations

Ever seen the :inverse_of option on an association and wondered what it did and why it was there?

class Criminal < ActiveRecord::Base
  belongs_to :prison, inverse_of: :criminals
end

class Prison < ActiveRecord::Base
  has_many :criminals, inverse_of: :prison
end

The first time I saw something like that, it seemed like an unnecessary distinction to make. Why is :inverse_of a thing?

Memory Optimization When Fetching Associated Records

It turns out that associated objects do not point to the same in-memory objects by default. To illustrate:

prison   = Prison.create(name: 'Bad House')
criminal = prison.criminals.create(name: 'Krazy 8')

# Without :inverse_of
criminal.prison == prison
# Prison Load (0.1ms)  SELECT "prisons".* FROM "prisons" WHERE "prisons"."id" = 2 LIMIT 1
# => true

# With :inverse_of
criminal.prison == prison
# => true

When we call criminal.prison without :inverse_of on both the :belongs_to and :has_many associations, it will hit the database. With :inverse_of, if we already have that prison record in memory then criminal.prison will point to the same prison.

It's worth clarifying that this only saves you a database look-up when going from Criminal to Prison (:belongs_to direction). Here's some console output going from the other direction:

prison = Prison.last
# Prison Load (0.1ms)  SELECT "prisons".* FROM "prisons" ORDER BY "prisons"."id" DESC LIMIT 1
# => #<Prison id: 3, name: "Broadmoor", created_at: "2014-10-10 20:26:38", updated_at: "2014-10-10 20:26:38">

criminal = prison.criminals.first
# Criminal Load (0.3ms)  SELECT "criminals".* FROM "criminals" WHERE "criminals"."prison_id" = 3 LIMIT 1
# => #<Criminal id: 3, name: "Charles Bronson", prison_id: 3, created_at: "2014-10-10 20:26:47", updated_at: "2014-10-10 20:26:47">

prison.criminals.first == criminal
# Criminal Load (0.2ms)  SELECT "criminals".* FROM "criminals" WHERE "criminals"."prison_id" = 3 LIMIT 1
# => true

A model's associations, as far as memory is concerned, are one-way bindings. The :inverse_of option basically gives us two-way memory bindings when one of the associations is a :belongs_to. A memory optimization isn't the only thing that :inverse_of gets you. Next, we'll take a look at two use cases involving the creation of associated records.


Craft vs. WordPress: The Good, the Bad, and the Ugly Data

Any web developer who has been working in the industry for more than a few days has probably heard of WordPress. Stay for a couple more months and there’s a good chance you’ve worked on a WordPress site — it’s a popular platform since it’s well-known, easy-to-use, and free.

You may have also heard of ExpressionEngine, Drupal, Joomla, and a few other CMS heavy-hitters. They all have their benefits and flaws, which is a topic for another time. I’d rather talk about the next up-and-comer and my new favorite, Craft.

Craft is a small CMS that was developed fairly recently by ExpressionEngine add-on developers Pixel & Tonic. Having worked with ExpressionEngine for a while, it’s obvious these guys really know the pain-points in any client-facing CMS. Everything they’ve built into Craft solves a problem I’ve had on almost every site I ever made with WordPress. If I had to make a CMS myself, it would probably resemble Craft pretty closely.

So, how does Craft stack up against the industry go-to?


Adding a NOT NULL Column to an Existing Table

Despite some exciting advances in the field, like Node, Redis, and Go, a well-structured relational database fronted by a Rails or Sinatra (or Django, etc.) app is still one of the most effective toolsets for building things for the web. In the coming weeks, I’ll be publishing a series of posts about how to be sure that you’re taking advantage of all your RDBMS has to offer.

ASSUMING MY LAST POST CONVINCED YOU of the why of marking required fields NOT NULL, the next question is how. When creating a brand new table, it’s straightforward enough:

CREATE TABLE employees (
    id integer NOT NULL,
    name character varying(255) NOT NULL,
    created_at timestamp without time zone,
    ...
);

When adding a column to an existing table, things get dicier. If there are already rows in the table, what should the database do when confronted with a new column that 1) cannot be null and 2) has no default value? Ideally, the database would allow you to add the column if there is no existing data, and throw an error if there is. As we’ll see, depending on your choice of database platform, this isn’t always the case.


Required Fields Should Be Marked NOT NULL

Despite some exciting advances in the field, like Node, Redis, and Go, a well-structured relational database fronted by a Rails or Sinatra (or Django, etc.) app is still one of the most effective toolsets for building things for the web. In the coming weeks, I’ll be publishing a series of posts about how to be sure that you’re taking advantage of all your RDBMS has to offer.

A “NOT NULL constraint” enforces that a database column does not accept null values. Null, according to Wikipedia, is

a special marker used in Structured Query Language (SQL) to indicate that a data value does not exist in the database. Introduced by the creator of the relational database model, E. F. Codd, SQL Null serves to fulfill the requirement that all true relational database management systems (RDBMS) support a representation of “missing information and inapplicable information.”

One could make the argument that null constraints in the database are unnecessary, since Rails includes the presence validation. What’s more, the presence validation handles blank (e.g. empty string) values that null constraints do not. For several reasons that I will lay out through the rest of this section, I contend that null constraints and presence validations should not be mutually exclusive, and in fact, if an attribute’s presence is required at the model level, its corresponding database column should always require a non-null value.