Categories
Code Wordpress

Modify WP_Query to use custom fields with custom post types

Most if not all of the WordPress sites that I build these days use Custom Post Types with Custom Fields.

I use the excellent CPT UI and Advanced Custom Fields plugins for these. I highly recommend them.

I’ll often want to change the way that WordPress displays these posts, which can be problematic. An easy way would be to add a new query to the page template, and discard the page’s main query object.

Not much of an issue, but it’s an extra and un-needed hit on the database.

A better way is to hook into that main query, and make your adjustments there.

Of course WordPress provides a hook for this, pre_get_posts, which is trivial to implement.

Ordering a custom post by a custom field

Let’s say we want to create an Event custom post. We’ve looked at Event Manager, an excellent plugin but overkill for our situation.

We use CPT UI to create the new Event CPT, and ensure that Has Archive is TRUE to enable the list of events to display.

Next, we use ACF to create a custom field called Event Date which uses a datepicker field type, and set it to display only on posts of type Event. And from ACF’s documentation: “If you intend to use this field to order your posts by, please leave the format as yymmdd.” This is exactly our situation so we leave the defaults.

So far, so good. We can create new events, set the date and view them on the site (after visiting the Permalinks page, of course).

However, WordPress displays the post in the order of publication, where we want them to display in the order of the Event’s date. Step in pre_get_posts.

Before we write the function, we also remember that this should only work on the site’s front end (i.e. not on the admin screen), and for the page’s main query only.

We then want to order the posts by the event_date from earliest to latest, i.e. Ascending.

Therefore:

function theme_event_cpt_orderby_event_date_custom_field($query)
{
    if(!is_admin()                                       // don't run on admin pages
       && $query->is_main_query()                        // target the main query only
       && $query->query_vars['post_type'] == 'event'     // run only for the Event post type
    )
    {
        $query->set('meta_key','event_date');            // choose this custom field
        $query->set('orderby', 'meta_value_num');        // order by the custom field specified
        $query->set('order', 'ASC');                     // order Ascending (low to high)
    }
}
add_action( 'pre_get_posts', 'theme_event_cpt_orderby_event_date_custom_field' );

Voila, our Event archive page now sorts events by the event date, making much more sense for our users.

WordPress provides more methods for even finer grained control. Thes methods and their arguments are detailed on the WP_Query documentation page.

Categories
Code News

WordPress Plugin for BugMuncher.com

Last week an email from Matt at BugMuncher dropped in my inbox.

BugMuncher is a tool for website owners to get quality feedback from their users. It places a small tab on the page, and users can send back information and even screengabs.

Matt wanted to make integrating BugMuncher in a WordPress site a simple and easy task.

I provided a simple configuration screen. It asked for the public API key, and users can also customize the text, colours and screen position of the widget. As long as the API key appears and is valid, the widget ‘just works’.

I also uploaded it into the WordPress plugin SVN repository for him.

It’s available now (and free!) on the WordPress plugin directory.

Categories
Code

Family Fortunes: Saving and Updating Laravel Relations

Although I’ve been using Laravel heavily for over two years, I still find things there that I didn’t really know about.

Today I took some time to really understand all the different ways of saving and associating related models. I had misunderstood the docs at first, but after some searching and playing with test code I’ve come to a better understanding.

One-To-Many and Belongs-To Relationships

To illustrate I’ll use an example Parent and a Child class. The docs used Post, Comment, User and Roles which threw me off, so I hope that my naming convention is clearer.

class Parent extends Model
{
  public function children()
  {
    return $this->hasMany(App\Child::class);
  }
}

class Child extends Model
{
  public function parent()
  {
    return $this->belongsTo(App\Parent::class);
  }
}

I always use plurals for hasMany() relationships as a reminder to myself how it goes.

The first example is saving a Child object to an existing Parent:

// Find an existing Parent record
$parent = App\Parent::find(2);

// Creates a new Child instance 
$child = new App\Child(['name' => 'mei']);

// effectively sets $child->parent_id to 2 then saves the instance to the DB
$parent->children()->save($child);

