Running a Fantasy League on WordPress (case study)

As you all know by now FIFA World Cup 2014 has started and like many others in the world we (at the office) decide to have a small betting pool but not just “pick your team and put money on it“. We went all out and create our own Fantasy League.

So once we have decided we’re going for a fantasy league we needed a site to manage and run the whole thing and since WordPress is a big part of our lives making the choice of CMS/framework was easy.

 

The plan

To make things challenging we create our own points system which works like this:

player position played Scored Didn’t Score Received
Offense – FW 1 2 per goal -1 0
Midfield – MF 1 1 per goal 0 0
Defense – DF 1 3 per Goal 0 -1 per Goal
Goal Keeper – GK 1 15 per Goal 0 -1 Per Goal

Next we needed to create our data set structure and we decided that the data will be stored as:

  • Player – A Custom Post Type player with a record for each player who is registered to play in the World Cup.
  • Game – A Custom Post Type game with a record for each game played in the World Cup.
  • User Squad – Custom Post Type squad which holds all players that a user selects during the draft.
  • Team – A Custom Taxonomy “team” with a record for each country who plays in the World Cup.

The Code

So to get started we created each of the custom post types and taxonomies this way:

Player Custom Post type

To create the post type I used our Post Type Generator and we get:

// Register Custom Post Type
function register_player_cpt() {

	$labels = array(
		'name'                => _x( 'Players', 'Post Type General Name', 'text_domain' ),
		'singular_name'       => _x( 'Player', 'Post Type Singular Name', 'text_domain' ),
		'menu_name'           => __( 'Players', 'text_domain' ),
		'parent_item_colon'   => __( '', 'text_domain' ),
		'all_items'           => __( 'All Players', 'text_domain' ),
		'view_item'           => __( 'View Player', 'text_domain' ),
		'add_new_item'        => __( 'Add New Player', 'text_domain' ),
		'add_new'             => __( 'Add New', 'text_domain' ),
		'edit_item'           => __( 'Edit Player', 'text_domain' ),
		'update_item'         => __( 'Update Player', 'text_domain' ),
		'search_items'        => __( 'Search Players', 'text_domain' ),
		'not_found'           => __( 'Not found', 'text_domain' ),
		'not_found_in_trash'  => __( 'Not found in Trash', 'text_domain' ),
	);
	$args = array(
		'label'               => __( 'player', 'text_domain' ),
		'description'         => __( 'World Cup players', 'text_domain' ),
		'labels'              => $labels,
		'supports'            => array( 'title', 'thumbnail', 'custom-fields', ),
		'hierarchical'        => false,
		'public'              => true,
		'show_ui'             => true,
		'show_in_menu'        => true,
		'show_in_nav_menus'   => true,
		'show_in_admin_bar'   => true,
		'menu_position'       => 5,
		'menu_icon'           => 'dashicons-id',
		'can_export'          => true,
		'has_archive'         => true,
		'exclude_from_search' => false,
		'publicly_queryable'  => true,
		'capability_type'     => 'page',
	);
	register_post_type( 'player', $args );

}

// Hook into the 'init' action
add_action( 'init', 'register_player_cpt', 0 );

View snippet Clone snippet Download snippet

The Data is save as:

  • Title – Player name (eg: LIONEL MESSI)
  • Featured Image – Player photo, which later we simply pulled an image from Wikipedia’s API.
  • Custom Field – Player Number.
  • Custom Field – Player Position (GK,DF,MF,FW).
  • Custom Field – Player Age.
  • Custom Field – Player Club.
  • Custom Taxonomy team – Player team/country.

All of the data is pulled from Wikipedia, with a simple import function we loaded the site with 736 players.
player post type

Game Custom Post type

To create the post type I once again used our Post Type Generator and we get:

