powered by Slim Framework
Follow @NesbittBrian rss

This blog trimmed the fat - Now powered by Slim 2

Jan 11, 2013

Today is the day! I have been meaning to blog about this for some time now. If you looked at the repo history you would see that this diet actually took place a few months ago. The Slim Framework released version 2.0 on September 13 last year and I was up and running a few days later. Today I will talk about it.

Repo

The repo is located at https://github.com/briannesbitt/nesbot.com. The latest and greatest is in master... there is no fancy branching model and there are no plans to use one.

Again?

Yes this will be the third rewrite of my blog. As you can see at the bottom of the readme the previous versions were done using the Playframework 1.2.3 (https://github.com/briannesbitt/v1.nesbot.com) and Playframework BETA 2.0 (https://github.com/briannesbitt/v2.nesbot.com). A blog is a simple and well understood application, which makes it a good first project when picking up a new language or framework. This time its a new framework as I have been using PHP on and off since the very early 3 days.

Running

If you have PHP 5.4+ installed and have the composer.phar you can be up and running in 10 seconds +/- 5 seconds for slower typers or copy & pasters.


git clone git://github.com/briannesbitt/nesbot.com.git
cd nesbot.com
curl -s http://getcomposer.org/installer | php
php composer.phar update
touch env.local
cd public
php -S 127.0.0.1:80

Local mode

The site begins at public/index.php which only contains a require statement for the app.php. After the composer require the script will attempt to determine the desired mode of the application. First it checks for a server variable and if not found it looks for a particular file. On live the server variable is used so the disk hit can be avoided. It is easily specified using fastcgi_param MODE live; in the nginx location configuration. When developing locally I use the built-in dev server in PHP 5.4+ so the file is used instead. The configuration for local mode in the app.php is very similar to the live config but does some extra asset bundling by executing the bundle.php script. This script will generate the posts, compile/combine/compress the less files, minify/combine the js files. This happens in local mode on each page hit which is handy when you are writing posts and want to refresh to read them and is more than fast enough you don't notice any lag in page render. This only happens on live when deploying which we will walk through later.

Posts

Posts are kept in simple PHP view files. The files use a name format of yyyy-m-d-slug.php and are stored in the views/posts/ directory. The title of the post is read from the first line of the view in a format like <?/*Post title*/?>. The site's datastore is a dynamically generated PHP file, posts.php, with an array of post data. The file is generated by reading all of the post view files, parsing the date, slug and title, sorting them by date and finally writing out the sorted array and necessary code. Adding a post in local mode is as simple as creating a new view file with a valid name, add the title and start typing.

The final piece of generated code creates the Posts object, adds each Post and returns it.


$postData = array();

$postData[] = new Post('A carpenter\'s house is always the last to get the attention it deserves', "carpenters-house-last-to-get-attention", \Carbon\Carbon::createFromTimestamp(1315411200));
...
$postData[] = new Post('Updated ContextSensitiveLoginLogout example to Slim 2.x', "updated-ContextSensitiveLoginLogout-example-to-slim-2", \Carbon\Carbon::createFromTimestamp(1354813200));
$postData[] = new Post('This blog trimmed the fat - Now powered by Slim 2', "this-blog-trimmed-the-fat-now-powered-by-slim-2", \Carbon\Carbon::createFromTimestamp(1357923600));

$posts = new Posts();
foreach($postData as $post)
{
   $posts->add($post);
}

return $posts;

The Post object consists of a title, slug and a posted object of type Carbon.

MasterView and BlogView

I have talked about a Slim MasterView before while setting up a Multilingual site using Slim. The class is based on the same code but this time I add a bit more functionality. Some default template variables are injected for urls and getting the site mode, pretty standard. Another feature added is the ability to render partials. This allows you to include other views without rendering it via the master template again. The partial method includes the default injected variables plus anything specified in the $data = array() parameter. The master template uses this internallly to include the childView and other sections of the template that have been separated into their own file.


public function partial($template, $data = array())
{
   $this->injectDefaultVariables($data);
   extract($data);
   require $this->getTemplatesDirectory().'/'.$template;
}

This project then uses a BlogView class that extends the MasterView we just talked about. It adds some project specific helpers like rendering a post, formatting the date etc.


public function renderPost(Post $post)
{
   $this->partial(sprintf('posts/%s-%s.php', $post->posted->format('Y-n-j'), $post->slug));
}

The other fancier helpers use a callable closure parameter to execute with a post as the context.


public function withPost($slug, $callable)
{
   $callable($this->posts->findBySlug($slug));
}

public function linkPost($slug, $callable)
{
   $post = $this->posts->findBySlug($slug);
   $callable($this->urlFullFor($post), $post->title);
}

These methods are used from the blog post view files.


<?$this->withPost("carpenters-house-last-to-get-attention", function ($post) {?>
   

The post <?=$post->title?> was the one I was talking about.

<?});?> <?$this->linkPost("carpenters-house-last-to-get-attention", function ($url, $title) {?>

The post at the url <?=$url?> with <?=$title?> was the one I was talking about.

<?});?>

Deployment

Deployment of the site is done with a simple shell script. Its easy enough for me to ssh in once in awhile to push. I could set this up with a git post commit hook or at the very least a simple plink script. If it were updated more frequently I would take the extra step to further automate it. The deploy script, which is included in the repo, first creates a new directory based on date +%Y%m%d%H%M%S. It then clones the repo to this new directory and runs a composer install to install the packages from the current .lock file. It does not run an update as I don't want to install any newer packages than what was last tested and saved in the .lock file. It continues by running the site bundle script. Once that is complete it checks for some existing directories to make sure the deploy completed correctly before flipping the switch. Finally it moves the current symbolic link to the new directory which makes the new version live.

If you look at the app.php file you'll notice that the logger is different in local vs live. The local version uses a /logs directory and the live version uses a /../logs. This is so the logs aren't wiped out with every deploy on live and the versions all write to a shared log up one directory from the webroots. You may want to maintain my local setup on live to separate the logs out, but you can always relate the timestamp in the log to the version that was live at the time based on the directory names. Since this site isn't deployed too often the single log directory is prefered.

What about using xyz to make it faster?

The current deployment is more than sufficient for me right now. If I needed to take a next step then I could dramatically decrease server crunching by using a simple front end page cache. You could easily setup a HttpRedis or Memcached cache and have the server only produce the pages on first load since the site is static between posts. You could also off load the whole site to a CDN as well as its fairly static.

Is this the last time?

For now I think it is... but the next time I want to take on a new language or framework you can be sure this blog will get yet another rewrite.

Updated ContextSensitiveLoginLogout example to Slim 2.x  Home Adding a SADDX command to Redis using Lua  
blog comments powered by Disqus