If called repeatedly, the above code will continue to save Child records with the same Parent.

So far so good. We now want to do the reverse of the above.

Let’s say we want a new parent for our child (programmatic adoption?):

// a new Parent object
$parent = App\Parent::find(31);

// updates $child->parent_id to 31
$child->parent()->associate($parent);
$child->save();

Our Child had parent_id of 2. Above, associate set the parent_id property of $child to 31, then $child->save() persists the change to the database.

Of course, it’s possible to for an object to have more than one parent relation (for example, a Comment can belong to a Post and an Author). As the association is only confirmed when save() is called, we can simply call associate with another parent object.

$parent = App\Parent::find(31);
// a new parent! 
$anotherParent = new App\AnotherParent(['name' => 'steve']);

// associate with our parent, as above
$child->parent()->associate($parent);

// set the second relationship 
$child->anotherParent()->associate($anotherParent);
$child->save();

Assuming that foreign keys are set, calling $child->save() before setting the second relationship would throw an error.

Now that the relationship is set, it’s also possible to sever it.

// sever the parent-child relationship
$child->parent()->dissociate();
$child->save()

In this example $child->parent_id is set to null. The save() method must be called to persist the change to the database.

Again, if foreign keys have been set an error would be thrown here. This code would only work if the foreign key (parent_id) is nullable.

Many-To-Many Relationships

Let’s move on from Parent and Child.

To illustrate the Many-To_-Many relationship we’ll use Post and Tag. These two are connected with the belongsToMany() method.

class Post extends Model
{
  public function tags()
  {
    return $this->belongsToMany(AppTag::class);
  }
}

class Tag extends Model
{
  public function posts()
  {
    return $this->belongsToMany(AppPost::class);
  }
}

A Post can have zero or more tags, and a Tag can have zero or more posts (in theory anyway, an unused tag is mostly pointless).

Such a relationship allows us to get all tags attached to a Post, and conversely get all posts attached to a Tag.

Unlike one to many relations, both records must be persisted before the following to work. The pivot table must have both ids.

In this example, a tags are added to a post.

$post = App\Post::find(3);

$tag = App\Tag::find(47);

$post->tags()->attach($tag->id);

The first argument of attach() is an id, but an Eloquent model can also be passed:

$post->tags()->attach($tag);

What happens above is that the pivot table gets a new record, with post_id set to 3 and tag_id to 47.

If your pivot table has extra fields, these can be set by passing an array as the second argument. The parent (in this case Post) model’s timestamp can be touched by passing in true as a third argument:

$post->tags()->attach($tag, ['expires' => date('Y-m-d H:i:s')], true);

To sever the relationship, detach() is used.

A single id or an array can be passed to detach(), or called without any arguments which detaches all of the parent’s relations.

// detaches $tag from $post
$post->tags()->detach($tag->id);

// detaches tags with the following ids from $post
$post->tags()->detach([41, 52, 54]);

$post->tags()->detach();

The first and second methods deletes rows corresponding to the $post with the given tag ids. The last method deletes all rows from the pivot table with that $post’s id.

Finally, the sync() method is provided to make life just a bit easier for us.

Let’s assume we’ve edited the post, and have remove one tag and attached another.

$post = App\Post::find(4);

// $post has tags with ids: 32, 45, 47

// tag 32 is no longer required, and we need to add tag with id 86

// so instead of this:
$post->tags()->detach(32);
$post->tags()->attach(86);

// we do this
$post->tags()->sync([45, 47, 86]);

Here the new tags [45, 47, 86] will be persisted, and tag 32 will be deleted from the pivot table. This also removes any logic for finding out which tags already exist for the post which are not meant to be removed.

Conclusion

Having spent time playing with this I have a much better appreciation of how it works.

Again, Laravel and Eloquent provide an elegant interface to the database, and lets us write readable code.

I’ve picked up a few points here myself, and I’ll be taking a look back at a few of my own projects to see where I can improve them.

Categories
Code

Fixing a WordPress pagination 404 error

I’ve recently had a load of WordPress work.