// Register Custom Post Type
function register_game_cpt() {

	$labels = array(
		'name'                => _x( 'Games', 'Post Type General Name', 'text_domain' ),
		'singular_name'       => _x( 'Game', 'Post Type Singular Name', 'text_domain' ),
		'menu_name'           => __( 'Games', 'text_domain' ),
		'parent_item_colon'   => __( '', 'text_domain' ),
		'all_items'           => __( 'All Games', 'text_domain' ),
		'view_item'           => __( 'View Game', 'text_domain' ),
		'add_new_item'        => __( 'Add New Game', 'text_domain' ),
		'add_new'             => __( 'Add New', 'text_domain' ),
		'edit_item'           => __( 'Edit Game', 'text_domain' ),
		'update_item'         => __( 'Update Game', 'text_domain' ),
		'search_items'        => __( 'Search Game', 'text_domain' ),
		'not_found'           => __( 'Not found', 'text_domain' ),
		'not_found_in_trash'  => __( 'Not found in Trash', 'text_domain' ),
	);
	$args = array(
		'label'               => __( 'game', 'text_domain' ),
		'description'         => __( 'World Cup Games', 'text_domain' ),
		'labels'              => $labels,
		'supports'            => array( 'title', 'custom-fields', ),
		'hierarchical'        => false,
		'public'              => true,
		'show_ui'             => true,
		'show_in_menu'        => true,
		'show_in_nav_menus'   => true,
		'show_in_admin_bar'   => true,
		'menu_position'       => 5,
		'menu_icon'           => 'dashicons-exerpt-view',
		'can_export'          => true,
		'has_archive'         => true,
		'exclude_from_search' => false,
		'publicly_queryable'  => true,
		'capability_type'     => 'page',
	);
	register_post_type( 'game', $args );

}

// Hook into the 'init' action
add_action( 'init', 'register_game_cpt', 0 );

View snippet Clone snippet Download snippet

The Data is save as:

  • Title – {Team a} {Team a score} vs. {Team b} {Team b score} – {Round}. (eg: Brazil 3 vs Croatia 1 – Round 1)
  • Custom Field – Team A (saved team term_id).
  • Custom Field – Team A score.
  • Custom Field – Team B (saved team term_id).
  • Custom Field – Team B score.

game post type

User Squad Custom Post type

Again to create the post type I used our Post Type Generator and we get:

// Register Custom Post Type
function register_user_squad_cpt() {

	$labels = array(
		'name'                => _x( 'Squads', 'Post Type General Name', 'text_domain' ),
		'singular_name'       => _x( 'Squad', 'Post Type Singular Name', 'text_domain' ),
		'menu_name'           => __( 'Squads', 'text_domain' ),
		'parent_item_colon'   => __( '', 'text_domain' ),
		'all_items'           => __( 'All Squads', 'text_domain' ),
		'view_item'           => __( 'View Squad', 'text_domain' ),
		'add_new_item'        => __( 'Add New Squad', 'text_domain' ),
		'add_new'             => __( 'Add New', 'text_domain' ),
		'edit_item'           => __( 'Edit Squad', 'text_domain' ),
		'update_item'         => __( 'Update Squad', 'text_domain' ),
		'search_items'        => __( 'Search Squad', 'text_domain' ),
		'not_found'           => __( 'Not found', 'text_domain' ),
		'not_found_in_trash'  => __( 'Not found in Trash', 'text_domain' ),
	);
	$args = array(
		'label'               => __( 'user_squad', 'text_domain' ),
		'description'         => __( 'User selected Squads', 'text_domain' ),
		'labels'              => $labels,
		'supports'            => array( 'title', 'custom-fields', ),
		'hierarchical'        => false,
		'public'              => true,
		'show_ui'             => true,
		'show_in_menu'        => true,
		'show_in_nav_menus'   => true,
		'show_in_admin_bar'   => true,
		'menu_position'       => 5,
		'menu_icon'           => 'dashicons-businessman',
		'can_export'          => true,
		'has_archive'         => true,
		'exclude_from_search' => false,
		'publicly_queryable'  => true,
		'capability_type'     => 'page',
	);
	register_post_type( 'user_squad', $args );

}

// Hook into the 'init' action
add_action( 'init', 'register_user_squad_cpt', 0 );

View snippet Clone snippet Download snippet

The Data is save as:

  • Title – User Squad name.
  • Custom Field – User draft pick * 14 (saved as player post_id)

user squad post type

Team Custom Taxonomy

To create the Taxonomy I used our Taxonomy Generator and we get:

// Register Custom Taxonomy
function reg_team_taxonomy() {

	$labels = array(
		'name'                       => _x( 'Teams', 'Taxonomy General Name', 'text_domain' ),
		'singular_name'              => _x( 'Team', 'Taxonomy Singular Name', 'text_domain' ),
		'menu_name'                  => __( 'Teams', 'text_domain' ),
		'all_items'                  => __( 'All Teams', 'text_domain' ),
		'parent_item'                => __( 'Team Group', 'text_domain' ),
		'parent_item_colon'          => __( 'Team Group:', 'text_domain' ),
		'new_item_name'              => __( 'New Team Name', 'text_domain' ),
		'add_new_item'               => __( 'Add New Team', 'text_domain' ),
		'edit_item'                  => __( 'Edit Team', 'text_domain' ),
		'update_item'                => __( 'Update Team', 'text_domain' ),
		'separate_items_with_commas' => __( 'Separate Teams with commas', 'text_domain' ),
		'search_items'               => __( 'Search Teams', 'text_domain' ),
		'add_or_remove_items'        => __( 'Add or remove Teams', 'text_domain' ),
		'choose_from_most_used'      => __( 'Choose from the most used Teams', 'text_domain' ),
		'not_found'                  => __( 'Not Found', 'text_domain' ),
	);
	$args = array(
		'labels'                     => $labels,
		'hierarchical'               => true,
		'public'                     => true,
		'show_ui'                    => true,
		'show_admin_column'          => true,
		'show_in_nav_menus'          => true,
		'show_tagcloud'              => true,
	);
	register_taxonomy( 'team', array( 'player', 'game' ), $args );

}

