Making CakePHP Templates in TextMate

I finally got around to exploring templates in the TextMate bundle editor. The PHP bundle does not have any templates, and so I set out to create a few to improve my workflow. I thought it would be helpful information for PHP developers using TextMate. I actually used the Ruby interpreter to power my templates, but you can definately use PHP, or any other interpreter you like (Python, Perl, etc.). You might want to check out Environment Variables in the TextMate manual.

For the impatient: download the bundle. You should go through this post and make sure you set up shell variables properly. They are useful and worth understanding.

Most of my PHP development is done using the CakePHP framework, and I prefer to create my own files rather than using the Bake console. Just a personal preference. My templates are already proving to save time I would spend copying/pasting code.

Automating the creation of templates will save you time. Expensive time that you don’t need to waste on repetitive stuff. You can even modify the existing templates provided in the bundle editor. I know virtually every HTML document I create will need at least one stylesheet, meta data, and scripts.

The Bundle Editor

The bundle editor provides amazing functionality to TextMate. I’m convinced that the reason I work on a Mac is TextMate and the many customizable, automated features like bundles ;).

To get our bundle started, fire up the bundle editor in Bundles > Bundle Editor > Show Bundle Editor. You could also use the shortcut, Control + Option + Command + B (the bottom three modifier keys + B). The bundle editor allows you to create snippets, macros, drag commands, templates, etc.
TextMate Bundle Editor

Creating the Bundle and Template

Templates require a few things to work. Firstly, a script that actually processes the template, allowing you to generate dynamic template elements on the fly—you’ll see how cool this feature is in a minute. Secondly, the actual template file that we will populate with our code.

  1. Create a new bundle by clicking the “+” icon on the bottom left of the TextMate bundle editor window and select “New Bundle”. Type in the name of the bundle; mine is called “CakePHP.”
  2. Now that the bundle is created, highlight it, click the “+” icon again, and select “New Template.” Enter the name of your template; mine is “Controller.”
  3. Highlight the template you just created and click the “+” icon one last time. Select “New template file”. It will be highlighted, name it something that matches the template and has the extension you want (in my case “.php”)

cakephp-textmate-bundle

Your bundle should look similar to mine. Now that the template script and template file are created, highlight the template file (mine is controller.php). Paste in the following code, I’ll explain it afterwards:

<?php
/**
 * Description
 * 
 * Copyright ${TM_YEAR}, ${TM_ORGANIZATION_NAME}. All Rights Reserved.
 *
 * @author ${TM_FULLNAME} <${AUTHOR_EMAIL}>
 * @created ${TM_DATE_FULL}
 */
class ${TM_CLASSNAME} extends AppController {
	/**
	 *
	 */
	public $name = '${TM_NAME_PARAM}';
	/**
	 *
	 */
	public $helpers = array();
	/**
	 *
	 */
	public $components = array();
	
	## Public Actions
	
	/**
	 *
	 */
	public function index() {}

	/**
	 *
	 */
	public function view($id=null){}
	
	## Admin Actions

	/**
	 *
	 */
	public function admin_index() {}

	/**
	 *
	 */
	public function admin_edit($id=null){}

	/**
	 *
	 */
	public function admin_delete($id=null){}

}

In my controller template, you will notice some dynamic elements and various actions/params that I use frequently in almost every controller.

The actual class name is generated by the file’s basename (the files name minus the extension) and is called ${TM_CLASSNAME} in the template. The basename variable is automatically available to templates. Those of you familiar with CakePHP will know that controller filenames use underscores. So the “UsersController” class file would be “users_controller.php” and the basename would be “users_controller”.

The template contains a variable called ${TM_FULLNAME} which is generated by the full name of your TextMate licence. You can see this name by clicking TextMate > Registration from the menu. The field is called “Owner”.

The other variables will be created in the template script and through shell variables (Section 9.2). Shell variables are reusable variables assigned in the preferences. These are really cool, because you can use them throughout the TextMate application. You can also create project-specific shell variables in your TextMate projects. Don’t think that they merely apply for this template only!