Much of these jobs are similar, so I’ve had a good chance to try and learn more about better theme and plugin design.

One of these sites had a strange error when paginating the posts page. Permalinks were set to Day and Name, with the category base set to news and blog index set to the news page.

This creates the illusion of a standard site with all news namespaced under /news.

Calling mysite.com/news gave the first page of posts, using the index.php template file. Calling mysite.com/news/my-category/ gave the latest posts within that category, and pagination worked correctly when calling mysite.com/news/my-category/page/2.

But, annoyingly, calling mysite.com/news/page/2 gave a 404 error.

I googled far and wide but could not find an exact match to my problem (although it seems to be common with custom post types).

Changing permalinks back to the default setting worked, so I left it at that so I could dig deeper.

After some googling I found a recommendation to use the Debug This plugin. It gives detailed breakdowns of debugging data on any page or post. I was only interested in the rewrite rules so this is as far as I got with it.

Using Debug This to list the rewrite rules, I could see which one was being matched and what the redirect was doing in the background. It’s done with the Debug This menu item, and selecting Query > Rewrites.

When I called mysite.com/news this was the output:

Matched Rule: (.?.+?)(/[0-9]+)?/?$
Matched Query: pagename=news&page=

Calling mysite.com/news/my-category/ gave:

Matched Rule: news/(.+?)/?$
Matched Query: category_name=my-category

And mysite.com/news/my-category/page/2 gave:

Matched Rule: news/(.+?)/page/?([0-9]{1,})/?$
Matched Query: category_name=my-category&paged=2

Strangely mysite.com/news/page/2 did not match any rules. So the quickest option was to add my own rule to get things going again.

The third matched rule above is close to what I want, just without the category part of the url. So news/page/?([0-9]{1,})/?$ should work OK, provided that it was checked before the other rules.

In functions.php I added the following code:

function mg_news_pagination_rewrite() {
  add_rewrite_rule(get_option('category_base').'/page/?([0-9]{1,})/?$', 'index.php?pagename='.get_option('category_base').'&paged=$matches[1]', 'top');
}
add_action('init', 'mg_news_pagination_rewrite');

This simply adds a rewrite rule for pagination of the category_base value.

I added top as a third argument to insert the rule at the beginning of the list. It would otherwise match news/(.+?)/?$ (as it did originally) where it would throw a 404 error after failing to find a page category.

I’ve purposely used get_option('category_base') instead of news here too, so you can copy and paste into your own functions.php file.

The Source of the Problem

After writing this post I tried to replicate this on a fresh install, to see if it’s a problem within WordPress or not.

Setting the blog index to a static page, e.g. blog, works fine. Posts are viewed under /blog and pagination works automatically, following the standard blog/page/2 pattern.

The problem arose as I had the category base set to news.

The WordPress rewrite rule list has the category rewrites at the beginning, followed by tags, comments, author, posts by date etc, then finally the wildcards for pages. You can view a sample full list of rules on the WordPress plugin API pages.

In this case news was right at the top, so the match was made before reaching the page wildcard rules.

This is how it’s meant to happen, and my trick to imitate a sub directory of news would not work by design.

However, I think that the solution above is valid.

Categories
Uncategorized

Deck and Shed foundations – How many points in a grid?

I do a lot of DIY at home.

My house is an old stone farmhouse, built in about the 1870s. As with all old houses, there’s plenty of work to do. And with a large garden/ex farm yard, there’s enough work outside to keep me occupied for years.

The next project is to clear some of the old stone walls at the back of the property. There is about 25 meters, much of which has collapsed. I’ve tried to carry out repairs, but dry stone walling is an art that cannot be learned in a few hours. So the walls have to go to be replaced with grass and decking.

Decking needs a good foundation, so I spent some time looking into this. As I also need a larger shed, I wondered if I could use the same solution for both.

I came across deck blocks. These are heavy concrete blocks that can support either an upright 4″ x 4″ post, or a 2″ beam laid across.

To calculate the price I needed to know how many I needed. To know that I made some sketches. I assumed that a block was needed for every meter of joist.

