FW/1 & QB Schema Builder

November 7, 2017

Not too long ago I wrote an article about working with FW/1 and QB; a query builder DSL written by Eric Peterson.

In this post, I wanted to touch on a new feature and component released in the latest version of QB, Schema Builder, and how to make use of it in an FW/1 application.

Schema Builder presents the ability to build out your database structure using the same awesome builder/chaining format as the Query Builder object. This allows us to create table layouts from the ground up in straight CFML.

Here's an example from the docs:

schema.create('users', function (table) {
  table.increments('id');
  table.string('email');
  table.string('password');
  table.timestamp('created_date').nullable();
  table.timestamp('modified_date').nullable();
});

Getting Started / Prerequisites


In my previous post, I went over how to setup and use QB in an FW/1 application. If you're unfamiliar with the setup, I highly recommend glancing through its content as I'll only be adding on to those code examples here.

So to tie everything together you'll want to have CommandBox installed, a basic FW/1 application structure using subsystems, a box.json for defining dependencies like FW/1 and QB (all explained in the linked post above) and a mapping in Application.cfc to the path where QB is kept.

Wiring Things Up With DI/1


With a functioning FW/1 application and QB set up as a subsystem, It's time to wire up QB and its components in DI/1.

Within the variables.framework.subsystems struct in Application.cfc, we'll define the QB components and their dependencies while also including the SchemaBuilder component.

qb.diLocations: ["/models"],
qb.diConfig: {
    loadListener: function(di1) {
        di1.declare("BaseGrammar").instanceOf("qb.models.Grammars.BaseGrammar").done()
           .declare("MySQLGrammar").instanceOf("qb.models.Grammars.MySQLGrammar").done()
           .declare("QueryUtils").instanceOf("qb.models.Query.QueryUtils").done()
           .declare("QueryBuilder").instanceOf("qb.models.Query.QueryBuilder")
           .withOverrides({
                grammar: di1.getBean("MySQLGrammar"),
                utils: di1.getBean("QueryUtils"),
                returnFormat: "array"
           }).done()
           .declare("SchemaBuilder").instanceOf("qb.models.Schema.SchemaBuilder")
           .asTransient()
           .withOverrides({
                grammar: di1.getBean("MySQLGrammar")
           });
    }
}

Breaking It Down

qb.diLocations: "/models" tells DI/1 to create a subsystem bean factory and gather objects in /qb/models based on the default conventions.

In qb.diConfig we define a load listener with a closure that takes the DI/1 object as an argument. This is for defining our bean factory settings. In the function block, we use DI/1's builder syntax to declare individual bean objects of QB's components. The declare() method is used to define an "alias" to the component. This is useful for having DI/1 automagically satisfy an object's constructor arguments. The declaration of "QueryBuilder" calls withOverrides() to pass in specific arguments to the object's constructor, or init method. Then we define "SchemaBuilder" as a transient object which takes the same grammar object (MySQLGrammar) as QueryBuilder.

Note: The declaration of MySQLGrammar. This is specific support in QB for the MySQL dialect. There are other options available. See the /qb/models/Grammars folder.

Now we just need to start our application from CommandBox to have a working environment.

Examples


Usage

To use the QueryBuilder from DI/1, we can call on it like so:

QueryBuilder = getBeanFactory('qb').getBean('QueryBuilder');

To use SchemaBuilder from DI/1, we call it the same as QueryBuilder:

SchemaBuilder = getBeanFactory('qb').getBean('SchemaBuilder');

Creating Tables

Let's build out a posts and authors table.

// Authors
SchemaBuilder.create('authors', function (table) {
  table.increments('UserID');
  table.string('FirstName', 150);
  table.string('LastName', 150);
  table.string('Username');
  table.string('Password', 60);
  table.timestamp('CreateDate');
  table.timestamp('ModifyDate');
});

// Posts
SchemaBuilder.create('posts', function (table) {
  table.increments('PostID');
  table.string('Slug');
  table.string('Title');
  table.longText('Body');
  table.integer('ViewCount');
  table.string('Keywords');
  table.string('Description');
  table.timestamp('CreateDate');
  table.timestamp('ModifyDate');
  table.timestamp('PublishDate');
  table.bit('IsDraft');
  table.bit('Active');
  table.unsignedInteger('UserID').references('UserID').onTable('authors');
});

The above examples demonstrate scaffolding out columns of various data types as well as defining incrementing IDs and constraints on another table.

Checking If A Table Exists

if (!SchemaBuilder.hasTable('posts')) {
  SchemaBuilder.create('posts', function (table) {
    // scaffold columns...
  });
}

Dropping Tables

// Drop
SchemaBuilder.drop('posts');

// Drop if exists
SchemaBuilder.dropIfExists('authors');

Altering Columns

// Add
SchemaBuilder.alter('authors', function (table) {
  table.addColumn(table.boolean('active'));
});

// Drop
SchemaBuilder.alter('authors', function (table) {
  table.dropColumn('active');
});

// Modify
SchemaBuilder.alter('authors', function (table) {
  table.modifyColumn('name', table.string('firstname'));
});

// Rename
SchemaBuilder.alter('authors', function (table) {
  table.renameColumn('name', table.string('firstname'));
});

Populating

We can then use QueryBuilder to populate the created tables.

// Author
QueryBuilder.from('authors').insert(
  (values = {
    firstname: 'John',
    lastname: 'Doe',
    username: 'jdoe',
    password: 'tvBvOpODv4BiPGwCcPFeenYIVFis6LuDgqX',
    createdate: now(),
    modifydate: now()
  })
);

// Post
QueryBuilder.from('posts').insert(
  (values = {
    slug: 'test',
    title: 'Test',
    body: 'This is a test.',
    viewcount: 0,
    keywords: '',
    description: '',
    createdate: now(),
    modifydate: now(),
    publishdate: now(),
    isdraft: 0,
    active: 1,
    userid: 1
  })
);

Retrieving Data

QueryBuilder.from('Posts').join('Authors', 'Authors.ID', '=', 'Posts.AuthorID').get();

What's Next?

Refer to the Official QB Docs for more examples.

To further see FW/1 & QB in action, you can check out my example application on GitHub.

Cheers.

© Copyright 2026 • A Rogue Creative Project 🥃