Let’s go ahead and create/update the shell variables this template will use:

  1. Navigate to TextMate > Preferences > Advanced and select Shell Variables.
  2. You should see a built-in shell variable called TM_ORGANIZATION_NAME with a made-up name. Edit the value of this field with the organization you want to appear in the block comment.
  3. Create a new shell variable called “AUTHOR_EMAIL” and put in your email.

TextMate shell variables

You can now use ${TM_ORGANIZATION_NAME} and ${AUTHOR_EMAIL} in your templates.

Template Script

The template script is where the real magic happens in our template. Select the template script of your bundle, and you will see a few fields. Update the fields with the following (see source code below image to copy):

Controller bundle script

Here is the source for the Command(s) field:

#!/usr/bin/env ruby -wKU

# METHODS
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
	if first_letter_in_uppercase
         lower_case_and_underscored_word.to_s.gsub(/\/(.?)/)
             { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
    else
    	lower_case_and_underscored_word.first.downcase + 
            camelize(lower_case_and_underscored_word)[1..-1]
    end
end

f = open(ENV["TM_NEW_FILE"], 'w')
template = open("controller.php").read

ENV["TM_YEAR"] = `date +%Y`.chomp
ENV["TM_DATE"] = `date +%Y-%m-%d`.chomp
ENV["TM_DATE_FULL"] = Time.now.strftime("%m-%d-%Y %I:%M %p")
basename = ENV['TM_NEW_FILE_BASENAME']

ENV["TM_CLASSNAME"] = camelize(basename)
ENV["TM_NAME_PARAM"] = camelize(basename.split('_controller').join)

if ENV["TM_SOFT_TABS"] == "YES"
	tab_size = ENV["TM_TAB_SIZE"].to_i
	tab_size = tab_size ? tab_size : 4
	template = template.gsub(/\t/, " "*tab_size)
end

template = template.gsub(/[$]\{([^}]+)\}/){|match| "#{ENV[$1]}" }
f.write template
f.close

Note that I’m borrowing the camelize method from the Rails API

You don’t have to fully understand the ruby script I’m using, but there are a few things I’ll touch on to help you understand what is going on.

  1. I wanted to customize the date to include the exact time the file was created, so I used Ruby’s Time class to create ENV["TM_DATE_FULL"] with the strftime method. I can now use it in my template as ${TM_DATE_FULL}.
  2. To follow CakePHP’s convention of camel-case class names, I use the camelize method to camelize the basename of the file and assign to ENV["TM_CLASSNAME"].
  3. The $name param of a CakePHP controller is the same as the class name, but omits “controller”, so I am splitting the file basename at ‘_controller’ and joining the first part back to a string; this string is also camel-cased using the camelize method.

The names match the custom fields in the template file—remember that they are case-sensitive.

Now when you create a new controller in a CakePHP project you can take advantage of your new template. Make sure all your template settings match my code above. You can test a template by clicking the “Test” button in the bundle editor template script.

We’re done. Create a new file in your TextMate project (Shift + Command + N in a project), select the template we just created from the dropdown, and click “create”.
textmate_new_from_template

I hope this is useful for others, it really improved my workflow. My bundle download also contains a model template too, so be sure to check out the bundle code in your new friend, the bundle editor.

Other Great Textmate Bundles

Some other textmate bundles I really cannot live without:

3 thoughts on “Making CakePHP Templates in TextMate

  1. bake more powerfull for tasks to init your code. you can bake tests and a lot of nice stuff if will write own bake templates

  2. I’ve updated the CakePHP Textmate Bundle to include this sort of stuff in my 1.3 branch and have merged it, with fixes to other items, to master on my repository. I’ve also merged both of the CakePHP bundles (there are two, one official, one not so much) and improved the labeling and grouping. You can download it here:

    http://github.com/josegonzalez/cakephp-tmbundle

    Your post only reminded me that this bundle existed and I hadn’t yet merged the 1.3 branch back into master :P

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>