// Hook into the 'init' action
add_action( 'init', 'reg_team_taxonomy', 0 );

View snippet Clone snippet Download snippet
You can see that I connected this taxonomy with both player and game post types.
The Data is save as:

  • Top level – group name
    • Team a
    • Team b
    • Team c
    • Team d

Eg:

  • Group A
    • Brazil
    • Croatia
    • Mexico
    • Camerron

team taxponomy

The Challenge

After we got our structure ready and imported all of the data (players and teams) we moved on to the draft where each user pulled a number out of a hat and we started picking players one per turn to fill our squads with some of the best soccer players in the World.
Everything was done and ready with one day to go until the World Cup tournament started. (did I mention that we started planning and building all of this 3 days before it started?).

Anyway the only part missing is a way to save each player performance on each game (played, scored, received …) and what we came up with is to save each player performance in each game as an array of games with data as the player post meta eg:

[array] => [
	[game 1] => {
		'scored'   => 2,
		'received' => 1
	},
	[game 2] => {
		'scored'   => 1,
		'received' => 0
	}
]

Were game 1, game 2 … is the game post_id.
performance

The Results

After the data was build, imported and stored all we had to do was display it and we got something like this:
grouplist
user squad
player

The lesson learned

What did we get out of this? other then pure fun during each game, we learned that planning your data set structure is the most critical and important part of the development.
When we created the views, the points calculations and the charts we had all of the native WordPress functions at our disposal which made things go faster and easier.

We did (at one point) think of creating the user selected squads as a custom taxonomy which would have made the draft period go faster and be much simpler but when we stated thinking about performance it was clear that we went the right way.

Is it the best way to do it? I really can’t say but I can say that this way was fast, easy, it works very well and we got to enjoy the World Cup in a whole new way.

Share on facebook
Share on twitter
Share on linkedin
Share on reddit
Share on whatsapp
Share on email
Ohad Raz

Ohad Raz

A WordPress Consultant, a plugins and themes developer,WordPress Development moderator and sometimes a WordPress core contributor.

12 Comments:

  • Alberto

    Hi Ohad!
    Thanks for the good article.
    One curiosity, how do you created the custom fields?

    Reply

  • Alberto

    Rami, I’ve understood that you are developing a “Code Generator” I was only asking if it was connected in some way with https://github.com/bainternet/Tax-Meta-Class.

    Anyway, thanks for your work!
    Bye!

    Reply

  • Pranav

    Firstly, thanks for the wonderful article. It’s an amazing case study to go through.

    It may sound as an amateur question, but I was just wondering did it enable users to visit the site and create their own team? You mentioned, “Where each user pulled a number out of a hat and we started picking players one per turn to fill our squads with some of the best soccer players in the World.” that implies the competition was within the same squad but more against individual players.

    And if the users were in fact able to compete against each other using their own unique team, then was it available on the front end. Since it seems the assigning of the squads, players etc was done at the complete back-end.

    Thanks and Regards,
    Pranav

    Reply

    • Ohad Raz

      The draft period was ran by an administrator which assigned the selected players to each user in the back-end and that same administrator was in charge of updating game scores during the World Cup.
      There was no user vs user games, just general games where the users collected points gained by the players they have picked and the Front-end reflected just that.

      Reply

  • deepak282886

    sir where to put these codes please help me i am a beginner.

    Reply

  • Bill

    Hello, i would like to know, if you could help me build a plugin a little different from the one you have created. F.e. instead of games -> fixtures, Teams not necessary, and some more fields for awarding points. Thank you in advance!

    Reply

  • Danny

    Hi @Deepak282886 did you ever find out where to put the codes? I’m in a similar position and would love to use this on my own WordPress site. Also I’d love to know if anyone has got this running on their own site?

    Reply

Leave a Reply

Subscribe to our Blog

Receive email notifications when new blog posts are published.