Decking foundation sketch

I quickly realised that this was a mathematical problem, so in order to further waste time, I decided to try and find an equation for any grid of x, y dimensions.

My first step was to verbosely write out some ideas on how it could work.

Scribbles

The first thing to draw my attention were the squares. A square, i.e. a grid where x=y, always had the same amount of blocks as (x + 1)2. So a square of 3 x 3 would have 16 blocks, or 42, 4 x 4 has 25 (52) and 5 x 5 has 36 (62).

While this is all very nice, it wasn’t useful for any scenarios where x != y, so I did not pursue.

As you can see from the notes, I eventually got to this equation:

4 + (2(x-1)) + (2(y-1)) + ((x-1)(y-1));

The 4 is the first square, it will always have 4 blocks.

The second part is the rest of the bocks on the x axis. Each will have 2 blocks, so as the first one (4) is accounted for, we multiply 2 with one less than x, or 2 * (x - 1). It’s the same for the y axis in the third part.

And the fourth part then counts all the 1 squares that are left. They all have just one block each, and their block would be one less than the total x or y, which gives (x-1)*(y-1).

Opening up* the equation gives

4 + 2x - 2 + 2y - 2 + xy - x - y + 1

Which simplifies to

x + y + xy + 1

Which does look nice, in my opinion.

I tried to search for other writings on this but with little success – surely I’m not the first to discover this? If you know of someone who’s done this properly I’d be grateful to know. Leave a message in the comments.

All I have to do now is get the shed up!

  • My maths vocabulary has diminished since my A-level
Categories
Code

Javascript 6 Nations’ Sweepstake Calculator

I do a bit of voluntary work for CR Caernarfon, my local rugby club.

In 2014 we organised a 6 Nations sweepstake to a bit of fun and raise some money for charity. If you’re not aware of the 6 Nations championship, it’s a springtime rugby competition between England, France, Ireland, Italy, Scotland and Wales. They play each other once and a winner is decided at the end.

Players are invited to guess the score for each match plus the number of tries. They’re also asked to predict the final table.

There are six point-scoring rules for the sweepstake, and with 15 matches (n(n-1)/2) it can take a few hours to calculate the points for each person. It was therefore a ripe programming challenge.

I coded a simple solution in javascript last year, but didn’t get used as there was no easy way for the organizer (hi, mum!) to input results. So I updated it for this year and it works well. Checkout the latest results.

The rules for individual matches are:

  1. 1 point for either predicting the correct winning team or predicting a draw for each match.
  2. 1 point for the correct score of each team in each match (max of 2).
  3. 1 point for the correct points difference between the teams in each match.
  4. 2 points for predicting the total number of tries (including penalty tries) in each match.

Maximum available points are 6 points for each match.

What I Did

(Note: the code is on github.)

First off I asked the organizer to input all data into a spreadsheet. Participants’ details were listed, along with their predictions for each match. Each match has a home score, away score and try count. This sheet was then exported as a CSV file.

D3.js is used to read in this data. I’ve used D3 on the Wales Yearbook so have a good grasp of its features. It has a csv method, d3.csv(url[[, accessor], callback]) which issues a GET request for the file, processes each row and then passes the result to a callback.

Using this method I turned the CSV data into a JSON structure for each participant:

return {
    // enw = name 
    // and anyone who's been involved in rugby will know the importance of nicknames!
    person : { name: d.enw1+' '+d.enw2, nickname : d.nickname, email : d.ebost },
    rankings : [d.finalFirst, d.finalSecond, d.finalThird, d.finalFourth, d.finalFifth, d.finalSixth],
    games : [
        {
            home: +d.game1Home,
            away: +d.game1Away,
            tries: +d.game1Tries
        },
        /* another 13 match results */
        {
            home: +d.game15Home,
            away: +d.game15Away,
            tries: +d.game15Tries
        },
      ]
    }

This object is added to an array which is then passed to the callback.

But before I discuss that, I’ll demonstrate the API created to calculate the scores.

A Sweepstake javascript object holds all the data and does the calculations. Briefly:

