Playlistr: Albums

Let's add an Album feature to our application and introduce some new ideas.

Albums

Let's start with another simple CRUD feature. This time to create, read, update and delete an Album in our application. We're going to introduce a few new ideas with this one.

This time we'll use qb inside our commandbox-migrations script rather than raw SQL.

Migrate Create

If you remember, we created our commandbox-migrations script in the terminal. We can do it again with migrate create albums

With the script template we can add our qb functions to build our table schema.

component {

	function up( schema ) {
		schema.create( "albums", function( table ) {
			table.increments( "id" );

			table.string( "name" );
		} );
	}

	function down( schema ) {
		schema.drop( "albums" );
	}

}
resources/database/migrations/2018_07_02_100000_albums.cfc

With qb we have access to closure methods that can create and drop a table as well as add columns definitions. We pass the schema from qb into our up() and down() methods to give us access to the features of qb.

The create() closure takes the name of the table we want to create, "albums", and a function to define the columns in that table. The increments() method adds an auto-incrementing integer primary key and string() adds a varchar(255) string column to the table.

In the down() method we'll use drop() to delete our "albums" table.

Album.cfc (Model)

This is the same as the Artist entity and saved as models/Album.cfc

index.cfm & createUpdate.cfm (Views)

These are also the same as those for the Artist.

Next we create our Album service and handler.

Service & Handler


Album Service

Now let's introduce the idea of interacting with our database via a Service component. Add the file models/AlbumService.cfc to your webroot. The start of this Service component should start by injecting WireBox and include a simple init() constructor method.

component {
	property wirebox inject="wirebox";

	public AlbumService function init() {
		return this;
	}
}
models/AlbumService.cfc

Now we can add new methods to build out the functionality to interface with our database using Quick features.

getByID()

public Album function getByID( required any id) {
	return wirebox.getInstance( "Album" )
		.findOrFail( id );
}
models/AlbumService.cfc

This should look similar to a portion of the update() action from the Artists handler.

getAll()

public array function getAll() {
	return wirebox.getInstance( "Album" )
		.orderby( "id" )
		.get();
}
models/AlbumService.cfc

Once again this should look familiar to a portion of the index() action. The get() method returns the collection back as an array.

create()

public Album function create( required struct values ) {
	return wirebox.getInstance( "Album" )
		.create( values );
}
models/AlbumService.cfc

Another similar method. This time we take in a structure of the name/value pairs and pass that on to populate a new row in our database.

update()

public void function update( required any id, required struct values ) {
	getByID( id )
		.update( values );
}
models/AlbumService.cfc

Here we chain the update() onto the end of the getByID() method we wrote earlier and pass on the structure of names/values to update an existing row in our database.

delete()

public void function delete( required any id ) {
	getByID( id )
		.delete();
}
models/AlbumService.cfc

Similar to update(), we chain to the end of getByID() to delete an existing row from our database.

Album Handler

With our new AlbumService we can simplify our handler. So first create your handler handlers/Albums.cfc and start with:

component extends="coldbox.system.EventHandler" {
	property AlbumService inject="AlbumService";
}
handlers/Albums.cfc

We've added property AlbumService inject="AlbumService"; to the component pseudo constructor to inject the AlbumService into the handler using the WireBox convention.

Action: index()

function index( event, rc, prc ) {
	prc.albums = AlbumService.getAll();

	if( prc.albums.len() == 0 ) {
		relocate( "albums.create" );
	}
}
handlers/Albums.cfc

We capture all the Albums into the variable prc.albums for our view with the getAll() method in the AlbumService component but bounce the visitor onto the create action if none are returned.

Action: create()

This is the same as Artists.

Action: createAction()

function createAction( event, rc, prc ) {
	param rc.name = "";

	AlbumService.create( {
		name: rc.name
	} );

	relocate( "albums" );
}
handlers/Albums.cfc

This is similar to the Artists action but it now uses the AlbumService to create the database row.

Action: update()

function update( event, rc, prc ) {
	param rc.id = "";

	var album = AlbumService.getByID( rc.id );

	prc.name = album.getName();

	prc.action = "Update";
	prc.formAction = "albums.updateAction";

	event.setView( "albums/createUpdate" );
}
handlers/Albums.cfc

Once again this is similar to the Artists action but it now uses the AlbumService to get our Album for updating.

Action: updateAction()

function updateAction( event, rc, prc ) {
	param rc.id = "";
	param rc.name = "";

	AlbumService.update( rc.id, {
		name: rc.name
	} );

	relocate( "albums" );
}
handlers/Albums.cfc

Again, like the Artists action we use the AlbumService to update our Album by passing it the id and name/value structure of the columns to be updated.

Action: delete()

function delete( event, rc, prc ) {
	param rc.id = "";

	AlbumService.delete( rc.id );

	relocate( "albums" );
}
handlers/Albums.cfc

Finally the delete() action uses the AlbumService to removed the selected row from the database.