var Sweep = {

    // array of participants
    players : [],

    // array of match results
    results : [],

    // a singleplayer object
    singleplayer : {},

    /**
     * Add a player to the object
     * 
     */
    addPlayer : function(name)
    {
        this.singleplayer = { name: name, matches: [] };
        this.players.push(this.singleplayer)
        return this;
    },

    /**
     * Add a match prediction. This must be chained to a player via addPlayer()
     * 
     */
    addScore : function(home, away, tries)
    {
        this.singleplayer.matches.push( {
            home : home,
            away : away,
            tries : tries
        });

        return this;
    },

    /**
     * Add a match result
     * 
     */
    addResult : function(name, home, away, tries)
    {        
        this.results.push({
            name: name,
            home : home,
            away : away,
            tries : tries
        });

        return this;
    },

    /**
     * Caculate and store the scores
     * 
     */
    calculate : function()
    {
        // loop through each player, calculate and store the points for each match
    },

    /**
     * Display the scores in a table
     * 
     */
    showResult : function()
    {
        // format the results in a table
    }

} 

Using this object we can pass in each participant’s predictions and the final match results. Using rows passed by the D3.csv callback:

for(var i in rows)
  {
    var player = rows[i];

    var result = Sweep.addPlayer(player.person.name);
    for(var j in player.games)
    {
        result.addScore(player.games[j].home, player.games[j].away, player.games[j].tries);
    }
  }

Each participant is now stored in the Sweepstake.players array.

Next up is to add the match results:

    // results from the 2015 first weekend
    Sweep.addResult('Cym v Llo', 16,21,3) // Wal v Eng
            .addResult('Eid v Iwe',3,26, 2) // Ita v Ire
            .addResult('Ffr v Alb',15,8,1) // Fra v Sco

And then still within the callback we call Sweepstake.calculate() and Sweepstake.showResult().

The calculate method loops through the players and checks their scores with the results:

// for each match
// individual points breakdown of points allocation
player.matches[j].points = {winner:'',score_home:'',score_away:'',diff:'',tries:'',total:0}

// match points difference
result.pd = Math.abs(result.home - result.away);

// match winner prediction
// Home Win
if( (player.matches[j].home > player.matches[j].away) && (result.home > result.away) 
||  // OR away win
(player.matches[j].home < player.matches[j].away) && (result.home < result.away) 
|| //  OR draw
(player.matches[j].home == player.matches[j].away && result.home == result.away)
)
{
player.matches[j].points.winner = '1'; // a string to be outputted in html
player.matches[j].points.total++; // final score as an integer
}

// compare with player's predictions

// home score
if(result.home == player.matches[j].home) 
{
player.matches[j].points.score_home = '1';
player.matches[j].points.total++;
}
// away score
if(result.away == player.matches[j].away) 
{
player.matches[j].points.score_away = '1';
player.matches[j].points.total++;
}

// prediction points difference
player.matches[j].pd = Math.abs(player.matches[j].home - player.matches[j].away);             
if(result.pd == player.matches[j].pd)
{
player.matches[j].points.diff = '1';
player.matches[j].points.total++;
}

// try count, two points
if(result.tries == player.matches[j].tries)
{
player.matches[j].points.tries = '2';
player.matches[j].points.total = player.matches[j].points.total + 2;
}

// total player score
player.points = player.points + player.matches[j].points.total;

The Sweepstake.showResult() is simply a method to output the above in a table row, and then render the full table on the page.

In order for participants to check their individual results, I added a click event to their name which opens a Bootstrap modal window. It gives a breakdown of their points for each match.

breakdown

This is a simple jQuery click() method to listen for the click on the player name, and then show the table of results in the modal.

Conclusion

What took hours now takes seconds to calculate, and results are updated on the club website after each round’s final game.

My main issue is that this is updated by editing the HTML file (with Sweepstake.addResult()) and uploading via FTP. I’d love to provide a way so that I can add scores off my smartphone, but this would call for back end programming, data stores and authentication etc. I will probably look at this for next year.

Finishing Off

As usual time constraints has stopped me in adding the rules for the final table, but as the competition has still not ended (in three weeks’ time, as of time of writing) I still have time for this. I’ll update this post accordingly.

All the code is available on my github repo.

Categories
Code

WordPress Bootstrap Plugin: Bootstrap Column widget

Download Bootstrap Column from WordPress Plugins, or fork Bootstrap Column widget on Github.

WordPress Bootstrap Plugin

Although I’ve built a few custom WordPress plugins over the years, this is the first I’ve released publicly.

bootstrap-column

Bootstrap Column simply adds a widget which nicely works with Bootstrap v3 based themes.

It’s not dissimilar to the standard WordPress textbox widget, but it offers extra Bootstrap v3 layout functionality.

Those familiar with Bootstrap will quickly understand how it works.

Bootstrap offers a 12 column grid within four break points at which to change the layout. These breakpoints start from mobile first:

  • Extra Small – for smartphones at less than 768px screen width
  • Small – for tablets between 768px and 992px
  • Medium – for larger tablets and small desktops between 992px and 1200px
  • Large – for everything else above 1200px

Bootstrap provides a set of classes which not only control column width, but also their behaviour at these different screen sizes.

For example, take the following <div class="col-lg-4 col-md-6">.

Both Small (sm) and Extra Small (xs) are left out, so Bootstrap will default this div to a full width layout, i.e. 12 columns.

At the Medium (md) breakpoint the div will stretch to 6 columns, or half the page width. At the Large (lg) breakpoint it will only be four columns in width.

Ideally it would be partnered with another div with this class value <div class="col-lg-8 col-md-6">. At Large this gives you two columns at 1/3 and 2/3 page widths. At Medium it gives two equally sized columns.

These CSS features are very powerful and enables very quick layouts and prototyping.

Hopefully my plugin will make things a bit easier for other Bootstrappers.

Poor Code

Working on the plugin has really highlighted the standard of coding in WordPress. Although it’s a quick way of adding widgets, the code’s structure is not to be copied.

The widget framework is a quick and easy way to create plugins, but very messy and has little in common with the SOLID principles of programming.

For example, the single responsibility principle is ignored here. The class that extends the plugin class is responsible for saving and updating the widget’s contents and settings, and also rendering the HTML for both the front and back ends.

A nice templating system would be a tidy addition to the current way of doing things.

Categories
Code

Developing DymaFi.tv with Symfony2

On June 22nd the DymaFi.tv website officially launched.

UPDATE: DymaFi.tv was nominated for the Kieran Hegarty Award for Innovation at the 2014 Celtic Media Festival!

Dyma fi.tv is a website which gives a platform to young people between the ages of 13 and 18. They can upload videos and share a part of their lives as part of the Children’s Commissioner for Wales’ Dyma Fi/See Me project. The aim of the project is to give a realistic picture of young people in Wales today – in their own words.

Phil Stead from Cwmni Da brought me on the project to head up the technical side.

This is the biggest project that I have worked on, and possibly the most stressful. But I’m extremely proud of what was produced and how it worked.

Symfony2

DymaFi.tv was built as a custom bundle on the Symfony2 framework. Although I’d read much on S2 beforehand, it was the first time I’d actually had the chance to use it for real.

We did briefly discuss using Drupal or WordPress, but I was sure that neither these would not have been the correct solution. I’ve used much of both over the years, and ploughing through their ageing codebases did not attract me.

I was pretty confident that Symfony2 would be straightforward to pick up, but it took me much longer than I had anticipated. While frustrating that I knew what I wanted to do, just not the way Symfony2 wanted to do it (!), learning the ‘proper’ way of developing software was a worthwhile journey. Having a much better understanding of how software is structured make developing new projects much easier.

What I really came to appreciate about Symfony2 was the way in which database tables are defined as entities, and their relationships are hardcoded into the files. Whilst difficult to understand at first, I now miss the solid relationships between the entities, forms and the data they controlled. One of my recent projects was a simple plugin for WordPress, and not having a solidified relationship between database and code is limiting and feels so old fashioned.

FrontEnd

The front end was designed by the hugely talented Iestyn Lloyd, using the Bootstrap (version 2.x) CSS framework. I chipped in with some of the HTML and CSS, but my main work on the frontend was the coding. jQuery is in there as well as Three.js for controlling the zooming videos.

Vimeo API Development

As the site was for uploading and showing video, we decided to use Vimeo’s platform. Each uploaded video was sent to Vimeo via their API for encoding. It was then simple to use their player to showcase the submitted videos on the site. I used Vimeo’s PHP library on Github, but did end up making small improvements.

Version Control and Deployment

Github was used for versioning and deployment. I wrote a bash script on the server for deployments, and this was ran manually whenever we needed an update. Me and Iestyn have overwritten each other’s files on many projects, So Github was a welcome change. Iestyn even managed to overcome his deep rooted fear of the command line!

Testing

The site was hosted on Digital Ocean’s servers. The site handled all out uploading tests admirably, and this is no doubt thanks to Symfony2’s solid architecture (although Digital Ocean’s solid state hard drives helped!).

To my shame no unit test were designed. This was my first SYmfony2 app, and I had often read that unit test were not advised on first projects.

Conclusion

I would have liked to develop a simple Page CMS, but time did not allow it. Static pages were updated through the usual deployment process. One of Symfony2’s main problems is that although Composer works brilliantly, finding the correct version numbers for the dependencies was a pain.

I also struggled with getting Sonata Admin to work, although when it finally caved in it worked well.

Unit Tests would have improved development, but again time was against me.

In redoing the project, I would like to have had a proper development server and a better way of deployment. But with having to engineer all levels of functionality (server install/config, backend, frontend) on the project there simply wasn’t time.

All in all I enjoyed the learning experience and look forward to using Symfony2 again. This time the learning curve will not be quite as steep.

Categories
Opinion

In Defence of QR Codes

It’s generally agreed upon that QR codes suck.

qr code
QR codes can be used for good, not just an inconvenient way of typing URLs.

Of the forums I frequent, many commenters opine the view that they suck and should no longer be used. There are even [tumblrs dedicated to doing this](http://wtfqrcodes.com/).

Whilst I agree with the vast majority, I do think that QR codes do have their place, it’s just that their place is not sending people to websites ([especially if they’re already on that website](http://wtfqrcodes.com/post/38133468630/audible-sigh-via-rebekahcancino)).

Whilst working at Antena TV we devised a solution to the inventory problem. The girl looking after the camera kits, sound kits, stands and booms would quickly lose track of who had what. Kit would disappear, then randomly reappear with no inkling of who had used it. This was a problem, and she quickly needed a solution.

I already had an idea of an app to keep tabs on it all, but sitting down to check boxes on a screen was not the most efficient way of recording an item being checked out. My then IT manager Simon Beech suggested we use QR codes on each item, and use the company iPhones to scan them.

And it worked out pretty well!

I built a simple CRUD app on CodeIgniter. The main feature was the check in/out. Each piece of kit had an URL which toggled it’s in/out status, who was doing the checking and the timestamp. All phones installed a QR reader and the staff briefed on how to use it.

So the whole inventory could be checked at a glance on a phone or computer. We knew if an item was in or out, and who had done the checking in/out.

The girl in charge of inventory was thrilled. It wasn’t as popular with the crew – they now had to up their accountability levels.

Categories
Tips

Quick and Easy Documentation With Awesome Screenshot

Many of the sites I build are based on Drupal or WordPress, which are then turned over to be managed by the client.

While documentation for these systems exist, I always need something customized for the individual site and client. In the past I’ve used Photoshop and OpenOffice to produce a document with instructions and screenshots.

This is fine, but does take a while to produce. So I was glad to find the Awesome Screenshot Chrome plugin.

It not only takes a full screen screenshot, but then gives you a simple image editor where you can add squares and circles, arrows and text.

Here’s a quick example I made using the WordPress post edit page:

Awesome Screenshot
Awesome Screenshot